Browse Source

feat: add usb adb device with cherrysh

sakumisu 1 năm trước cách đây
mục cha
commit
e4954d4194
9 tập tin đã thay đổi với 908 bổ sung0 xóa
  1. 5 0
      Kconfig
  2. 1 0
      README.md
  3. 1 0
      README_zh.md
  4. 4 0
      cherryusb.cmake
  5. 301 0
      class/adb/usbd_adb.c
  6. 38 0
      class/adb/usbd_adb.h
  7. BIN
      demo/adb/cherryadb.png
  8. 330 0
      demo/adb/cherrysh_port.c
  9. 228 0
      demo/adb/usbd_adb_template.c

+ 5 - 0
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

+ 1 - 0
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):

+ 1 - 0
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):

+ 4 - 0
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")

+ 301 - 0
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));
+}

+ 38 - 0
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 */

BIN
demo/adb/cherryadb.png


+ 330 - 0
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));

+ 228 - 0
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);
+}