Initial commit

This commit is contained in:
Puck Meerburg
2025-12-30 13:48:35 +00:00
commit 0f7b02c8a6
67 changed files with 20228 additions and 0 deletions

232
src/usb/core.c Normal file
View File

@@ -0,0 +1,232 @@
#include <stdlib.h>
#include "core.h"
#include "CH58x_common.h"
static volatile uint8_t *ep_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 *ep_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 uint8_t recvlens[8];
uint8_t bConfigurationValue = 0;
// The hardware requires EP0 and EP4 to share a contiguous DMA buffer.
// Fuck if I know why.
__attribute__((aligned(4))) volatile uint8_t ep04_buf[64 + 64 + 64];
__attribute__((aligned(4))) volatile uint8_t epbuf[(8 - 2) * 2 * 64];
volatile uint8_t *buf_for_ep(uint8_t ep, int dir_in) {
if (ep == 0) return ep04_buf;
if (ep == 4) return ep04_buf + (dir_in ? 128 : 64);
// EP0 and EP4 aren't in here. so skip them.
ep -= 1;
if (ep > 3) ep -= 1;
return epbuf + (ep * 128 + (dir_in ? 64 : 0));
}
// Configure the response to set for the next token for the endpoint.
// The interrupt handler will reconfigure this to return NAK whenever
// the next token has been received.
enum usb_endpoint_state set_endpoint_state(uint8_t endpoint, enum usb_endpoint_state state) {
int dir_in = endpoint & 0x80; // 0 -> OUT endpoint, 1 -> IN endpoint
int ep_num = endpoint & 0x7F;
if (dir_in) {
uint8_t prev_state = *ep_ctrl_regs[ep_num] & MASK_UEP_T_RES;
*ep_ctrl_regs[ep_num] = (*ep_ctrl_regs[ep_num] & ~MASK_UEP_T_RES) | (state & 0x03);
return prev_state;
} else {
uint8_t prev_state = (*ep_ctrl_regs[ep_num] & MASK_UEP_R_RES) >> 2;
*ep_ctrl_regs[ep_num] = (*ep_ctrl_regs[ep_num] & ~MASK_UEP_R_RES) | ((state & 0x03) << 2);
return prev_state;
}
}
// Toggle the DATA0/DATA1 toggle bit, indicating which of
// DATA0 and DATA1 to use fo rthe next receiving or sending of data.
// These bits are only toggled once data has been successfully transferred;
// a la USB_EP_STATE_ACK.
//
// The SETUP control transfers on endoint 0 _always_ use DATA0.
static void flip_endpoint_data_toggle(uint8_t endpoint) {
int dir_in = endpoint & 0x80;
int ep_num = endpoint & 0x7F;
if (dir_in) {
*ep_ctrl_regs[ep_num] ^= RB_UEP_T_TOG;
} else {
*ep_ctrl_regs[ep_num] ^= RB_UEP_R_TOG;
}
}
static void handle_bus_reset() {
R8_USB_INT_FG = RB_UIF_BUS_RST;
R8_USB_DEV_AD = 0;
bConfigurationValue = 0;
}
static void handle_suspend() {
R8_USB_INT_FG = RB_UIF_SUSPEND;
// TODO
}
static void handle_sof() {
R8_USB_INT_FG = RB_UIF_HST_SOF;
}
__HIGH_CODE
static void handle_transfer(uint8_t ep_num, int is_in) {
uint8_t endpoint = ep_num | (is_in ? 0x80 : 0);
// We may have received a transfer.
// If we expected this transfer, toggle the DATA0/DATA1 bits.
enum usb_endpoint_state prev_state = set_endpoint_state(endpoint, USB_EP_STATE_NAK);
if (prev_state == USB_EP_STATE_ACK) {
flip_endpoint_data_toggle(endpoint);
}
if (ep_num == 0) {
handle_ctrl_transfer(is_in);
return;
}
if (!is_in) {
recvlens[endpoint] = R8_USB_RX_LEN;
}
}
void usb_init() {
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;
R16_UEP0_DMA = ep04_buf;
R16_UEP1_DMA = epbuf + 0 * 128;
R16_UEP2_DMA = epbuf + 1 * 128;
R16_UEP3_DMA = epbuf + 2 * 128;
R16_UEP5_DMA = epbuf + 3 * 128;
R16_UEP6_DMA = epbuf + 4 * 128;
R16_UEP7_DMA = epbuf + 5 * 128;
for (int i = 0; i < 8; i++) {
set_endpoint_state(i, USB_EP_STATE_NAK);
set_endpoint_state(i | 0x80, USB_EP_STATE_NAK);
}
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;
PFIC_EnableIRQ(USB_IRQn);
}
__INTERRUPT
__HIGH_CODE
void USB_IRQHandler(void) {
uint8_t interrupt = R8_USB_INT_FG;
if (interrupt & RB_UIF_BUS_RST) {
handle_bus_reset();
}
if (interrupt & RB_UIF_SUSPEND) {
handle_suspend();
}
if (interrupt & RB_UIF_TRANSFER) {
uint8_t status = R8_USB_INT_ST;
uint8_t ep_num = status & MASK_UIS_ENDP;
uint8_t token = ((status & MASK_UIS_TOKEN) >> 4) & 0x03;
if (token == 0x00) { // OUT
if (status & RB_UIS_TOG_OK) handle_transfer(ep_num, 0);
R8_USB_INT_FG = RB_UIF_TRANSFER;
} else if (token == 0x01) { // SOF
handle_sof();
R8_USB_INT_FG = RB_UIF_TRANSFER;
} else if (token == 0x02) { // IN
handle_transfer(ep_num, 1);
R8_USB_INT_FG = RB_UIF_TRANSFER;
}
if ((status & RB_UIS_SETUP_ACT)) {
handle_setup_request();
R8_USB_INT_FG = RB_UIF_TRANSFER;
}
}
}
// Receive up to buflen bytes from selected OUT endpoint.
// If data is not yet available, returns -1.
int16_t usb_recv(uint8_t endpoint, void *buffer, size_t buflen) {
if (recvlens[endpoint] == 0xFF) {
set_endpoint_state(endpoint, USB_EP_STATE_ACK);
return -1;
}
if (recvlens[endpoint] < buflen) {
buflen = recvlens[endpoint];
}
recvlens[endpoint] = 0xFF;
memcpy(buffer, buf_for_ep(endpoint, 0), buflen);
return buflen;
}
// Sends up to buflen bytes to selected IN endpoint.
// If the buffer is still busy, returns -1.
int16_t usb_xmit(uint8_t endpoint, void *buffer, size_t buflen) {
if ((*ep_ctrl_regs[endpoint] & MASK_UEP_T_RES) != USB_EP_STATE_NAK) {
return -1;
}
if (buflen > 64) buflen = 64;
memcpy(buf_for_ep(endpoint, 1), buffer, buflen);
*ep_t_len_regs[endpoint] = buflen;
set_endpoint_state(endpoint | 0x80, USB_EP_STATE_ACK);
return buflen;
}
int usb_can_xmit(uint8_t endpoint) {
return (*ep_ctrl_regs[endpoint] & MASK_UEP_T_RES) == USB_EP_STATE_NAK;
}
int usb_connected(void) {
return !!(R8_USB_MIS_ST & RB_UMS_SUSPEND);
}

61
src/usb/core.h Normal file
View File

@@ -0,0 +1,61 @@
#pragma once
#include <stdint.h>
#include <stdlib.h>
enum usb_endpoint_state {
USB_EP_STATE_ACK = 0,
USB_EP_STATE_TIMEOUT = 1,
USB_EP_STATE_NAK = 2,
USB_EP_STATE_STALL = 3,
};
struct __attribute__((packed)) usb_control_request {
uint8_t bmRequestType;
uint8_t bRequest;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
};
extern struct usb_control_request usb_control_request;
extern void *control_transfer_buf;
extern uint16_t control_transfer_len;
extern uint8_t bConfigurationValue;
extern volatile uint8_t ep04_buf[64 + 64 + 64];
extern volatile uint8_t epbuf[(8 - 2) * 2 * 64];
enum usb_control_resp {
USB_CONTROL_RESP_PASS,
USB_CONTROL_RESP_ACK,
USB_CONTROL_RESP_STALL,
};
enum usb_control_state {
USB_CONTROL_SETUP,
USB_CONTROL_DATA_SENT,
USB_CONTROL_FINISHED,
};
typedef enum usb_control_resp (*usb_control_request_handler)(enum usb_control_state);
void usb_register_handler(usb_control_request_handler);
enum usb_control_request_type {
USB_CONTROL_REQUEST_TYPE_OUT,
USB_CONTROL_REQUEST_TYPE_IN,
USB_CONTROL_REQUEST_TYPE_NO_DATA,
};
void ctrl_respond_out(void *buf, uint16_t len);
void handle_ctrl_transfer(int is_in);
int16_t usb_recv(uint8_t endpoint, void *buffer, size_t buflen);
int16_t usb_xmit(uint8_t endpoint, void *buffer, size_t buflen);
int usb_can_xmit(uint8_t ep);
void usb_init();
void handle_setup_request();
enum usb_endpoint_state set_endpoint_state(uint8_t endpoint, enum usb_endpoint_state state);
int usb_connected(void);

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

@@ -0,0 +1,362 @@
#include <stdlib.h>
#include "core.h"
#include "CH58x_common.h"
struct usb_control_request usb_control_request;
// These track the currently ongoing control transfer.
void *control_transfer_buf;
uint16_t control_transfer_len;
void ctrl_respond_out(void *buf, uint16_t len) {
control_transfer_buf = buf;
if (len > usb_control_request.wLength) len = usb_control_request.wLength;
control_transfer_len = len;
}
__HIGH_CODE
static enum usb_control_request_type ctrlreq_type() {
if (!usb_control_request.wLength) {
return USB_CONTROL_REQUEST_TYPE_NO_DATA;
}
return usb_control_request.bmRequestType & 0x80 ? USB_CONTROL_REQUEST_TYPE_IN : USB_CONTROL_REQUEST_TYPE_OUT;
}
uint8_t device_descriptor[18] = {
0x12,
0x01,
0x00, 0x02,
0x00,
0x00,
0x00,
0x40,
0x16, 0x04,
0x20, 0x50,
0x20, 0x04,
0x00,
0x00,
0x00,
0x01,
};
uint8_t config_descriptor[0x6b] = {
0x09, // bLength
0x02, // bDescriptorType
0x6b, 0x00, // wTotalLength
0x03, // bNumInterfaces
0x01, // bConfigurationValue
0x00, // iConfiguration
0x80, // bmAttributes
0x64, // bMaxPower
0x09, // bLength
0x04, // bDescriptorType
0x00, // bInterfaceNumber
0x00, // bAlternateSetting
0x02, // bNumEndpoints
0x03, // bInterfaceClass (HID)
0x00, // bInterfaceSubClass
0x00, // bInterfaceProtocol
0x00, // iInterface
// HID descriptor
0x09,
0x21,
0x11, 0x01,
0x00,
0x01,
0x22,
0x18, 0x00,
0x07,
0x05,
0x82,
0x03,
0x10, 0x00,
0x01,
0x07,
0x05,
0x02,
0x03,
0x10, 0x00,
0x01,
0x08, // bLength
0x0b, // bDescriptorType ~ INTERFACE ASSOCIATION DESCRIPTOR
0x01, // bFirstInterface
0x02, // bInterfaceCount
0x02, // bFunctionClass 0x02
0x02, // bFunctionSubclass 0x02
0x01, // bFunctionProtocol 0x01
0x00, // iFunction
0x09, // bLength
0x04, // bDescriptorType
0x01, // bInterfaceNumber
0x00, // bAlternateSetting
0x01, // bNumEndpoints
0x02, // bInterfaceClass
0x02, // bInterfaceSubClass
0x01, // bInterfaceProtocol
0x00, // iInterface
0x05, // bLength
0x24, // bDescriptorType
0x00, // bDescriptorSubtype
0x10, 0x01, // bcdCDC
0x05, // bLength
0x24, // bDescriptorType
0x01, // bDescriptorSubtype
0x00, // bmCapabilities
0x01, // bDataInterface
// ACM descripotor
0x04, // bLength
0x24, // bDescriptorType
0x02, // bDescriptorSubtype
0x00, // bmCapabilities
// ACM union descriptor
0x05, // bLength
0x24, // bDescriptorType
0x06, // bDescriptorSubtype
0x00, // bMasterInterface
0x01, // bSlaveInterface0
0x07, // bLength
0x05, // bDescriptorType
0x88, // bEndpointAddress
0x03, // bmAttributes
0x10, 0x00, // wMaxPacketSize
0x01, // bInterval
0x09, // bLength
0x04, // bDescriptorType
0x02, // bInterfaceNumber
0x00, // bAlternateSetting
0x02, // bNumEndpoints
0x0a, // bInterfaceClass
0x00, // bInterfaceSubClass
0x00, // bInterfaceProtocol
0x00, // iInterface
0x07, // bLength
0x05, // bDescriptorType
0x81, // bEndpointAddress
0x02, // bmAttributes
0x20, 0x00, // wMaxPacketSize
0x01, // bInterval
0x07, // bLength
0x05, // bDescriptorType
0x01, // bEndpointAddress
0x02, // bmAttributes
0x20, 0x00, // wMaxPacketSize
0x01, // bInterval
};
uint8_t report_descriptor[24] = {
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
0x09, 0x01, // Usage (0x01)
0xA1, 0x01, // Collection (Application)
0x09, 0x02, // Usage (0x02)
0x15, 0x00, // Logical Minimum (0)
0x25, 0xFF, // Logical Maximum (255)
0x75, 0x08, // Report Size (8)
0x95, 0x40, // Report Count (64)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x09, 0x03, // Usage (0x03)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0 // End Collection
};
__HIGH_CODE
static enum usb_control_resp handle_hid_request(enum usb_control_state state) {
if ((usb_control_request.bmRequestType & 0x7F) != 0x21 && (usb_control_request.bmRequestType & 0x7f) != 1) return USB_CONTROL_RESP_PASS;
if (usb_control_request.wIndex != 0) return USB_CONTROL_RESP_PASS;
if (usb_control_request.bmRequestType == 0x81 && usb_control_request.bRequest == 0x06 && usb_control_request.wValue == 0x2200) {
if (state == USB_CONTROL_SETUP) ctrl_respond_out(report_descriptor, 24);
return USB_CONTROL_RESP_ACK;
}
return USB_CONTROL_RESP_STALL;
}
__HIGH_CODE
static enum usb_control_resp handle_main_request(enum usb_control_state state) {
if (usb_control_request.bmRequestType & 0x7F != 0x00) return USB_CONTROL_RESP_PASS;
switch (usb_control_request.bRequest) {
case 0: // GET_STATUS
if (state == USB_CONTROL_SETUP) {
static uint16_t status_val = 0;
ctrl_respond_out((void *)&status_val, 2);
}
return USB_CONTROL_RESP_ACK;
case 1: // CLEAR_FEATURE
case 3: // SET_FEATURE
return USB_CONTROL_RESP_STALL;
case 5: // SET_ADDRESS
if (state == USB_CONTROL_FINISHED) {
R8_USB_DEV_AD = (uint8_t) usb_control_request.wValue;
}
return USB_CONTROL_RESP_ACK;
case 6: // GET_DESCRIPTOR
if ((usb_control_request.wValue & 0xFF00) == 0x0100) {
if (state == USB_CONTROL_SETUP) ctrl_respond_out(device_descriptor, sizeof(device_descriptor));
} else if ((usb_control_request.wValue & 0xFF00) == 0x0200) {
if (state == USB_CONTROL_SETUP) ctrl_respond_out(config_descriptor, sizeof(config_descriptor));
} else {
return USB_CONTROL_RESP_STALL;
}
return USB_CONTROL_RESP_ACK;
case 7: // SET_DESCRIPTOR
return USB_CONTROL_RESP_STALL;
case 8: // GET_CONFIGURATION
if (state == USB_CONTROL_SETUP) {
ctrl_respond_out(&bConfigurationValue, 1);
}
return USB_CONTROL_RESP_STALL;
case 9: // SET_CONFIGURATION
if (usb_control_request.wValue > 1 || usb_control_request.wLength || usb_control_request.wIndex) {
return USB_CONTROL_RESP_STALL;
}
return USB_CONTROL_RESP_ACK;
case 10: // GET_INTERFACE
case 11: // SET_INTERFACE
case 12: // SYNCH_FRAME
return USB_CONTROL_RESP_STALL;
}
return USB_CONTROL_RESP_STALL;
}
static usb_control_request_handler handlers[16] = {0};
static int handlers_index = 0;
void usb_register_handler(usb_control_request_handler handler) {
handlers[handlers_index++] = handler;
}
__HIGH_CODE
static enum usb_control_resp call_handler(enum usb_control_state state) {
enum usb_control_resp r = handle_main_request(state);
if (r != USB_CONTROL_RESP_PASS) return r;
r = handle_hid_request(state);
if (r != USB_CONTROL_RESP_PASS) return r;
for (int i = 0; i < handlers_index; i++) {
if (!handlers[i]) continue;
enum usb_control_resp resp = handlers[i](state);
if (resp != USB_CONTROL_RESP_PASS) return resp;
}
return USB_CONTROL_RESP_STALL;
}
__HIGH_CODE
void handle_ctrl_transfer(int is_in) {
if (!control_transfer_buf) {
call_handler(USB_CONTROL_FINISHED);
if (is_in) {
R8_UEP0_CTRL = RB_UEP_T_TOG | RB_UEP_R_TOG | UEP_R_RES_ACK | UEP_T_RES_STALL;
return;
}
R8_UEP0_CTRL = RB_UEP_T_TOG | RB_UEP_R_TOG | UEP_R_RES_STALL | UEP_T_RES_STALL;
return;
}
if (is_in) {
// Account for the bytes having been sent.
if (control_transfer_len >= 64) {
control_transfer_len -= 64;
control_transfer_buf += 64;
} else
control_transfer_len = 0;
// If we have more data to transfer, set up the next
// DMA.
if (control_transfer_len > 0) {
memcpy(ep04_buf, control_transfer_buf, control_transfer_len > 64 ? 64 : control_transfer_len);
R8_UEP0_T_LEN = control_transfer_len > 64 ? 64 : control_transfer_len;
set_endpoint_state(0x80, USB_EP_STATE_ACK);
} else {
enum usb_control_resp result = call_handler(USB_CONTROL_DATA_SENT);
control_transfer_buf = 0;
set_endpoint_state(0x00, result == USB_CONTROL_RESP_ACK ? USB_EP_STATE_ACK : USB_EP_STATE_STALL);
}
} else {
uint8_t ack_bytes = R8_USB_RX_LEN;
if (ack_bytes > control_transfer_len) {
ack_bytes = control_transfer_len;
}
control_transfer_len -= ack_bytes;
memcpy(control_transfer_buf, ep04_buf, ack_bytes);
control_transfer_buf += ack_bytes;
if (control_transfer_len > 0) {
// We have more data to receive. so re-ready the endpoint.
set_endpoint_state(0x00, USB_EP_STATE_ACK);
} else {
enum usb_control_resp result = call_handler(USB_CONTROL_DATA_SENT);
control_transfer_buf = 0;
set_endpoint_state(0x80, result == USB_CONTROL_RESP_ACK ? USB_EP_STATE_ACK : USB_EP_STATE_STALL);
}
}
}
__HIGH_CODE
void handle_setup_request() {
// Copy the setup request from the EP0 buffer.
memcpy(&usb_control_request, ep04_buf, 8);
// We don't expect the previous request to be driven anymore.
// Reset the control transfer.
control_transfer_buf = 0;
control_transfer_len = 0;
// The next requests (and responses) will start with DATA1.
R8_UEP0_CTRL = RB_UEP_T_TOG | RB_UEP_R_TOG | UEP_R_RES_NAK | UEP_T_RES_NAK;
if (call_handler(USB_CONTROL_SETUP) != USB_CONTROL_RESP_ACK) {
control_transfer_buf = 0;
control_transfer_len = 0;
// We need to stall this request.
// This is done by stalling either the first data stage packet,
// or the status stage packet.
// In an OUT type control request, the next request will be an OUT.
// In other cases, it will always be IN.
if (ctrlreq_type() != USB_CONTROL_REQUEST_TYPE_OUT) {
set_endpoint_state(0x80, USB_EP_STATE_STALL);
} else {
set_endpoint_state(0x00, USB_EP_STATE_STALL);
}
} else {
if (ctrlreq_type() != USB_CONTROL_REQUEST_TYPE_OUT) {
int mlen = control_transfer_len;
if (mlen > 64) mlen = 64;
memcpy(ep04_buf, control_transfer_buf, mlen);
R8_UEP0_T_LEN = mlen;
set_endpoint_state(0x80, USB_EP_STATE_ACK);
} else {
set_endpoint_state(0x00, USB_EP_STATE_ACK);
}
}
}