1 /*
2 * Copyright 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <fcntl.h>
18 #include <chrono>
19
20 #include "casimir_controller.h"
21
22 namespace cuttlefish {
23
24 using namespace casimir::rf;
25 using namespace std::literals::chrono_literals;
26 using pdl::packet::slice;
27
Init(int casimir_rf_port)28 Result<void> CasimirController::Init(int casimir_rf_port) {
29 CF_EXPECT(!sock_->IsOpen());
30
31 sock_ = cuttlefish::SharedFD::SocketLocalClient(casimir_rf_port, SOCK_STREAM);
32 CF_EXPECT(sock_->IsOpen(),
33 "Failed to connect to casimir with RF port" << casimir_rf_port);
34
35 int flags = sock_->Fcntl(F_GETFL, 0);
36 CF_EXPECT_GE(flags, 0, "Failed to get FD flags of casimir socket");
37 CF_EXPECT_EQ(sock_->Fcntl(F_SETFL, flags | O_NONBLOCK), 0,
38 "Failed to set casimir socket nonblocking");
39 return {};
40 }
41
SelectNfcA()42 Result<uint16_t> CasimirController::SelectNfcA() {
43 PollCommandBuilder poll_command;
44 poll_command.technology_ = Technology::NFC_A;
45 CF_EXPECT(Write(poll_command), "Failed to send NFC-A poll command");
46
47 auto res = CF_EXPECT(ReadRfPacket(10s), "Failed to get NFC-A poll response");
48
49 auto rf_packet = RfPacketView::Create(slice(res));
50 if (rf_packet.IsValid()) {
51 auto poll_response = NfcAPollResponseView::Create(rf_packet);
52 if (poll_response.IsValid() && poll_response.GetIntProtocol() == 0b01) {
53 return poll_response.GetSender();
54 }
55 }
56 return CF_ERR("Invalid Poll-A response");
57 }
58
SelectT4AT(uint16_t sender_id)59 Result<void> CasimirController::SelectT4AT(uint16_t sender_id) {
60 T4ATSelectCommandBuilder t4at_select_command;
61 t4at_select_command.sender_ = sender_id;
62 t4at_select_command.param_ = 0;
63 CF_EXPECT(Write(t4at_select_command), "Failed to send T4AT select command");
64
65 auto res = CF_EXPECT(ReadRfPacket(1s), "Failed to get T4AT response");
66
67 // Note: T4AT select response implies NFC_A and ISO_DEP
68 auto rf_packet = RfPacketView::Create(slice(res));
69 if (rf_packet.IsValid()) {
70 auto select_response = T4ATSelectResponseView::Create(rf_packet);
71 if (select_response.IsValid() && select_response.GetSender() == sender_id) {
72 return {};
73 }
74 }
75 return CF_ERR("Invalid T4AT response");
76 }
77
Poll()78 Result<uint16_t> CasimirController::Poll() {
79 CF_EXPECT(sock_->IsOpen());
80
81 uint16_t sender_id = CF_EXPECT(SelectNfcA(), "Failed to select NFC-A");
82 CF_EXPECT(SelectT4AT(sender_id), "Failed to select T4AT");
83 return sender_id;
84 }
85
SendApdu(uint16_t receiver_id,const std::shared_ptr<std::vector<uint8_t>> & apdu)86 Result<std::shared_ptr<std::vector<uint8_t>>> CasimirController::SendApdu(
87 uint16_t receiver_id, const std::shared_ptr<std::vector<uint8_t>>& apdu) {
88 CF_EXPECT(sock_->IsOpen());
89
90 DataBuilder data_builder;
91 data_builder.data_ = *apdu.get();
92 data_builder.receiver_ = receiver_id;
93 data_builder.technology_ = Technology::NFC_A;
94 data_builder.protocol_ = Protocol::ISO_DEP;
95
96 CF_EXPECT(Write(data_builder), "Failed to send APDU bytes");
97
98 auto res = CF_EXPECT(ReadRfPacket(3s), "Failed to get APDU response");
99 auto rf_packet = RfPacketView::Create(slice(res));
100 if (rf_packet.IsValid()) {
101 auto data = DataView::Create(rf_packet);
102 if (data.IsValid() && rf_packet.GetSender() == receiver_id) {
103 return std::make_shared<std::vector<uint8_t>>(std::move(data.GetData()));
104 }
105 }
106 return CF_ERR("Invalid APDU response");
107 }
108
Write(const RfPacketBuilder & rf_packet)109 Result<void> CasimirController::Write(const RfPacketBuilder& rf_packet) {
110 std::vector<uint8_t> raw_bytes = rf_packet.SerializeToBytes();
111 uint16_t header_bytes_le = htole16(raw_bytes.size());
112 ssize_t written = WriteAll(sock_, reinterpret_cast<char*>(&header_bytes_le),
113 sizeof(header_bytes_le));
114 CF_EXPECT_EQ(written, sizeof(header_bytes_le),
115 "Failed to write packet header to casimir socket, errno="
116 << sock_->GetErrno());
117
118 written = WriteAll(sock_, reinterpret_cast<char*>(raw_bytes.data()),
119 raw_bytes.size());
120 CF_EXPECT_EQ(written, raw_bytes.size(),
121 "Failed to write packet payload to casimir socket, errno="
122 << sock_->GetErrno());
123
124 return {};
125 }
126
ReadExact(size_t size,std::chrono::milliseconds timeout)127 Result<std::shared_ptr<std::vector<uint8_t>>> CasimirController::ReadExact(
128 size_t size, std::chrono::milliseconds timeout) {
129 size_t total_read = 0;
130 auto out = std::make_shared<std::vector<uint8_t>>(size);
131 auto prev_time = std::chrono::steady_clock::now();
132 while (timeout.count() > 0) {
133 PollSharedFd poll_fd = {
134 .fd = sock_,
135 .events = EPOLLIN,
136 .revents = 0,
137 };
138 int res = sock_.Poll(&poll_fd, 1, timeout.count());
139 CF_EXPECT_GE(res, 0, "Failed to poll on the casimir socket");
140 CF_EXPECT_EQ(poll_fd.revents, EPOLLIN,
141 "Unexpected poll result for reading");
142
143 // Nonblocking read, so don't need to care about timeout.
144 ssize_t read =
145 sock_->Read((void*)&(out->data()[total_read]), size - total_read);
146 CF_EXPECT_GT(
147 read, 0,
148 "Failed to read from casimir socket, errno=" << sock_->GetErrno());
149
150 total_read += read;
151 if (total_read >= size) {
152 return out;
153 }
154
155 auto current_time = std::chrono::steady_clock::now();
156 timeout -= std::chrono::duration_cast<std::chrono::milliseconds>(
157 current_time - prev_time);
158 }
159
160 return CF_ERR("Failed to read from casimir socket; timed out");
161 }
162
163 // Note: Although rf_packets.h doesn't document nor include packet header,
164 // the header is necessary to know total packet size.
ReadRfPacket(std::chrono::milliseconds timeout)165 Result<std::shared_ptr<std::vector<uint8_t>>> CasimirController::ReadRfPacket(
166 std::chrono::milliseconds timeout) {
167 auto start_time = std::chrono::steady_clock::now();
168
169 auto res = CF_EXPECT(ReadExact(sizeof(uint16_t), timeout),
170 "Failed to read RF packet header");
171 slice packet_size_slice(res);
172 int16_t packet_size = packet_size_slice.read_le<uint16_t>();
173
174 auto current_time = std::chrono::steady_clock::now();
175 timeout -= std::chrono::duration_cast<std::chrono::milliseconds>(
176 current_time - start_time);
177 return CF_EXPECT(ReadExact(packet_size, timeout),
178 "Failed to read RF packet payload");
179 }
180
181 } // namespace cuttlefish
182