summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Quattlebaum <rquattle@google.com>2017-07-26 16:57:25 -0700
committerRobert Quattlebaum <rquattle@google.com>2017-10-02 12:56:26 -0700
commit1572e354fd9654ae8cb19caa7b8f88877586f362 (patch)
treeae99fb37fdc6487de670bc68d1248b76660a2cc8
parent88e93c19f24d9b4c9e1799111c5e1820cb24bcee (diff)
downloadlowpan-1572e354fd9654ae8cb19caa7b8f88877586f362.tar.gz
lowpan_hdlc_adapter: New HAL adapter for wpantund
This tool is designed to quickly enable wpantund to use the ILowpanDevice HAL, introduced in change Ib4a1ee35d1a02e3a38be406825bae3fdf9b0072f. Long term, this capability should be built into wpantund, making this tool obsolete. This change also includes related updates to the product makefiles. Bug: b/64090883 Change-Id: I9d7a80df20c69b8c335b88d984d9f8909d483fcd
-rw-r--r--build/lowpan-hal-default.mk18
-rw-r--r--build/lowpan.mk1
-rw-r--r--build/wpantund.mk6
-rw-r--r--build/wpantund.rc13
-rw-r--r--lowpan_hdlc_adapter/Android.mk39
-rw-r--r--lowpan_hdlc_adapter/hdlc_lite.c97
-rw-r--r--lowpan_hdlc_adapter/hdlc_lite.h57
-rw-r--r--lowpan_hdlc_adapter/lowpan_hdlc_adapter.cpp279
8 files changed, 510 insertions, 0 deletions
diff --git a/build/lowpan-hal-default.mk b/build/lowpan-hal-default.mk
new file mode 100644
index 0000000..923139f
--- /dev/null
+++ b/build/lowpan-hal-default.mk
@@ -0,0 +1,18 @@
+#
+## Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+PRODUCT_PACKAGES += \
+ android.hardware.lowpan@1.0-service
+
diff --git a/build/lowpan.mk b/build/lowpan.mk
index d1881d2..ebe855e 100644
--- a/build/lowpan.mk
+++ b/build/lowpan.mk
@@ -14,4 +14,5 @@
# limitations under the License.
include frameworks/opt/net/lowpan/build/lowpan-service.mk
+include frameworks/opt/net/lowpan/build/lowpan-hal-default.mk
include frameworks/opt/net/lowpan/build/wpantund.mk
diff --git a/build/wpantund.mk b/build/wpantund.mk
index e21ffa2..2e504c1 100644
--- a/build/wpantund.mk
+++ b/build/wpantund.mk
@@ -15,3 +15,9 @@
PRODUCT_PACKAGES += \
wpantund
+
+PRODUCT_COPY_FILES += \
+ frameworks/opt/net/lowpan/build/wpantund.rc:system/etc/init/wpantund.rc
+
+PRODUCT_PACKAGES += \
+ lowpan_hdlc_adapter
diff --git a/build/wpantund.rc b/build/wpantund.rc
new file mode 100644
index 0000000..f63369e
--- /dev/null
+++ b/build/wpantund.rc
@@ -0,0 +1,13 @@
+service wpantund /system/bin/wpantund -s ${ro.lowpan.wpantund.socket:-system:/system/bin/lowpan_hdlc_adapter} -o Config:Daemon:ExternalNetifManagement 1 -I ${lowpan.interface:-wpan0}
+ disabled
+ class main
+ user lowpan
+ group lowpan inet vpn
+ capabilities NET_ADMIN NET_RAW
+ setenv SHELL /system/bin/sh
+
+on property:ro.lowpan.hal.device=*
+ enable lowpan_hal_1_0
+
+on property:init.svc.lowpan_hal_1_0=running
+ enable wpantund
diff --git a/lowpan_hdlc_adapter/Android.mk b/lowpan_hdlc_adapter/Android.mk
new file mode 100644
index 0000000..ece994c
--- /dev/null
+++ b/lowpan_hdlc_adapter/Android.mk
@@ -0,0 +1,39 @@
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := lowpan_hdlc_adapter
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := \
+ lowpan_hdlc_adapter.cpp \
+ hdlc_lite.c
+
+LOCAL_SHARED_LIBRARIES := \
+ liblog \
+ libcutils \
+ libdl \
+ libbase \
+ libutils \
+ libhardware
+
+LOCAL_SHARED_LIBRARIES += \
+ libhidlbase \
+ libhidltransport \
+ android.hardware.lowpan@1.0
+
+include $(BUILD_EXECUTABLE)
diff --git a/lowpan_hdlc_adapter/hdlc_lite.c b/lowpan_hdlc_adapter/hdlc_lite.c
new file mode 100644
index 0000000..39cd202
--- /dev/null
+++ b/lowpan_hdlc_adapter/hdlc_lite.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hdlc_lite.h"
+
+uint16_t hdlc_crc16(uint16_t aFcs, uint8_t aByte)
+{
+ // CRC-16/CCITT, CRC-16/CCITT-TRUE, CRC-CCITT
+ // width=16 poly=0x1021 init=0x0000 refin=true refout=true xorout=0x0000 check=0x2189 name="KERMIT"
+ // http://reveng.sourceforge.net/crc-catalogue/16.htm#crc.cat.kermit
+ static const uint16_t sFcsTable[256] =
+ {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+ };
+ return (aFcs >> 8) ^ sFcsTable[(aFcs ^ aByte) & 0xff];
+}
+
+uint16_t hdlc_crc16_finalize(uint16_t fcs)
+{
+ return fcs ^ 0xFFFF;
+}
+
+bool hdlc_byte_needs_escape(uint8_t byte)
+{
+ switch(byte)
+ {
+ case HDLC_BYTE_SPECIAL:
+ case HDLC_BYTE_ESC:
+ case HDLC_BYTE_FLAG:
+ case HDLC_BYTE_XOFF:
+ case HDLC_BYTE_XON:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+int hdlc_write_byte(uint8_t* out_buffer, uint8_t byte)
+{
+ int ret = 1;
+
+ if (hdlc_byte_needs_escape(byte))
+ {
+ *out_buffer++ = HDLC_BYTE_ESC;
+ ret++;
+ byte = byte ^ HDLC_ESCAPE_XFORM;
+ }
+
+ *out_buffer = byte;
+
+ return ret;
+}
diff --git a/lowpan_hdlc_adapter/hdlc_lite.h b/lowpan_hdlc_adapter/hdlc_lite.h
new file mode 100644
index 0000000..4d4d554
--- /dev/null
+++ b/lowpan_hdlc_adapter/hdlc_lite.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HEADER_HDLC_LITE_H_INCLUDED
+#define HEADER_HDLC_LITE_H_INCLUDED 1
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+static const uint8_t kHdlcResetSignal[] = { 0x7E, 0x13, 0x11, 0x7E };
+static const uint16_t kHdlcCrcCheckValue = 0xf0b8;
+static const uint16_t kHdlcCrcResetValue = 0xffff;
+
+#define HDLC_BYTE_FLAG 0x7E
+#define HDLC_BYTE_ESC 0x7D
+#define HDLC_BYTE_XON 0x11
+#define HDLC_BYTE_XOFF 0x13
+#define HDLC_BYTE_SPECIAL 0xF8
+#define HDLC_ESCAPE_XFORM 0x20
+
+/** HDLC CRC function */
+extern uint16_t hdlc_crc16(uint16_t fcs, uint8_t byte);
+
+/** HDLC CRC Finalize function */
+extern uint16_t hdlc_crc16_finalize(uint16_t fcs);
+
+/** Returns true if the byte needs to be escaped, false otherwise */
+extern bool hdlc_byte_needs_escape(uint8_t byte);
+
+/**
+ * Writes one or two HDLC-encoded bytes to out_buffer,
+ * and returns how many bytes were written.
+ */
+extern int hdlc_write_byte(uint8_t* out_buffer, uint8_t byte);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif // HEADER_HDLC_LITE_H_INCLUDED
diff --git a/lowpan_hdlc_adapter/lowpan_hdlc_adapter.cpp b/lowpan_hdlc_adapter/lowpan_hdlc_adapter.cpp
new file mode 100644
index 0000000..c47094b
--- /dev/null
+++ b/lowpan_hdlc_adapter/lowpan_hdlc_adapter.cpp
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "lowpan-hdlc-adapter"
+
+#include "hdlc_lite.h"
+
+#include <unistd.h>
+
+#include <hidl/HidlTransportSupport.h>
+#include <hidl/ServiceManagement.h>
+#include <hidl/Status.h>
+#include <hardware/hardware.h>
+#include <utils/Thread.h>
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+#include <log/log.h>
+#include <android/hardware/lowpan/1.0/ILowpanDevice.h>
+#include <android/hidl/manager/1.0/IServiceManager.h>
+
+#define LOWPAN_HDLC_ADAPTER_MAX_FRAME_SIZE 2048
+
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::configureRpcThreadpool;
+using ::android::hardware::hidl_death_recipient;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::joinRpcThreadpool;
+using ::android::sp;
+using namespace ::android::hardware::lowpan::V1_0;
+using namespace ::android;
+
+struct LowpanDeathRecipient : hidl_death_recipient {
+ LowpanDeathRecipient() = default;
+ virtual void serviceDied(uint64_t /*cookie*/, const wp<::android::hidl::base::V1_0::IBase>& /*who*/) {
+ ALOGE("LowpanDevice died");
+ exit(EXIT_FAILURE);
+ }
+};
+
+struct LowpanDeviceCallback : public ILowpanDeviceCallback {
+ int mFd;
+ static const uint32_t kMaxFrameSize = LOWPAN_HDLC_ADAPTER_MAX_FRAME_SIZE;
+public:
+ LowpanDeviceCallback(int fd): mFd(fd) { }
+ virtual ~LowpanDeviceCallback() = default;
+
+ Return<void> onReceiveFrame(const hidl_vec<uint8_t>& data) override {
+ if (data.size() > kMaxFrameSize) {
+ ALOGE("TOOBIG: Frame received from device is too big");
+ return Return<void>();
+ }
+
+ int bufferIndex = 0;
+ uint16_t fcs = kHdlcCrcResetValue;
+ uint8_t buffer[kMaxFrameSize*2 + 5]; // every character escaped, escaped crc, and frame marker
+ uint8_t c;
+
+ for (size_t i = 0; i < data.size(); i++)
+ {
+ c = data[i];
+ fcs = hdlc_crc16(fcs, c);
+ bufferIndex += hdlc_write_byte(buffer + bufferIndex, c);
+ }
+
+ fcs = hdlc_crc16_finalize(fcs);
+
+ bufferIndex += hdlc_write_byte(buffer + bufferIndex, uint8_t(fcs & 0xFF));
+ bufferIndex += hdlc_write_byte(buffer + bufferIndex, uint8_t((fcs >> 8) & 0xFF));
+
+ buffer[bufferIndex++] = HDLC_BYTE_FLAG;
+
+ if (write(mFd, buffer, bufferIndex) != bufferIndex) {
+ ALOGE("IOFAIL: write: %s (%d)", strerror(errno), errno);
+ exit(EXIT_FAILURE);
+ }
+
+ return Return<void>();
+ }
+
+ Return<void> onEvent(LowpanEvent event, LowpanStatus status) override {
+ switch (event) {
+ case LowpanEvent::OPENED:
+ ALOGI("Device opened");
+ break;
+
+ case LowpanEvent::CLOSED:
+ ALOGI("Device closed");
+ break;
+
+ case LowpanEvent::RESET:
+ ALOGI("Device reset");
+ break;
+
+ case LowpanEvent::ERROR:
+ switch (status) {
+ case LowpanStatus::IOFAIL:
+ ALOGE("IOFAIL: Input/Output error from device. Terminating.");
+ exit(EXIT_FAILURE);
+ break;
+ case LowpanStatus::GARBAGE:
+ ALOGW("GARBAGE: Bad frame from device.");
+ break;
+ case LowpanStatus::TOOBIG:
+ ALOGW("TOOBIG: Device sending frames that are too large.");
+ break;
+ default:
+ ALOGW("Unknown error %d", status);
+ break;
+ }
+ break;
+ }
+ return Return<void>();
+ }
+};
+
+class ReadThread : public Thread {
+ int kReadThreadBufferSize;
+
+ sp<ILowpanDevice> mService;
+ int mFd;
+ uint8_t mBuffer[LOWPAN_HDLC_ADAPTER_MAX_FRAME_SIZE];
+ int mBufferIndex;
+ bool mUnescapeNextByte;
+ uint16_t mFcs;
+
+public:
+ ReadThread(sp<ILowpanDevice> service, int fd):
+ Thread(false /*canCallJava*/), kReadThreadBufferSize(service->getMaxFrameSize()), mService(service), mFd(fd), mBufferIndex(0), mUnescapeNextByte(false),mFcs(kHdlcCrcResetValue) {
+ if (kReadThreadBufferSize < 16) {
+ ALOGE("Device returned bad max frame size: %d bytes", kReadThreadBufferSize);
+ exit(EXIT_FAILURE);
+ }
+ if ((size_t)kReadThreadBufferSize > sizeof(mBuffer)) {
+ kReadThreadBufferSize = (int)sizeof(mBuffer);
+ }
+ }
+
+ virtual ~ReadThread() {}
+
+private:
+
+ bool threadLoop() override {
+ uint8_t buffer[LOWPAN_HDLC_ADAPTER_MAX_FRAME_SIZE];
+
+ while (!exitPending()) {
+ ssize_t bytesRead = read(mFd, buffer, sizeof(buffer));
+ if (exitPending()) {
+ break;
+ }
+
+ if (bytesRead < 0) {
+ ALOGE("IOFAIL: read: %s (%d)", strerror(errno), errno);
+ exit(EXIT_FAILURE);
+ break;
+ }
+ feedBytes(buffer, bytesRead);
+ }
+
+ return false;
+ }
+
+ void feedBytes(const uint8_t* dataPtr, ssize_t dataLen) {
+ while(dataLen--) {
+ feedByte(*dataPtr++);
+ }
+ }
+
+ void sendFrame(uint8_t* p_data, uint16_t data_len) {
+ hidl_vec<uint8_t> data;
+ data.setToExternal(p_data, data_len);
+ mService->sendFrame(data);
+ }
+
+ void feedByte(uint8_t byte) {
+ if (mBufferIndex >= kReadThreadBufferSize) {
+ ALOGE("TOOBIG: HDLC frame too big (Max: %d)", kReadThreadBufferSize);
+ mUnescapeNextByte = false;
+ mBufferIndex = 0;
+ mFcs = kHdlcCrcResetValue;
+
+ } else if (byte == HDLC_BYTE_FLAG) {
+ if (mBufferIndex <= 2) {
+ // Ignore really small frames.
+ // Don't remove this or we will underflow our
+ // index for onReceiveFrame(), below!
+
+ } else if (mUnescapeNextByte || (mFcs != kHdlcCrcCheckValue)) {
+ ALOGE("GARBAGE: HDLC frame with bad CRC (LEN:%d, mFcs:0x%04X)", mBufferIndex, mFcs);
+
+ } else {
+ // -2 for CRC
+ sendFrame(mBuffer, uint16_t(mBufferIndex - 2));
+ }
+
+ mUnescapeNextByte = false;
+ mBufferIndex = 0;
+ mFcs = kHdlcCrcResetValue;
+
+ } else if (byte == HDLC_BYTE_ESC) {
+ mUnescapeNextByte = true;
+
+ } else if (hdlc_byte_needs_escape(byte)) {
+ // Skip all other control codes.
+
+ } else {
+ if (mUnescapeNextByte) {
+ byte = byte ^ HDLC_ESCAPE_XFORM;
+ mUnescapeNextByte = false;
+ }
+
+ mFcs = hdlc_crc16(mFcs, byte);
+ mBuffer[mBufferIndex++] = byte;
+ }
+ }
+};
+
+int main(int argc, char* argv []) {
+ using ::android::hardware::defaultServiceManager;
+ using Transport = ::android::hidl::manager::V1_0::IServiceManager::Transport;
+
+ const char* serviceName = "default";
+
+ if (argc >= 2) {
+ serviceName = argv[1];
+ }
+
+ sp<ILowpanDevice> service = ILowpanDevice::getService(serviceName, false /* getStub */);
+
+ if (service == nullptr) {
+ ALOGE("Unable to find LowpanDevice named \"%s\"", serviceName);
+ exit(EXIT_FAILURE);
+ }
+
+ service->linkToDeath(new LowpanDeathRecipient(), 0 /*cookie*/);
+
+ configureRpcThreadpool(1, true /* callerWillJoin */);
+
+ sp<ILowpanDeviceCallback> callback = new LowpanDeviceCallback(STDOUT_FILENO);
+
+ {
+ auto status = service->open(callback);
+ if (status.isOk()) {
+ if (status == LowpanStatus::OK) {
+ ALOGD("%s: open() ok.", serviceName);
+ } else {
+ ALOGE("%s: open() failed: (%d).", serviceName, LowpanStatus(status));
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ ALOGE("%s: open() failed: transport error", serviceName);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ sp<Thread> readThread = new ReadThread(service, STDIN_FILENO);
+
+ readThread->run("ReadThread");
+
+ joinRpcThreadpool();
+
+ ALOGI("Shutting down");
+
+ return EXIT_SUCCESS;
+}