From 627e038dbb86586946e6d9fbdb6b072156e83ccc Mon Sep 17 00:00:00 2001 From: Epagris Date: Wed, 10 Apr 2024 23:21:03 +0200 Subject: [PATCH] - CDC class OS-managed blocking buffer added; - some low-level USB function signatures have been changed --- class/cdc.c | 41 ++++++-- class/cdc.h | 11 +- usb.c | 4 +- usb.h | 2 +- usb_driver.c | 289 ++++++++++++++++++++++++++------------------------- usb_driver.h | 2 +- 6 files changed, 187 insertions(+), 162 deletions(-) diff --git a/class/cdc.c b/class/cdc.c index 0bb78db..1893680 100644 --- a/class/cdc.c +++ b/class/cdc.c @@ -6,9 +6,16 @@ #include "../usb.h" +#include + // state -static USB_CdcState cdcs; -static uint8_t cdc_buffer[USB_CDC_BUFSIZE]; +static USB_CdcState cdcs = { 0 }; +static uint8_t tx_buffer[USB_CDC_PCKT_BUFSIZE]; + +#define USB_CDC_FIFO_MEM_SIZE (2048) + +static uint8_t fifo_mem[USB_CDC_FIFO_MEM_SIZE]; +static BFifo fifo; void usb_cdc_init(const USB_CdcAssignments *as) { // clear the structure @@ -24,10 +31,11 @@ void usb_cdc_init(const USB_CdcAssignments *as) { // fill-in assigments cdcs.ep_assignments = *as; - // assign buffer pointer - USB_CdcBuffer * buf = &cdcs.buffer; - buf->p = cdc_buffer; - buf->size = USB_CDC_BUFSIZE; + // initialize buffer + bfifo_create(&fifo, fifo_mem, USB_CDC_FIFO_MEM_SIZE); + + // from now on CDC is considered initialized + cdcs.initialized = true; } int usb_cdc_process_and_return(USB_CallbackEvent *cbevt) { @@ -37,12 +45,12 @@ int usb_cdc_process_and_return(USB_CallbackEvent *cbevt) { switch (cbevt->setup_request->bRequest) { case USB_CDC_SET_LINE_CODING: // set line coding memcpy(&cdcs.line_coding, cbevt->data, sizeof(USB_Cdc_LineCodingStruct)); - MSG("%u\n", cdcs.line_coding.dwDTERate); + //MSG("%u\n", cdcs.line_coding.dwDTERate); ret = 0; break; case USB_CDC_SET_CONTROL_LINE_STATE: // set control line state memcpy(&cdcs.control_line_state, cbevt->data, sizeof(USB_Cdc_ControlLineStateStruct)); - MSG("%u\n", cdcs.control_line_state.D); + //MSG("%u\n", cdcs.control_line_state.D); ret = 0; break; default: @@ -63,6 +71,7 @@ int usb_cdc_process_and_return(USB_CallbackEvent *cbevt) { if (cbevt->ep == cdcs.ep_assignments.data_ep) { //MSG("%c\n", cbevt->data[0]); ret = 0; + usbcore_write(cdcs.ep_assignments.data_ep, cbevt->data, cbevt->size); // echo } break; @@ -75,7 +84,13 @@ int usb_cdc_process_and_return(USB_CallbackEvent *cbevt) { } else if (cbevt->ep == cdcs.ep_assignments.data_ep) { // if data are requested //usbcore_write(cdcs.ep_assignments.data_ep, NULL, 0); // send ZLP ret = 0; - // TODO!! + + // read from the fifo + uint32_t readSize = bfifo_read(&fifo, tx_buffer, USB_CDC_PCKT_BUFSIZE); + if (readSize > 0) { + uint32_t writeSize = usbcore_write(cdcs.ep_assignments.data_ep, tx_buffer, readSize); // write data acquired from the buffer + bfifo_pop(&fifo, writeSize, 0); // pop with no blocking + } } } @@ -86,6 +101,12 @@ int usb_cdc_process_and_return(USB_CallbackEvent *cbevt) { return ret; } +void usb_event_callback(USB_CallbackEvent *cbevt) { + usb_cdc_process_and_return(cbevt); +} + void usb_cdc_write(const uint8_t * data, uint32_t size) { - + if (cdcs.initialized) { + bfifo_push_all(&fifo, data, size); + } } \ No newline at end of file diff --git a/class/cdc.h b/class/cdc.h index 2919532..03640cd 100644 --- a/class/cdc.h +++ b/class/cdc.h @@ -46,17 +46,11 @@ typedef struct { uint8_t data_ep : 4; // data endpoint } USB_CdcAssignments; -typedef struct { - uint32_t size; - uint8_t * p; - uint8_t read_idx, write_idx; -} USB_CdcBuffer; - typedef struct { USB_CdcAssignments ep_assignments; // endpoint assignments - USB_CdcBuffer buffer; // buffer USB_Cdc_LineCodingStruct line_coding; // line coding USB_Cdc_ControlLineStateStruct control_line_state; // control line state + bool initialized; // CDC is initialized } USB_CdcState; // ---------------- @@ -68,11 +62,12 @@ typedef struct { // ---------------- -#define USB_CDC_BUFSIZE (256) +#define USB_CDC_PCKT_BUFSIZE (120) // ---------------- void usb_cdc_init(const USB_CdcAssignments * as); int usb_cdc_process_and_return(USB_CallbackEvent * cbevt); +void usb_cdc_write(const uint8_t * data, uint32_t size); #endif /* CORE_USB_CLASS_CDC */ diff --git a/usb.c b/usb.c index b9e1705..b844ed1 100644 --- a/usb.c +++ b/usb.c @@ -222,6 +222,6 @@ void usbcore_process_nonsetup_event(USBDRV_CallbackCompound *cbcpd) { } } -void usbcore_write(uint8_t ep, const uint8_t *data, uint16_t size) { - usbdrv_arm_IN_endpoint(ep, data, size); +uint32_t usbcore_write(uint8_t ep, const uint8_t *data, uint16_t size) { + return usbdrv_arm_IN_endpoint(ep, data, size); } \ No newline at end of file diff --git a/usb.h b/usb.h index 15bc28f..72f0b02 100644 --- a/usb.h +++ b/usb.h @@ -4,6 +4,6 @@ #include "usb_callback_event.h" void usbcore_init(); // initialize USB core -void usbcore_write(uint8_t ep, const uint8_t *data, uint16_t size); // write data to endpoint +uint32_t usbcore_write(uint8_t ep, const uint8_t *data, uint16_t size); // write data to endpoint, return with number of bytes actually written #endif /* CORE_USB_USB */ diff --git a/usb_driver.c b/usb_driver.c index f297a1a..6e46d52 100644 --- a/usb_driver.c +++ b/usb_driver.c @@ -82,6 +82,7 @@ void usbdrv_init() { usbdrv_initial_ep0_setup(); usbdrv_power_and_connect(true); + NVIC_SetPriority(OTG_FS_IRQn, 6); NVIC_EnableIRQ(OTG_FS_IRQn); } @@ -471,27 +472,31 @@ void usbdrv_fetch_received_data(uint8_t ep, uint16_t len) { } // write data to specific endpoint FIFO -void usbdrv_arm_IN_endpoint(uint8_t ep, const uint8_t *data, uint16_t len) { +uint32_t usbdrv_arm_IN_endpoint(uint8_t ep, const uint8_t *data, uint16_t len) { /*WRITE_FIELD(USBINEP[ep].DIEPTSIZ, USB_OTG_DIEPTSIZ_XFRSIZ, len); WRITE_FIELD(USBINEP[ep].DIEPTSIZ, USB_OTG_DIEPTSIZ_PKTCNT, 1);*/ // endpoint is already armed - if (gs.ep_IN[ep].task_commenced == USB_EPEVT_ARMED) { - return; - } + // if (gs.ep_IN[ep].task_commenced == USB_EPEVT_ARMED) { + // return; + // } - gs.ep_IN[ep].task_commenced = USB_EPEVT_ARMED; + // gs.ep_IN[ep].task_commenced = USB_EPEVT_ARMED; + + // determine final write size + uint32_t freeSize = USBINEP[ep].DTXFSTS * sizeof(uint32_t); // get free transmit buffer size + uint16_t writeSize = MIN(freeSize, len); // limit transmit size to free size // calculate packet count based on max packet size uint16_t mps = gs.ep_IN[ep].max_packet_size; uint16_t packet_count = 1; // for ZLPs - if (len > 0) { // if length is nonzero - packet_count = len / mps + (((len % mps) > 0) ? 1 : 0); + if (writeSize > 0) { // if length is nonzero + packet_count = writeSize / mps + (((writeSize % mps) > 0) ? 1 : 0); } // TODO: ZLP ending // program DIEPTSIZ with transfer length (TODO: currently only a single transfer!) - USBINEP[ep].DIEPTSIZ = (packet_count << USB_OTG_DIEPTSIZ_PKTCNT_Pos) | len; + USBINEP[ep].DIEPTSIZ = (packet_count << USB_OTG_DIEPTSIZ_PKTCNT_Pos) | writeSize; // enable endpoint and cancel responding NAK SET_BIT(USBINEP[ep].DIEPCTL, USB_OTG_DIEPCTL_EPENA | USB_OTG_DIEPCTL_CNAK); @@ -501,7 +506,7 @@ void usbdrv_arm_IN_endpoint(uint8_t ep, const uint8_t *data, uint16_t len) { // push full dwords volatile uint32_t *p = (uint32_t *)USBFIFO(ep); - uint32_t floorlen_dwords = len >> 2; + uint32_t floorlen_dwords = writeSize >> 2; for (uint16_t i = 0; i < floorlen_dwords; i++) { uint16_t i0 = 4 * i; uint32_t dword = data[i0] | (data[i0 + 1] << 8) | (data[i0 + 2] << 16) | (data[i0 + 3] << 24); @@ -521,6 +526,9 @@ void usbdrv_arm_IN_endpoint(uint8_t ep, const uint8_t *data, uint16_t len) { // unmask USB interrupts SET_BIT(USBG->GAHBCFG, USB_OTG_GAHBCFG_GINT); + + // return with written size + return writeSize; } // arm OUT endpoint @@ -602,151 +610,152 @@ void usbdrv_process_event(uint8_t evt_code, USBDRV_EventData *evt_data) { } switch (gs.state) { - case USB_FSM_INITIAL_WAIT_SPEEDNEG: // wait for speed negotitation to conclude - if (evt_code == USB_EVT_SPEEDNEG_DONE) { - gs.state = USB_FSM_SETUP_OPERATE; // wait for speed negotiation + case USB_FSM_INITIAL_WAIT_SPEEDNEG: // wait for speed negotitation to conclude + if (evt_code == USB_EVT_SPEEDNEG_DONE) { + gs.state = USB_FSM_SETUP_OPERATE; // wait for speed negotiation + } + break; + case USB_FSM_SETUP_OPERATE: { // expect SETUP transactions first, then everything else as well + switch (evt_code) { + case USB_EVT_RECEPTION_DONE: { // reception done + + // for (uint32_t i = 0; i < 100000; i++) { + // __NOP(); + // } + + USBDRV_EventData evt_data = {0}; + usbdrv_process_rx_fifo_top(&evt_data); // process rx fifo top + + // fetch data if data are available + if ((evt_data.rx.pckt_status == USB_PCKT_STATUS_SETUP_DATA_RECV) || + (evt_data.rx.pckt_status == USB_PCKT_STATUS_OUT_DATA_RECV)) { + usbdrv_fetch_received_data(evt_data.rx.ep_num, evt_data.rx.byte_count); // fetch the data + } + + // act according to what we have received + if (evt_data.rx.ep_num == 0) { // EP0 special treatment + int stage = -1; + if (evt_data.rx.pckt_status == USB_PCKT_STATUS_SETUP_CPLT) { + stage = UST_SETUP; + USBMSG("--SETUP\n"); + } else if (evt_data.rx.pckt_status == USB_PCKT_STATUS_OUT_TRANSFER_CPLT) { + stage = UST_DATA; + + // OUT transaction has fired + if (gs.ep_OUT[0].task_commenced == USB_EPEVT_ARMED) { + gs.ep_OUT[0].task_commenced = USB_EPEVT_IDLE; + } + + USBMSG("--DATA\n"); + } + + // process setup packet + if (stage != -1) { + usbcore_process_setup_pckt(gs.rx_buf, gs.rx_buf_level, stage); + } + + // SET_BIT(USBG->GINTMSK, USB_OTG_GINTMSK_RXFLVLM); // unmask interrupt + } else { // not EP0 + if (evt_data.rx.pckt_status == USB_PCKT_STATUS_OUT_DATA_RECV) { // TODO maybe the + USBDRV_CallbackCompound cbcpd; + cbcpd.ep = evt_data.rx.ep_num; + cbcpd.dir = USB_OUT; + cbcpd.code = USB_CBC_OUT; + cbcpd.data = gs.rx_buf; + cbcpd.size = gs.rx_buf_level; + usbcore_process_nonsetup_event(&cbcpd); + } } break; - case USB_FSM_SETUP_OPERATE: { // expect SETUP transactions - switch (evt_code) { - case USB_EVT_RECEPTION_DONE: { // reception done + } - for (uint32_t i = 0; i < 100000; i++) { - __NOP(); + case USB_EVT_OUT_DONE: { // some OUT operations have finished + for (uint8_t ep = 0; ep < USB_NUM_OF_ENDPOINTS; ep++) { + if (gs.ep_OUT[ep].is_configured) { // if the endpoint is running + if (READ_BIT(USBOUTEP[ep].DOEPINT, USB_OTG_DOEPINT_STUP)) { // setup done + SET_BIT(USBOUTEP[ep].DOEPINT, USB_OTG_DOEPINT_STUP); + USBMSG("SETUP\n"); } - USBDRV_EventData evt_data = {0}; - usbdrv_process_rx_fifo_top(&evt_data); // process rx fifo top + if (READ_BIT(USBOUTEP[ep].DOEPINT, USB_OTG_DOEPINT_XFRC)) { // OUT transaction done + SET_BIT(USBOUTEP[ep].DOEPINT, USB_OTG_DOEPINT_XFRC); + USBMSG("OUT\n"); - // fetch data if data are available - if ((evt_data.rx.pckt_status == USB_PCKT_STATUS_SETUP_DATA_RECV) || - (evt_data.rx.pckt_status == USB_PCKT_STATUS_OUT_DATA_RECV)) { - usbdrv_fetch_received_data(evt_data.rx.ep_num, evt_data.rx.byte_count); // fetch the data - } - - // act according to what we have received - if (evt_data.rx.ep_num == 0) { // EP0 special treatment - int stage = -1; - if (evt_data.rx.pckt_status == USB_PCKT_STATUS_SETUP_CPLT) { - stage = UST_SETUP; - USBMSG("--SETUP\n"); - } else if (evt_data.rx.pckt_status == USB_PCKT_STATUS_OUT_TRANSFER_CPLT) { - stage = UST_DATA; - - // OUT transaction has fired - if (gs.ep_OUT[0].task_commenced == USB_EPEVT_ARMED) { - gs.ep_OUT[0].task_commenced = USB_EPEVT_IDLE; - } - - USBMSG("--DATA\n"); + // reset commenced task state + if (gs.ep_OUT[ep].task_commenced == USB_EPEVT_ARMED) { + gs.ep_OUT[ep].task_commenced = USB_EPEVT_IDLE; } - // process setup packet - if (stage != -1) { - usbcore_process_setup_pckt(gs.rx_buf, gs.rx_buf_level, stage); - } - - //SET_BIT(USBG->GINTMSK, USB_OTG_GINTMSK_RXFLVLM); // unmask interrupt - } else { // not EP0 - if (evt_data.rx.pckt_status == USB_PCKT_STATUS_OUT_DATA_RECV) { // TODO maybe the - USBDRV_CallbackCompound cbcpd; - cbcpd.ep = evt_data.rx.ep_num; - cbcpd.dir = USB_OUT; - cbcpd.code = USB_CBC_OUT; - cbcpd.data = gs.rx_buf; - cbcpd.size = gs.rx_buf_level; - usbcore_process_nonsetup_event(&cbcpd); - } - } - break; - } - - case USB_EVT_OUT_DONE: { // some OUT operations have finished - for (uint8_t ep = 0; ep < USB_NUM_OF_ENDPOINTS; ep++) { - if (gs.ep_OUT[ep].is_configured) { // if the endpoint is running - if (READ_BIT(USBOUTEP[ep].DOEPINT, USB_OTG_DOEPINT_STUP)) { // setup done - SET_BIT(USBOUTEP[ep].DOEPINT, USB_OTG_DOEPINT_STUP); - USBMSG("SETUP\n"); - } - - if (READ_BIT(USBOUTEP[ep].DOEPINT, USB_OTG_DOEPINT_XFRC)) { // OUT transaction done - SET_BIT(USBOUTEP[ep].DOEPINT, USB_OTG_DOEPINT_XFRC); - USBMSG("OUT\n"); - - // reset commenced task state - if (gs.ep_OUT[ep].task_commenced == USB_EPEVT_ARMED) { - gs.ep_OUT[ep].task_commenced = USB_EPEVT_IDLE; - } - - usbdrv_arm_OUT_endpoint(ep, gs.ep_OUT[ep].max_packet_size); - } - } + usbdrv_arm_OUT_endpoint(ep, gs.ep_OUT[ep].max_packet_size); } } - - case USB_EVT_IN_DONE: { // some IN operations have finished - - // callback compound - USBDRV_CallbackCompound cbcpd; - cbcpd.dir = USB_IN; - cbcpd.data = NULL; - cbcpd.size = 0; - - for (uint8_t ep = 0; ep < USB_NUM_OF_ENDPOINTS; ep++) { - cbcpd.ep = ep; - if (gs.ep_IN[ep].is_configured) { // if the endpoint is running - if (READ_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_TOC)) { // timeout done - SET_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_TOC); - USBMSG("TO\n"); - - gs.ep_IN[ep].task_commenced = USB_EPEVT_IDLE; - } - - if (READ_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_XFRC)) { // IN transaction done - SET_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_XFRC); - - // reset commenced task state - if (gs.ep_IN[ep].task_commenced == USB_EPEVT_ARMED) { - gs.ep_IN[ep].task_commenced = USB_EPEVT_IDLE; - } - - USBMSG("IN [%d]\n", ep); - - cbcpd.code = USB_CBC_IN_DONE; - usbcore_process_nonsetup_event(&cbcpd); - } - - if (READ_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_ITTXFE)) { // IN endpoint IN token received with Tx FIFO empty interrupt - SET_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_ITTXFE); - - // reset stalled state - if (gs.ep_IN[ep].task_commenced == USB_EPEVT_STALLED) { - usbdrv_stall_endpoint(ep, USB_IN, false); - } - - // USBMSG("IN FIFOEMPTY [%d]\n", ep); - - cbcpd.code = USB_CBC_IN_FIFOEMPTY; - usbcore_process_nonsetup_event(&cbcpd); - } - } - } - - // // set new address if it's already waiting - // if (gs.new_address != 0) { - // WRITE_FIELD(USBD->DCFG, USB_OTG_DCFG_DAD, gs.new_address); - // gs.new_address = 0; - // USBMSG("ADDR SET\n"); - // } - } - - default: - break; } + break; + } + + case USB_EVT_IN_DONE: { // some IN operations have finished + + // callback compound + USBDRV_CallbackCompound cbcpd; + cbcpd.dir = USB_IN; + cbcpd.data = NULL; + cbcpd.size = 0; + + for (uint8_t ep = 0; ep < USB_NUM_OF_ENDPOINTS; ep++) { + cbcpd.ep = ep; + if (gs.ep_IN[ep].is_configured) { // if the endpoint is running + if (READ_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_TOC)) { // timeout done + SET_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_TOC); + USBMSG("TO\n"); + + gs.ep_IN[ep].task_commenced = USB_EPEVT_IDLE; + } + + if (READ_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_XFRC)) { // IN transaction done + SET_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_XFRC); + + // reset commenced task state + if (gs.ep_IN[ep].task_commenced == USB_EPEVT_ARMED) { + gs.ep_IN[ep].task_commenced = USB_EPEVT_IDLE; + } + + USBMSG("IN [%d]\n", ep); + + cbcpd.code = USB_CBC_IN_DONE; + usbcore_process_nonsetup_event(&cbcpd); + } + + if (READ_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_ITTXFE)) { // IN endpoint IN token received with Tx FIFO empty interrupt + SET_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_ITTXFE); + + // reset stalled state + if (gs.ep_IN[ep].task_commenced == USB_EPEVT_STALLED) { + usbdrv_stall_endpoint(ep, USB_IN, false); + } + + // USBMSG("IN FIFOEMPTY [%d]\n", ep); + + cbcpd.code = USB_CBC_IN_FIFOEMPTY; + usbcore_process_nonsetup_event(&cbcpd); + } + } + } + + // // set new address if it's already waiting + // if (gs.new_address != 0) { + // WRITE_FIELD(USBD->DCFG, USB_OTG_DCFG_DAD, gs.new_address); + // gs.new_address = 0; + // USBMSG("ADDR SET\n"); + // } } default: break; + } + } + + default: + break; } } @@ -804,7 +813,7 @@ void OTG_FS_IRQHandler() { if (ints & USB_OTG_GINTSTS_RXFLVL) { // SET_BIT(USBG->GINTSTS, USB_OTG_GINTSTS_RXFLVL); // clear interrupt USBMSG("RX DONE\n"); - //CLEAR_BIT(USBG->GINTMSK, USB_OTG_GINTMSK_RXFLVLM); // mask interrupt until processing is done + // CLEAR_BIT(USBG->GINTMSK, USB_OTG_GINTMSK_RXFLVLM); // mask interrupt until processing is done usbdrv_process_event(USB_EVT_RECEPTION_DONE, NULL); // process event // usbdrv_push_event(USB_EVT_RECEPTION_DONE, NULL); // push event } diff --git a/usb_driver.h b/usb_driver.h index 3fcc42c..351d534 100644 --- a/usb_driver.h +++ b/usb_driver.h @@ -138,7 +138,7 @@ void usbdrv_set_rx_fifo_size(uint16_t size); // set Rx FIFO size void usbdrv_fetch_received_data(uint8_t ep, uint16_t len); // fetch received data from RX FIFO to receive buffer void usbdrv_process_rx_fifo_top(USBDRV_EventData *evt_data); // see what's on top of Rx FIFO -void usbdrv_arm_IN_endpoint(uint8_t ep, const uint8_t *data, uint16_t len); // write data to specific endpoint FIFO +uint32_t usbdrv_arm_IN_endpoint(uint8_t ep, const uint8_t *data, uint16_t len); // write data to specific endpoint FIFO #define USBDRV_ARM_IN_ZLP(ep) usbdrv_arm_IN_endpoint((ep), NULL, 0) void usbdrv_arm_OUT_endpoint(uint8_t ep, uint8_t size); // arm OUT endpoint bool usbdrv_get_endpoint_interrupt_flag(uint8_t ep, uint8_t dir); // get endpoint interrupt flag