Initial commit
This commit is contained in:
232
src/usb/core.c
Normal file
232
src/usb/core.c
Normal 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
61
src/usb/core.h
Normal 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
362
src/usb/ctrl.c
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user