Home | History | Annotate | Download | only in lowpan_hdlc_adapter
      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