M include/usb_device.h => include/usb_device.h +25 -3
@@ 4,9 4,31 @@
//
#include <stm32h747xx.h>
+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
M src/main.c => src/main.c +1 -1
@@ 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) {}
}
M src/usb_device.c => src/usb_device.c +143 -21
@@ 2,67 2,82 @@
#include <stdlib.h>
#include <stddef.h>
#include <stm32h747xx.h>
+#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;
}