-
-
Save shima-529/2138a003ab26c884585e5e9128169ee2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "stm32f0xx.h" | |
#include <usb_struct.h> | |
#include <usb_desc/usb_packet.h> | |
#include <usb_desc/usb_descriptor.h> | |
#include <stdio.h> | |
#include <string.h> | |
//#define iprintf(...) | |
/* CHANGED | |
* - do not change addr_rx & count_rx while transfer flow | |
*/ | |
/* Pointer Notation | |
* - type = uintptr_t : This is an pointer for PMA. | |
* Local offset of PMA is stored. | |
* - type = void * or others | |
* : This is an pointer for main memory. | |
*/ | |
/* xfer/recv calls | |
* - On reception of SETUP packet ==> call send() if necessary | |
* - On IN/OUT ==> call send_next() or stop() | |
*/ | |
volatile char usb_is_ready; | |
// External Descriptors | |
extern const USBDeviceDescriptor dev_desc; | |
extern const USBInterfaceDescriptor if_desc; | |
extern const USBStringDescriptor string_desc[]; | |
extern const uint8_t USBD_CDC_CfgHSDesc[]; | |
extern const __attribute__((aligned(2))) uint8_t conf_desc[]; | |
const uint8_t __attribute__((aligned(2))) ReportDescriptor[50]; | |
// Struct for USB Peripheral | |
typedef struct { | |
uint16_t addr_tx; | |
uint16_t count_tx; | |
uint16_t addr_rx; | |
uint16_t count_rx; | |
} PacketBuffer; | |
// Some macros | |
#define MIN(x, y) ((x) < (y) ? (x) : (y)) | |
#define USB_PMASIZE 1024 | |
#define USB_EPnR(n) *(volatile uint16_t *)((uintptr_t)&USB->EP0R + 4 * (n)) | |
enum USBSetupPacketRequest { | |
GET_STATUS = 0, CLEAR_FEATURE = 1, SET_FEATURE = 3, | |
SET_ADDRESS = 5, GET_DESCRIPTOR = 6, SET_DESCRIPTOR = 7, | |
GET_CONFIGURATION = 8, SET_CONFIGURATION = 9, | |
GET_INTERFACE = 10, SET_INTERFACE = 11, SYNCH_FRAME = 12 | |
}; | |
enum USBDescriptorType { | |
DEVICE = 1, CONFIGURATION = 2, STRING = 3, INTERFACE = 4, | |
ENDPOINT = 5, DEVICE_QUALIFIER = 6, | |
OTHER_SPEED_CONFIGURATION = 7, INTERFACE_POWER = 8, | |
HID_REPORT = 0x22 | |
}; | |
// Vars for PMA buffer | |
typedef struct { | |
uint16_t setup_packet; | |
uint16_t device_desc; | |
uint16_t config_desc; | |
uint16_t string_desc[10]; | |
uint16_t report_desc; | |
} PMAOffsets; | |
typedef struct { | |
PMAOffsets offsets; | |
uint16_t sp; | |
} PMAInfo; | |
volatile static PMAInfo pma_allocation_info; | |
// FSM | |
typedef enum { | |
ST_RESET, SETUP, IN, OUT, STATUS_IN, STATUS_OUT, ST_ERROR | |
} EP0_fsm; | |
// Storage Var for the coming xfer | |
typedef struct { | |
uint16_t ptr; | |
uint16_t current_pos; | |
uint16_t len; | |
} DataInfo; | |
typedef struct { | |
uint8_t packet_size; | |
PacketBuffer *pb_ptr; | |
DataInfo xfer_info; | |
DataInfo recv_info; | |
EP0_fsm fsm; | |
USBSetupPacket last_setup; | |
} USBEndpointInfo; | |
volatile static USBEndpointInfo ep_info[2]; | |
// Here begin Utility Funcs | |
uint16_t alloc(int size) { | |
const uint16_t ret = pma_allocation_info.sp; | |
pma_allocation_info.sp += size; | |
if( size & 1 ) pma_allocation_info.sp++; | |
return ret; | |
} | |
static void pma_out(void *dest, const uintptr_t src, int size) { | |
const volatile uint16_t *src_p = (void *)(src + USB_PMAADDR); | |
volatile uint16_t *dest_p = dest; | |
if( size & 1 ) size++; | |
const uint16_t wSize = size / 2; | |
for(int i = 0; i < wSize; i++) { | |
dest_p[i] = src_p[i]; | |
} | |
} | |
static void pma_in(const uintptr_t dest, const void *src, int size) { | |
const volatile uint16_t *src_p = src; | |
volatile uint16_t *dest_p = (void *)(dest + USB_PMAADDR); | |
if( size & 1 ) size++; | |
const uint16_t wSize = size / 2; | |
for(int i = 0; i < wSize; i++) { | |
dest_p[i] = src_p[i]; | |
} | |
} | |
void usb_init(void) { | |
RCC->APB1ENR |= RCC_APB1ENR_USBEN; | |
USB->CNTR &= ~USB_CNTR_PDWN; | |
for(volatile int i=0; i<100; i++); // wait for voltage to be stable | |
USB->CNTR &= ~USB_CNTR_FRES; | |
USB->BCDR |= USB_BCDR_DPPU; // pull-up DP so as to notify the connection to the host | |
USB->CNTR = USB_CNTR_RESETM; // enable reset interrupt | |
NVIC_SetPriority(USB_IRQn, 0); // Highest Priority | |
NVIC_EnableIRQ(USB_IRQn); | |
} | |
static void usb_endpoint_set_status(int endp, uint16_t status, uint16_t mask) { | |
const uint16_t reg = USB_EPnR(endp); | |
USB_EPnR(endp) = (reg & (USB_EPREG_MASK | mask)) ^ status; | |
} | |
static void search_descriptor(uint16_t wValue) { | |
const uint8_t desc_type = (wValue >> 8) & 0xFF; | |
const uint16_t wLength = ep_info[0].last_setup.wLength; | |
volatile PMAOffsets *offsets = &pma_allocation_info.offsets; | |
switch( desc_type ) { | |
case DEVICE: // Device Descriptor | |
if( offsets->device_desc == 0 ) { | |
offsets->device_desc = alloc(sizeof(USBDeviceDescriptor)); | |
pma_in(offsets->device_desc, &dev_desc, sizeof(USBDeviceDescriptor)); | |
} | |
ep_info[0].xfer_info.ptr = offsets->device_desc; | |
ep_info[0].xfer_info.len = MIN(wLength, sizeof(USBDeviceDescriptor)); | |
break; | |
case CONFIGURATION: | |
if( offsets->config_desc == 0 ) { | |
offsets->config_desc = alloc(67); | |
pma_in(offsets->config_desc, conf_desc, 67); | |
} | |
ep_info[0].xfer_info.ptr = offsets->config_desc; | |
ep_info[0].xfer_info.len = MIN(wLength, 67); | |
break; | |
case STRING: | |
{ | |
const uint8_t desc_no = wValue & 0xFF; | |
const int len = string_desc[desc_no].bLength; | |
if( offsets->string_desc[desc_no] == 0 ) { | |
offsets->string_desc[desc_no] = alloc(len); | |
pma_in(offsets->string_desc[desc_no], &string_desc[desc_no], len); | |
} | |
ep_info[0].xfer_info.ptr = offsets->string_desc[desc_no]; | |
ep_info[0].xfer_info.len = MIN(wLength, len); | |
break; | |
} | |
case HID_REPORT: | |
if( offsets->report_desc == 0 ) { | |
offsets->report_desc = alloc(50); | |
pma_in(offsets->report_desc, ReportDescriptor, 50); | |
} | |
ep_info[0].xfer_info.ptr = offsets->report_desc; | |
ep_info[0].xfer_info.len = MIN(wLength, 50); | |
usb_is_ready = 1; | |
break; | |
default: | |
return; | |
break; | |
} | |
} | |
static void usb_ep_send(int ep_num, uint16_t addr, uint16_t size) { | |
ep_info[ep_num].pb_ptr->addr_tx = addr; | |
ep_info[ep_num].pb_ptr->count_tx = size; | |
usb_endpoint_set_status(ep_num, USB_EP_TX_VALID, USB_EPTX_STAT); | |
} | |
void usb_ep_receive(int ep_num, uint16_t addr, uint16_t size) { | |
if( ep_info[ep_num].pb_ptr->addr_rx == 0 ) { | |
ep_info[ep_num].pb_ptr->addr_rx = addr; | |
if( size > 62 ) { | |
ep_info[ep_num].pb_ptr->count_rx = ((size / 32) - 1) << 10; | |
ep_info[ep_num].pb_ptr->count_rx |= 1 << 15; | |
}else{ | |
ep_info[ep_num].pb_ptr->count_rx = size << 10; | |
} | |
} | |
usb_endpoint_set_status(ep_num, USB_EP_RX_VALID, USB_EPRX_STAT); | |
} | |
const volatile static uint16_t non_zero = 0; | |
static void usb_ep0_handle_setup(void) { | |
const volatile USBSetupPacket *p = &ep_info[0].last_setup; | |
if( p->bRequest == GET_DESCRIPTOR ) { | |
search_descriptor(p->wValue); | |
usb_ep_send(0, ep_info[0].xfer_info.ptr, MIN(ep_info[0].packet_size, ep_info[0].xfer_info.len)); | |
} | |
if( p->bRequest == GET_CONFIGURATION ) { | |
uint16_t addr; | |
pma_in(addr = alloc(1), (const void *)&non_zero, 1); | |
usb_ep_send(0, addr, 1); | |
} | |
if( p->bRequest == SET_CONFIGURATION ) { | |
iprintf("SET_CONF: 0x%04x\n", p->wValue); | |
} | |
} | |
static void usb_ep0_prepare_for_status(void) { | |
if( ep_info[0].fsm == STATUS_OUT ) { | |
usb_ep_receive(0, 0, 0); | |
} | |
if( ep_info[0].fsm == STATUS_IN ) { | |
usb_ep_send(0, 0, 0); | |
} | |
} | |
static void usb_ep0_handle_status(void) { | |
if( ep_info[0].last_setup.bRequest == SET_ADDRESS ) { | |
USB->DADDR = USB_DADDR_EF | (ep_info[0].last_setup.wValue & 0x7F); | |
} | |
// prepare for the next SETUP transaction | |
usb_ep_receive(0, pma_allocation_info.offsets.setup_packet, | |
sizeof(USBSetupPacket) | |
); | |
} | |
static void usb_ep0_handle_in(void) { | |
// Now data is left to be xferred. | |
// This means the last IN transaction is completed but the data length is not enough. | |
if( ep_info[0].fsm != IN ) return; // malfunction of fsm | |
const uint16_t packet_size = ep_info[0].packet_size; | |
const uint16_t len_to_be_xferred = ep_info[0].xfer_info.len - packet_size; | |
// const uint16_t pos = ep_info[0].xfer_info.current_pos; | |
if( len_to_be_xferred > 0 && len_to_be_xferred <= packet_size ) { // IN transaction will end this time. | |
ep_info[0].xfer_info.current_pos += packet_size; | |
ep_info[0].xfer_info.len -= packet_size; | |
usb_ep_send(0, ep_info[0].xfer_info.current_pos, ep_info[0].xfer_info.len); | |
}else{ | |
// unsupported. | |
} | |
} | |
// Inputs: current state, g_current_setup_packet | |
// Outputs: next state | |
static EP0_fsm next_state_ep0(void) { | |
const volatile USBSetupPacket *last_setup = &ep_info[0].last_setup; | |
const volatile DataInfo *xfer_info = &ep_info[0].xfer_info; | |
// const volatile DataInfo *recv_info = &ep_info[0].recv_info; | |
switch(ep_info[0].fsm) { | |
case SETUP: | |
if( last_setup->bmRequestType & 0x80 ) { | |
return (last_setup->wLength != 0) ? IN : STATUS_IN; | |
}else{ | |
return (last_setup->wLength != 0) ? OUT : STATUS_IN; | |
} | |
break; | |
case IN: | |
if( xfer_info->len > ep_info[0].packet_size ) { | |
return IN; | |
} | |
return STATUS_OUT; | |
break; | |
case OUT: | |
return STATUS_IN; | |
break; | |
case STATUS_IN: | |
break; | |
case STATUS_OUT: | |
break; | |
default: | |
break; | |
} | |
return ST_ERROR; | |
} | |
static void dump_setup_packet(void) { | |
iprintf("bmResuestType: 0x%02x\n", ep_info[0].last_setup.bmRequestType); | |
iprintf("bRequest: %d\n", ep_info[0].last_setup.bRequest); | |
iprintf("wValue: 0x%x\n", ep_info[0].last_setup.wValue); | |
iprintf("wIndex: 0x%x\n", ep_info[0].last_setup.wIndex); | |
iprintf("wLength: 0x%x\n", ep_info[0].last_setup.wLength); | |
} | |
volatile int8_t __attribute__((aligned(2))) report[4] = { | |
0, // Button | |
0, // dX | |
0, // dY | |
}; | |
uint16_t addr_report; | |
void USB_IRQHandler(void) { | |
uint16_t flag = USB->ISTR; | |
if( flag & USB_ISTR_RESET ) { | |
iprintf("\nRESET\n"); | |
USB->ISTR &= ~USB_ISTR_RESET; | |
USB->CNTR |= USB_CNTR_CTRM | USB_CNTR_RESETM; | |
memset((void *)&pma_allocation_info, 0, sizeof(PMAInfo)); | |
memset((void *)ep_info, 0, sizeof(USBEndpointInfo) * 2); | |
USB->BTABLE = alloc(sizeof(PacketBuffer) * 2); | |
USB->DADDR = USB_DADDR_EF; // enable the functionality | |
// initialize EP0 | |
ep_info[0].fsm = ST_RESET; | |
ep_info[0].packet_size = 64; | |
ep_info[0].pb_ptr = (PacketBuffer *)((uintptr_t)USB->BTABLE + USB_PMAADDR); | |
ep_info[0].pb_ptr->addr_rx = 0; | |
USB->EP0R = USB_EP_CONTROL; | |
pma_allocation_info.offsets.setup_packet = alloc(64); | |
usb_ep_receive(0, | |
pma_allocation_info.offsets.setup_packet, | |
ep_info[0].packet_size | |
); | |
// initialize EP1 | |
ep_info[1].packet_size = 64; | |
ep_info[1].pb_ptr = (PacketBuffer *)((uintptr_t)USB->BTABLE + USB_PMAADDR + sizeof(PacketBuffer)); | |
USB->EP1R = USB_EP_INTERRUPT | 0x01; | |
addr_report = alloc(3); // size of report, in this case. | |
pma_in(addr_report, (const void *)report, 3); | |
usb_ep_send(1, | |
addr_report, | |
3 | |
); | |
} | |
while( (flag = USB->ISTR) & USB_ISTR_CTR ) { | |
const uint16_t ep_num = flag & USB_ISTR_EP_ID; | |
const uint16_t epreg = USB_EPnR(ep_num); | |
if( ep_num == 0 ) { | |
if( epreg & USB_EP_CTR_RX ) { | |
// DIR == 1 : IRQ by SETUP transaction. CTR_RX is set. | |
USB_EPnR(0) = epreg & ~USB_EP_CTR_RX & USB_EPREG_MASK; | |
if( epreg & USB_EP_SETUP ) { | |
ep_info[ep_num].fsm = SETUP; | |
pma_out( | |
(void *)&ep_info[ep_num].last_setup, | |
pma_allocation_info.offsets.setup_packet, | |
sizeof(USBSetupPacket) | |
); | |
iprintf("** SETUP\n"); | |
dump_setup_packet(); | |
usb_ep0_handle_setup(); | |
ep_info[ep_num].fsm = next_state_ep0(); | |
usb_ep0_prepare_for_status(); | |
} | |
else{ | |
// IRQ by OUT transaction | |
if( ep_info[ep_num].fsm == STATUS_OUT ) { | |
iprintf("STATUS_OUT\n"); | |
usb_ep0_handle_status(); | |
}else if( ep_info[ep_num].fsm == OUT ) { | |
ep_info[ep_num].fsm = next_state_ep0(); | |
usb_ep0_prepare_for_status(); | |
} | |
} | |
} | |
if( epreg & USB_EP_CTR_TX ){ | |
// DIR = 0 : IRQ by IN transaction. CTR_TX is set. | |
USB_EPnR(0) = epreg & ~USB_EP_CTR_TX & USB_EPREG_MASK; | |
if( ep_info[ep_num].fsm == STATUS_IN ) { | |
iprintf("STATUS_IN\n"); | |
usb_ep0_handle_status(); | |
}else if( ep_info[ep_num].fsm == IN ) { | |
iprintf("IN\n"); | |
ep_info[ep_num].fsm = next_state_ep0(); | |
usb_ep0_handle_in(); | |
usb_ep0_prepare_for_status(); | |
} | |
} | |
}else if( ep_num == 1 ){ | |
USB_EPnR(1) = epreg & ~USB_EP_CTR_TX & USB_EPREG_MASK; | |
pma_in(addr_report, (const void *)report, 3); | |
usb_ep_send(1, | |
addr_report, | |
3 | |
); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment