usb: handle device request and route to interfaces

This commit is contained in:
Dien-Nhung Nguyen-Phu 2024-07-11 21:05:17 +07:00
parent 515c8a8eb9
commit 4f3b6f4283
8 changed files with 629 additions and 0 deletions

View File

@ -57,7 +57,11 @@ 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 \
# ASM sources
ASM_SOURCES = \

View File

@ -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)
@ -139,6 +141,8 @@ int main()
debug_init();
PRINT("\nDebug console is on UART%d\n", DEBUG);
usb_start();
led_init();
TMR0_TimerInit(SCAN_T / 2);
TMR0_ITCfg(ENABLE, TMR0_3_IT_CYC_END);

151
src/usb/ctrl.c Normal file
View File

@ -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
}

176
src/usb/dev.c Normal file
View File

@ -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);
}

126
src/usb/setup.c Normal file
View File

@ -0,0 +1,126 @@
#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();
init();
PFIC_EnableIRQ(USB_IRQn);
}

8
src/usb/usb.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef __USB_H__
#define __USB_H__
#include <stdint.h>
void usb_start();
#endif /* __USB_H__ */

130
src/usb/utils.c Normal file
View File

@ -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;
}

30
src/usb/utils.h Normal file
View File

@ -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__ */