aboutsummaryrefslogtreecommitdiff
path: root/src/usb
diff options
context:
space:
mode:
authorFrançois Cartegnie <281376+fcartegnie@users.noreply.github.com>2024-08-20 13:43:09 +0700
committerGitHub <noreply@github.com>2024-08-20 13:43:09 +0700
commitf5874d607315ff88b27414299089b8528b5bb07c (patch)
tree9db23b8e6cdfb06d8bab77e83224e604f20c4629 /src/usb
parentb25d62d1362cfd711e6617a9212e3eddc4e9fddd (diff)
parentc3912e01a2767334103cd139d875d50c834f3f05 (diff)
Merge pull request #35 from kienvo/usb
feat: Receive data over USB
Diffstat (limited to 'src/usb')
-rw-r--r--src/usb/composite/cdc-serial.c202
-rw-r--r--src/usb/composite/hiddev.c230
-rw-r--r--src/usb/ctrl.c151
-rw-r--r--src/usb/debug.c82
-rw-r--r--src/usb/debug.h14
-rw-r--r--src/usb/dev.c176
-rw-r--r--src/usb/setup.c132
-rw-r--r--src/usb/usb.h16
-rw-r--r--src/usb/utils.c130
-rw-r--r--src/usb/utils.h30
10 files changed, 1163 insertions, 0 deletions
diff --git a/src/usb/composite/cdc-serial.c b/src/usb/composite/cdc-serial.c
new file mode 100644
index 0000000..99f0c40
--- /dev/null
+++ b/src/usb/composite/cdc-serial.c
@@ -0,0 +1,202 @@
+#include <stdint.h>
+#include <memory.h>
+
+#include "CH58x_common.h"
+
+#include "../utils.h"
+#include "../debug.h"
+
+#define NOTI_EP_NUM (2)
+#define DATA_EP_NUM (3)
+#define ACM_IF_NUM (1)
+
+#define USB_DESCTYPE_CS_INTERFACE 0x24
+
+static __attribute__((aligned(4))) uint8_t noti_ep_buf[64 + 64];
+static uint8_t *const noti_ep_out = noti_ep_buf;
+static uint8_t *const noti_ep_in = noti_ep_buf + 64;
+
+static __attribute__((aligned(4))) uint8_t data_ep_buf[64 + 64];
+static uint8_t *const data_ep_out = data_ep_buf;
+static uint8_t *const data_ep_in = data_ep_buf + 64;
+
+static void (*on_write)(uint8_t *buf, uint16_t len);
+
+/* CDC Communication interface */
+static USB_ITF_DESCR acm_if_desc = {
+ .bLength = sizeof(USB_ITF_DESCR),
+ .bDescriptorType = USB_DESCR_TYP_INTERF,
+ .bInterfaceNumber = ACM_IF_NUM,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 3, // A Notification and RX/TX Endpoint
+
+ .bInterfaceClass = 0x02, /* Communications and CDC Control */
+ .bInterfaceSubClass = 2, /* ACM subclass */
+ .bInterfaceProtocol = 1, /* AT Command V.250 protocol */
+
+ .iInterface = 0 /* No string descriptor */
+};
+
+/* Header Functional descriptor */
+static uint8_t header_func_desc[] = {
+ 5, /* bLength */
+ USB_DESCTYPE_CS_INTERFACE, /* bDescriptortype */
+ 0x00, /* bDescriptorsubtype, HEADER */
+ 0x10, 0x01, /* bcdCDC */
+};
+
+/* ACM Functional descriptor */
+static uint8_t acm_func_desc[] = {
+ 4, /* bLength */
+ USB_DESCTYPE_CS_INTERFACE, /* bDescriptortype */
+ 0x02, /* bDescriptorsubtype, ABSTRACT CONTROL MANAGEMENT */
+ 0x02, /* bmCapabilities: Supports subset of ACM commands */
+};
+
+/* Call Management Functional descriptor */
+static uint8_t callmgr_func_desc[] = {
+ 4, /* bLength */
+ USB_DESCTYPE_CS_INTERFACE, /* bDescriptortype */
+ 0x01, /* bDescriptorsubtype, CALL MANAGEMENT */
+ 0x03, /* bmCapabilities, DIY */
+};
+
+/* Notification Endpoint descriptor */
+static USB_ENDP_DESCR noti_ep_desc = {
+ .bLength = sizeof(USB_ENDP_DESCR),
+ .bDescriptorType = USB_DESCR_TYP_ENDP,
+ .bEndpointAddress = 0x80 | NOTI_EP_NUM, /* IN endpoint */
+ .bmAttributes = 0x03, /* Interrupt transfer */
+ .wMaxPacketSize = 64, /* bytes */
+ .bInterval = 0xff
+};
+
+/* Data TX Endpoint descriptor */
+static USB_ENDP_DESCR tx_ep_desc = {
+ .bLength = sizeof(USB_ENDP_DESCR),
+ .bDescriptorType = USB_DESCR_TYP_ENDP,
+ .bEndpointAddress = 0x80 | DATA_EP_NUM, /* IN endpoint */
+ .bmAttributes = 0x02, /* Bulk */
+ .wMaxPacketSize = MAX_PACKET_SIZE, /* bytes */
+ .bInterval = 0xff
+};
+
+/* Data RX Endpoint descriptor */
+static USB_ENDP_DESCR rx_ep_desc = {
+ .bLength = sizeof(USB_ENDP_DESCR),
+ .bDescriptorType = USB_DESCR_TYP_ENDP,
+ .bEndpointAddress = DATA_EP_NUM, /* OUT endpoint */
+ .bmAttributes = 0x02, /* Bulk */
+ .wMaxPacketSize = MAX_PACKET_SIZE, /* bytes */
+ .bInterval = 0xff
+};
+
+static void cdc_request_handler(USB_SETUP_REQ * request)
+{
+ _TRACE();
+ // Handle CDC Class Request here
+}
+
+static void noti_ep_handler()
+{
+ _TRACE();
+ // Handle Subclass Request here
+}
+
+static volatile uint16_t transferred;
+
+static void data_ep_handler()
+{
+ _TRACE();
+ static int tog;
+
+ uint8_t token = R8_USB_INT_ST & MASK_UIS_TOKEN;
+ switch(token) {
+ case UIS_TOKEN_OUT:
+ if (on_write)
+ on_write(data_ep_out, R8_USB_RX_LEN);
+ tog = !tog;
+ set_handshake(DATA_EP_NUM, USB_ACK, tog, 0);
+ break;
+
+ case UIS_TOKEN_IN:
+ if (transferred == 0) {
+ transferred = 1;
+ } else {
+ set_handshake(DATA_EP_NUM, USB_NAK, 1, 0);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+// In case we want to send something to the host,
+// or want to see the log over ttyACMx
+void cdc_fill_IN(uint8_t *buf, uint8_t len)
+{
+ if (len > tx_ep_desc.wMaxPacketSize)
+ return;
+
+ static int tog;
+
+ memcpy(data_ep_in, buf, len);
+ set_handshake(DATA_EP_NUM, USB_ACK, tog, len);
+
+ tog = !tog;
+ transferred = 0;
+}
+
+static int wait_until_sent(uint16_t timeout_ms)
+{
+ while(timeout_ms--) {
+ if (transferred) {
+ return 0;
+ }
+ DelayMs(1);
+ }
+ return -1;
+}
+
+int cdc_tx_poll(uint8_t *buf, int len, uint16_t timeout_ms)
+{
+ int i = 0;
+ while (len > tx_ep_desc.wMaxPacketSize) {
+ cdc_fill_IN(buf + i, tx_ep_desc.wMaxPacketSize);
+ if (wait_until_sent(timeout_ms))
+ return -1;
+
+ i += tx_ep_desc.wMaxPacketSize;
+ len -= tx_ep_desc.wMaxPacketSize;
+ }
+ cdc_fill_IN(buf + i, len);
+ if (wait_until_sent(timeout_ms))
+ return -1;
+
+ return 0;
+}
+
+void cdc_onWrite(void (*cb)(uint8_t *buf, uint16_t len))
+{
+ on_write = cb;
+}
+
+void cdc_acm_init()
+{
+ cfg_desc_append(&acm_if_desc);
+ cfg_desc_append(header_func_desc);
+ cfg_desc_append(acm_func_desc);
+ cfg_desc_append(callmgr_func_desc);
+
+ cfg_desc_append(&noti_ep_desc);
+ cfg_desc_append(&rx_ep_desc);
+ cfg_desc_append(&tx_ep_desc);
+
+ if_cb_register(ACM_IF_NUM, cdc_request_handler);
+ ep_cb_register(NOTI_EP_NUM, noti_ep_handler);
+ ep_cb_register(DATA_EP_NUM, data_ep_handler);
+
+ dma_register(NOTI_EP_NUM, noti_ep_buf);
+ dma_register(DATA_EP_NUM, data_ep_buf);
+} \ No newline at end of file
diff --git a/src/usb/composite/hiddev.c b/src/usb/composite/hiddev.c
new file mode 100644
index 0000000..bb075e5
--- /dev/null
+++ b/src/usb/composite/hiddev.c
@@ -0,0 +1,230 @@
+#include <stdint.h>
+#include <memory.h>
+
+#include "CH58x_common.h"
+
+#include "../utils.h"
+#include "../debug.h"
+
+#define EP_NUM (1)
+#define IF_NUM (0)
+
+static __attribute__((aligned(4))) uint8_t ep_buf[64 + 64];
+static uint8_t *const ep_out = ep_buf;
+static uint8_t *const ep_in = ep_buf + 64;
+
+static void (*on_write)(uint8_t *buf, uint16_t len);
+
+static USB_ITF_DESCR if_desc = {
+ .bLength = sizeof(USB_ITF_DESCR),
+ .bDescriptorType = USB_DESCR_TYP_INTERF,
+ .bInterfaceNumber = IF_NUM,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 2, /* One for read, one for write */
+ .bInterfaceClass = 0x03, /* HID class */
+ .bInterfaceSubClass = 0, /* No subclass */
+ .bInterfaceProtocol = 0, /* Not a Mouse nor Keyboard */
+ .iInterface = 0 /* Index of string descriptor */
+};
+
+static USB_HID_DESCR hid_desc = {
+ .bLength = sizeof(USB_HID_DESCR),
+ .bDescriptorType = USB_DESCR_TYP_HID,
+ .bcdHID = 0x0100,
+ .bCountryCode = 0x00,
+ .bNumDescriptors = 0x01,
+ .bDescriptorTypeX = 0x22,
+ .wDescriptorLengthL = 0x22,
+ .wDescriptorLengthH = 0x00
+};
+
+static USB_ENDP_DESCR read_ep_desc = {
+ .bLength = sizeof(USB_ENDP_DESCR),
+ .bDescriptorType = USB_DESCR_TYP_ENDP,
+ .bEndpointAddress = 0x80 | EP_NUM, /* IN endpoint */
+ .bmAttributes = 0x03, /* exchange data over Interrupt */
+ .wMaxPacketSize = MAX_PACKET_SIZE, /* bytes */
+ .bInterval = 0xff /* polling interval */
+};
+
+static USB_ENDP_DESCR write_ep_desc = {
+ .bLength = sizeof(USB_ENDP_DESCR),
+ .bDescriptorType = USB_DESCR_TYP_ENDP,
+ .bEndpointAddress = EP_NUM, /* IN endpoint */
+ .bmAttributes = 0x03, /* exchange data over Interrupt */
+ .wMaxPacketSize = MAX_PACKET_SIZE, /* bytes */
+ .bInterval = 8 /* polling interval */
+};
+
+static const uint8_t report_desc[] = {
+ 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
+ 0x09, 0x01, // Usage (0x01)
+ 0xA1, 0x01, // Collection (Application)
+
+ /* IN */
+ 0x09, 0x02, // Usage (0x02)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x26, 0x00, 0xFF, // Logical Maximum (-256)
+ 0x75, 0x08, // Report Size (8)
+ 0x95, 0x40, // Report Count (64)
+ 0x81, 0x06, // INPUT
+
+ /* OUT */
+ 0x09, 0x02, // Usage (0x02)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x26, 0x00, 0xFF, // Logical Maximum (-256)
+ 0x75, 0x08, // Report Size (8)
+ 0x95, 0x40, // Report Count (64)
+ 0x91, 0x06, // OUTPUT
+
+ 0xC0, // End Collection
+};
+
+static void hid_request_handler(USB_SETUP_REQ * request)
+{
+ _TRACE();
+ static uint8_t report_val, idle_val;
+ uint8_t req = request->bRequest;
+ uint16_t type = request->wValue >> 8;
+
+ switch(req) {
+ case USB_GET_DESCRIPTOR:
+ PRINT("- USB_GET_DESCRIPTOR\n");
+ PRINT("bDescriptorType: 0x%02x\n", type);
+ switch (type)
+ {
+ case USB_DESCR_TYP_REPORT:
+ PRINT("- USB_DESCR_TYP_REPORT\n");
+ ctrl_start_load_block(report_desc, sizeof(report_desc));
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case HID_GET_REPORT:
+ PRINT("- HID_GET_REPORT\n");
+ break;
+
+ case HID_GET_IDLE:
+ PRINT("- HID_GET_IDLE\n");
+ ctrl_start_load_block(&idle_val, 1);
+ break;
+
+ case HID_GET_PROTOCOL:
+ PRINT("- HID_GET_PROTOCOL\n");
+ ctrl_start_load_block(&report_val, 1);
+ break;
+
+ case HID_SET_REPORT:
+ PRINT("- HID_SET_REPORT\n");
+ ctrl_ack();
+ break;
+
+ case HID_SET_IDLE:
+ PRINT("- HID_SET_IDLE\n");
+ ctrl_ack();
+ break;
+
+ case HID_SET_PROTOCOL:
+ PRINT("- HID_SET_PROTOCOL\n");
+ break;
+
+ default:
+ break;
+ }
+}
+
+static volatile uint16_t transferred;
+
+static void ep_handler()
+{
+ _TRACE();
+ static int tog;
+ uint8_t token = R8_USB_INT_ST & MASK_UIS_TOKEN;
+
+ switch(token) {
+ case UIS_TOKEN_OUT:
+ if (on_write)
+ on_write(ep_out, R8_USB_RX_LEN);
+ set_handshake(EP_NUM, USB_ACK, tog, 0);
+ tog = !tog;
+ break;
+
+ case UIS_TOKEN_IN:
+ if (transferred == 0) {
+ transferred = 1;
+ } else {
+ set_handshake(EP_NUM, USB_NAK, 1, 0);
+ }
+ break;
+ break;
+
+ default:
+ break;
+ }
+}
+
+// In case we want to send something to the host,
+// or want to see the log over hidraw by `cat /dev/hidrawX`
+void hiddev_fill_IN(uint8_t *buf, uint8_t len)
+{
+ if (len > read_ep_desc.wMaxPacketSize)
+ return;
+
+ static int tog;
+
+ memcpy(ep_in, buf, len);
+ set_handshake(EP_NUM, USB_ACK, tog, len);
+
+ tog = !tog;
+ transferred = 0;
+}
+
+static int wait_until_sent(uint16_t timeout_ms)
+{
+ while(timeout_ms--) {
+ if (transferred) {
+ return 0;
+ }
+ DelayMs(1);
+ }
+ return -1;
+}
+
+int hiddev_tx_poll(uint8_t *buf, int len, uint16_t timeout_ms)
+{
+ int i = 0;
+ while (len > read_ep_desc.wMaxPacketSize) {
+ hiddev_fill_IN(buf + i, read_ep_desc.wMaxPacketSize);
+ if (wait_until_sent(timeout_ms))
+ return -1;
+
+ i += read_ep_desc.wMaxPacketSize;
+ len -= read_ep_desc.wMaxPacketSize;
+ }
+ hiddev_fill_IN(buf + i, len);
+ if (wait_until_sent(timeout_ms))
+ return -1;
+
+ return 0;
+}
+
+void hiddev_onWrite(void (*cb)(uint8_t *buf, uint16_t len))
+{
+ on_write = cb;
+}
+
+void hiddev_init()
+{
+ cfg_desc_append(&if_desc);
+ cfg_desc_append(&hid_desc);
+ cfg_desc_append(&read_ep_desc);
+ cfg_desc_append(&write_ep_desc);
+
+ if_cb_register(IF_NUM, hid_request_handler);
+ ep_cb_register(EP_NUM, ep_handler);
+
+ dma_register(EP_NUM, ep_out);
+}
diff --git a/src/usb/ctrl.c b/src/usb/ctrl.c
new file mode 100644
index 0000000..30aa7dd
--- /dev/null
+++ b/src/usb/ctrl.c
@@ -0,0 +1,151 @@
+
+#include "CH58x_common.h"
+
+#include "utils.h"
+#include "debug.h"
+
+extern void (*if_handlers[])(USB_SETUP_REQ *request);
+extern void (*ep_handlers[])();
+
+void handle_devreq(USB_SETUP_REQ *request);
+
+static uint8_t address;
+
+/* EP0 + EP4(IN + OUT) */
+__attribute__((aligned(4))) uint8_t ep0buf[64 + 64 + 64];
+
+void usb_set_address(uint8_t ad)
+{
+ address = ad;
+}
+
+void ctrl_start_load_block(void *buf, uint16_t len)
+{
+ usb_start_load_block(ep0buf, buf, len, 1);
+}
+
+static void route_interfaces(USB_SETUP_REQ *request)
+{
+ _TRACE();
+
+ uint8_t ifn = request->wIndex & 0xff;
+ PRINT("wInterfaceNumber: 0x%02x\n", ifn);
+
+ if (if_handlers[ifn])
+ if_handlers[ifn](request);
+}
+
+static void ep_handler()
+{
+ _TRACE();
+
+ USB_SETUP_REQ *request = (USB_SETUP_REQ *)ep0buf;
+ uint8_t req = request->bRequest;
+
+ /* Each interface will have their own request handler */
+ uint8_t recip = request->bRequestType & USB_REQ_RECIP_MASK;
+ if (recip == USB_REQ_RECIP_INTERF) {
+ route_interfaces(request);
+ return;
+ }
+
+ uint8_t token = R8_USB_INT_ST & MASK_UIS_TOKEN;
+ switch(token) {
+
+ case UIS_TOKEN_SETUP:
+ print_setuppk(req);
+
+ if (recip == USB_REQ_RECIP_DEVICE) {
+ handle_devreq(request);
+ }
+ break;
+
+ case UIS_TOKEN_OUT:
+ if (req == USB_CLEAR_FEATURE) {
+ ctrl_ack();
+ }
+
+ case UIS_TOKEN_IN:
+ R8_USB_DEV_AD = address;
+ usb_load_next_chunk();
+ break;
+
+ default:
+ break;
+ }
+}
+
+void ctrl_init()
+{
+ ep_cb_register(0, ep_handler);
+ dma_register(0, ep0buf);
+}
+
+static void route_enpoints()
+{
+ _TRACE();
+
+ /* Workaround to solve a setup packet with EP 0x02 for unknown reason.
+ This happens when return from the bootloader or after a sotfware reset. */
+ uint8_t token = R8_USB_INT_ST & MASK_UIS_TOKEN;
+ if (token == UIS_TOKEN_SETUP) {
+ ep_handlers[0]();
+ }
+
+ uint8_t ep_num = R8_USB_INT_ST & MASK_UIS_ENDP;
+
+ if (ep_num < 8 && ep_handlers[ep_num])
+ ep_handlers[ep_num]();
+}
+
+static void handle_busReset()
+{
+ _TRACE();
+ R8_USB_DEV_AD = 0;
+ R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
+ R8_UEP1_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
+ R8_UEP2_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
+ R8_UEP3_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
+ R8_UEP4_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
+ R8_UEP5_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
+ R8_UEP6_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
+ R8_UEP7_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
+}
+
+static void handle_powerChange()
+{
+ _TRACE();
+ if (R8_USB_MIS_ST & RB_UMS_SUSPEND) {
+ ;// suspend
+ }
+ else {
+ ;// resume
+ }
+}
+
+__INTERRUPT
+__HIGH_CODE
+void USB_IRQHandler(void) {
+ uint8_t intflag = R8_USB_INT_FG;
+ clear_handshake_sent_flag();
+ PRINT("\nusb: new interrupt\n");
+ print_intflag_reg();
+
+ if (intflag & RB_UIF_TRANSFER) {
+ PRINT("usb: RX Length reg: %d\n", R8_USB_RX_LEN);
+ print_status_reg();
+
+ route_enpoints();
+ }
+ else if (intflag & RB_UIF_BUS_RST) {
+ handle_busReset();
+ }
+ else if (intflag & RB_UIF_SUSPEND) {
+ handle_powerChange();
+ }
+
+ if (handshake_sent() == 0) {
+ PRINT("WARN: This transaction is being IGNORED!\n");
+ }
+ R8_USB_INT_FG = intflag; // clear interrupt flags
+} \ No newline at end of file
diff --git a/src/usb/debug.c b/src/usb/debug.c
new file mode 100644
index 0000000..0c9f443
--- /dev/null
+++ b/src/usb/debug.c
@@ -0,0 +1,82 @@
+#include "CH58x_common.h"
+
+void print_setuppk(USB_SETUP_REQ *request)
+{
+ PRINT("Setup request:\n");
+
+ char *dir[] = {"OUT", "IN"};
+ char *type[] = {"Standard", "Class", "Vendor", "Reserved Type"};
+ char *recipient[] = {
+ "Device", "Interface", "Endpoint", "Other","Reserved Recipient"
+ };
+ char *bRequest[] = {
+ "GET_STATUS",
+ "CLEAR_FEATURE",
+ "Reserved",
+ "SET_FEATURE",
+ "Reserved",
+ "SET_ADDRESS",
+ "GET_DESCRIPTOR",
+ "SET_DESCRIPTOR",
+ "GET_CONFIGURATION",
+ "SET_CONFIGURATION",
+ "GET_INTERFACE",
+ "SET_INTERFACE",
+ "SYNCH_FRAME",
+ };
+
+ uint8_t t = request->bRequestType;
+ uint8_t req = request->bRequest;
+ uint8_t type_val = (t & USB_REQ_TYP_MASK) >> 5;
+ uint8_t recip_val = t & USB_REQ_RECIP_MASK;
+ if (recip_val > 4)
+ recip_val = 4;
+
+ PRINT("\t- bRequestType: 0x%02x (%s|%s|%s)\n", t, dir[t>>7],
+ type[type_val],
+ recipient[recip_val]);
+
+ PRINT("\t- bRequest: 0x%02x (%s)\n", req, req > 12 ?
+ "N/A" : bRequest[req]);
+ PRINT("\t- wValue: 0x%04x\n", request->wValue);
+
+ PRINT("\t- wIndex%s: 0x%04x\n",
+ recip_val == USB_REQ_RECIP_INTERF ? " (wInterfaceNumber)" : "",
+ request->wIndex);
+
+ PRINT("\t- wLength: 0x%04x\n", request->wLength);
+}
+
+void print_status_reg()
+{
+ char *token_type[] = {
+ "OUT", "SOF","IN", "SETUP",
+ };
+ uint8_t reg = R8_USB_INT_ST;
+ uint8_t is_setup = reg & RB_UIS_SETUP_ACT;
+ uint8_t toggle = reg & RB_UIS_TOG_OK;
+ uint8_t token = (reg & MASK_UIS_TOKEN) >> 4;
+ uint8_t ep_num = reg & MASK_UIS_ENDP;
+ PRINT("usb: Status reg: 0x%02x (%s|%s|%s|EP:%d)\n", reg,
+ is_setup ? "SETUP??" : "0",
+ toggle ? "TOGGLE OK" : "0", token_type[token], ep_num);
+}
+
+void print_intflag_reg()
+{
+ uint8_t reg = R8_USB_INT_FG;
+ uint8_t is_nak = reg & RB_U_IS_NAK;
+ uint8_t tog_ok = reg & RB_U_TOG_OK;
+ uint8_t sie = reg & RB_U_SIE_FREE;
+ uint8_t overflow = reg & RB_UIF_FIFO_OV;
+ uint8_t suspend = reg & RB_UIF_SUSPEND;
+ uint8_t xfer_complete = reg & RB_UIF_TRANSFER;
+ uint8_t bus_reset = reg & RB_UIF_BUS_RST;
+ PRINT("usb: Interrupt reg: 0x%02x (%s|%s|%s|%s|%s|%s)\n", reg,
+ is_nak ? "NAK received" : "0",
+ tog_ok ? "Toggle ok" : "0",
+ sie ? "SIE" : "0",
+ overflow ? "FIFO overflow" : "0",
+ suspend ? "Suspend" : "0",
+ xfer_complete ? "Xfer completed" : "0");
+} \ No newline at end of file
diff --git a/src/usb/debug.h b/src/usb/debug.h
new file mode 100644
index 0000000..1279115
--- /dev/null
+++ b/src/usb/debug.h
@@ -0,0 +1,14 @@
+#ifndef __DEBUG_H__
+#define __DEBUG_H__
+
+#include "CH58x_common.h"
+
+
+#define _TRACE() PRINT("> "); PRINT(__FILE__);PRINT(": "); PRINT(__func__); \
+ PRINT("()\n")
+
+void print_setuppk(USB_SETUP_REQ *setup_req_pk);
+void print_status_reg();
+void print_intflag_reg();
+
+#endif /* __DEBUG_H__ */
diff --git a/src/usb/dev.c b/src/usb/dev.c
new file mode 100644
index 0000000..fb28e43
--- /dev/null
+++ b/src/usb/dev.c
@@ -0,0 +1,176 @@
+#include "CH58x_common.h"
+
+#include "utils.h"
+#include "debug.h"
+
+extern uint8_t *cfg_desc;
+
+void usb_set_address(uint8_t ad);
+
+USB_DEV_DESCR dev_desc = {
+ .bLength = sizeof(USB_DEV_DESCR),
+ .bDescriptorType = 0x01,
+ .bcdUSB = 0x0110,
+
+ .bDeviceClass = 0x00,
+ .bDeviceSubClass = 0x00,
+ .bDeviceProtocol = 0x00,
+
+ .bMaxPacketSize0 = MAX_PACKET_SIZE,
+
+ .idVendor = 0x0416,
+ .idProduct = 0x5020,
+ .bcdDevice = 0x0000,
+ .iManufacturer = 1,
+ .iProduct = 2,
+ .iSerialNumber = 3,
+ .bNumConfigurations = 0x01
+};
+
+/* String Descriptor Zero, Specifying Languages Supported by the Device */
+static uint8_t lang_desc[] = {
+ 0x04, /* bLength */
+ 0x03, /* bDescriptorType */
+ 0x09, 0x04 /* wLANGID - en-US */
+};
+
+static uint16_t vendor_info[] = {
+ 36 | /* bLength */
+ 0x03 << 8, /* bDescriptorType */
+
+ /* bString */
+ 'F', 'O', 'S', 'S', 'A', 'S', 'I', 'A', ' ',
+ 'W', 'A', 'S', ' ', 'H', 'E', 'R', 'E'
+};
+
+static uint16_t product_info[] = {
+ 32 | /* bLength */
+ 0x03 << 8, /* bDescriptorType */
+
+ /* bString */
+ 'L', 'E', 'D', ' ',
+ 'B', 'a', 'd', 'g', 'e', ' ',
+ 'M', 'a', 'g', 'i', 'c'
+};
+
+// TODO: auto update firmware version by CI here
+static uint16_t serial_number[] = {
+ 47 * 2 | /* bLength */
+ 0x03 << 8, /* bDescriptorType */
+
+ /* bString */
+ 'N', 'o', 't', ' ', 'y', 'e', 't', ' ',
+ 'i', 'm', 'p', 'l', 'e', 'm', 'e', 'n', 't', 'e', 'd', '\n',
+ 'P', 'R', 'E', 'S', 'S', ' ', 'A', 'L', 'T', '+', 'F', '4', ' ',
+ 'T', 'O', ' ', 'C', 'O', 'N', 'T', 'I', 'N', 'U', 'E', '.'
+};
+
+static void desc_dev(USB_SETUP_REQ *request)
+{
+ ctrl_start_load_block(&dev_desc, dev_desc.bLength);
+}
+
+static void desc_config(USB_SETUP_REQ *request)
+{
+ ctrl_start_load_block(cfg_desc, request->wLength);
+}
+
+static void desc_string(USB_SETUP_REQ *request)
+{
+ uint8_t *string_index[32] = {
+ lang_desc,
+ vendor_info,
+ product_info,
+ serial_number
+ };
+ uint8_t index = request->wValue & 0xff;
+ if (index <= sizeof(string_index))
+ ctrl_start_load_block(string_index[index], string_index[index][0]);
+}
+
+static void dev_getDesc(USB_SETUP_REQ *request)
+{
+ _TRACE();
+ uint8_t type = request->wValue >> 8;
+
+ PRINT("Descriptor type: 0x%02x\n", type);
+
+ static const void (*desc_type_handlers[4])(USB_SETUP_REQ *request) = {
+ NULL,
+ desc_dev,
+ desc_config,
+ desc_string
+ };
+ if (type <= 3 && desc_type_handlers[type])
+ desc_type_handlers[type](request);
+}
+
+static void dev_getStatus(USB_SETUP_REQ *request)
+{
+ _TRACE();
+ // Remote Wakeup disabled | Bus powered, hardcoded for now
+ uint8_t buf[] = {0, 0};
+ ctrl_start_load_block(buf, 2);
+}
+
+static void dev_clearFeature(USB_SETUP_REQ *request)
+{
+ _TRACE();
+ // DEVICE_REMOTE_WAKEUP and TEST_MODE are not available, ignore.
+}
+
+static void dev_setFeature(USB_SETUP_REQ *request)
+{
+ _TRACE();
+ // DEVICE_REMOTE_WAKEUP and TEST_MODE are not available, ignore.
+}
+
+static void dev_setAddress(USB_SETUP_REQ *request)
+{
+ _TRACE();
+ /* new address will be loadled in the next IN poll,
+ so here just sending a ACK */
+ usb_set_address(request->wValue & 0xff);
+ ctrl_ack();
+}
+
+// For now, multiple configuration is not supported
+static uint8_t devcfg;
+
+static void dev_getConfig(USB_SETUP_REQ *request)
+{
+ _TRACE();
+ ctrl_start_load_block(&devcfg, 1);
+}
+
+static void dev_setConfig(USB_SETUP_REQ *request)
+{
+ _TRACE();
+ devcfg = (request->wValue) & 0xff;
+ ctrl_ack();
+}
+
+void handle_devreq(USB_SETUP_REQ *request)
+{
+ _TRACE();
+
+ static const void (*dev_req_handlers[13])(USB_SETUP_REQ *request) = {
+ dev_getStatus,
+ dev_clearFeature,
+ NULL, // Reserved
+ dev_setFeature,
+ NULL, // Reserved
+ dev_setAddress,
+ dev_getDesc,
+ NULL, // set desc
+ dev_getConfig,
+ dev_setConfig,
+ NULL, // get interface
+ NULL, // set interface
+ NULL, // sync frame
+ };
+
+ uint8_t req = request->bRequest;
+ if (req <= 12 && dev_req_handlers[req])
+ dev_req_handlers[req](request);
+}
diff --git a/src/usb/setup.c b/src/usb/setup.c
new file mode 100644
index 0000000..d53c318
--- /dev/null
+++ b/src/usb/setup.c
@@ -0,0 +1,132 @@
+#include <stdlib.h>
+
+#include "CH58x_common.h"
+
+#include "utils.h"
+
+uint8_t *cfg_desc;
+
+// FIXME: here wasting 1KiB of ram
+void (*if_handlers[256])(USB_SETUP_REQ *request);
+void (*ep_handlers[7])();
+
+/* Configuration Descriptor template */
+USB_CFG_DESCR cfg_static = {
+ .bLength = sizeof(USB_CFG_DESCR),
+ .bDescriptorType = 0x02,
+ .wTotalLength = sizeof(USB_CFG_DESCR), // will be updated on cfg_desc_add()
+ .bNumInterfaces = 0, // will be updated on cfg_desc_add()
+ .bConfigurationValue = 0x01,
+ .iConfiguration = 4,
+ .bmAttributes = 0xA0,
+ .MaxPower = 50 // mA
+};
+
+void cfg_desc_append(void *desc)
+{
+ uint8_t cfg_len = 0;
+ uint8_t len = ((uint8_t *)desc)[0];
+ if (cfg_desc) // attempting to add a non-Configuration Descriptor
+ cfg_len = ((USB_CFG_DESCR *)cfg_desc)->wTotalLength;
+ uint8_t newlen = cfg_len + len;
+
+ cfg_desc = realloc(cfg_desc, newlen); // TODO: add a safe check here
+
+ memcpy(cfg_desc + cfg_len, desc, len);
+
+ ((USB_CFG_DESCR *)cfg_desc)->wTotalLength = newlen;
+ ((USB_CFG_DESCR *)cfg_desc)->bNumInterfaces += ((uint8_t *)desc)[1] == 0x04;
+}
+
+int ep_cb_register(int ep_num, void (*cb)())
+{
+ if (ep_num > 8)
+ return -1;
+ if (ep_handlers[ep_num]) // already registerd
+ return -1;
+
+ ep_handlers[ep_num] = cb;
+ return 0;
+}
+
+int if_cb_register(uint8_t if_num, void (*cb)(USB_SETUP_REQ *request))
+{
+ if (if_handlers[if_num]) // already registered
+ return -1;
+
+ if_handlers[if_num] = cb;
+ return 0;
+}
+
+void dma_register(uint8_t ep_num, void *buf)
+{
+ if (ep_num > 7) // CH582 support only 8 endpoint
+ return;
+
+ volatile uint16_t *p_regs[] = {
+ &R16_UEP0_DMA,
+ &R16_UEP1_DMA,
+ &R16_UEP2_DMA,
+ &R16_UEP3_DMA,
+ NULL,
+ &R16_UEP5_DMA,
+ &R16_UEP6_DMA,
+ &R16_UEP7_DMA,
+ };
+ volatile uint8_t *ctrl_regs[] = {
+ &R8_UEP0_CTRL,
+ &R8_UEP1_CTRL,
+ &R8_UEP2_CTRL,
+ &R8_UEP3_CTRL,
+ &R8_UEP4_CTRL,
+ &R8_UEP5_CTRL,
+ &R8_UEP6_CTRL,
+ &R8_UEP7_CTRL,
+ };
+ // ep4's dma buffer is hardcoded pointing to
+ // the next ep0
+ if (ep_num != 4)
+ *p_regs[ep_num] = (uint16_t)(uint32_t)buf;
+
+ *ctrl_regs[ep_num] = UEP_R_RES_ACK | UEP_T_RES_NAK;
+}
+
+static void init(void)
+{
+ R8_USB_CTRL = 0x00;
+
+ R8_UEP4_1_MOD = RB_UEP4_RX_EN | RB_UEP4_TX_EN |
+ RB_UEP1_RX_EN | RB_UEP1_TX_EN;
+ R8_UEP2_3_MOD = RB_UEP2_RX_EN | RB_UEP2_TX_EN |
+ RB_UEP3_RX_EN | RB_UEP3_TX_EN;
+ R8_UEP567_MOD = RB_UEP7_RX_EN | RB_UEP7_TX_EN |
+ RB_UEP6_RX_EN | RB_UEP6_TX_EN |
+ RB_UEP5_RX_EN | RB_UEP5_TX_EN;
+
+ R16_PIN_ANALOG_IE |= RB_PIN_USB_IE | RB_PIN_USB_DP_PU;
+ R8_UDEV_CTRL = RB_UD_PD_DIS | RB_UD_PORT_EN;
+
+ R8_USB_DEV_AD = 0x00;
+ R8_USB_INT_FG = 0xFF;
+ R8_USB_CTRL = RB_UC_DEV_PU_EN | RB_UC_INT_BUSY | RB_UC_DMA_EN;
+ R8_USB_INT_EN = RB_UIE_SUSPEND | RB_UIE_BUS_RST | RB_UIE_TRANSFER;
+}
+
+void ctrl_init();
+void hiddev_init();
+void cdc_acm_init();
+
+void usb_start() {
+ cfg_desc_append(&cfg_static);
+
+ ctrl_init();
+
+ /* This should be placed first, the python script always looks
+ for the first interface (not the interface number) */
+ hiddev_init();
+
+ cdc_acm_init();
+
+ init();
+ PFIC_EnableIRQ(USB_IRQn);
+} \ No newline at end of file
diff --git a/src/usb/usb.h b/src/usb/usb.h
new file mode 100644
index 0000000..f1d41f9
--- /dev/null
+++ b/src/usb/usb.h
@@ -0,0 +1,16 @@
+#ifndef __USB_H__
+#define __USB_H__
+
+#include <stdint.h>
+
+void cdc_fill_IN(uint8_t *buf, uint8_t len);
+int cdc_tx_poll(uint8_t *buf, int len, uint16_t timeout_ms);
+void cdc_onWrite(void (*cb)(uint8_t *buf, uint16_t len));
+
+void hiddev_fill_IN(uint8_t *buf, uint8_t len);
+int hiddev_tx_poll(uint8_t *buf, int len, uint16_t timeout_ms);
+void hiddev_onWrite(void (*cb)(uint8_t *buf, uint16_t len));
+
+void usb_start();
+
+#endif /* __USB_H__ */
diff --git a/src/usb/utils.c b/src/usb/utils.c
new file mode 100644
index 0000000..5c6a83e
--- /dev/null
+++ b/src/usb/utils.c
@@ -0,0 +1,130 @@
+#include "CH58x_common.h"
+
+#include "utils.h"
+#include "debug.h"
+
+// A response a has been set, if not, that transaction is not handled
+int res_sent;
+
+static uint8_t *current_IN_buf;
+
+static volatile uint8_t *len_regs[] = {
+ &R8_UEP0_T_LEN,
+ &R8_UEP1_T_LEN,
+ &R8_UEP2_T_LEN,
+ &R8_UEP3_T_LEN,
+ &R8_UEP4_T_LEN,
+ &R8_UEP5_T_LEN,
+ &R8_UEP6_T_LEN,
+ &R8_UEP7_T_LEN
+};
+static volatile uint8_t *ctrl_regs[] = {
+ &R8_UEP0_CTRL,
+ &R8_UEP1_CTRL,
+ &R8_UEP2_CTRL,
+ &R8_UEP3_CTRL,
+ &R8_UEP4_CTRL,
+ &R8_UEP5_CTRL,
+ &R8_UEP6_CTRL,
+ &R8_UEP7_CTRL
+};
+
+static void __handshake(uint8_t ep_num, int dir, int type, int tog, uint8_t len)
+{
+ _TRACE();
+ if (ep_num > 7)
+ return;
+
+ char *type_mean[] = { "ACK", "NO_RESP", "NAK", "STALL"};
+ PRINT("Loaded %d byte, DATA%d with an %s to EP%dIN.\n",
+ len, tog != 0, type_mean[type], ep_num);
+
+ uint8_t ctrl = type << (dir ? 0 : 2);
+ ctrl |= (tog) ? RB_UEP_T_TOG : RB_UEP_R_TOG;
+
+ res_sent = 1;
+ *len_regs[ep_num] = len;
+ *ctrl_regs[ep_num] = ctrl;
+}
+
+void set_handshake(uint8_t ep_num, int type, int tog, uint8_t len)
+{
+ _TRACE();
+ __handshake(ep_num, 1, type, tog, len);
+}
+
+void ctrl_ack()
+{
+ _TRACE();
+ set_handshake(0, USB_ACK, 1, 0);
+}
+
+void clear_handshake_sent_flag()
+{
+ res_sent = 0;
+}
+
+int handshake_sent()
+{
+ return res_sent;
+}
+
+static uint8_t *p_buf;
+static uint16_t remain_len, req_len, _tog;
+
+static uint16_t
+load_chunk(void *ep_buf, void *block, uint16_t block_len, uint16_t req_len,
+ int tog)
+{
+ _TRACE();
+ uint16_t remain_len = 0;
+
+ if (req_len > MAX_PACKET_SIZE)
+ req_len = MAX_PACKET_SIZE;
+
+ if (block_len >= req_len) {
+ remain_len = block_len - req_len;
+ block_len = req_len;
+ }
+ p_buf = block;
+
+ memcpy(ep_buf, p_buf, block_len);
+ p_buf += block_len;
+
+ set_handshake(0, USB_ACK, tog, block_len);
+
+ PRINT("remain_len: %d\n", remain_len);
+ return remain_len;
+}
+
+void usb_flush()
+{
+ _TRACE();
+ remain_len = 0;
+ _tog = 0;
+}
+
+int usb_load_next_chunk()
+{
+ _TRACE();
+ if (remain_len) {
+ remain_len = load_chunk(current_IN_buf, p_buf, remain_len, req_len,
+ _tog);
+ _tog = !_tog;
+ } else {
+ _tog = 0;
+ return -1;
+ }
+ return remain_len;
+}
+
+uint16_t
+usb_start_load_block(void *ep_IN_buf, void *buf, uint16_t len, int tog)
+{
+ _TRACE();
+ req_len = len;
+ current_IN_buf = ep_IN_buf;
+ remain_len = load_chunk(ep_IN_buf, buf, len, len, tog);
+ _tog = !tog;
+ return remain_len;
+}
diff --git a/src/usb/utils.h b/src/usb/utils.h
new file mode 100644
index 0000000..595a8b7
--- /dev/null
+++ b/src/usb/utils.h
@@ -0,0 +1,30 @@
+#ifndef __UTILS_H__
+#define __UTILS_H__
+
+#include "CH58x_common.h"
+
+enum USB_RESPONSE_TYPE {
+ USB_ACK = 0,
+ USB_NO_RESP,
+ USB_NAK,
+ USB_STALL,
+};
+
+void cfg_desc_append(void *desc);
+int ep_cb_register(int ep_num, void (*cb)());
+int if_cb_register(uint8_t if_num, void (*cb)(USB_SETUP_REQ *request));
+void dma_register(uint8_t endpoint_number, void *buf_addr);
+
+uint16_t
+usb_start_load_block(void *ep_IN_buf, void *buf, uint16_t len, int tog);
+void ctrl_start_load_block(void *buf, uint16_t len);
+int usb_load_next_chunk();
+void usb_flush();
+
+void set_handshake(uint8_t ep_num, int type, int tog, uint8_t len);
+void ctrl_ack();
+
+void clear_handshake_sent_flag();
+int handshake_sent();
+
+#endif /* __UTILS_H__ */