sustain-pedal-hid-sw/firmware/usb.c

669 lines
15 KiB
C
Raw Normal View History

2023-08-10 23:00:40 +02:00
#include "usb.h"
#include <stm32l0xx.h>
#include <stddef.h>
#define ENDPOINT_COUNT (8)
enum endpoint_state {EP_STATE_DISABLED, EP_STATE_NAK, EP_STATE_STALL, EP_STATE_VALID};
static void (*ep_rx_data_callback)(uint8_t endpoint, const uint8_t *buffer, uint32_t len) = NULL;
static enum control_state (*ep_rx_setup_received_callback)(uint8_t endpoint, const struct setup_packet *setup_pkg) = NULL;
static void (*ep_tx_complete_callback)(uint8_t endpoint);
static void (*usb_sof_callback)(void) = NULL;
static void (*usb_reset_callback)(void) = NULL;
static void (*usb_configured_callback)(uint16_t config_idx) = NULL;
struct usb_endpoint_info {
uint16_t rx_pma_size;
uint16_t tx_pma_size;
enum usb_ep_type type;
struct setup_packet last_setup;
enum control_state ctrl_state;
volatile uint16_t *ep_reg;
uint32_t tx_count;
const uint8_t *tx_ptr;
uint32_t rx_size;
uint8_t *rx_buffer;
};
static const struct usb_descriptor_entry *usb_descriptors = NULL;
static uint16_t * const pma_base_ptr = (uint16_t *)0x40006000UL;
struct usb_endpoint_info endpoints[ENDPOINT_COUNT];
static void setup_usb_clock(void)
{
uint32_t reload_val;
const uint32_t felim = 0x22;
const uint32_t f_target = 48000000UL;
const uint32_t f_usb_sof = 1000UL;
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
/* Enable the internal voltage reference. This is needed for HSI48 */
SYSCFG->CFGR3 |= SYSCFG_CFGR3_EN_VREFINT;
while (!(SYSCFG->CFGR3 & SYSCFG_CFGR3_VREFINT_RDYF));
SYSCFG->CFGR3 |= SYSCFG_CFGR3_ENREF_HSI48;
RCC->CRRCR = RCC_CRRCR_HSI48ON;
while(!(RCC->CRRCR & RCC_CRRCR_HSI48RDY));
RCC->CCIPR |= RCC_CCIPR_HSI48SEL;
/* Configure Clock recovery from USB SOF (1kHz) */
RCC->APB1ENR |= RCC_APB1ENR_CRSEN;
reload_val = (f_target / f_usb_sof) - 1;
CRS->CFGR = (reload_val & 0xFFFF) | (felim << 16) | CRS_CFGR_SYNCSRC_1;
CRS->CR = CRS_CR_CEN | CRS_CR_AUTOTRIMEN;
}
void usb_init(const struct usb_callbacks *callbacks)
{
setup_usb_clock();
/* Activate register clock for USB */
RCC->APB1ENR |= RCC_APB1ENR_USBEN;
/* Power up the USB interface but hold in reset */
USB->CNTR = USB_CNTR_FRES;
endpoints[0].ep_reg = &USB->EP0R;
endpoints[1].ep_reg = &USB->EP1R;
endpoints[2].ep_reg = &USB->EP2R;
endpoints[3].ep_reg = &USB->EP3R;
endpoints[4].ep_reg = &USB->EP4R;
endpoints[5].ep_reg = &USB->EP5R;
endpoints[6].ep_reg = &USB->EP6R;
endpoints[7].ep_reg = &USB->EP7R;
/* Setup the callbacks */
ep_rx_data_callback = callbacks->ep_rx_data_callback;
ep_tx_complete_callback = callbacks->ep_tx_complete_callback;
ep_rx_setup_received_callback = callbacks->ep_rx_setup_received_callback;
usb_configured_callback = callbacks->usb_configured_callback;
usb_reset_callback = callbacks->usb_reset_callback;
usb_sof_callback = callbacks->usb_sof_callback;
}
static void write_b_table(uint32_t addr, uint16_t value)
{
volatile uint16_t *ptr = pma_base_ptr;
ptr += (addr >> 1);
*ptr = value;
}
static uint16_t read_b_table(uint32_t addr)
{
volatile uint16_t *ptr = pma_base_ptr;
ptr += (addr >> 1);
return *ptr;
}
static uint16_t read_rx_pma(uint8_t ep, uint8_t *buff, uint32_t size)
{
uint16_t rx_btable_entry;
uint16_t pma_addr;
uint8_t *app_addr;
uint16_t count;
uint16_t i;
rx_btable_entry = read_b_table(ep * 8 + 6);
count = rx_btable_entry & 0x3FF;
pma_addr = read_b_table(ep * 8 + 4);
app_addr = (uint8_t *)pma_base_ptr;
app_addr += (pma_addr >> 0);
/* Byte addresed writes seem to work just fine */
for (i = 0; i < count && i < size; i++) {
buff[i] = app_addr[i];
}
return count;
}
static void write_tx_pma(uint8_t ep, const uint8_t *src, uint32_t len)
{
uint16_t tx_addr;
uint16_t *pma = pma_base_ptr;
uint32_t i;
uint32_t halfword_cnt;
bool padding;
write_b_table(ep * 8 + 2, len);
if (!len)
return;
tx_addr = read_b_table(ep * 8);
padding = len % 2 ? true : false;
halfword_cnt = len >> 1;
pma += tx_addr >> 1;
for (i = 0; i < halfword_cnt; i++) {
pma[i] = (uint16_t)src[2 * i] | (((uint16_t)src[2 * i + 1]) << 8);
}
if (padding) {
pma[i] = (uint16_t)src[2 * i];
}
}
static const struct usb_descriptor_entry *find_descriptor_from_setup_request(uint16_t w_index, uint16_t w_value)
{
int i;
const struct usb_descriptor_entry *entry = NULL;
for (i = 0; usb_descriptors[i].descriptor != NULL; i++) {
if (usb_descriptors[i].w_index == w_index && usb_descriptors[i].w_value == w_value) {
entry = &usb_descriptors[i];
break;
}
}
return entry;
}
void usb_endpoint_config(enum usb_ep_type type, uint8_t epnum, bool rx, bool tx)
{
uint16_t epreg = 0;
struct usb_endpoint_info *ep_info;
if (epnum >= ENDPOINT_COUNT)
return;
ep_info = &endpoints[epnum];
write_b_table(epnum * 8 + 0, 64 + epnum * 136);
write_b_table(epnum * 8 + 2, 0);
write_b_table(epnum * 8 + 4, 64 + epnum * 136 + 72);
write_b_table(epnum * 8 + 6, (1 << 10) | (1<<15));
epreg = *ep_info->ep_reg;
epreg &= ~(1<<15);
switch (type) {
case EP_CONTROL:
epreg |= (1<<9);
ep_info->type = EP_CONTROL;
ep_info->ctrl_state = CONTROL_SETUP;
ep_info->tx_pma_size = 64;
ep_info->rx_pma_size = 64;
case EP_BULK:
ep_info->type = EP_BULK;
ep_info->ctrl_state = CONTROL_SETUP;
ep_info->tx_pma_size = 64;
ep_info->rx_pma_size = 64;
break;
case EP_INTERRUPT:
epreg |= (1<<10) | (1<<9);
ep_info->type = EP_INTERRUPT;
ep_info->ctrl_state = CONTROL_SETUP;
ep_info->tx_pma_size = 64;
ep_info->rx_pma_size = 64;
break;
case EP_ISOCHRON:
epreg |= (1<<10);
ep_info->type = EP_ISOCHRON;
ep_info->ctrl_state = CONTROL_SETUP;
ep_info->tx_pma_size = 64;
ep_info->rx_pma_size = 64;
break;
default:
return;
break;
}
if (tx) {
epreg |= (1<<5);
}
if (rx) {
epreg |= (1<<13);
}
epreg |= epnum;
*ep_info->ep_reg = epreg;
}
static void usb_endpoint_reset_int_flags(uint8_t endpoint, bool rx, bool tx)
{
uint16_t ep_reg;
if (endpoint >= ENDPOINT_COUNT)
return;
ep_reg = *endpoints[endpoint].ep_reg;
ep_reg &= ~((1<<14) | (1<<6) | (1<<5) | (1<<13) | (1<<12) | (1<<4));
/* Dont clear flag */
if (rx) {
ep_reg &= ~(1<<15);
} else {
ep_reg |= (1<<15);
}
if (tx) {
ep_reg &= ~(1<<7);
} else {
ep_reg |= (1<<7);
}
*endpoints[endpoint].ep_reg = ep_reg;
}
static void usb_endpoint_set_rx_state(uint8_t endpoint, enum endpoint_state state)
{
uint16_t ep_reg;
uint16_t ep_rx_target = 0;
if (endpoint >= ENDPOINT_COUNT)
return;
ep_reg = *endpoints[endpoint].ep_reg;
/* Mask out all toggle flags that we don't want to change */
ep_reg &= ~((1<<14) | (1<<6) | (1<<5) | (1<<4));
/* Dont clear flag */
ep_reg |= (1<<15) | (1<<7);
/* Prepare the target value */
switch (state) {
case EP_STATE_DISABLED:
ep_rx_target = 0;
break;
case EP_STATE_NAK:
ep_rx_target = (1<<13);
break;
case EP_STATE_VALID:
ep_rx_target = (1<<13) | (1<<12);
break;
case EP_STATE_STALL:
ep_rx_target = (1<<12);
default:
return;
}
/* Generate toggle mask for the TX bits */
ep_reg ^= ep_rx_target;
*endpoints[endpoint].ep_reg = ep_reg;
}
static void usb_endpoint_set_tx_state(uint8_t endpoint, enum endpoint_state state)
{
uint16_t ep_reg;
uint16_t ep_tx_target = 0;
if (endpoint >= ENDPOINT_COUNT)
return;
ep_reg = *endpoints[endpoint].ep_reg;
/* Mask out all toggle flags that we don't want to change */
ep_reg &= ~((1<<14) | (1<<13) | (1<<12) | (1<<6));
/* Dont clear flag */
ep_reg |= (1<<15) | (1<<7);
/* Prepare the target value */
switch (state) {
case EP_STATE_DISABLED:
ep_tx_target = 0;
break;
case EP_STATE_NAK:
ep_tx_target = (1<<5);
break;
case EP_STATE_VALID:
ep_tx_target = (1<<5) | (1<<4);
break;
case EP_STATE_STALL:
ep_tx_target = (1<<4);
default:
return;
}
/* Generate toggle mask for the TX bits */
ep_reg ^= ep_tx_target;
*endpoints[endpoint].ep_reg = ep_reg;
}
void usb_endpoint_stall(uint8_t endpoint, bool tx_dir, bool rx_dir)
{
if (tx_dir)
usb_endpoint_set_tx_state(endpoint, EP_STATE_STALL);
}
int usb_endpoint_send(uint8_t endpoint, const uint8_t *data, uint32_t len)
{
struct usb_endpoint_info *epinfo;
uint32_t tx_cnt;
if (endpoint >= ENDPOINT_COUNT)
return -1001;
epinfo = &endpoints[endpoint];
if (epinfo->tx_pma_size == 0)
return -1;
tx_cnt = len < epinfo->tx_pma_size ? len : epinfo->tx_pma_size;
/* Copy data to pma and save the data pointer to the next word to be transmitted */
if (tx_cnt < len) {
epinfo->tx_ptr = &data[tx_cnt];
epinfo->tx_count = len - tx_cnt;
} else {
epinfo->tx_count = 0;
epinfo->tx_ptr = NULL;
}
write_tx_pma(endpoint, data, tx_cnt);
/* Prepare endpoint for TX */
usb_endpoint_set_tx_state(endpoint, EP_STATE_VALID);
return 0;
}
int usb_endpoint_prepare_receive(uint8_t endpoint, uint8_t *buffer, uint32_t bufflen)
{
struct usb_endpoint_info *info;
if (endpoint >= ENDPOINT_COUNT)
return -1001;
info = &endpoints[endpoint];
if (info->rx_buffer || info->rx_size) {
return -1;
}
__disable_irq();
info->rx_buffer = buffer;
info->rx_size = bufflen;
usb_endpoint_set_rx_state(endpoint, EP_STATE_VALID);
__enable_irq();
return 0;
}
void usb_endpoint_send_status_stage(uint8_t endpoint)
{
struct usb_endpoint_info *info;
if (endpoint >= ENDPOINT_COUNT)
return;
info = &endpoints[endpoint];
info->ctrl_state = CONTROL_STATUS_TX;
usb_endpoint_send(endpoint, NULL, 0);
}
void usb_enable(const struct usb_descriptor_entry *descriptors)
{
if (!descriptors)
return;
usb_descriptors = descriptors;
/* Enable device with address 0 */
USB->DADDR = USB_DADDR_EF;
/* Clear all interrupts */
USB->ISTR = 0;
/* Set buffer description table to beginning of PMA */
USB->BTABLE = 0x0U;
/* Actiovate USB module (clear reset bit) and setup the interrupt masks */
USB->CNTR = USB_CNTR_CTRM | USB_CNTR_ERRM | USB_CNTR_RESETM | USB_CNTR_SOFM;
/* Enable internal D+ pullup to tell the host we're here */
USB->BCDR = USB_BCDR_DPPU;
NVIC_EnableIRQ(USB_IRQn);
}
static void usb_handle_reset(void)
{
int i;
/* Reset USB address */
USB->DADDR = USB_DADDR_EF;
for (i = 0; i < ENDPOINT_COUNT; i++) {
endpoints[i].rx_size = 0;
endpoints[i].rx_buffer = NULL;
endpoints[i].tx_count = 0;
endpoints[i].tx_ptr = NULL;
}
usb_endpoint_config(EP_CONTROL, 0, true, true);
usb_endpoint_set_rx_state(0, EP_STATE_VALID);
}
void usb_ep0_send_status(void)
{
struct usb_endpoint_info *ep_info;
ep_info = &endpoints[0];
ep_info->ctrl_state = CONTROL_STATUS_TX;
usb_endpoint_send(0, NULL, 0);
}
void usb_ep0_handle_setup(void)
{
uint16_t cnt;
enum control_state next_state;
const struct usb_descriptor_entry *descriptor;
struct usb_endpoint_info *ep_info;
struct setup_packet *setup;
ep_info = &endpoints[0];
ep_info->ctrl_state = CONTROL_SETUP;
setup = &ep_info->last_setup;
cnt = read_rx_pma(0, (uint8_t *)setup, 8);
if (cnt != 8) {
usb_endpoint_stall(0, true, true);
return;
}
if ((setup->bm_req_type == 0x80 || setup->bm_req_type == 0x81 || setup->bm_req_type == 0x82) && setup->b_request == 6) {
/* Get descriptor request */
descriptor = find_descriptor_from_setup_request(setup->w_index, setup->w_value);
if (!descriptor) {
usb_endpoint_stall(0, true, true);
}
cnt = setup->w_length < descriptor->size ? setup->w_length : descriptor->size;
usb_endpoint_send(0, descriptor->descriptor, cnt);
ep_info->ctrl_state = CONTROL_DATA_TX;
} else if (setup->bm_req_type == 0x00 && setup->b_request == 5) {
/* Set address command
* Send out status stage and set address after status has finished
*/
usb_ep0_send_status();
} else if (setup->bm_req_type == 0x00 && setup->b_request == 9) {
/* Set configuration */
if (usb_configured_callback)
usb_configured_callback(setup->w_value);
usb_ep0_send_status();
} else {
next_state = CONTROL_NOT_HANDLED;
/* Check if the callback for setup requests is set and try to handle it this way */
if (ep_rx_setup_received_callback)
next_state = ep_rx_setup_received_callback(0, setup);
/* We could not handle the setup request if handle state != 0 */
if (next_state == CONTROL_NOT_HANDLED) {
usb_endpoint_stall(0, true, true);
} else {
/* Control handled */
switch (next_state) {
case CONTROL_STATUS_TX:
usb_ep0_send_status();
break;
case CONTROL_DATA_TX:
break;
case CONTROL_DATA_RX:
break;
default:
usb_endpoint_stall(0, true, true);
break;
}
}
ep_info->ctrl_state = (next_state != CONTROL_NOT_HANDLED ? next_state : CONTROL_SETUP);
}
return;
}
void usb_ep0_handle_tx(void)
{
struct usb_endpoint_info *info = &endpoints[0];
if (info->tx_count) {
/* Do the rest */
usb_endpoint_send(0, info->tx_ptr, info->tx_count);
} else {
if (info->ctrl_state == CONTROL_DATA_TX) {
info->ctrl_state = CONTROL_STATUS_RX;
/* Listen for new setup packets */
usb_endpoint_set_rx_state(0, EP_STATE_VALID);
} else if (info->ctrl_state == CONTROL_STATUS_TX) {
/* we've sent a status back. Check if we should set our address now */
if (info->last_setup.bm_req_type == 0x00 && info->last_setup.b_request == 0x5) {
USB->DADDR = USB_DADDR_EF | info->last_setup.w_value;
}
info->ctrl_state = CONTROL_SETUP;
/* Listen for new setup packets */
usb_endpoint_set_rx_state(0, EP_STATE_VALID);
} else {
if (ep_tx_complete_callback)
ep_tx_complete_callback(0);
}
}
}
void usb_handle_rx_packet(uint8_t ep)
{
uint16_t epreg;
struct usb_endpoint_info *info;
uint16_t pkg_len;
uint8_t *buffer;
info = &endpoints[ep];
epreg = *info->ep_reg;
usb_endpoint_reset_int_flags(ep, true, false);
if (epreg & (1<<11) && ep == 0) {
info->ctrl_state = CONTROL_SETUP;
usb_ep0_handle_setup();
} else if (ep == 0 && info->ctrl_state == CONTROL_STATUS_RX) {
info->ctrl_state = CONTROL_SETUP;
/* Check the received status frame */
pkg_len = read_rx_pma(0, NULL, 0);
if (pkg_len != 0) {
usb_endpoint_stall(0, true, true);
} else {
/* Ready to receive next datum */
usb_endpoint_set_rx_state(0, EP_STATE_VALID);
}
} else {
if (epreg & (1<<11)) {
(void)read_rx_pma(ep, (uint8_t *)&info->last_setup, 8);
if (ep_rx_setup_received_callback)
ep_rx_setup_received_callback(ep, &info->last_setup);
}
if (info->rx_buffer && info->rx_size) {
pkg_len = read_rx_pma(ep, info->rx_buffer, info->rx_size);
buffer = info->rx_buffer;
info->rx_size = 0;
info->rx_buffer = 0;
ep_rx_data_callback(ep, buffer, pkg_len);
}
}
}
void usb_handle_tx_packet_sent(uint8_t ep)
{
struct usb_endpoint_info *info = &endpoints[ep];
usb_endpoint_reset_int_flags(ep, false, true);
if (ep == 0) {
usb_ep0_handle_tx();
} else {
if (info->tx_count && info->tx_ptr) {
usb_endpoint_send(ep, info->tx_ptr, info->tx_count);
} else {
if (ep_tx_complete_callback)
ep_tx_complete_callback(ep);
}
}
}
void USB_IRQHandler(void)
{
uint16_t istr = USB->ISTR;
uint8_t endpoint;
bool dir_tx;
if (istr & USB_ISTR_RESET) {
USB->ISTR = (uint16_t)(~USB_ISTR_RESET);
usb_handle_reset();
if (usb_reset_callback)
usb_reset_callback();
}
if (istr & USB_ISTR_CTR) {
/* correct transfer interrupt */
endpoint = istr & USB_ISTR_EP_ID;
dir_tx = (istr & USB_ISTR_DIR) ? false : true;
if (dir_tx) {
usb_handle_tx_packet_sent(endpoint);
} else {
usb_handle_rx_packet(endpoint);
}
/* Clear interrupt */
USB->ISTR = (uint16_t)(~USB_ISTR_CTR);
}
if (istr & USB_ISTR_SOF) {
USB->ISTR = (uint16_t)(~USB_ISTR_SOF);
if (usb_sof_callback)
usb_sof_callback();
}
if (istr & USB_ISTR_ERR) {
USB->ISTR = (uint16_t)(~USB_ISTR_ERR);
}
__DSB();
}