You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by ac...@apache.org on 2020/11/08 16:12:01 UTC
[incubator-nuttx-apps] 02/02: system: add Android Debug Bridge
daemon
This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx-apps.git
commit c1c488e835f7433ddec4de57024a4e3444359f96
Author: Simon Piriou <sp...@gmail.com>
AuthorDate: Mon Oct 26 17:18:48 2020 +0100
system: add Android Debug Bridge daemon
---
system/adb/Kconfig | 158 +++++++++++++++++++++++++++++
system/adb/Make.defs | 23 +++++
system/adb/Makefile | 92 +++++++++++++++++
system/adb/adb_banner.c | 117 ++++++++++++++++++++++
system/adb/adb_main.c | 119 ++++++++++++++++++++++
system/adb/logcat_service.c | 213 +++++++++++++++++++++++++++++++++++++++
system/adb/shell_pipe.c | 237 ++++++++++++++++++++++++++++++++++++++++++++
system/adb/shell_pipe.h | 56 +++++++++++
system/adb/shell_service.c | 209 ++++++++++++++++++++++++++++++++++++++
9 files changed, 1224 insertions(+)
diff --git a/system/adb/Kconfig b/system/adb/Kconfig
new file mode 100644
index 0000000..83a8019
--- /dev/null
+++ b/system/adb/Kconfig
@@ -0,0 +1,158 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+menuconfig SYSTEM_ADBD
+ tristate "ADB daemon application"
+ default n
+ ---help---
+ Enable support for adb daemon.
+
+if SYSTEM_ADBD
+
+config ADBD_PROGNAME
+ string "Program name"
+ default "adbd"
+ ---help---
+ This is the name of the program that will be used.
+
+config ADBD_STACKSIZE
+ int "Stack size"
+ default DEFAULT_TASK_STACKSIZE
+ ---help---
+ The size of stack allocated for the adb daemon task.
+
+config ADBD_PRIORITY
+ int "Task priority"
+ default 100
+ ---help---
+ The priority of the adb daemon task.
+
+config ADBD_AUTHENTICATION
+ bool "Authentication support"
+ default n
+ ---help---
+ Enable authentication for adb daemon.
+
+if ADBD_AUTHENTICATION
+
+config ADBD_AUTH_PUBKEY
+ bool "Public key authentication"
+ default n
+ ---help---
+ Enable hook to accept new public keys.
+
+config ADBD_TOKEN_SIZE
+ int "Authentication token size"
+ default 20
+
+endif # ADBD_AUTHENTICATION
+
+if ! BOARDCTL_UNIQUEID
+config ADBD_DEVICE_ID
+ string "Default adb device id"
+ default ""
+endif # BOARDCTL_UNIQUEID
+
+config ADBD_PRODUCT_NAME
+ string "Default adb product name"
+ default "adb dev"
+
+config ADBD_PRODUCT_MODEL
+ string "Default adb product model"
+ default "adb board"
+
+config ADBD_PRODUCT_DEVICE
+ string "Default adb product device"
+ default "NuttX device"
+
+config ADBD_FEATURES
+ string "Default adb server features list"
+ default "cmd"
+
+config ADBD_PAYLOAD_SIZE
+ int "Normal ADB frame size"
+ default 64
+ ---help---
+ Normal frame size in bytes.
+
+config ADBD_CNXN_PAYLOAD_SIZE
+ int "Connection frame size"
+ default 1024
+ ---help---
+ Connection frame is bigger than others.
+ Can be between 128 to 256 bytes in most of the cases.
+ If authentication is enabled, frame size must bigger
+ to receive public key from host (around 1024 bytes).
+
+config ADBD_FRAME_MAX
+ int "Frame pool size"
+ default 1
+ ---help---
+ ADB frame pool size.
+
+config ADBD_TCP_SERVER
+ bool "Network socket transport support"
+ depends on NET_TCP
+ default n
+ ---help---
+ Run adb daemon on network socket.
+
+config ADBD_TCP_SERVER_PORT
+ int "Network socket transport port"
+ depends on ADBD_TCP_SERVER
+ default 5555
+ ---help---
+ Port used by adb daemon socket server
+
+config ADBD_USB_SERVER
+ bool "USB transport support"
+ depends on USBADB
+ default n
+ ---help---
+ Run adb daemon on USB bus
+
+config ADBD_LOGCAT_SERVICE
+ bool "ADB logcat support"
+ depends on RAMLOG_SYSLOG
+ default n
+ ---help---
+ Enable "adb logcat" feature.
+
+config ADBD_FILE_SERVICE
+ bool "ADB file sync support"
+ default n
+ ---help---
+ Enable "adb ls/push/pull" feature.
+
+ config ADBD_SHELL_SERVICE
+ bool "ADB shell support"
+ depends on NSH_CONSOLE
+ default n
+ ---help---
+ Enable "adb shell" feature.
+
+config ADBD_FILE_SYMLINK
+ bool "File service symlink support"
+ default n
+ depends on PSEUDOFS_SOFTLINKS
+ ---help---
+ Enable fs symlink support.
+
+config ADBD_BOARD_INIT
+ bool "Board initialization"
+ depends on LIB_BOARDCTL
+ default n
+ ---help---
+ Setup board before running adb daemon.
+
+config ADBD_NET_INIT
+ bool "Network initialization"
+ default n
+ depends on NET
+ select NETUTILS_NETINIT
+ ---help---
+ This option enables/disables all network initialization in ADB server.
+
+endif # SYSTEM_ADBD
diff --git a/system/adb/Make.defs b/system/adb/Make.defs
new file mode 100644
index 0000000..ead70b7
--- /dev/null
+++ b/system/adb/Make.defs
@@ -0,0 +1,23 @@
+############################################################################
+# system/adb/Make.defs
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership. The
+# ASF licenses this file to you 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.
+#
+############################################################################
+
+ifneq ($(CONFIG_SYSTEM_ADBD),)
+CONFIGURED_APPS += $(APPDIR)/system/adb
+endif
diff --git a/system/adb/Makefile b/system/adb/Makefile
new file mode 100644
index 0000000..f5e7778
--- /dev/null
+++ b/system/adb/Makefile
@@ -0,0 +1,92 @@
+############################################################################
+# system/adb/Makefile
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership. The
+# ASF licenses this file to you 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 $(APPDIR)/Make.defs
+
+ADB_DIR := $(APPDIR)/system/adb
+CONFIG_ADBD_URL ?= "https://github.com/spiriou/microADB.git"
+CONFIG_ADBD_VERSION ?= bbd1e74bd795aa2fc53eae2b76bff993d6ccaa37
+
+ADB_UNPACKNAME := microADB
+ADB_UNPACKDIR := $(ADB_DIR)/$(ADB_UNPACKNAME)
+
+$(ADB_UNPACKDIR):
+ @echo "Downloading: $(ADB_UNPACKNAME)"
+ $(call DELDIR, "$@")
+ $(Q) mkdir "$@"
+ $(Q) cd "$@" && git init && \
+ git remote add origin "$(CONFIG_ADBD_URL)" && \
+ git fetch origin $(CONFIG_ADBD_VERSION) --depth=1 && \
+ git reset --hard FETCH_HEAD
+
+# adb server app
+
+PROGNAME := $(CONFIG_ADBD_PROGNAME)
+PRIORITY := $(CONFIG_ADBD_PRIORITY)
+STACKSIZE := $(CONFIG_ADBD_STACKSIZE)
+MODULE := $(CONFIG_ADB_SERVER)
+
+# Files
+
+MAINSRC := adb_main.c
+CSRCS += adb_banner.c
+CSRCS += $(ADB_UNPACKNAME)/adb_client.c
+CSRCS += $(ADB_UNPACKNAME)/adb_frame.c
+
+CSRCS += $(ADB_UNPACKNAME)/hal/hal_uv.c
+CSRCS += $(ADB_UNPACKNAME)/hal/hal_uv_packet.c
+
+CFLAGS += -D__NUTTX__=1
+CFLAGS += -I$(ADB_UNPACKNAME)
+
+ifeq ($(CONFIG_ADBD_TCP_SERVER),y)
+CSRCS += $(ADB_UNPACKNAME)/hal/hal_uv_client_tcp.c
+endif
+
+ifeq ($(CONFIG_ADBD_USB_SERVER),y)
+CSRCS += $(ADB_UNPACKNAME)/hal/hal_uv_client_usb.c
+endif
+
+ifeq ($(CONFIG_ADBD_AUTHENTICATION),y)
+CSRCS += $(ADB_UNPACKNAME)/adb_auth_key.c
+endif
+
+ifeq ($(CONFIG_ADBD_FILE_SERVICE),y)
+CSRCS += $(ADB_UNPACKNAME)/file_sync_service.c
+endif
+
+ifeq ($(CONFIG_ADBD_LOGCAT_SERVICE),y)
+CSRCS += logcat_service.c
+endif
+
+ifeq ($(CONFIG_ADBD_SHELL_SERVICE),y)
+CSRCS += shell_service.c
+CSRCS += shell_pipe.c
+endif
+
+context:: $(ADB_UNPACKDIR)
+
+clean::
+ $(call DELFILE, $(OBJS))
+
+distclean::
+ $(call DELDIR, $(ADB_UNPACKDIR))
+
+include $(APPDIR)/Application.mk
diff --git a/system/adb/adb_banner.c b/system/adb/adb_banner.c
new file mode 100644
index 0000000..e446756
--- /dev/null
+++ b/system/adb/adb_banner.c
@@ -0,0 +1,117 @@
+/****************************************************************************
+ * system/adb/adb_main.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "adb.h"
+#ifdef CONFIG_BOARDCTL_UNIQUEID
+#include <sys/boardctl.h>
+#include <string.h>
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int adb_fill_connect_data(char *buf, size_t bufsize)
+{
+ size_t len;
+ size_t remaining = bufsize;
+
+#ifdef CONFIG_BOARDCTL_UNIQUEID
+ /* Get board id */
+
+ int ret;
+ uint8_t board_id[CONFIG_BOARDCTL_UNIQUEID_SIZE];
+
+ memset(board_id, 0, CONFIG_BOARDCTL_UNIQUEID_SIZE);
+ ret = boardctl(BOARDIOC_UNIQUEID, (uintptr_t)board_id);
+
+ if (ret)
+ {
+ /* Failed to get board id */
+
+ adb_log("failed to get board id\n");
+ len = snprintf(buf, remaining, "device::");
+ }
+ else
+ {
+ /* FIXME only keep first 4 bytes */
+
+ len = snprintf(buf, remaining, "device:%x:", *(uint32_t *)board_id);
+ }
+#else
+ len = snprintf(buf, remaining, "device:" CONFIG_ADBD_DEVICE_ID ":");
+#endif
+
+ if (len >= remaining)
+ {
+ return -1;
+ }
+
+#ifdef CONFIG_ADBD_PRODUCT_NAME
+ remaining -= len;
+ buf += len;
+ len = snprintf(buf, remaining,
+ "ro.product.name=" CONFIG_ADBD_PRODUCT_NAME ";");
+
+ if (len >= remaining)
+ {
+ return bufsize;
+ }
+#endif
+
+#ifdef CONFIG_ADBD_PRODUCT_MODEL
+ remaining -= len;
+ buf += len;
+ len = snprintf(buf, remaining,
+ "ro.product.model=" CONFIG_ADBD_PRODUCT_MODEL ";");
+
+ if (len >= remaining)
+ {
+ return bufsize;
+ }
+#endif
+
+#ifdef CONFIG_ADBD_PRODUCT_DEVICE
+ remaining -= len;
+ buf += len;
+ len = snprintf(buf, remaining,
+ "ro.product.device=" CONFIG_ADBD_PRODUCT_DEVICE ";");
+
+ if (len >= remaining)
+ {
+ return bufsize;
+ }
+#endif
+
+ remaining -= len;
+ buf += len;
+ len = snprintf(buf, remaining, "features=" CONFIG_ADBD_FEATURES);
+
+ if (len >= remaining)
+ {
+ return bufsize;
+ }
+
+ return bufsize - remaining + len;
+}
diff --git a/system/adb/adb_main.c b/system/adb/adb_main.c
new file mode 100644
index 0000000..d80c746
--- /dev/null
+++ b/system/adb/adb_main.c
@@ -0,0 +1,119 @@
+/****************************************************************************
+ * system/adb/adb_main.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "adb.h"
+
+#ifdef CONFIG_ADBD_BOARD_INIT
+#include <sys/boardctl.h>
+#endif
+
+#ifdef CONFIG_ADBD_NET_INIT
+#include "netutils/netinit.h"
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+void adb_log_impl(FAR const char *func, int line, FAR const char *fmt, ...)
+{
+ va_list ap;
+ fprintf(stderr, "%s (%d): ", func, line);
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+int main(int argc, FAR char **argv)
+{
+ UNUSED(argc);
+ UNUSED(argv);
+
+ adb_context_t *ctx;
+
+#ifdef CONFIG_ADBD_BOARD_INIT
+{
+ boardctl(BOARDIOC_INIT, 0);
+
+#if defined(CONFIG_ADBD_USB_SERVER) && \
+ defined(CONFIG_USBDEV_COMPOSITE) && \
+ defined (CONFIG_BOARDCTL_USBDEVCTRL)
+
+ /* Setup composite USB device */
+
+ struct boardioc_usbdev_ctrl_s ctrl;
+ int ret;
+ FAR void *handle;
+
+ /* Perform architecture-specific initialization */
+
+ ctrl.usbdev = BOARDIOC_USBDEV_COMPOSITE;
+ ctrl.action = BOARDIOC_USBDEV_INITIALIZE;
+ ctrl.instance = 0;
+ ctrl.config = 0;
+ ctrl.handle = NULL;
+
+ ret = boardctl(BOARDIOC_USBDEV_CONTROL, (uintptr_t)&ctrl);
+ if (ret < 0)
+ {
+ printf("boardctl(BOARDIOC_USBDEV_CONTROL) failed: %d\n", ret);
+ return 1;
+ }
+
+ /* Initialize the USB composite device device */
+
+ ctrl.usbdev = BOARDIOC_USBDEV_COMPOSITE;
+ ctrl.action = BOARDIOC_USBDEV_CONNECT;
+ ctrl.instance = 0;
+ ctrl.config = 0;
+ ctrl.handle = &handle;
+
+ ret = boardctl(BOARDIOC_USBDEV_CONTROL, (uintptr_t)&ctrl);
+ if (ret < 0)
+ {
+ printf("boardctl(BOARDIOC_USBDEV_CONTROL) failed: %d\n", ret);
+ return 1;
+ }
+#endif /* ADBD_USB_SERVER && USBDEV_COMPOSITE && BOARDCTL_USBDEVCTRL */
+}
+
+#endif /* CONFIG_ADBD_BOARD_INIT */
+
+#ifdef CONFIG_ADBD_NET_INIT
+ /* Bring up the network */
+
+ netinit_bringup();
+#endif
+
+ ctx = adb_hal_create_context();
+ if (!ctx)
+ {
+ return -1;
+ }
+
+ adb_hal_run(ctx);
+ adb_hal_destroy_context(ctx);
+ return 0;
+}
diff --git a/system/adb/logcat_service.c b/system/adb/logcat_service.c
new file mode 100644
index 0000000..c65f3de
--- /dev/null
+++ b/system/adb/logcat_service.c
@@ -0,0 +1,213 @@
+/****************************************************************************
+ * system/adb/logcat_service_uv.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include <nuttx/syslog/ramlog.h>
+#include <unistd.h>
+
+#include "adb.h"
+#include "logcat_service.h"
+#include "hal/hal_uv_priv.h"
+
+/****************************************************************************
+ * Private types
+ ****************************************************************************/
+
+typedef struct alog_service_s
+{
+ adb_service_t service;
+ uv_poll_t poll;
+ int wait_ack;
+} alog_service_t;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static void logcat_on_data_available(uv_poll_t * handle,
+ int status, int events);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static int alog_on_write(adb_service_t *service, apacket *p)
+{
+ UNUSED(p);
+ UNUSED(service);
+ return -1;
+}
+
+static void alog_on_kick(struct adb_service_s *service)
+{
+ alog_service_t *svc = container_of(service, alog_service_t, service);
+ if (!svc->wait_ack)
+ {
+ int ret;
+ ret = uv_poll_start(&svc->poll, UV_READABLE, logcat_on_data_available);
+ assert(ret == 0);
+ }
+}
+
+static int alog_on_ack(adb_service_t *service, apacket *p)
+{
+ UNUSED(p);
+ alog_service_t *svc = container_of(service, alog_service_t, service);
+ svc->wait_ack = 0;
+ alog_on_kick(service);
+ return 0;
+}
+
+static void close_cb(uv_handle_t *handle)
+{
+ alog_service_t *service = container_of(handle, alog_service_t, poll);
+ free(service);
+}
+
+static void alog_close(struct adb_service_s *service)
+{
+ int fd;
+ int ret;
+ alog_service_t *svc = container_of(service, alog_service_t, service);
+
+ ret = uv_fileno((uv_handle_t *)&svc->poll, &fd);
+ assert(ret == 0);
+
+ close(fd);
+ uv_close((uv_handle_t *)&svc->poll, close_cb);
+}
+
+static const adb_service_ops_t logcat_ops =
+{
+ .on_write_frame = alog_on_write,
+ .on_ack_frame = alog_on_ack,
+ .on_kick = alog_on_kick,
+ .close = alog_close
+};
+
+static void logcat_on_data_available(uv_poll_t * handle,
+ int status, int events)
+{
+ int ret;
+ int fd;
+ apacket_uv_t *ap;
+ alog_service_t *service = container_of(handle, alog_service_t, poll);
+ adb_client_uv_t *client = (adb_client_uv_t *)handle->data;
+
+ ap = adb_uv_packet_allocate(client, 0);
+ if (ap == NULL)
+ {
+ uv_poll_stop(handle);
+ return;
+ }
+
+ if (status)
+ {
+ adb_log("status error %d\n", status);
+
+ /* Fatal error, stop service */
+
+ goto exit_stop_service;
+ }
+
+ assert(uv_fileno((uv_handle_t *)handle, &fd) == 0);
+ ret = read(fd, ap->p.data, CONFIG_ADBD_PAYLOAD_SIZE);
+
+ if (ret < 0)
+ {
+ adb_log("frame read failed %d %d\n", ret, errno);
+ if (errno == EAGAIN)
+ {
+ /* TODO this should never happen */
+
+ goto exit_release_packet;
+ }
+
+ /* Fatal error, stop service */
+
+ goto exit_stop_service;
+ }
+
+ if (ret == 0)
+ {
+ goto exit_release_packet;
+ }
+
+ service->wait_ack = 1;
+ uv_poll_stop(handle);
+
+ ap->p.write_len = ret;
+ ap->p.msg.arg0 = service->service.id;
+ ap->p.msg.arg1 = service->service.peer_id;
+ adb_send_data_frame(&client->client, &ap->p);
+ return;
+
+exit_release_packet:
+ adb_hal_apacket_release(&client->client, &ap->p);
+ return;
+
+exit_stop_service:
+ adb_service_close(&client->client, &service->service, &ap->p);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+adb_service_t * logcat_service(adb_client_t *client, const char *params)
+{
+ int ret;
+ alog_service_t *service =
+ (alog_service_t *)malloc(sizeof(alog_service_t));
+
+ if (service == NULL)
+ {
+ return NULL;
+ }
+
+ service->service.ops = &logcat_ops;
+ service->wait_ack = 0;
+
+ /* TODO parse params string to extract logcat parameters */
+
+ ret = open(CONFIG_SYSLOG_DEVPATH, O_RDONLY | O_CLOEXEC);
+
+ if (ret < 0)
+ {
+ adb_log("failed to open %s (%d)\n", CONFIG_SYSLOG_DEVPATH, errno);
+ free(service);
+ return NULL;
+ }
+
+ uv_handle_t *handle = adb_uv_get_client_handle(client);
+ ret = uv_poll_init(handle->loop, &service->poll, ret);
+ assert(ret == 0);
+
+ service->poll.data = client;
+ alog_on_kick(&service->service);
+
+ return &service->service;
+}
diff --git a/system/adb/shell_pipe.c b/system/adb/shell_pipe.c
new file mode 100644
index 0000000..ad59b71
--- /dev/null
+++ b/system/adb/shell_pipe.c
@@ -0,0 +1,237 @@
+/****************************************************************************
+ * system/adb/shell_pipe.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <uv.h>
+#include <unistd.h>
+#include "adb.h"
+#include "shell_pipe.h"
+#include "hal/hal_uv_priv.h"
+
+#include <nshlib/nshlib.h>
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static void shell_on_data_available(uv_poll_t * handle,
+ int status, int events)
+{
+ int ret;
+ apacket_uv_t *ap;
+ shell_pipe_t *pipe = container_of(handle, shell_pipe_t, handle);
+
+ adb_client_t *client = (adb_client_t *)pipe->handle.data;
+
+ if (status)
+ {
+ adb_log("status error %d\n", status);
+
+ /* FIXME missing logic here */
+
+ pipe->on_data_cb(pipe, NULL);
+ return;
+ }
+
+ ap = adb_uv_packet_allocate((adb_client_uv_t *)client, 0);
+ if (ap == NULL)
+ {
+ /* frame allocation failed. Try again later */
+
+ uv_poll_stop(&pipe->handle);
+ return;
+ }
+
+ int nread = 0;
+ do
+ {
+ ret = read(handle->io_watcher.fd, &ap->p.data[nread], 1);
+
+ if (ret == 0)
+ {
+ /* EOF */
+
+ break;
+ }
+
+ if (ret < 0)
+ {
+ /* Revisit. EAGAIN should not happen but it happens a lot */
+
+ if (errno == EAGAIN)
+ {
+ if (nread <= 0)
+ {
+ adb_hal_apacket_release(
+ (adb_client_t *)pipe->handle.data, &ap->p);
+ return;
+ }
+ break;
+ }
+
+ /* Release packet and forward error */
+
+ adb_hal_apacket_release((adb_client_t *)pipe->handle.data, &ap->p);
+ pipe->on_data_cb(pipe, NULL);
+ return;
+ }
+
+ /* FIXME CR LF conversion */
+
+ if (ap->p.data[nread++] == '\n')
+ {
+ ap->p.data[nread++] = '\r';
+ }
+ }
+ while (nread < CONFIG_ADBD_PAYLOAD_SIZE - 1);
+
+ ap->p.msg.data_length = nread;
+ pipe->on_data_cb(pipe, &ap->p);
+}
+
+static void shell_pipe_close_callback(uv_handle_t *handle)
+{
+ shell_pipe_t *pipe = container_of(handle, shell_pipe_t, handle);
+
+ /* Notify caller pipe is closed */
+
+ pipe->close_cb(pipe);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int shell_pipe_setup(adb_client_t *client, shell_pipe_t *apipe)
+{
+ apipe->handle.data = client;
+ return 0;
+}
+
+void shell_pipe_destroy(shell_pipe_t *pipe, void (*close_cb)(shell_pipe_t *))
+{
+ pipe->close_cb = close_cb;
+ close(pipe->write_fd);
+ uv_close((uv_handle_t *)&pipe->handle, shell_pipe_close_callback);
+}
+
+int shell_pipe_write(shell_pipe_t *pipe, const void *buf, size_t count)
+{
+ /* TODO revisit */
+
+ return write(pipe->write_fd, buf, count);
+}
+
+int shell_pipe_start(shell_pipe_t *pipe,
+ void (*on_data_cb)(shell_pipe_t *, apacket *))
+{
+ pipe->on_data_cb = on_data_cb;
+ return uv_poll_start(&pipe->handle, UV_READABLE, shell_on_data_available);
+}
+
+int shell_pipe_exec(char * const argv[], shell_pipe_t *apipe,
+ void (*on_data_cb)(shell_pipe_t *, apacket *))
+{
+ int ret;
+ int in_fds[2];
+ int out_fds[2];
+
+ adb_client_uv_t *client = (adb_client_uv_t *)apipe->handle.data;
+
+ /* Create pipe for stdin */
+
+ ret = pipe(in_fds);
+ assert(ret == 0);
+ ret = pipe(out_fds);
+ assert(ret == 0);
+
+ /* TODO check return code */
+
+ apipe->write_fd = in_fds[1];
+
+ /* Setup stdout (read: adb, write: child) */
+
+ ret = dup2(out_fds[1], 1);
+ assert(ret == 0);
+
+ ret = close(out_fds[1]);
+ assert(ret == 0);
+
+ ret = fcntl(out_fds[0], F_GETFD);
+ assert(ret >= 0);
+ ret = fcntl(out_fds[0], F_SETFD, ret | FD_CLOEXEC);
+ assert(ret == 0);
+ ret = fcntl(out_fds[0], F_GETFL);
+ assert(ret >= 0);
+ ret = fcntl(out_fds[0], F_SETFL, ret | O_NONBLOCK);
+ assert(ret >= 0);
+
+ /* Setup stdin */
+
+ ret = dup2(in_fds[0], 0);
+ assert(ret == 0);
+
+ ret = close(in_fds[0]);
+ assert(ret == 0);
+
+ ret = fcntl(in_fds[1], F_GETFD);
+ assert(ret >= 0);
+ ret = fcntl(in_fds[1], F_SETFD, ret | FD_CLOEXEC);
+ assert(ret == 0);
+ ret = fcntl(in_fds[1], F_GETFL);
+ assert(ret >= 0);
+ ret = fcntl(in_fds[1], F_SETFL, ret | O_NONBLOCK);
+ assert(ret == 0);
+
+ ret = uv_poll_init(
+ adb_uv_get_client_handle(client)->loop,
+ &apipe->handle, out_fds[0]);
+
+ /* TODO check return code */
+
+ assert(ret == 0);
+
+ /* Create shell process */
+
+ ret = task_create("ADB shell", CONFIG_SYSTEM_NSH_PRIORITY,
+ CONFIG_SYSTEM_NSH_STACKSIZE, nsh_consolemain,
+ argv);
+
+ /* Close stdin and stdout */
+
+ dup2(2, 0);
+ dup2(2, 1);
+
+ /* TODO check return code */
+
+ assert(ret >= 0);
+
+ /* Start listening shell process stdout */
+
+ ret = shell_pipe_start(apipe, on_data_cb);
+
+ /* TODO check return code */
+
+ assert(ret == 0);
+ return 0;
+}
diff --git a/system/adb/shell_pipe.h b/system/adb/shell_pipe.h
new file mode 100644
index 0000000..16ca80f
--- /dev/null
+++ b/system/adb/shell_pipe.h
@@ -0,0 +1,56 @@
+/****************************************************************************
+ * system/adb/shell_pipe.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <uv.h>
+#include "adb.h"
+
+/****************************************************************************
+ * Private types
+ ****************************************************************************/
+
+struct shell_pipe_s
+{
+ uv_poll_t handle;
+ int write_fd;
+ void (*close_cb)(struct shell_pipe_s *);
+ void (*on_data_cb)(struct shell_pipe_s *, struct apacket_s *);
+};
+
+typedef struct shell_pipe_s shell_pipe_t;
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+int shell_pipe_setup(adb_client_t *client, shell_pipe_t *pipe);
+int shell_pipe_start(shell_pipe_t *pipe,
+ void (*on_data_cb)(shell_pipe_t *, apacket *));
+void shell_pipe_destroy(shell_pipe_t *pipe,
+ void (*close_cb)(shell_pipe_t *));
+int shell_pipe_write(shell_pipe_t *pipe, const void *buf, size_t count);
+
+int shell_pipe_exec(char * const argv[], shell_pipe_t *pipe,
+ void (*on_data_cb)(shell_pipe_t *, apacket *));
+int shell_exec_builtin(const char *appname, FAR char *const *argv,
+ shell_pipe_t *apipe);
diff --git a/system/adb/shell_service.c b/system/adb/shell_service.c
new file mode 100644
index 0000000..04bdfeb
--- /dev/null
+++ b/system/adb/shell_service.c
@@ -0,0 +1,209 @@
+/****************************************************************************
+ * system/adb/shell_service.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdlib.h>
+
+#include "adb.h"
+#include "shell_service.h"
+#include "shell_pipe.h"
+
+/****************************************************************************
+ * Private types
+ ****************************************************************************/
+
+typedef struct ash_service_s
+{
+ adb_service_t service;
+ shell_pipe_t pipe;
+ adb_client_t *client;
+} ash_service_t;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static void exec_on_data_available(shell_pipe_t * pipe, apacket * p);
+
+static int shell_ack(adb_service_t *service, apacket *p);
+static int shell_write(adb_service_t *service, apacket *p);
+static void shell_close(struct adb_service_s *service);
+static void shell_kick(adb_service_t *service);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static void exec_on_data_available(shell_pipe_t * pipe, apacket * p)
+{
+ ash_service_t *service = container_of(pipe, ash_service_t, pipe);
+
+ if (p->msg.data_length <= 0)
+ {
+ /* Got EOF */
+
+ adb_service_close(service->client, &service->service, p);
+ return;
+ }
+
+ p->write_len = p->msg.data_length;
+ p->msg.arg0 = service->service.id;
+ p->msg.arg1 = service->service.peer_id;
+ adb_send_data_frame(service->client, p);
+}
+
+static int shell_write(adb_service_t *service, apacket *p)
+{
+ int ret;
+ ash_service_t *svc = container_of(service, ash_service_t, service);
+ UNUSED(svc);
+
+ if (p->msg.data_length <= 0)
+ {
+ return -1;
+ }
+
+ ret = shell_pipe_write(&svc->pipe, p->data, p->msg.data_length);
+
+ if (ret < 0)
+ {
+ /* Shell process terminated, close service */
+
+ return -1;
+ }
+
+ assert(ret == p->msg.data_length);
+ return 0;
+}
+
+static int shell_ack(adb_service_t *service, apacket *p)
+{
+ UNUSED(service);
+ UNUSED(p);
+ return 0;
+}
+
+static void shell_kick(adb_service_t *service)
+{
+ int ret;
+ ash_service_t *svc = container_of(service, ash_service_t, service);
+ ret = shell_pipe_start(&svc->pipe, exec_on_data_available);
+
+ /* TODO handle return code */
+
+ assert(ret == 0);
+}
+
+static void shell_on_close(shell_pipe_t *pipe)
+{
+ ash_service_t *svc = container_of(pipe, ash_service_t, pipe);
+ free(svc);
+}
+
+static void shell_close(adb_service_t *service)
+{
+ ash_service_t *svc = container_of(service, ash_service_t, service);
+
+ /* FIXME missing logic here if shell process is still running */
+
+ shell_pipe_destroy(&svc->pipe, shell_on_close);
+}
+
+static const adb_service_ops_t shell_ops =
+{
+ .on_write_frame = shell_write,
+ .on_ack_frame = shell_ack,
+ .on_kick = shell_kick,
+ .close = shell_close
+};
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+adb_service_t * shell_service(adb_client_t *client, const char *params)
+{
+ UNUSED(params);
+ UNUSED(client);
+
+ int ret;
+ char **argv;
+ const char *target;
+ ash_service_t *service =
+ (ash_service_t *)malloc(sizeof(ash_service_t));
+
+ if (service == NULL)
+ {
+ return NULL;
+ }
+
+ service->client = client;
+ service->service.ops = &shell_ops;
+
+ ret = shell_pipe_setup(client, &service->pipe);
+
+ /* TODO check return code */
+
+ assert(ret == 0);
+
+ /* Check parameters after "shell:" */
+
+ target = ¶ms[6];
+
+ if (target[0] != 0)
+ {
+ /* Build argv: <nsh -c "command">
+ * argv[0] => "-c"
+ * argv[1] => command
+ * argv[2] => NULL
+ *
+ * malloc content:
+ * - x3 argv pointers
+ * - 3 characters: "-c\0"
+ * - space for command string
+ */
+
+ argv = malloc(sizeof(char *) * 3 + 3 + (strlen(target)+1));
+
+ argv[0] = (char *)&argv[3];
+ argv[1] = &((char *)&argv[3])[3];
+ argv[2] = NULL;
+ strcpy(argv[0], "-c");
+ strcpy(argv[1], target);
+ }
+ else
+ {
+ argv = NULL;
+ }
+
+ ret = shell_pipe_exec(argv, &service->pipe,
+ exec_on_data_available);
+
+ /* TODO check return code */
+
+ assert(ret == 0);
+
+ free(argv);
+
+ return &service->service;
+}