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