summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsakumisu <[email protected]>2024-08-17 20:58:56 +0800
committersakumisu <[email protected]>2024-08-17 21:06:06 +0800
commite4954d41949e32e8b8fcdc3882c0ca611b8cec40 (patch)
tree80e8788a69d17f410b774b91c41d42cebeef7664
parent07c6c296ed39c9356d7b4f2ba9ae7be525935aa3 (diff)
feat: add usb adb device with cherrysh
-rw-r--r--Kconfig5
-rw-r--r--README.md1
-rw-r--r--README_zh.md1
-rw-r--r--cherryusb.cmake4
-rw-r--r--class/adb/usbd_adb.c301
-rw-r--r--class/adb/usbd_adb.h38
-rw-r--r--demo/adb/cherryadb.pngbin0 -> 42508 bytes
-rw-r--r--demo/adb/cherrysh_port.c330
-rw-r--r--demo/adb/usbd_adb_template.c228
9 files changed, 908 insertions, 0 deletions
diff --git a/Kconfig b/Kconfig
index a630e73c..c2b2ab4c 100644
--- a/Kconfig
+++ b/Kconfig
@@ -105,6 +105,11 @@ if CHERRYUSB
prompt "Enable usb dfu device"
default n
+ config CHERRYUSB_DEVICE_ADB
+ bool
+ prompt "Enable usb adb device"
+ default n
+
choice
prompt "Select usb device template"
default CHERRYUSB_DEVICE_TEMPLATE_NONE
diff --git a/README.md b/README.md
index a0645bbc..5e817c4c 100644
--- a/README.md
+++ b/README.md
@@ -68,6 +68,7 @@ CherryUSB Device Stack has the following functions:
- Support WINUSB1.0、WINUSB2.0、WEBUSB、BOS
- Support Vendor class
- Support UF2
+- Support Android Debug Bridge (Only support shell)
- Support multi device with the same USB IP
CherryUSB Device Stack resource usage (GCC 10.2 with -O2):
diff --git a/README_zh.md b/README_zh.md
index 6552acab..091b5cad 100644
--- a/README_zh.md
+++ b/README_zh.md
@@ -68,6 +68,7 @@ CherryUSB Device 协议栈当前实现以下功能:
- 支持 WINUSB1.0、WINUSB2.0、WEBUSB、BOS
- 支持 Vendor 类 class
- 支持 UF2
+- 支持 Android Debug Bridge (Only support shell)
- 支持相同 USB IP 的多从机
CherryUSB Device 协议栈资源占用说明(GCC 10.2 with -O2):
diff --git a/cherryusb.cmake b/cherryusb.cmake
index f5d6fa51..beec519c 100644
--- a/cherryusb.cmake
+++ b/cherryusb.cmake
@@ -36,6 +36,7 @@ ${CMAKE_CURRENT_LIST_DIR}/class/audio
${CMAKE_CURRENT_LIST_DIR}/class/video
${CMAKE_CURRENT_LIST_DIR}/class/wireless
${CMAKE_CURRENT_LIST_DIR}/class/midi
+${CMAKE_CURRENT_LIST_DIR}/class/adb
${CMAKE_CURRENT_LIST_DIR}/class/vendor/net
${CMAKE_CURRENT_LIST_DIR}/class/vendor/serial
${CMAKE_CURRENT_LIST_DIR}/class/vendor/wifi
@@ -70,6 +71,9 @@ if(CONFIG_CHERRYUSB_DEVICE)
if(CONFIG_CHERRYUSB_DEVICE_DFU)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/dfu/usbd_dfu.c)
endif()
+ if(CONFIG_CHERRYUSB_DEVICE_ADB)
+ list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/adb/usbd_adb.c)
+ endif()
if(DEFINED CONFIG_CHERRYUSB_DEVICE_DCD)
if("${CONFIG_CHERRYUSB_DEVICE_DCD}" STREQUAL "fsdev")
diff --git a/class/adb/usbd_adb.c b/class/adb/usbd_adb.c
new file mode 100644
index 00000000..b193e35f
--- /dev/null
+++ b/class/adb/usbd_adb.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2024, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbd_core.h"
+#include "usbd_adb.h"
+
+#define ADB_OUT_EP_IDX 0
+#define ADB_IN_EP_IDX 1
+
+#define ADB_STATE_READ_MSG 0
+#define ADB_STATE_READ_DATA 1
+#define ADB_STATE_WRITE_MSG 2
+#define ADB_STATE_WRITE_DATA 3
+#define ADB_STATE_AWRITE_MSG 4
+#define ADB_STATE_AWRITE_DATA 5
+
+#define MAX_PAYLOAD_V1 (4 * 1024)
+#define MAX_PAYLOAD_V2 (256 * 1024)
+#define MAX_PAYLOAD MAX_PAYLOAD_V1
+#define A_VERSION 0x01000000
+
+#define A_SYNC 0x434e5953
+#define A_CNXN 0x4e584e43
+#define A_OPEN 0x4e45504f
+#define A_OKAY 0x59414b4f
+#define A_CLSE 0x45534c43
+#define A_WRTE 0x45545257
+#define A_AUTH 0x48545541
+
+struct adb_msg {
+ uint32_t command; /* command identifier constant (A_CNXN, ...) */
+ uint32_t arg0; /* first argument */
+ uint32_t arg1; /* second argument */
+ uint32_t data_length; /* length of payload (0 is allowed) */
+ uint32_t data_crc32; /* crc32 of data payload */
+ uint32_t magic; /* command ^ 0xffffffff */
+};
+
+struct adb_packet {
+ struct adb_msg msg;
+ uint8_t payload[MAX_PAYLOAD];
+};
+
+struct usbd_adb {
+ uint8_t state;
+ uint8_t common_state;
+ uint8_t write_state;
+ bool writable;
+ uint32_t localid;
+ uint32_t shell_remoteid;
+ uint32_t file_remoteid;
+} adb_client;
+
+static struct usbd_endpoint adb_ep_data[2];
+
+USB_NOCACHE_RAM_SECTION struct adb_packet tx_packet;
+USB_NOCACHE_RAM_SECTION struct adb_packet rx_packet;
+
+static inline uint32_t adb_packet_checksum(struct adb_packet *packet)
+{
+ uint32_t sum = 0;
+ int i;
+
+ for (i = 0; i < packet->msg.data_length; ++i) {
+ sum += (uint32_t)(packet->payload[i]);
+ }
+
+ return sum;
+}
+
+static uint32_t usbd_adb_get_remoteid(uint32_t localid)
+{
+ if (localid == ADB_SHELL_LOALID) {
+ return adb_client.shell_remoteid;
+ } else {
+ return adb_client.file_remoteid;
+ }
+}
+
+static void adb_send_msg(struct adb_packet *packet)
+{
+ adb_client.common_state = ADB_STATE_WRITE_MSG;
+
+ packet->msg.data_crc32 = adb_packet_checksum(packet);
+ packet->msg.magic = packet->msg.command ^ 0xffffffff;
+
+ usbd_ep_start_write(0, adb_ep_data[ADB_IN_EP_IDX].ep_addr, (uint8_t *)&packet->msg, sizeof(struct adb_msg));
+}
+
+static void adb_send_okay(struct adb_packet *packet, uint32_t localid)
+{
+ packet->msg.command = A_OKAY;
+ packet->msg.arg0 = localid;
+ packet->msg.arg1 = usbd_adb_get_remoteid(localid);
+ packet->msg.data_length = 0;
+
+ adb_send_msg(&tx_packet);
+}
+
+static void adb_send_close(struct adb_packet *packet, uint32_t localid, uint32_t remoteid)
+{
+ packet->msg.command = A_CLSE;
+ packet->msg.arg0 = localid;
+ packet->msg.arg1 = remoteid;
+ packet->msg.data_length = 0;
+
+ adb_send_msg(&tx_packet);
+}
+
+void usbd_adb_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes)
+{
+ if (adb_client.common_state == ADB_STATE_READ_MSG) {
+ if (nbytes != sizeof(struct adb_msg)) {
+ USB_LOG_ERR("invalid adb msg size:%d\r\n", nbytes);
+ return;
+ }
+
+ USB_LOG_DBG("command:%x arg0:%x arg1:%x len:%d\r\n",
+ rx_packet.msg.command,
+ rx_packet.msg.arg0,
+ rx_packet.msg.arg1,
+ rx_packet.msg.data_length);
+
+ if (rx_packet.msg.data_length) {
+ /* setup next out ep read transfer */
+ adb_client.common_state = ADB_STATE_READ_DATA;
+ usbd_ep_start_read(busid, adb_ep_data[ADB_OUT_EP_IDX].ep_addr, rx_packet.payload, rx_packet.msg.data_length);
+ } else {
+ if (rx_packet.msg.command == A_CLSE) {
+ adb_client.writable = false;
+ usbd_adb_notify_write_done();
+ USB_LOG_INFO("Close remoteid:%x\r\n", rx_packet.msg.arg0);
+ }
+ adb_client.common_state = ADB_STATE_READ_MSG;
+ /* setup first out ep read transfer */
+ usbd_ep_start_read(busid, adb_ep_data[ADB_OUT_EP_IDX].ep_addr, (uint8_t *)&rx_packet.msg, sizeof(struct adb_msg));
+ }
+ } else if (adb_client.common_state == ADB_STATE_READ_DATA) {
+ switch (rx_packet.msg.command) {
+ case A_SYNC:
+
+ break;
+ case A_CNXN: /* CONNECT(version, maxdata, "system-id-string") */
+ char *support_feature = "device::"
+ "ro.product.name=cherryadb;"
+ "ro.product.model=cherrysh;"
+ "ro.product.device=cherryadb;"
+ "features=cmd,shell_v1";
+
+ tx_packet.msg.command = A_CNXN;
+ tx_packet.msg.arg0 = A_VERSION;
+ tx_packet.msg.arg1 = MAX_PAYLOAD;
+ tx_packet.msg.data_length = strlen(support_feature);
+ memcpy(tx_packet.payload, support_feature, strlen(support_feature));
+
+ adb_send_msg(&tx_packet);
+
+ adb_client.writable = false;
+ break;
+ case A_OPEN: /* OPEN(local-id, 0, "destination") */
+ rx_packet.payload[rx_packet.msg.data_length] = '\0';
+
+ if (strncmp((const char *)rx_packet.payload, "shell:", 6) == 0) {
+ adb_client.localid = ADB_SHELL_LOALID;
+ adb_client.shell_remoteid = rx_packet.msg.arg0;
+ adb_send_okay(&tx_packet, ADB_SHELL_LOALID);
+
+ USB_LOG_INFO("Open shell service, remoteid:%x\r\n", rx_packet.msg.arg0);
+ } else if (strncmp((const char *)rx_packet.payload, "sync:", 5) == 0) {
+ adb_client.localid = ADB_FILE_LOALID;
+ adb_client.file_remoteid = rx_packet.msg.arg0;
+ adb_send_okay(&tx_packet, ADB_FILE_LOALID);
+ USB_LOG_INFO("Open file service, remoteid:%x\r\n", rx_packet.msg.arg0);
+ }
+ break;
+ case A_OKAY:
+
+ break;
+ case A_CLSE:
+
+ break;
+ case A_WRTE: /* WRITE(local-id, remote-id, "data") */
+ if ((rx_packet.msg.arg0 == adb_client.shell_remoteid) && (rx_packet.msg.arg1 == ADB_SHELL_LOALID)) {
+ adb_send_okay(&tx_packet, rx_packet.msg.arg1);
+ } else if ((rx_packet.msg.arg0 == adb_client.file_remoteid) && (rx_packet.msg.arg1 == ADB_FILE_LOALID)) {
+ adb_send_okay(&tx_packet, rx_packet.msg.arg1);
+ } else {
+ adb_send_close(&tx_packet, 0, rx_packet.msg.arg0);
+ }
+ break;
+ case A_AUTH:
+
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+void usbd_adb_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes)
+{
+ if (adb_client.common_state == ADB_STATE_WRITE_MSG) {
+ if (tx_packet.msg.data_length) {
+ adb_client.common_state = ADB_STATE_WRITE_DATA;
+ usbd_ep_start_write(busid, adb_ep_data[ADB_IN_EP_IDX].ep_addr, tx_packet.payload, tx_packet.msg.data_length);
+ } else {
+ if (rx_packet.msg.command == A_WRTE) {
+ adb_client.writable = true;
+ if (adb_client.localid == ADB_SHELL_LOALID) {
+ usbd_adb_notify_shell_read(rx_packet.payload, rx_packet.msg.data_length);
+ } else {
+ }
+ }
+ adb_client.common_state = ADB_STATE_READ_MSG;
+ /* setup first out ep read transfer */
+ usbd_ep_start_read(busid, adb_ep_data[ADB_OUT_EP_IDX].ep_addr, (uint8_t *)&rx_packet.msg, sizeof(struct adb_msg));
+ }
+ } else if (adb_client.common_state == ADB_STATE_WRITE_DATA) {
+ adb_client.common_state = ADB_STATE_READ_MSG;
+ /* setup first out ep read transfer */
+ usbd_ep_start_read(busid, adb_ep_data[ADB_OUT_EP_IDX].ep_addr, (uint8_t *)&rx_packet.msg, sizeof(struct adb_msg));
+ } else if (adb_client.write_state == ADB_STATE_AWRITE_MSG) {
+ if (tx_packet.msg.data_length) {
+ adb_client.write_state = ADB_STATE_AWRITE_DATA;
+ usbd_ep_start_write(busid, adb_ep_data[ADB_IN_EP_IDX].ep_addr, tx_packet.payload, tx_packet.msg.data_length);
+ } else {
+ }
+ } else if (adb_client.write_state == ADB_STATE_AWRITE_DATA) {
+ usbd_adb_notify_write_done();
+ }
+}
+
+void adb_notify_handler(uint8_t busid, uint8_t event, void *arg)
+{
+ switch (event) {
+ case USBD_EVENT_INIT:
+ break;
+ case USBD_EVENT_DEINIT:
+ break;
+ case USBD_EVENT_RESET:
+ break;
+ case USBD_EVENT_CONFIGURED:
+ adb_client.common_state = ADB_STATE_READ_MSG;
+ /* setup first out ep read transfer */
+ usbd_ep_start_read(busid, adb_ep_data[ADB_OUT_EP_IDX].ep_addr, (uint8_t *)&rx_packet.msg, sizeof(struct adb_msg));
+ break;
+
+ default:
+ break;
+ }
+}
+
+struct usbd_interface *usbd_adb_init_intf(uint8_t busid, struct usbd_interface *intf, uint8_t in_ep, uint8_t out_ep)
+{
+ intf->class_interface_handler = NULL;
+ intf->class_endpoint_handler = NULL;
+ intf->vendor_handler = NULL;
+ intf->notify_handler = adb_notify_handler;
+
+ adb_ep_data[ADB_OUT_EP_IDX].ep_addr = out_ep;
+ adb_ep_data[ADB_OUT_EP_IDX].ep_cb = usbd_adb_bulk_out;
+ adb_ep_data[ADB_IN_EP_IDX].ep_addr = in_ep;
+ adb_ep_data[ADB_IN_EP_IDX].ep_cb = usbd_adb_bulk_in;
+
+ usbd_add_endpoint(busid, &adb_ep_data[ADB_OUT_EP_IDX]);
+ usbd_add_endpoint(busid, &adb_ep_data[ADB_IN_EP_IDX]);
+
+ return intf;
+}
+
+bool usbd_adb_can_write(void)
+{
+ return adb_client.writable;
+}
+
+int usbd_abd_write(uint32_t localid, const uint8_t *data, uint32_t len)
+{
+ struct adb_packet *packet;
+
+ packet = &tx_packet;
+ packet->msg.command = A_WRTE;
+ packet->msg.arg0 = localid;
+ packet->msg.arg1 = usbd_adb_get_remoteid(localid);
+ packet->msg.data_length = len;
+ memcpy(packet->payload, data, len);
+
+ packet->msg.data_crc32 = adb_packet_checksum(packet);
+ packet->msg.magic = packet->msg.command ^ 0xffffffff;
+
+ adb_client.write_state = ADB_STATE_AWRITE_MSG;
+ usbd_ep_start_write(0, adb_ep_data[ADB_IN_EP_IDX].ep_addr, (uint8_t *)&packet->msg, sizeof(struct adb_msg));
+ return 0;
+}
+
+void usbd_adb_close(uint32_t localid)
+{
+ adb_send_close(&tx_packet, 0, usbd_adb_get_remoteid(localid));
+} \ No newline at end of file
diff --git a/class/adb/usbd_adb.h b/class/adb/usbd_adb.h
new file mode 100644
index 00000000..4dbc730f
--- /dev/null
+++ b/class/adb/usbd_adb.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2024, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBD_ADB_H
+#define USBD_ADB_H
+
+#include <stdint.h>
+
+#define ADB_SHELL_LOALID 0x01
+#define ADB_FILE_LOALID 0x02
+
+// clang-format off
+#define ADB_DESCRIPTOR_INIT(bFirstInterface, in_ep, out_ep, wMaxPacketSize) \
+ USB_INTERFACE_DESCRIPTOR_INIT(bFirstInterface, 0x00, 0x02, 0xff, 0x42, 0x01, 0x02), \
+ USB_ENDPOINT_DESCRIPTOR_INIT(in_ep, 0x02, wMaxPacketSize, 0x00), \
+ USB_ENDPOINT_DESCRIPTOR_INIT(out_ep, 0x02, wMaxPacketSize, 0x00)
+// clang-format on
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct usbd_interface *usbd_adb_init_intf(uint8_t busid, struct usbd_interface *intf, uint8_t in_ep, uint8_t out_ep);
+
+void usbd_adb_notify_shell_read(uint8_t *data, uint32_t len);
+void usbd_adb_notify_file_read(uint8_t *data, uint32_t len);
+void usbd_adb_notify_write_done(void);
+bool usbd_adb_can_write(void);
+int usbd_abd_write(uint32_t localid, const uint8_t *data, uint32_t len);
+void usbd_adb_close(uint32_t localid);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBD_ADB_H */ \ No newline at end of file
diff --git a/demo/adb/cherryadb.png b/demo/adb/cherryadb.png
new file mode 100644
index 00000000..512586b9
--- /dev/null
+++ b/demo/adb/cherryadb.png
Binary files differ
diff --git a/demo/adb/cherrysh_port.c b/demo/adb/cherrysh_port.c
new file mode 100644
index 00000000..85971724
--- /dev/null
+++ b/demo/adb/cherrysh_port.c
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 2024, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "FreeRTOS.h"
+#include "task.h"
+#include "event_groups.h"
+#include "csh.h"
+#include "usbd_core.h"
+#include "usbd_adb.h"
+#include "chry_ringbuffer.h"
+
+static chry_ringbuffer_t shell_rb;
+static uint8_t mempool[1024];
+
+#ifndef task_repl_PRIORITY
+#define task_repl_PRIORITY (configMAX_PRIORITIES - 4U)
+#endif
+
+#ifndef task_exec_PRIORITY
+#define task_exec_PRIORITY (configMAX_PRIORITIES - 5U)
+#endif
+
+static chry_shell_t csh;
+static volatile bool login = false;
+
+static StaticTask_t task_buffer_repl;
+static StaticTask_t task_buffer_exec;
+
+static StackType_t task_stack_repl[1024];
+static StackType_t task_stack_exec[1024];
+
+static TaskHandle_t task_hdl_repl = NULL;
+static TaskHandle_t task_hdl_exec = NULL;
+
+static EventGroupHandle_t event_hdl;
+static StaticEventGroup_t event_grp;
+
+void usbd_adb_notify_shell_read(uint8_t *data, uint32_t len)
+{
+ chry_ringbuffer_write(&shell_rb, data, len);
+
+ BaseType_t xHigherPriorityTaskWoken = pdFALSE;
+ xEventGroupSetBitsFromISR(event_hdl, 0x10, &xHigherPriorityTaskWoken);
+ portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
+}
+
+void usbd_adb_notify_write_done(void)
+{
+ BaseType_t xHigherPriorityTaskWoken = pdFALSE;
+ xEventGroupSetBitsFromISR(event_hdl, 0x20, &xHigherPriorityTaskWoken);
+ portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
+}
+
+static uint16_t csh_sput_cb(chry_readline_t *rl, const void *data, uint16_t size)
+{
+ (void)rl;
+
+ if (!usb_device_is_configured(0)) {
+ return size;
+ }
+
+ if (usbd_adb_can_write() && size) {
+ usbd_abd_write(ADB_SHELL_LOALID, data, size);
+ xEventGroupWaitBits(event_hdl, 0x20, pdTRUE, pdFALSE, portMAX_DELAY);
+ }
+
+ return size;
+}
+
+static uint16_t csh_sget_cb(chry_readline_t *rl, void *data, uint16_t size)
+{
+ (void)rl;
+
+ return chry_ringbuffer_read(&shell_rb, data, size);
+}
+
+static void wait_char(void)
+{
+ EventBits_t event;
+wait:
+ /* In order to lock the log from being disrupted , wait for REPL task execution to complete */
+ event = xEventGroupWaitBits(event_hdl, (0x10 | 0x01 | 0x04), pdTRUE, pdFALSE, portMAX_DELAY);
+ if ((event & 0x10) == 0) {
+ if (event & 0x01) {
+ chry_readline_erase_line(&csh.rl);
+ xEventGroupSetBits(event_hdl, 0x02);
+ }
+ if (event & 0x04) {
+ chry_readline_edit_refresh(&csh.rl);
+ xEventGroupSetBits(event_hdl, 0x08);
+ }
+
+ goto wait;
+ }
+}
+
+static void task_repl(void *param)
+{
+ (void)param;
+ int ret;
+ volatile uint8_t *pexec = (void *)&csh.exec;
+
+ for (;;) {
+ restart:
+ if (login) {
+ goto repl;
+ } else {
+ }
+
+ ret = csh_login(&csh);
+ if (ret == 0) {
+ login = true;
+ } else if (ret == 1) {
+ /*!< no enough char */
+ wait_char();
+ continue;
+ } else {
+ continue;
+ }
+
+ repl:
+ ret = chry_shell_task_repl(&csh);
+
+ if (ret == -1) {
+ /*!< error */
+ goto restart;
+ } else if (ret == 1) {
+ /*!< no enough char */
+ wait_char();
+ } else {
+ /*!< restart */
+ }
+
+ /*!< check flag */
+ if (*pexec == CSH_STATUS_EXEC_DONE) {
+ *pexec = CSH_STATUS_EXEC_IDLE;
+ chry_readline_auto_refresh(&csh.rl, true);
+ chry_readline_ignore(&csh.rl, false);
+ chry_readline_edit_refresh(&csh.rl);
+ }
+
+ if (login == false) {
+ chry_readline_erase_line(&csh.rl);
+ csh.rl.noblock = false;
+ }
+ }
+}
+
+static void task_exec(void *param)
+{
+ (void)param;
+
+ /*!< execute shell command */
+ chry_shell_task_exec(&csh);
+
+ /*!< notify REPL task execute done */
+ xEventGroupSetBits(event_hdl, 0x10);
+
+ /*!< wait for REPL task delete */
+ vTaskSuspend(NULL);
+}
+
+int chry_shell_port_create_context(chry_shell_t *csh, int argc, const char **argv)
+{
+ volatile TaskHandle_t *p_task_hdl_exec = (void *)&task_hdl_exec;
+ (void)csh;
+ (void)argc;
+ (void)argv;
+
+ if (*p_task_hdl_exec != NULL) {
+ vTaskDelete(*p_task_hdl_exec);
+ }
+
+ *p_task_hdl_exec = xTaskCreateStatic(task_exec, "task_exec", 1024U, NULL, task_exec_PRIORITY, task_stack_exec, &task_buffer_exec);
+ return 0;
+}
+
+void chry_shell_port_default_handler(chry_shell_t *csh, int sig)
+{
+ volatile uint8_t *pexec = (void *)&csh->exec;
+ volatile TaskHandle_t *p_task_hdl_exec = (void *)&task_hdl_exec;
+
+ switch (sig) {
+ case CSH_SIGINT:
+ case CSH_SIGQUIT:
+ case CSH_SIGKILL:
+ case CSH_SIGTERM:
+ break;
+ default:
+ return;
+ }
+
+ /*!< force delete task */
+ if (*p_task_hdl_exec != NULL) {
+ vTaskDelete(task_hdl_exec);
+ *p_task_hdl_exec = NULL;
+ }
+
+ switch (sig) {
+ case CSH_SIGINT:
+ csh->rl.sput(&csh->rl, "^SIGINT" CONFIG_CSH_NEWLINE, sizeof("^SIGINT" CONFIG_CSH_NEWLINE) - 1);
+ break;
+ case CSH_SIGQUIT:
+ csh->rl.sput(&csh->rl, "^SIGQUIT" CONFIG_CSH_NEWLINE, sizeof("^SIGQUIT" CONFIG_CSH_NEWLINE) - 1);
+ break;
+ case CSH_SIGKILL:
+ csh->rl.sput(&csh->rl, "^SIGKILL" CONFIG_CSH_NEWLINE, sizeof("^SIGKILL" CONFIG_CSH_NEWLINE) - 1);
+ break;
+ case CSH_SIGTERM:
+ csh->rl.sput(&csh->rl, "^SIGTERM" CONFIG_CSH_NEWLINE, sizeof("^SIGTERM" CONFIG_CSH_NEWLINE) - 1);
+ break;
+ default:
+ return;
+ }
+
+ *pexec = CSH_STATUS_EXEC_IDLE;
+ chry_readline_auto_refresh(&csh->rl, true);
+ chry_readline_ignore(&csh->rl, false);
+ chry_readline_edit_refresh(&csh->rl);
+}
+
+int shell_init(bool need_login)
+{
+ chry_shell_init_t csh_init;
+
+ if (chry_ringbuffer_init(&shell_rb, mempool, sizeof(mempool))) {
+ return -1;
+ }
+
+ if (need_login) {
+ login = false;
+ } else {
+ login = true;
+ }
+
+ /*!< I/O callback */
+ csh_init.sput = csh_sput_cb;
+ csh_init.sget = csh_sget_cb;
+
+#if defined(CONFIG_CSH_SYMTAB) && CONFIG_CSH_SYMTAB
+ extern const int __fsymtab_start;
+ extern const int __fsymtab_end;
+ extern const int __vsymtab_start;
+ extern const int __vsymtab_end;
+
+ /*!< get table from ld symbol */
+ csh_init.command_table_beg = &__fsymtab_start;
+ csh_init.command_table_end = &__fsymtab_end;
+ csh_init.variable_table_beg = &__vsymtab_start;
+ csh_init.variable_table_end = &__vsymtab_end;
+#endif
+
+#if defined(CONFIG_CSH_PROMPTEDIT) && CONFIG_CSH_PROMPTEDIT
+ static char csh_prompt_buffer[128];
+
+ /*!< set prompt buffer */
+ csh_init.prompt_buffer = csh_prompt_buffer;
+ csh_init.prompt_buffer_size = sizeof(csh_prompt_buffer);
+#endif
+
+#if defined(CONFIG_CSH_HISTORY) && CONFIG_CSH_HISTORY
+ static char csh_history_buffer[128];
+
+ /*!< set history buffer */
+ csh_init.history_buffer = csh_history_buffer;
+ csh_init.history_buffer_size = sizeof(csh_history_buffer);
+#endif
+
+#if defined(CONFIG_CSH_LNBUFF_STATIC) && CONFIG_CSH_LNBUFF_STATIC
+ static char csh_line_buffer[128];
+
+ /*!< set linebuffer */
+ csh_init.line_buffer = csh_line_buffer;
+ csh_init.line_buffer_size = sizeof(csh_line_buffer);
+#endif
+
+ csh_init.uid = 0;
+ csh_init.user[0] = "cherry";
+
+ /*!< The port hash function is required,
+ and the strcmp attribute is used weakly by default,
+ int chry_shell_port_hash_strcmp(const char *hash, const char *str); */
+ csh_init.hash[0] = "12345678"; /*!< If there is no password, set to NULL */
+ csh_init.host = "cherryadb";
+ csh_init.user_data = NULL;
+
+ int ret = chry_shell_init(&csh, &csh_init);
+ if (ret) {
+ return -1;
+ }
+
+ task_hdl_exec = NULL;
+ event_hdl = xEventGroupCreateStatic(&event_grp);
+ task_hdl_repl = xTaskCreateStatic(task_repl, "task_repl", 1024U, NULL, task_repl_PRIORITY, task_stack_repl, &task_buffer_repl);
+
+ return 0;
+}
+
+void shell_lock(void)
+{
+ xEventGroupSetBits(event_hdl, 0x01);
+ xEventGroupWaitBits(event_hdl, 0x02, pdTRUE, pdTRUE, portMAX_DELAY);
+}
+
+void shell_unlock(void)
+{
+ xEventGroupSetBits(event_hdl, 0x04);
+ xEventGroupWaitBits(event_hdl, 0x08, pdTRUE, pdTRUE, portMAX_DELAY);
+}
+
+static int csh_exit(int argc, char **argv)
+{
+ (void)argc;
+ (void)argv;
+
+ usbd_adb_close(ADB_SHELL_LOALID);
+
+ return 0;
+}
+CSH_SCMD_EXPORT_ALIAS(csh_exit, exit, );
+
+#define __ENV_PATH "/sbin:/bin"
+const char ENV_PATH[] = __ENV_PATH;
+CSH_RVAR_EXPORT(ENV_PATH, PATH, sizeof(__ENV_PATH));
+
+#define __ENV_ZERO ""
+const char ENV_ZERO[] = __ENV_ZERO;
+CSH_RVAR_EXPORT(ENV_ZERO, ZERO, sizeof(__ENV_ZERO));
diff --git a/demo/adb/usbd_adb_template.c b/demo/adb/usbd_adb_template.c
new file mode 100644
index 00000000..0c367d8a
--- /dev/null
+++ b/demo/adb/usbd_adb_template.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2024, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbd_core.h"
+#include "usbd_adb.h"
+
+/*!< endpoint address */
+#define WINUSB_IN_EP 0x81
+#define WINUSB_OUT_EP 0x02
+
+#define USBD_VID 0xFFFF
+#define USBD_PID 0xFFFF
+#define USBD_MAX_POWER 100
+#define USBD_LANGID_STRING 1033
+
+/*!< config descriptor size */
+#define USB_CONFIG_SIZE (9 + 9 + 7 + 7)
+
+#ifdef CONFIG_USB_HS
+#define WINUSB_MAX_MPS 512
+#else
+#define WINUSB_MAX_MPS 64
+#endif
+
+#define WCID_VENDOR_CODE 0x17
+#define ADB_INTF_NUM 0
+
+__ALIGN_BEGIN const uint8_t WCID_StringDescriptor_MSOS[18] __ALIGN_END = {
+ ///////////////////////////////////////
+ /// MS OS string descriptor
+ ///////////////////////////////////////
+ 0x12, /* bLength */
+ USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
+ /* MSFT100 */
+ 'M', 0x00, 'S', 0x00, 'F', 0x00, 'T', 0x00, /* wcChar_7 */
+ '1', 0x00, '0', 0x00, '0', 0x00, /* wcChar_7 */
+ WCID_VENDOR_CODE, /* bVendorCode */
+ 0x00, /* bReserved */
+};
+
+__ALIGN_BEGIN const uint8_t WINUSB_WCIDDescriptor[40] __ALIGN_END = {
+ ///////////////////////////////////////
+ /// WCID descriptor
+ ///////////////////////////////////////
+ 0x28, 0x00, 0x00, 0x00, /* dwLength */
+ 0x00, 0x01, /* bcdVersion */
+ 0x04, 0x00, /* wIndex */
+ 0x01, /* bCount */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bReserved_7 */
+
+ ///////////////////////////////////////
+ /// WCID function descriptor
+ ///////////////////////////////////////
+ ADB_INTF_NUM, /* bFirstInterfaceNumber */
+ 0x01, /* bReserved */
+ /* Compatible ID */
+ 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, /* cCID_8: WINUSB */
+ /* */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* cSubCID_8 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bReserved_6 */
+};
+
+__ALIGN_BEGIN const uint8_t WINUSB_IF0_WCIDProperties[142] __ALIGN_END = {
+ ///////////////////////////////////////
+ /// WCID property descriptor
+ ///////////////////////////////////////
+ 0x8e, 0x00, 0x00, 0x00, /* dwLength */
+ 0x00, 0x01, /* bcdVersion */
+ 0x05, 0x00, /* wIndex */
+ 0x01, 0x00, /* wCount */
+
+ ///////////////////////////////////////
+ /// registry propter descriptor
+ ///////////////////////////////////////
+ 0x84, 0x00, 0x00, 0x00, /* dwSize */
+ 0x01, 0x00, 0x00, 0x00, /* dwPropertyDataType */
+ 0x28, 0x00, /* wPropertyNameLength */
+ /* DeviceInterfaceGUID */
+ 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, /* wcName_20 */
+ 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, /* wcName_20 */
+ 't', 0x00, 'e', 0x00, 'r', 0x00, 'f', 0x00, /* wcName_20 */
+ 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, /* wcName_20 */
+ 'U', 0x00, 'I', 0x00, 'D', 0x00, 0x00, 0x00, /* wcName_20 */
+ 0x4e, 0x00, 0x00, 0x00, /* dwPropertyDataLength */
+ /* {1D4B2365-4749-48EA-B38A-7C6FDDDD7E26} */
+ '{', 0x00, '1', 0x00, 'D', 0x00, '4', 0x00, /* wcData_39 */
+ 'B', 0x00, '2', 0x00, '3', 0x00, '6', 0x00, /* wcData_39 */
+ '5', 0x00, '-', 0x00, '4', 0x00, '7', 0x00, /* wcData_39 */
+ '4', 0x00, '9', 0x00, '-', 0x00, '4', 0x00, /* wcData_39 */
+ '8', 0x00, 'E', 0x00, 'A', 0x00, '-', 0x00, /* wcData_39 */
+ 'B', 0x00, '3', 0x00, '8', 0x00, 'A', 0x00, /* wcData_39 */
+ '-', 0x00, '7', 0x00, 'C', 0x00, '6', 0x00, /* wcData_39 */
+ 'F', 0x00, 'D', 0x00, 'D', 0x00, 'D', 0x00, /* wcData_39 */
+ 'D', 0x00, '7', 0x00, 'E', 0x00, '2', 0x00, /* wcData_39 */
+ '6', 0x00, '}', 0x00, 0x00, 0x00, /* wcData_39 */
+};
+
+const uint8_t *WINUSB_IFx_WCIDProperties[] = {
+ WINUSB_IF0_WCIDProperties,
+};
+
+struct usb_msosv1_descriptor msosv1_desc = {
+ .string = WCID_StringDescriptor_MSOS,
+ .vendor_code = WCID_VENDOR_CODE,
+ .compat_id = WINUSB_WCIDDescriptor,
+ .comp_id_property = WINUSB_IFx_WCIDProperties,
+};
+
+/*!< global descriptor */
+static const uint8_t adb_descriptor[] = {
+ USB_DEVICE_DESCRIPTOR_INIT(USB_2_0, 0x00, 0x00, 0x00, USBD_VID, USBD_PID, 0x0100, 0x01),
+ USB_CONFIG_DESCRIPTOR_INIT(USB_CONFIG_SIZE, 0x01, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER),
+ ADB_DESCRIPTOR_INIT(ADB_INTF_NUM, WINUSB_IN_EP, WINUSB_OUT_EP, WINUSB_MAX_MPS),
+ ///////////////////////////////////////
+ /// string0 descriptor
+ ///////////////////////////////////////
+ USB_LANGID_INIT(USBD_LANGID_STRING),
+ ///////////////////////////////////////
+ /// string1 descriptor
+ ///////////////////////////////////////
+ 0x14, /* bLength */
+ USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
+ 'C', 0x00, /* wcChar0 */
+ 'h', 0x00, /* wcChar1 */
+ 'e', 0x00, /* wcChar2 */
+ 'r', 0x00, /* wcChar3 */
+ 'r', 0x00, /* wcChar4 */
+ 'y', 0x00, /* wcChar5 */
+ 'U', 0x00, /* wcChar6 */
+ 'S', 0x00, /* wcChar7 */
+ 'B', 0x00, /* wcChar8 */
+ ///////////////////////////////////////
+ /// string2 descriptor
+ ///////////////////////////////////////
+ 0x14, /* bLength */
+ USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
+ 'C', 0x00, /* wcChar0 */
+ 'h', 0x00, /* wcChar1 */
+ 'e', 0x00, /* wcChar2 */
+ 'r', 0x00, /* wcChar3 */
+ 'r', 0x00, /* wcChar4 */
+ 'y', 0x00, /* wcChar5 */
+ 'A', 0x00, /* wcChar6 */
+ 'D', 0x00, /* wcChar7 */
+ 'B', 0x00, /* wcChar8 */
+ ///////////////////////////////////////
+ /// string3 descriptor
+ ///////////////////////////////////////
+ 0x1C, /* bLength */
+ USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
+ 'C', 0x00, /* wcChar0 */
+ 'h', 0x00, /* wcChar1 */
+ 'e', 0x00, /* wcChar2 */
+ 'r', 0x00, /* wcChar3 */
+ 'r', 0x00, /* wcChar4 */
+ 'y', 0x00, /* wcChar5 */
+ 'A', 0x00, /* wcChar6 */
+ 'D', 0x00, /* wcChar7 */
+ 'B', 0x00, /* wcChar8 */
+ '2', 0x00, /* wcChar9 */
+ '0', 0x00, /* wcChar10 */
+ '2', 0x00, /* wcChar11 */
+ '4', 0x00, /* wcChar12 */
+#ifdef CONFIG_USB_HS
+ ///////////////////////////////////////
+ /// device qualifier descriptor
+ ///////////////////////////////////////
+ 0x0a,
+ USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER,
+ 0x00,
+ 0x02,
+ 0x02,
+ 0x02,
+ 0x01,
+ 0x40,
+ 0x01,
+ 0x00,
+#endif
+ 0x00
+};
+
+static void usbd_event_handler(uint8_t busid, uint8_t event)
+{
+ switch (event) {
+ case USBD_EVENT_RESET:
+ break;
+ case USBD_EVENT_CONNECTED:
+ break;
+ case USBD_EVENT_DISCONNECTED:
+ break;
+ case USBD_EVENT_RESUME:
+ break;
+ case USBD_EVENT_SUSPEND:
+ break;
+ case USBD_EVENT_CONFIGURED:
+
+ break;
+ case USBD_EVENT_SET_REMOTE_WAKEUP:
+ break;
+ case USBD_EVENT_CLR_REMOTE_WAKEUP:
+ break;
+
+ default:
+ break;
+ }
+}
+
+static struct usbd_interface intf0;
+
+extern int shell_init(bool need_login);
+void cherryadb_init(uint8_t busid, uint32_t reg_base)
+{
+ /* default password is : 12345678 */
+ /* shell_init() must be called in-task */
+ if (0 != shell_init(false)) {
+ /* shell failed to be initialized */
+ printf("Failed to initialize shell\r\n");
+ for (;;) {
+ ;
+ }
+ }
+
+ usbd_desc_register(busid, adb_descriptor);
+ usbd_add_interface(busid, usbd_adb_init_intf(busid, &intf0, WINUSB_IN_EP, WINUSB_OUT_EP));
+ usbd_initialize(busid, reg_base, usbd_event_handler);
+} \ No newline at end of file