From 46e1209d05670724ee540c8727d21398f09f5882 Mon Sep 17 00:00:00 2001 From: Rutherther Date: Sun, 20 Oct 2024 17:55:37 +0200 Subject: [PATCH] wip: usb --- include/usb_device.h | 28 +++++++- src/main.c | 2 +- src/usb_device.c | 164 +++++++++++++++++++++++++++++++++++++------ 3 files changed, 169 insertions(+), 25 deletions(-) diff --git a/include/usb_device.h b/include/usb_device.h index 627ceeb13aa8e8fadd403d4990a0ab3be92e008c..99ffef91266458438cbc634f4d27cc6c346c6b86 100644 --- a/include/usb_device.h +++ b/include/usb_device.h @@ -4,9 +4,31 @@ // #include +typedef enum { + // error states first so they are lowest + CONTROL_ERROR = -4, + ERROR = -3, + UNKNOWN_INTERRUPT = -2, + OTG_ERROR = -1, // shouldn't happen, since otg is not used + + INIT = 0, + RESET = 1, + RESET_DONE = 2, + SET_ADDRESS_RCVD = 3, + SET_CONFIG_RCVD = 4, + ENUMERATED = 5, +} usb_device_state_t; + typedef struct { - volatile USB_OTG_GlobalTypeDef *core; - volatile USB_OTG_DeviceTypeDef *device; + USB_OTG_GlobalTypeDef *core; + USB_OTG_DeviceTypeDef *device; + + USB_OTG_OUTEndpointTypeDef *out; + USB_OTG_INEndpointTypeDef *in; + + uint8_t endpoint_count; + + usb_device_state_t state; } usb_device_t; struct usb_configuration_t; @@ -24,6 +46,6 @@ extern usb_device_t* usb1_device; void* usb_device_init(void* peripheral_address, usb_class_t* class, void* buffer); void usb_device_setup(void* device); -void* usb_device_wait_for_handshake(void* device); +void usb_device_wait_for_handshake(void* device); #endif // USB_DEVICE_H diff --git a/src/main.c b/src/main.c index b82c7f30a2f1a79b27d9882ebd1a9fae19f37ab5..b07bf5e84a68a91867dec97d97af49bd7e007d8f 100644 --- a/src/main.c +++ b/src/main.c @@ -134,7 +134,7 @@ void main() void* usb_otg = usb_device_init(USB1_OTG_HS, NULL, NULL); usb_device_setup(usb_otg); - /* usb_device_wait_for_handshake(usb_otg); */ + usb_device_wait_for_handshake(usb_otg); while(1) {} } diff --git a/src/usb_device.c b/src/usb_device.c index 307f533f78ced1f57321fb1df2453f6bf3f72a0f..f4e1f51a9924de71377b4bf9dd5cd9ba034ad426 100644 --- a/src/usb_device.c +++ b/src/usb_device.c @@ -2,67 +2,82 @@ #include #include #include +#include "registers.h" /* USB_OTG_GlobalTypeDef */ usb_device_t* usb1_device; -void* usb_device_init(void* peripheral_address, usb_class_t* class, void* buffer) -{ +void* usb_device_init(void* peripheral_address, usb_class_t* class, void* buffer) { if (buffer == NULL) { buffer = (void*)malloc(sizeof(usb_device_t)); } usb_device_t* device = (usb_device_t*)buffer; - device->core = peripheral_address; + device->state = INIT; + device->core = peripheral_address + USB_OTG_GLOBAL_BASE; device->device = peripheral_address + USB_OTG_DEVICE_BASE; + device->out = peripheral_address + USB_OTG_OUT_ENDPOINT_BASE; + device->in = peripheral_address + USB_OTG_IN_ENDPOINT_BASE; + device->endpoint_count = 8; + // TODO: clarify how this should work... usb1_device = device; return device; } -void usb_device_reset(void* device_ptr) -{ +void usb_device_reset(void* device_ptr) { usb_device_t* device = (usb_device_t*)device_ptr; + + // Idle while ((device->core->GRSTCTL & USB_OTG_GRSTCTL_AHBIDL) == 0); + // Reset device->core->GRSTCTL |= USB_OTG_GRSTCTL_CSRST; + // Wait for reset while (device->core->GRSTCTL & USB_OTG_GRSTCTL_CSRST); + // Wait for idle while ((device->core->GRSTCTL & USB_OTG_GRSTCTL_AHBIDL) == 0); } -void usb_device_clear_interrupts(void* device_ptr) -{ +void usb_device_clear_interrupts(void* device_ptr) { usb_device_t* device = (usb_device_t*)device_ptr; device->core->GINTSTS = 0; // clear interrupts // for other interrupts the rx queue has to be read } -void usb_device_setup(void* device_ptr) -{ +void usb_device_wait_for_handshake(void* device_ptr) { + usb_device_t* device = (usb_device_t*)device_ptr; + + while (device->state != ENUMERATED); +} + +void usb_device_setup(void* device_ptr) { RCC->AHB1ENR |= RCC_AHB1ENR_USB1OTGHSEN | RCC_AHB1ENR_USB1OTGHSULPIEN; volatile uint32_t dummy; dummy = RCC->AHB1ENR; dummy = RCC->AHB1ENR; - NVIC_SetPriority(OTG_FS_IRQn, 2); - NVIC_EnableIRQ(OTG_FS_IRQn); + NVIC_SetPriority(OTG_HS_IRQn, 1); + NVIC_EnableIRQ(OTG_HS_IRQn); // 1. core initialization usb_device_t* device = (usb_device_t*)device_ptr; - RCC->AHB1ENR |= RCC_AHB1ENR_USB1OTGHSULPIEN | RCC_AHB1ENR_USB1OTGHSEN; + device->device->DCTL |= USB_OTG_DCTL_SDIS; - /* PWR->CR3 |= PWR_CR3_USBREGEN; */ - /* while ((PWR->CR3 & PWR_CR3_USB33RDY) == 0); */ + PWR->CR3 |= PWR_CR3_USB33DEN; + while ((PWR->CR3 & PWR_CR3_USB33RDY) == 0); device->core->GCCFG |= USB_OTG_GCCFG_PWRDWN; device->core->GUSBCFG |= USB_OTG_GUSBCFG_FDMOD; + usb_device_reset(device_ptr); + // global interrupt mask // rx fifo non empty // periodic tx fifo empty level @@ -73,8 +88,9 @@ void usb_device_setup(void* device_ptr) // TODO: based on enumeration speed set TOCAL - fs timeout calibration device->core->GUSBCFG = USB_OTG_GUSBCFG_FDMOD | USB_OTG_GUSBCFG_ULPIEVBUSI | USB_OTG_GUSBCFG_ULPIEVBUSD | USB_OTG_GUSBCFG_TRDT_3 | USB_OTG_GUSBCFG_TRDT_2; + // TODO: are these needed? device mode is forced... // unmask otg interrupt mask - device->core->GINTMSK |= USB_OTG_GINTMSK_OTGINT | USB_OTG_GINTMSK_MMISM; + /* device->core->GINTMSK |= USB_OTG_GINTMSK_OTGINT | USB_OTG_GINTMSK_MMISM; */ // device initialization @@ -83,21 +99,127 @@ void usb_device_setup(void* device_ptr) // TODO device threshold control register IF DMA - usb_device_reset(device_ptr); + // unmask interrupts usb reset, enumeration done, early suspend, usb suspend, sof + device->core->GINTMSK |= USB_OTG_GINTMSK_ENUMDNEM | USB_OTG_GINTMSK_RSTDEM | USB_OTG_GINTMSK_USBRST | USB_OTG_GINTMSK_ESUSPM | USB_OTG_GINTMSK_SOFM | USB_OTG_GINTMSK_RXFLVLM; // sdis bit device->device->DCTL = ~(USB_OTG_DCTL_SDIS); - // unmask interrupts usb reset, enumeration done, early suspend, usb suspend, sof - device->core->GINTMSK |= USB_OTG_GINTMSK_ENUMDNEM | USB_OTG_GINTMSK_RSTDEM | USB_OTG_GINTMSK_USBRST | USB_OTG_GINTMSK_ESUSPM | USB_OTG_GINTMSK_SOFM; - // wait for usbrst interrupt - the reset - while (((device->core->GINTSTS) & USB_OTG_GINTSTS_USBRST) == 0) {} + /* while (((device->core->GINTSTS) & USB_OTG_GINTSTS_USBRST) == 0) {} */ // wait for enumdne interrupt - end of reset - while (((device->core->GINTSTS) & USB_OTG_GINTSTS_ENUMDNE) == 0) {} + /* while (((device->core->GINTSTS) & USB_OTG_GINTSTS_ENUMDNE) == 0) {} */ } +void usb_handle_control_interrupt(usb_device_t *device) { + uint32_t daint = device->device->DAINT; + + if (daint & (1 << USB_OTG_DAINT_IEPINT_Pos)) { + // in (that means sending data) + } else if (daint & (1 << USB_OTG_DAINT_OEPINT_Pos)) { + // out (that means receiving data) + } else { + device->state = CONTROL_ERROR; + } +} + +void usb_handle_endpoint_interrupt(usb_device_t* device) { + // TODO + + if ((device->state < ENUMERATED) || (device->device->DAINT & ((1 << USB_OTG_DAINT_IEPINT_Pos) | (1 << USB_OTG_DAINT_OEPINT_Pos)))) { + usb_handle_control_interrupt(device); + } else { + // new data received, put it to application level?. + } +} + +// NOTE: this is irq handler void otg_hs_handler(void) { + usb_device_t* device = usb1_device; + + // Reset detected + if (device->core->GINTSTS & USB_OTG_GINTSTS_USBRST) { + reg_set_bits(&device->core->GINTMSK, USB_OTG_GINTMSK_IEPINT | USB_OTG_GINTMSK_OEPINT); + reg_set_bits(&device->device->DOEPMSK, USB_OTG_GINTMSK_IEPINT | USB_OTG_GINTMSK_OEPINT); + + // TODO: should this be done for ep 0 as well? + for (int ep = 1; ep < device->endpoint_count; ep++) { + USB_OTG_OUTEndpointTypeDef *out = device->out + ep; + out->DOEPCTL |= USB_OTG_DOEPCTL_SNAK; + } + + device->device->DAINTMSK |= 1 << USB_OTG_DAINTMSK_IEPM_Pos; + device->device->DAINTMSK |= 1 << USB_OTG_DAINTMSK_OEPM_Pos; + + device->device->DOEPMSK |= USB_OTG_DOEPMSK_STUPM | USB_OTG_DOEPMSK_XFRCM; + device->device->DIEPMSK |= USB_OTG_DIEPMSK_XFRCM | USB_OTG_DIEPMSK_TOM; + + // 512 bytes + device->core->GRXFSIZ = 512 / 4; + // 64 bytes, beginning of ram + device->core->DIEPTXF[0] = (0) << USB_OTG_DIEPTXF_INEPTXSA_Pos | (64 / 4) << USB_OTG_DIEPTXF_INEPTXFD_Pos; + + // 3 packets to receive as setup + reg_write_bits_pos(&device->out->DOEPTSIZ, 3, USB_OTG_DOEPTSIZ_STUPCNT_Pos, 3); + + device->state = RESET; + // clear it + reg_set_bits(&device->core->GINTSTS, USB_OTG_GINTSTS_USBRST); + + return; + } + + // Reset ended + if (device->core->GINTSTS & USB_OTG_GINTSTS_ENUMDNE) { + // The enumerated speed + /* (device->device->DSTS & USB_OTG_DSTS_ENUMSPD_Msk) >> USB_OTG_DSTS_ENUMSPD_Pos; */ + // We do not need to know the speed, it can be either full speed or high + // speed, both have maximum size 64 bytes. Only low speed or super high + // speed would differ, but these are not supported! + reg_write_bits_pos(&device->in->DIEPCTL, 64, USB_OTG_DIEPCTL_MPSIZ_Pos, 0x7FFUL); + + device->state = RESET_DONE; + // clear it + reg_set_bits(&device->core->GINTSTS, USB_OTG_GINTSTS_ENUMDNE); + + return; + } + + // Start of frame + if (device->core->GINTSTS & USB_OTG_GINTSTS_SOF) { + // Nothing to do? + reg_set_bits(&device->core->GINTSTS, USB_OTG_GINTSTS_SOF); + return; + } + + // OTG interrupt, should not be triggered since forced device mode + if (device->core->GINTSTS & USB_OTG_GINTSTS_OTGINT) { + device->state = OTG_ERROR; + reg_set_bits(&device->core->GINTSTS, USB_OTG_GINTSTS_OTGINT); + return; + } + + // OTG interrupt, should not be triggered since forced device mode + if (device->core->GINTSTS & USB_OTG_GINTSTS_MMIS) { + device->state = OTG_ERROR; + reg_set_bits(&device->core->GINTSTS, USB_OTG_GINTSTS_MMIS); + return; + } + + // Setup, data... + if (device->core->GINTSTS & (USB_OTG_GINTSTS_OEPINT | USB_OTG_GINTSTS_IEPINT)) { + // TODO + usb_handle_endpoint_interrupt(device); + return; + } + + if (device->core->GINTSTS & (USB_OTG_GINTSTS_RXFLVL)) { + // TODO + usb_handle_endpoint_interrupt(device); + return; + } + + device->state = UNKNOWN_INTERRUPT; }