diff options
| author | Fran莽ois Cartegnie <281376+fcartegnie@users.noreply.github.com> | 2024-08-20 13:43:09 +0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-08-20 13:43:09 +0700 |
| commit | f5874d607315ff88b27414299089b8528b5bb07c (patch) | |
| tree | 9db23b8e6cdfb06d8bab77e83224e604f20c4629 | |
| parent | b25d62d1362cfd711e6617a9212e3eddc4e9fddd (diff) | |
| parent | c3912e01a2767334103cd139d875d50c834f3f05 (diff) | |
Merge pull request #35 from kienvo/usb
feat: Receive data over USB
| -rw-r--r-- | CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_usbdev.c | 113 | ||||
| -rw-r--r-- | CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_common.h | 1 | ||||
| -rw-r--r-- | CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_usbdev.h | 177 | ||||
| -rw-r--r-- | Makefile | 15 | ||||
| -rw-r--r-- | src/main.c | 52 | ||||
| -rw-r--r-- | src/usb/composite/cdc-serial.c | 202 | ||||
| -rw-r--r-- | src/usb/composite/hiddev.c | 230 | ||||
| -rw-r--r-- | src/usb/ctrl.c | 151 | ||||
| -rw-r--r-- | src/usb/debug.c | 82 | ||||
| -rw-r--r-- | src/usb/debug.h | 14 | ||||
| -rw-r--r-- | src/usb/dev.c | 176 | ||||
| -rw-r--r-- | src/usb/setup.c | 132 | ||||
| -rw-r--r-- | src/usb/usb.h | 16 | ||||
| -rw-r--r-- | src/usb/utils.c | 130 | ||||
| -rw-r--r-- | src/usb/utils.h | 30 |
15 files changed, 1225 insertions, 296 deletions
diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_usbdev.c b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_usbdev.c deleted file mode 100644 index a730581..0000000 --- a/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_usbdev.c +++ /dev/null @@ -1,113 +0,0 @@ -/********************************** (C) COPYRIGHT *******************************
- * File Name : CH58x_usbdev.c
- * Author : WCH
- * Version : V1.2
- * Date : 2021/11/17
- * Description
- *********************************************************************************
- * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
- * Attention: This software (modified or not) and binary are used for
- * microcontroller manufactured by Nanjing Qinheng Microelectronics.
- *******************************************************************************/
-
-#include "CH58x_common.h"
-
-uint8_t *pEP0_RAM_Addr;
-uint8_t *pEP1_RAM_Addr;
-uint8_t *pEP2_RAM_Addr;
-uint8_t *pEP3_RAM_Addr;
-
-/*********************************************************************
- * @fn USB_DeviceInit
- *
- * @brief USB设备功能初始化,4个端点,8个通道。
- *
- * @param none
- *
- * @return none
- */
-void USB_DeviceInit(void)
-{
- R8_USB_CTRL = 0x00; // 先设定模式,取消 RB_UC_CLR_ALL
-
- R8_UEP4_1_MOD = RB_UEP4_RX_EN | RB_UEP4_TX_EN | RB_UEP1_RX_EN | RB_UEP1_TX_EN; // 端点4 OUT+IN,端点1 OUT+IN
- R8_UEP2_3_MOD = RB_UEP2_RX_EN | RB_UEP2_TX_EN | RB_UEP3_RX_EN | RB_UEP3_TX_EN; // 端点2 OUT+IN,端点3 OUT+IN
-
- R16_UEP0_DMA = (uint16_t)(uint32_t)pEP0_RAM_Addr;
- R16_UEP1_DMA = (uint16_t)(uint32_t)pEP1_RAM_Addr;
- R16_UEP2_DMA = (uint16_t)(uint32_t)pEP2_RAM_Addr;
- R16_UEP3_DMA = (uint16_t)(uint32_t)pEP3_RAM_Addr;
-
- R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
- R8_UEP1_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK | RB_UEP_AUTO_TOG;
- R8_UEP2_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK | RB_UEP_AUTO_TOG;
- R8_UEP3_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK | RB_UEP_AUTO_TOG;
- R8_UEP4_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
-
- R8_USB_DEV_AD = 0x00;
- R8_USB_CTRL = RB_UC_DEV_PU_EN | RB_UC_INT_BUSY | RB_UC_DMA_EN; // 启动USB设备及DMA,在中断期间中断标志未清除前自动返回NAK
- R16_PIN_ANALOG_IE |= RB_PIN_USB_IE | RB_PIN_USB_DP_PU; // 防止USB端口浮空及上拉电阻
- R8_USB_INT_FG = 0xFF; // 清中断标志
- R8_UDEV_CTRL = RB_UD_PD_DIS | RB_UD_PORT_EN; // 允许USB端口
- R8_USB_INT_EN = RB_UIE_SUSPEND | RB_UIE_BUS_RST | RB_UIE_TRANSFER;
-}
-
-/*********************************************************************
- * @fn DevEP1_IN_Deal
- *
- * @brief 端点1数据上传
- *
- * @param l - 上传数据长度(<64B)
- *
- * @return none
- */
-void DevEP1_IN_Deal(uint8_t l)
-{
- R8_UEP1_T_LEN = l;
- R8_UEP1_CTRL = (R8_UEP1_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_ACK;
-}
-
-/*********************************************************************
- * @fn DevEP2_IN_Deal
- *
- * @brief 端点2数据上传
- *
- * @param l - 上传数据长度(<64B)
- *
- * @return none
- */
-void DevEP2_IN_Deal(uint8_t l)
-{
- R8_UEP2_T_LEN = l;
- R8_UEP2_CTRL = (R8_UEP2_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_ACK;
-}
-
-/*********************************************************************
- * @fn DevEP3_IN_Deal
- *
- * @brief 端点3数据上传
- *
- * @param l - 上传数据长度(<64B)
- *
- * @return none
- */
-void DevEP3_IN_Deal(uint8_t l)
-{
- R8_UEP3_T_LEN = l;
- R8_UEP3_CTRL = (R8_UEP3_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_ACK;
-}
-
-/*********************************************************************
- * @fn DevEP4_IN_Deal
- *
- * @brief 端点4数据上传
- *
- * @param l - 上传数据长度(<64B)
- *
- * @return none
- */
-void DevEP4_IN_Deal(uint8_t l)
-{
- R8_UEP4_T_LEN = l;
- R8_UEP4_CTRL = (R8_UEP4_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_ACK;
-}
diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_common.h b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_common.h index f77af25..965cac6 100644 --- a/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_common.h +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_common.h @@ -84,7 +84,6 @@ #include "CH58x_sys.h"
#include "CH58x_timer.h"
#include "CH58x_spi.h"
-#include "CH58x_usbdev.h"
#include "CH58x_usbhost.h"
#include "ISP583.h"
diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_usbdev.h b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_usbdev.h deleted file mode 100644 index 95ad005..0000000 --- a/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_usbdev.h +++ /dev/null @@ -1,177 +0,0 @@ -/********************************** (C) COPYRIGHT *******************************
- * File Name : CH57x_usbdev.h
- * Author : WCH
- * Version : V1.2
- * Date : 2021/11/17
- * Description
- *********************************************************************************
- * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
- * Attention: This software (modified or not) and binary are used for
- * microcontroller manufactured by Nanjing Qinheng Microelectronics.
- *******************************************************************************/
-
-#ifndef __CH58x_USBDEV_H__
-#define __CH58x_USBDEV_H__
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* HID类请求 */
-#define DEF_USB_GET_IDLE 0x02 /* get idle for key or mouse */
-#define DEF_USB_GET_PROTOCOL 0x03 /* get protocol for bios type */
-#define DEF_USB_SET_REPORT 0x09 /* set report for key */
-#define DEF_USB_SET_IDLE 0x0A /* set idle for key or mouse */
-#define DEF_USB_SET_PROTOCOL 0x0B /* set protocol for bios type */
-
-/* 以下缓存区是USB模块收发使用的数据缓冲区,总共9个通道(9块缓存),用户可根据实际使用的通道数定义相应缓存区 */
-extern uint8_t *pEP0_RAM_Addr; //ep0(64)+ep4_out(64)+ep4_in(64)
-extern uint8_t *pEP1_RAM_Addr; //ep1_out(64)+ep1_in(64)
-extern uint8_t *pEP2_RAM_Addr; //ep2_out(64)+ep2_in(64)
-extern uint8_t *pEP3_RAM_Addr; //ep3_out(64)+ep3_in(64)
-
-extern uint8_t *pU2EP0_RAM_Addr; //ep0(64)+ep4_out(64)+ep4_in(64)
-extern uint8_t *pU2EP1_RAM_Addr; //ep1_out(64)+ep1_in(64)
-extern uint8_t *pU2EP2_RAM_Addr; //ep2_out(64)+ep2_in(64)
-extern uint8_t *pU2EP3_RAM_Addr; //ep3_out(64)+ep3_in(64)
-
-#define pSetupReqPak ((PUSB_SETUP_REQ)pEP0_RAM_Addr)
-#define pEP0_DataBuf (pEP0_RAM_Addr)
-#define pEP1_OUT_DataBuf (pEP1_RAM_Addr)
-#define pEP1_IN_DataBuf (pEP1_RAM_Addr + 64)
-#define pEP2_OUT_DataBuf (pEP2_RAM_Addr)
-#define pEP2_IN_DataBuf (pEP2_RAM_Addr + 64)
-#define pEP3_OUT_DataBuf (pEP3_RAM_Addr)
-#define pEP3_IN_DataBuf (pEP3_RAM_Addr + 64)
-#define pEP4_OUT_DataBuf (pEP0_RAM_Addr + 64)
-#define pEP4_IN_DataBuf (pEP0_RAM_Addr + 128)
-
-#define pU2SetupReqPak ((PUSB_SETUP_REQ)pU2EP0_RAM_Addr)
-#define pU2EP0_DataBuf (pU2EP0_RAM_Addr)
-#define pU2EP1_OUT_DataBuf (pU2EP1_RAM_Addr)
-#define pU2EP1_IN_DataBuf (pU2EP1_RAM_Addr + 64)
-#define pU2EP2_OUT_DataBuf (pU2EP2_RAM_Addr)
-#define pU2EP2_IN_DataBuf (pU2EP2_RAM_Addr + 64)
-#define pU2EP3_OUT_DataBuf (pU2EP3_RAM_Addr)
-#define pU2EP3_IN_DataBuf (pU2EP3_RAM_Addr + 64)
-#define pU2EP4_OUT_DataBuf (pU2EP0_RAM_Addr + 64)
-#define pU2EP4_IN_DataBuf (pU2EP0_RAM_Addr + 128)
-
-/**
- * @brief USB设备功能初始化,4个端点,8个通道。
- */
-void USB_DeviceInit(void);
-
-/**
- * @brief USB设备应答传输处理
- */
-void USB_DevTransProcess(void);
-
-/**
- * @brief 端点1下传数据处理
- *
- * @param l - 待处理数据长度(<64B)
- */
-void DevEP1_OUT_Deal(uint8_t l);
-
-/**
- * @brief 端点2下传数据处理
- *
- * @param l - 待处理数据长度(<64B)
- */
-void DevEP2_OUT_Deal(uint8_t l);
-
-/**
- * @brief 端点3下传数据处理
- *
- * @param l - 待处理数据长度(<64B)
- */
-void DevEP3_OUT_Deal(uint8_t l);
-
-/**
- * @brief 端点4下传数据处理
- *
- * @param l - 待处理数据长度(<64B)
- */
-void DevEP4_OUT_Deal(uint8_t l);
-
-/**
- * @brief 端点1数据上传
- *
- * @param l - 上传数据长度(<64B)
- */
-void DevEP1_IN_Deal(uint8_t l);
-
-/**
- * @brief 端点2数据上传
- *
- * @param l - 上传数据长度(<64B)
- */
-void DevEP2_IN_Deal(uint8_t l);
-
-/**
- * @brief 端点3数据上传
- *
- * @param l - 上传数据长度(<64B)
- */
-void DevEP3_IN_Deal(uint8_t l);
-
-/**
- * @brief 端点4数据上传
- *
- * @param l - 上传数据长度(<64B)
- */
-void DevEP4_IN_Deal(uint8_t l);
-
-/**
- * @brief 查询端点1是否上传完成
- *
- * @return 0-未完成 (!0)-已完成
- */
-#define EP1_GetINSta() (R8_UEP1_CTRL & UEP_T_RES_NAK)
-
-/**
- * @brief 查询端点2是否上传完成
- *
- * @return 0-未完成 (!0)-已完成
- */
-#define EP2_GetINSta() (R8_UEP2_CTRL & UEP_T_RES_NAK)
-
-/**
- * @brief 查询端点3是否上传完成
- *
- * @return 0-未完成 (!0)-已完成
- */
-#define EP3_GetINSta() (R8_UEP3_CTRL & UEP_T_RES_NAK)
-
-/**
- * @brief 查询端点4是否上传完成
- *
- * @return 0-未完成 (!0)-已完成
- */
-#define EP4_GetINSta() (R8_UEP4_CTRL & UEP_T_RES_NAK)
-
-void USB2_DeviceInit(void); /* USB2设备功能初始化,4个端点,8个通道 */
-void USB2_DevTransProcess(void); /* USB2设备应答传输处理 */
-
-void U2DevEP1_OUT_Deal(uint8_t l); /* 设备端点1下传通道处理 */
-void U2DevEP2_OUT_Deal(uint8_t l); /* 设备端点2下传通道处理 */
-void U2DevEP3_OUT_Deal(uint8_t l); /* 设备端点3下传通道处理 */
-void U2DevEP4_OUT_Deal(uint8_t l); /* 设备端点4下传通道处理 */
-
-void U2DevEP1_IN_Deal(uint8_t l); /* 设备端点1上传通道处理 */
-void U2DevEP2_IN_Deal(uint8_t l); /* 设备端点2上传通道处理 */
-void U2DevEP3_IN_Deal(uint8_t l); /* 设备端点3上传通道处理 */
-void U2DevEP4_IN_Deal(uint8_t l); /* 设备端点4上传通道处理 */
-
-// 0-未完成 (!0)-已完成
-#define U2EP1_GetINSta() (R8_U2EP1_CTRL & UEP_T_RES_NAK) /* 查询端点1是否上传完成 */
-#define U2EP2_GetINSta() (R8_U2EP2_CTRL & UEP_T_RES_NAK) /* 查询端点2是否上传完成 */
-#define U2EP3_GetINSta() (R8_U2EP3_CTRL & UEP_T_RES_NAK) /* 查询端点3是否上传完成 */
-#define U2EP4_GetINSta() (R8_U2EP4_CTRL & UEP_T_RES_NAK) /* 查询端点4是否上传完成 */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // __CH58x_USBDEV_H__
@@ -7,8 +7,8 @@ TARGET = badgemagic-ch582 ###################################### # building variables ###################################### -# debug build? -DEBUG = 1 +# Uncomment below line to enable debugging +# DEBUG = 1 # optimization for size OPT = -Os @@ -37,7 +37,6 @@ CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_uart0.c \ CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_timer1.c \ CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_pwm.c \ CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_usbhostClass.c \ -CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_usbdev.c \ CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_adc.c \ CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_usbhostBase.c \ CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_timer3.c \ @@ -58,7 +57,13 @@ src/ble/profile/devinfo.c \ src/ble/setup.c \ src/ble/peripheral.c \ src/data.c \ - +src/usb/utils.c \ +src/usb/setup.c \ +src/usb/ctrl.c \ +src/usb/debug.c \ +src/usb/dev.c \ +src/usb/composite/hiddev.c \ +src/usb/composite/cdc-serial.c \ # ASM sources ASM_SOURCES = \ @@ -108,7 +113,7 @@ ASFLAGS = $(MCU) $(AS_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections CFLAGS = $(MCU) $(C_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections ifeq ($(DEBUG), 1) -CFLAGS += -g -gdwarf-2 +CFLAGS += -g -gdwarf-2 -DDEBUG=$(DEBUG) endif @@ -10,6 +10,8 @@ #include "ble/setup.h"
#include "ble/profile.h"
+#include "usb/usb.h"
+
#define FB_WIDTH (LED_COLS * 4)
#define SCROLL_IRATIO (16)
#define SCAN_F (2000)
@@ -92,6 +94,40 @@ void ble_start() legacy_registerService();
}
+static void usb_receive(uint8_t *buf, uint16_t len)
+{
+ static uint16_t rx_len, data_len;
+ static uint8_t *data;
+
+ PRINT("dump first 8 bytes: %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7]);
+
+ if (rx_len == 0) {
+ if (memcmp(buf, "wang", 5))
+ return;
+
+ int init_len = len > LEGACY_HEADER_SIZE ? len : sizeof(data_legacy_t);
+ init_len += MAX_PACKET_SIZE;
+ data = malloc(init_len);
+ }
+
+ memcpy(data + rx_len, buf, len);
+ rx_len += len;
+
+ if (!data_len) {
+ data_legacy_t *d = (data_legacy_t *)data;
+ uint16_t n = bigendian16_sum(d->sizes, 8);
+ data_len = LEGACY_HEADER_SIZE + LED_ROWS * n;
+ data = realloc(data, data_len);
+ }
+
+ if ((rx_len > LEGACY_HEADER_SIZE) && rx_len >= data_len) {
+ data_flatSave(data, data_len);
+ SYS_ResetExecute();
+ }
+}
+
void handle_mode_transition()
{
static int prev_mode;
@@ -123,10 +159,26 @@ void handle_mode_transition() prev_mode = mode;
}
+static void debug_init()
+{
+ GPIOA_SetBits(GPIO_Pin_9);
+ GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU);
+ GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA);
+ UART1_DefInit();
+ UART1_BaudRateCfg(921600);
+}
+
int main()
{
SetSysClock(CLK_SOURCE_PLL_60MHz);
+ debug_init();
+ PRINT("\nDebug console is on UART%d\n", DEBUG);
+
+ cdc_onWrite(usb_receive);
+ hiddev_onWrite(usb_receive);
+ usb_start();
+
led_init();
TMR0_TimerInit(SCAN_T / 2);
TMR0_ITCfg(ENABLE, TMR0_3_IT_CYC_END);
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(¬i_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__ */ |