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; 52 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: 65 LowpanDeviceCallback(int fd): mFd(fd), mOpenError(-1) {} 66 virtual ~LowpanDeviceCallback() = default; 67 68 int waitForOpenStatus() { 69 std::unique_lock<std::mutex> lock(mMutex); 70 if (mOpenError == -1) { 71 mConditionVariable.wait(lock); 72 } 73 return mOpenError; 74 } 75 76 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 111 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: 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 188 virtual ~ReadThread() {} 189 190 private: 191 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 217 void feedBytes(const uint8_t* dataPtr, ssize_t dataLen) { 218 while(dataLen--) { 219 feedByte(*dataPtr++); 220 } 221 } 222 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 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 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