• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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