1 /*
2 * Copyright (C) 2017 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 #define LOG_TAG "lowpan-hdlc-adapter"
18
19 #include "hdlc_lite.h"
20
21 #include <unistd.h>
22
23 #include <mutex>
24 #include <condition_variable>
25
26 #include <hidl/HidlTransportSupport.h>
27 #include <hidl/ServiceManagement.h>
28 #include <hidl/Status.h>
29 #include <hardware/hardware.h>
30 #include <utils/Thread.h>
31 #include <utils/Errors.h>
32 #include <utils/StrongPointer.h>
33 #include <log/log.h>
34 #include <android/hardware/lowpan/1.0/ILowpanDevice.h>
35 #include <android/hidl/manager/1.0/IServiceManager.h>
36
37 #define LOWPAN_HDLC_ADAPTER_MAX_FRAME_SIZE 2048
38
39 using ::android::hardware::Return;
40 using ::android::hardware::Void;
41 using ::android::hardware::configureRpcThreadpool;
42 using ::android::hardware::hidl_death_recipient;
43 using ::android::hardware::hidl_string;
44 using ::android::hardware::hidl_vec;
45 using ::android::hardware::joinRpcThreadpool;
46 using ::android::sp;
47 using namespace ::android::hardware::lowpan::V1_0;
48 using namespace ::android;
49
50 struct LowpanDeathRecipient : hidl_death_recipient {
51 LowpanDeathRecipient() = default;
serviceDiedLowpanDeathRecipient52 virtual void serviceDied(uint64_t /*cookie*/, const wp<::android::hidl::base::V1_0::IBase>& /*who*/) {
53 ALOGE("LowpanDevice died");
54 exit(EXIT_FAILURE);
55 }
56 };
57
58 struct LowpanDeviceCallback : public ILowpanDeviceCallback {
59 int mFd;
60 std::mutex mMutex;
61 std::condition_variable mConditionVariable;
62 int mOpenError;
63 static const uint32_t kMaxFrameSize = LOWPAN_HDLC_ADAPTER_MAX_FRAME_SIZE;
64 public:
LowpanDeviceCallbackLowpanDeviceCallback65 LowpanDeviceCallback(int fd): mFd(fd), mOpenError(-1) {}
66 virtual ~LowpanDeviceCallback() = default;
67
waitForOpenStatusLowpanDeviceCallback68 int waitForOpenStatus() {
69 std::unique_lock<std::mutex> lock(mMutex);
70 if (mOpenError == -1) {
71 mConditionVariable.wait(lock);
72 }
73 return mOpenError;
74 }
75
onReceiveFrameLowpanDeviceCallback76 Return<void> onReceiveFrame(const hidl_vec<uint8_t>& data) override {
77 if (data.size() > kMaxFrameSize) {
78 ALOGE("TOOBIG: Frame received from device is too big");
79 return Return<void>();
80 }
81
82 int bufferIndex = 0;
83 uint16_t fcs = kHdlcCrcResetValue;
84 uint8_t buffer[kMaxFrameSize*2 + 5]; // every character escaped, escaped crc, and frame marker
85 uint8_t c;
86
87 for (size_t i = 0; i < data.size(); i++)
88 {
89 c = data[i];
90 fcs = hdlc_crc16(fcs, c);
91 bufferIndex += hdlc_write_byte(buffer + bufferIndex, c);
92 }
93
94 fcs = hdlc_crc16_finalize(fcs);
95
96 bufferIndex += hdlc_write_byte(buffer + bufferIndex, uint8_t(fcs & 0xFF));
97 bufferIndex += hdlc_write_byte(buffer + bufferIndex, uint8_t((fcs >> 8) & 0xFF));
98
99 buffer[bufferIndex++] = HDLC_BYTE_FLAG;
100
101 std::unique_lock<std::mutex> lock(mMutex);
102
103 if (write(mFd, buffer, bufferIndex) != bufferIndex) {
104 ALOGE("IOFAIL: write: %s (%d)", strerror(errno), errno);
105 exit(EXIT_FAILURE);
106 }
107
108 return Return<void>();
109 }
110
onEventLowpanDeviceCallback111 Return<void> onEvent(LowpanEvent event, LowpanStatus status) override {
112 std::unique_lock<std::mutex> lock(mMutex);
113
114 switch (event) {
115 case LowpanEvent::OPENED:
116 if (mOpenError == -1) {
117 mOpenError = 0;
118 mConditionVariable.notify_all();
119 }
120 ALOGI("Device opened");
121 break;
122
123 case LowpanEvent::CLOSED:
124 ALOGI("Device closed");
125 exit(EXIT_SUCCESS);
126 break;
127
128 case LowpanEvent::RESET:
129 ALOGI("Device reset");
130 break;
131
132 case LowpanEvent::ERROR:
133 if (mOpenError == -1) {
134 mOpenError = int(status);
135 mConditionVariable.notify_all();
136 }
137 switch (status) {
138 case LowpanStatus::IOFAIL:
139 ALOGE("IOFAIL: Input/Output error from device. Terminating.");
140 exit(EXIT_FAILURE);
141 break;
142 case LowpanStatus::GARBAGE:
143 ALOGW("GARBAGE: Bad frame from device.");
144 break;
145 case LowpanStatus::TOOBIG:
146 ALOGW("TOOBIG: Device sending frames that are too large.");
147 break;
148 default:
149 ALOGW("Unknown error %d", status);
150 break;
151 }
152 break;
153 }
154 return Return<void>();
155 }
156 };
157
158 class ReadThread : public Thread {
159 int kReadThreadBufferSize;
160
161 sp<ILowpanDevice> mService;
162 int mFd;
163 uint8_t mBuffer[LOWPAN_HDLC_ADAPTER_MAX_FRAME_SIZE];
164 int mBufferIndex;
165 bool mUnescapeNextByte;
166 uint16_t mFcs;
167 sp<LowpanDeviceCallback> mCallback;
168
169 public:
ReadThread(sp<ILowpanDevice> service,int fd,sp<LowpanDeviceCallback> callback)170 ReadThread(sp<ILowpanDevice> service, int fd, sp<LowpanDeviceCallback> callback):
171 Thread(false /*canCallJava*/),
172 kReadThreadBufferSize(service->getMaxFrameSize()),
173 mService(service),
174 mFd(fd),
175 mBufferIndex(0),
176 mUnescapeNextByte(false),
177 mFcs(kHdlcCrcResetValue),
178 mCallback(callback) {
179 if (kReadThreadBufferSize < 16) {
180 ALOGE("Device returned bad max frame size: %d bytes", kReadThreadBufferSize);
181 exit(EXIT_FAILURE);
182 }
183 if ((size_t)kReadThreadBufferSize > sizeof(mBuffer)) {
184 kReadThreadBufferSize = (int)sizeof(mBuffer);
185 }
186 }
187
~ReadThread()188 virtual ~ReadThread() {}
189
190 private:
191
threadLoop()192 bool threadLoop() override {
193 uint8_t buffer[LOWPAN_HDLC_ADAPTER_MAX_FRAME_SIZE];
194
195 if (int error = mCallback->waitForOpenStatus()) {
196 ALOGE("Call to `open()` failed: %d", error);
197 exit(EXIT_FAILURE);
198 }
199
200 while (!exitPending()) {
201 ssize_t bytesRead = read(mFd, buffer, sizeof(buffer));
202 if (exitPending()) {
203 break;
204 }
205
206 if (bytesRead < 0) {
207 ALOGE("IOFAIL: read: %s (%d)", strerror(errno), errno);
208 exit(EXIT_FAILURE);
209 break;
210 }
211 feedBytes(buffer, bytesRead);
212 }
213
214 return false;
215 }
216
feedBytes(const uint8_t * dataPtr,ssize_t dataLen)217 void feedBytes(const uint8_t* dataPtr, ssize_t dataLen) {
218 while(dataLen--) {
219 feedByte(*dataPtr++);
220 }
221 }
222
sendFrame(uint8_t * p_data,uint16_t data_len)223 void sendFrame(uint8_t* p_data, uint16_t data_len) {
224 hidl_vec<uint8_t> data;
225 data.setToExternal(p_data, data_len);
226 mService->sendFrame(data);
227 }
228
feedByte(uint8_t byte)229 void feedByte(uint8_t byte) {
230 if (mBufferIndex >= kReadThreadBufferSize) {
231 ALOGE("TOOBIG: HDLC frame too big (Max: %d)", kReadThreadBufferSize);
232 mUnescapeNextByte = false;
233 mBufferIndex = 0;
234 mFcs = kHdlcCrcResetValue;
235
236 } else if (byte == HDLC_BYTE_FLAG) {
237 if (mBufferIndex <= 2) {
238 // Ignore really small frames.
239 // Don't remove this or we will underflow our
240 // index for onReceiveFrame(), below!
241
242 } else if (mUnescapeNextByte || (mFcs != kHdlcCrcCheckValue)) {
243 ALOGE("GARBAGE: HDLC frame with bad CRC (LEN:%d, mFcs:0x%04X)", mBufferIndex, mFcs);
244
245 } else {
246 // -2 for CRC
247 sendFrame(mBuffer, uint16_t(mBufferIndex - 2));
248 }
249
250 mUnescapeNextByte = false;
251 mBufferIndex = 0;
252 mFcs = kHdlcCrcResetValue;
253
254 } else if (byte == HDLC_BYTE_ESC) {
255 mUnescapeNextByte = true;
256
257 } else if (hdlc_byte_needs_escape(byte)) {
258 // Skip all other control codes.
259
260 } else {
261 if (mUnescapeNextByte) {
262 byte = byte ^ HDLC_ESCAPE_XFORM;
263 mUnescapeNextByte = false;
264 }
265
266 mFcs = hdlc_crc16(mFcs, byte);
267 mBuffer[mBufferIndex++] = byte;
268 }
269 }
270 };
271
main(int argc,char * argv[])272 int main(int argc, char* argv []) {
273 using ::android::hardware::defaultServiceManager;
274 using Transport = ::android::hidl::manager::V1_0::IServiceManager::Transport;
275
276 const char* serviceName = "default";
277
278 if (argc >= 2) {
279 serviceName = argv[1];
280 }
281
282 sp<ILowpanDevice> service = ILowpanDevice::getService(serviceName, false /* getStub */);
283
284 if (service == nullptr) {
285 ALOGE("Unable to find LowpanDevice named \"%s\"", serviceName);
286 exit(EXIT_FAILURE);
287 }
288
289 service->linkToDeath(new LowpanDeathRecipient(), 0 /*cookie*/);
290
291 configureRpcThreadpool(1, true /* callerWillJoin */);
292
293 sp<LowpanDeviceCallback> callback = new LowpanDeviceCallback(STDOUT_FILENO);
294
295 {
296 auto status = service->open(callback);
297 if (status.isOk()) {
298 if (status == LowpanStatus::OK) {
299 ALOGD("%s: open() ok.", serviceName);
300 } else {
301 ALOGE("%s: open() failed: (%d).", serviceName, LowpanStatus(status));
302 exit(EXIT_FAILURE);
303 }
304 } else {
305 ALOGE("%s: open() failed: transport error", serviceName);
306 exit(EXIT_FAILURE);
307 }
308 }
309
310 sp<Thread> readThread = new ReadThread(service, STDIN_FILENO, callback);
311
312 readThread->run("ReadThread");
313
314 joinRpcThreadpool();
315
316 ALOGI("Shutting down");
317
318 return EXIT_SUCCESS;
319 }
320