From 6406222be3d73e94582607534004f5e59809d8f3 Mon Sep 17 00:00:00 2001 From: Epagris Date: Tue, 30 Apr 2024 23:09:39 +0200 Subject: [PATCH] - USB FIFO filling while transmission is in progress is implemented (huh) --- class/cdc.c | 8 +- class/cdc.h | 2 +- class/eem.c | 277 ++++++++++++++++++++++++++++++- class/eem.h | 55 +++++++ desc/usb_config.json | 29 +++- desc/usb_desc.c | 343 +++++++++++++++++++------------------- desc/usb_desc.h | 77 ++++----- usb.c | 239 ++++++++++++++------------- usb.h | 3 +- usb_callback_event.h | 2 + usb_driver.c | 381 ++++++++++++++++++++++++++++++++++++------- usb_driver.h | 32 +++- 12 files changed, 1042 insertions(+), 406 deletions(-) diff --git a/class/cdc.c b/class/cdc.c index 3821a90..b4a8346 100644 --- a/class/cdc.c +++ b/class/cdc.c @@ -89,23 +89,23 @@ int usb_cdc_process_and_return(USB_CallbackEvent *cbevt) { ret = 0; usb_cdc_read_callback(cbevt->data, cbevt->size); - //usbcore_write(cdcs.ep_assignments.data_ep, cbevt->data, cbevt->size); // echo + //usbcore_schedule_transmission(cdcs.ep_assignments.data_ep, cbevt->data, cbevt->size); // echo } break; } case USB_CBEVT_IN: { if (cbevt->ep == cdcs.ep_assignments.control_ep) { // if notification feeding is requested - usbcore_write(cdcs.ep_assignments.control_ep, NULL, 0); // send ZLP + usbcore_schedule_transmission(cdcs.ep_assignments.control_ep, NULL, 0); // send ZLP ret = 0; } else if (cbevt->ep == cdcs.ep_assignments.data_ep) { // if data are requested - //usbcore_write(cdcs.ep_assignments.data_ep, NULL, 0); // send ZLP + //usbcore_schedule_transmission(cdcs.ep_assignments.data_ep, NULL, 0); // send ZLP ret = 0; // 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 + uint32_t writeSize = usbcore_schedule_transmission(cdcs.ep_assignments.data_ep, tx_buffer, readSize); // write data acquired from the buffer bfifo_pop(&fifo, writeSize, 0); // pop with no blocking } } diff --git a/class/cdc.h b/class/cdc.h index 68b9bdb..82f71df 100644 --- a/class/cdc.h +++ b/class/cdc.h @@ -62,7 +62,7 @@ typedef struct { // ---------------- -#define USB_CDC_PCKT_BUFSIZE (72) +#define USB_CDC_PCKT_BUFSIZE (300) // ---------------- diff --git a/class/eem.c b/class/eem.c index 0658aa1..1b524da 100644 --- a/class/eem.c +++ b/class/eem.c @@ -4,8 +4,28 @@ #include "../usb.h" +#include +#include + +#include + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + static USB_EemState eems = {0}; +static uint8_t netbBuf[USB_EEM_FIFO_SIZE]; // Network bound FIFO memory +static uint8_t hostbBuf[USB_EEM_FIFO_SIZE]; // Host bound FIFO memory + +#define EEM_EVENT_QUEUE_LENGTH (16) +#define EEM_FRAME_QUEUE_LENGTH (24) +#define EEM_PCKT_SIZE (64) +#define EEM_TRANSFER_UNIT (128) + +static uint8_t transferBuffer[EEM_TRANSFER_UNIT]; // transfer buffer + +void usb_eem_thread(void *); + void usb_eem_init(uint8_t data_ep) { // clear the state memset(&eems, 0, sizeof(USB_EemState)); @@ -13,21 +33,81 @@ void usb_eem_init(uint8_t data_ep) { // store data endpoint number eems.data_ep = data_ep; + // create event queue + eems.eventQueue = osMessageQueueNew(EEM_EVENT_QUEUE_LENGTH, sizeof(uint8_t), NULL); + + // create network bound and host bound FIFOs + bfifo_create(&(eems.hostbFifo), netbBuf, USB_EEM_FIFO_SIZE); + bfifo_create(&(eems.netbFifo), hostbBuf, USB_EEM_FIFO_SIZE); + + // create queue for hostbound frames + eems.hostbFrameQ = osMessageQueueNew(EEM_FRAME_QUEUE_LENGTH, sizeof(USB_EemFrame *), NULL); + + // no transmission or reception is in progress + eems.hostbState = EEM_IDLE; + eems.netbState = EEM_IDLE; + + // no EchoResponse is queued + eems.echoRespQueued = false; + + // create thread + osThreadAttr_t attr; + bzero(&attr, sizeof(osThreadAttr_t)); + attr.priority = osPriorityNormal; + attr.stack_size = 2048; + attr.name = "eem"; + eems.th = osThreadNew(usb_eem_thread, NULL, &attr); + // EEM is operational eems.initialized = true; } +void usb_eem_push_event(USB_EemEvent evt) { + osMessageQueuePut(eems.eventQueue, &evt, 0, 0); +} + +USB_EemEvent usb_eem_pop_event() { + USB_EemEvent evt; + osMessageQueueGet(eems.eventQueue, &evt, 0, osWaitForever); + return evt; +} + int usb_eem_process_and_return(USB_CallbackEvent *cbevt) { switch (cbevt->type) { case USB_CBEVT_UNKNOWN_REQ: break; case USB_CBEVT_OUT: + if ((cbevt->ep == eems.data_ep)) { // verify endpoint + bfifo_push(&(eems.netbFifo), cbevt->data, cbevt->size); // store portion of netbound packet + usb_eem_push_event(EEM_EVT_NETBOUND_PCKT_RECEIVED); // push notification + } break; case USB_CBEVT_IN: - if (cbevt->ep == eems.data_ep) { // verify endpoint number - usbcore_write(eems.data_ep, NULL, 0); // send ZLP + if (cbevt->ep == eems.data_ep) { // verify endpoint number + if ((eems.hostbFrame != NULL) && (eems.hostbFrameIndex < eems.hostbFrameLength)) { // if there's something in the FIFO + uint8_t *readPtr = ((uint8_t *)eems.hostbFrame) + eems.hostbFrameIndex; // calculate read pointer + uint16_t bytesLeft = eems.hostbFrameLength - eems.hostbFrameIndex; // calculate bytes left + uint32_t writeSize = usbcore_schedule_transmission(eems.data_ep, readPtr, bytesLeft); // attempt to schedule transmission + + //bool enableZLP = (bytesLeft < EEM_TRANSFER_UNIT) && ((bytesLeft % EEM_PCKT_SIZE) == 0); // ZLP transmission should be enabled if this was the last write and transfer size is integer multiple of the USB packet size + //cbevt->enable_autozlp = enableZLP; + + eems.hostbFrameIndex += writeSize; // advance frame index + //MSG("U: %u T: %u\n", writeSize, bytesLeft); + + // if (eems.hostbFrameIndex >= eems.hostbFrameLength) { + // MSG("---\n"); + // } + } else { // no data is waiting for transmission + usbcore_schedule_transmission(eems.data_ep, NULL, 0); // send ZLP + } + + // if FIFO is empty or flushed, then send notification + if ((eems.hostbFrame == NULL) || (eems.hostbFrameIndex >= eems.hostbFrameLength)) { + usb_eem_push_event(EEM_EVT_HOSTBOUND_TRANSFER_DONE); // push notification + } } break; @@ -37,3 +117,196 @@ int usb_eem_process_and_return(USB_CallbackEvent *cbevt) { return 0; } + +// ---------------------- + +#define EEM_HEADER_SIZE (2) // EEM header size in bytes +#define EEM_HEADER_TYPE_DEFAULT (0) // default header type code +#define EEM_HEADER_TYPE_CMD (1) // CMD header type code + +// TODO rename: swap GET and the next word +#define EEM_IS_CMD_PCKT(h) ((((h)[1] & 0x80) >> 7) == EEM_HEADER_TYPE_CMD) // checks if header signals a following command packet or not +#define EEM_GET_CMD(h) ((h[1] >> 3) & 0b111) // get the actual command code from the header +#define EEM_GET_CMD_PARAM(h) (((uint16_t)(h)[0]) + ((((uint16_t)(h)[1]) & 0b111) << 8)) // get command arguments from the header + +#define EEM_GET_DEFAULT_CRC_PRESENT(h) (((h)[1] >> 6) & 0b1) // checks if CRC is present +#define EEM_GET_DEFAULT_FRAME_LENGTH(h) (((uint16_t)(h)[0]) + ((((uint16_t)(h)[1]) & 0x3F) << 8)) // get Ethernet frame length (14-bits) + +#define EEM_BUILD_HEADER(type, crc_present, len) (((uint16_t)(((type) & 0b1) << 15)) | ((uint16_t)(((crc_present) & 0b1) << 14)) | ((len) & 0x3FFF)) +#define EEM_HEADER_GET_LENGTH(h) (((h) & 0x3FFF)) + +void usb_eem_process_networkbound() { + // extract netbound FIFO pointer for easier handling + BFifo *bf = &(eems.netbFifo); + + switch (eems.netbState) { + case EEM_IDLE: { /* IDLE state, can start a new operation */ + // verify, that the packet is big enough to contain the header + if (bfifo_get_used(bf) < EEM_HEADER_SIZE) { + break; // if not, then break (either a single byte, or a ZLP was received) + } + + // certainly, we can acquire the header at this point + + // get header + uint8_t header[EEM_HEADER_SIZE]; + bfifo_read(bf, header, EEM_HEADER_SIZE); // read + bfifo_pop(bf, 2, 0); // pop (with no timeout) + + // check if this packet was a command packet + if (EEM_IS_CMD_PCKT(header)) { + // if it is was a command, then fetch its fields + uint8_t bmEEMCmd = EEM_GET_CMD(header); + uint16_t bmEEMCmdParam = EEM_GET_CMD_PARAM(header); + + // process only Echo commands (only if no previous EchoResponse was queued) + if ((bmEEMCmd == EEM_ECHO) && (!eems.echoRespQueued)) { + // store payload length + eems.netbSizeLeft = bmEEMCmdParam; // payload comes in the 11-bit parameter + + // signal, that we will respond at the end the Echo packet + eems.echoRespQueued = true; + + // step into the 'transfer in progress' state + eems.netbState = EEM_TRANSFER_IN_PROGRESS; + + MSG("ECHO!\n"); + } + } else { // this is a regular packet + // store Ethernet frame length + eems.netbFrameSize = EEM_GET_DEFAULT_FRAME_LENGTH(header); // set the frame size + + // process packet with non-zero length + if (eems.netbFrameSize != 0) { + eems.netbSizeLeft = eems.netbFrameSize; // the full frame is ahead + + // step into the 'transfer in progress' state + eems.netbState = EEM_TRANSFER_IN_PROGRESS; + + // MSG("DEFAULT! %u\n", eems.netbFrameSize); + } + } + + } break; + + case EEM_TRANSFER_IN_PROGRESS: { /* transfer is in progress, collect bytes */ + if (bfifo_get_used(bf) >= eems.netbFrameSize) { // if the full packet is in the buffer, then read it! + // create raw packet if Ethernet interface is defined + if (eems.intf != NULL) { + RawPckt raw; + bzero(&raw, sizeof(RawPckt)); + raw.size = eems.netbFrameSize; // set size + raw.payload = dynmem_alloc(raw.size); // allocate buffer + if (raw.payload != NULL) { + bfifo_read(bf, raw.payload, raw.size); // copy payload to raw packet + ethinf_transmit(eems.intf, &raw); // transmit packet + } else { + MSG("EEM -> ETH packet allocation failed!\n"); + } + } + + // pop data from the packet + bfifo_pop(bf, eems.netbFrameSize, 0); + + // MSG("PKT OK! %u\n", eems.netbFrameSize); + + eems.netbState = EEM_IDLE; + } + } break; + } +} + +void usb_eem_process_hostbound() { + // see if there are some packets waiting for transmission + if (osMessageQueueGetCount(eems.hostbFrameQ) == 0) { + return; // no packets + } + + /*// check that forward buffer is indeed empty + BFifo * bf = &eems.hostbFifo; + if (bfifo_get_used(bf) > 0) { + return; // we don't want to overwrite concatenate transfers + }*/ + + // check if transmission is done indeed + if ((eems.hostbFrame != NULL) && (eems.hostbFrameIndex < eems.hostbFrameLength)) { + return; + } + + // invalidate frame + USB_EemFrame * oldEemFrame = eems.hostbFrame; + eems.hostbFrame = NULL; + + // release frame + dynmem_free(oldEemFrame); + + // at this point, we can safely pop from the frame queue, popping will not block, + // and the forward queue is certainly empty + + // get EEM frame pointer + USB_EemFrame *eemFrame; + osMessageQueueGet(eems.hostbFrameQ, &eemFrame, 0, osWaitForever); + + // push frame into the transfer queue + /* uint32_t frameLength = EEM_HEADER_GET_LENGTH(eemFrame->header); + bfifo_push(bf, (const uint8_t *) eemFrame, frameLength + EEM_HEADER_SIZE); // push header and payload */ + + // setup frame length and reset read index + eems.hostbFrameLength = EEM_HEADER_GET_LENGTH(eemFrame->header) + EEM_HEADER_SIZE; + eems.hostbFrameIndex = 0; + + // set the frame pointer + eems.hostbFrame = eemFrame; + + // MSG("%u\n", frameLength); +} + +void usb_eem_ethernet_intercept_cb(EthInterface *intf, const RawPckt *rawPckt) { + // check for free space in the hostbound frame queue + if (osMessageQueueGetSpace(eems.hostbFrameQ) == 0) { + MSG("Hostbound frame queue is full, frame dropped!\n"); + return; + } + + // copy packet + USB_EemFrame *eemFrame = dynmem_alloc(sizeof(USB_EemFrame) + rawPckt->size); + if (eemFrame == NULL) { + MSG("Allocation while intercepting traffic failed!\n"); + return; + } + + // at this point, allocation was certainly succesful + + // build header: CRC is calculated and present, size comes from the raw packet size + eemFrame->header = EEM_BUILD_HEADER(EEM_HEADER_TYPE_DEFAULT, 1, rawPckt->size); + + // copy payload + memcpy(eemFrame->payload, rawPckt->payload, rawPckt->size); + + // push onto hostbound queue + osMessageQueuePut(eems.hostbFrameQ, &eemFrame, 0, osWaitForever); // will not block, since we checked, that there is space in the queue +} + +void usb_eem_set_intf(EthInterface *intf) { + eems.intf = intf; // store interface + ethinf_set_intercept_callback(intf, usb_eem_ethernet_intercept_cb); // set intercept callback +} + +void usb_eem_thread(void *) { + for (;;) { + // pop event + USB_EemEvent evt = usb_eem_pop_event(); + + switch (evt) { + // process netbound packet + case EEM_EVT_NETBOUND_PCKT_RECEIVED: + usb_eem_process_networkbound(); + break; + case EEM_EVT_HOSTBOUND_TRANSFER_DONE: + usb_eem_process_hostbound(); + break; + default: + break; + } + } +} \ No newline at end of file diff --git a/class/eem.h b/class/eem.h index 1a9daf5..6d039c3 100644 --- a/class/eem.h +++ b/class/eem.h @@ -5,9 +5,58 @@ #include "../usb_callback_event.h" +#include + +#include + +#define USB_EEM_FIFO_SIZE (2048) + +typedef enum { + EEM_ECHO = 0, // Echo + EEM_ECHO_RESPONSE, // Echo response + EEM_SUSPEND_HINT, // Suspend hint + EEM_RESPONSE_HINT, // Response hint + EEM_RESPONSE_COMPLETE_HINT, // Response complete hint + EEM_TICKLE, // Tickle +} USB_EemCmd; + +typedef enum { + EEM_IDLE = 0, // IDLE state, a new transfer may be initiated + EEM_TRANSFER_IN_PROGRESS // transfer is in progress, new transfers have to wait +} USB_EemFsmState; + +typedef enum { + EEM_EVT_NONE = 0, // no event + EEM_EVT_NETBOUND_PCKT_RECEIVED, // a USB packet has been received for a netbound Ethernet frame + // EEM_EVT_HOSTBOUND_FRAME_IN_QUEUE, // a new hostbound frame is in the queue + EEM_EVT_HOSTBOUND_TRANSFER_DONE, // the previous hostbound transfer has completed +} USB_EemEvent; + +/** + * Ethernet frame prepended with EEM header +*/ typedef struct { + uint16_t header; ///< EEM header (CRC present?, Frame Length) + uint8_t payload[]; ///< Actual payload +} USB_EemFrame; + +typedef struct { + EthInterface * intf; // interface for packet interception and injection uint8_t data_ep; // bulk data in and out endpoint pair + osMessageQueueId_t eventQueue; // event queue + BFifo hostbFifo; // Hostbound FIFO + BFifo netbFifo; // Network bound FIFO + USB_EemFsmState netbState; // state of host bound operations + uint16_t netbFrameSize; // size of network bound packet + uint16_t netbSizeLeft; // number of bytes expected from the host sending into current network bound packet + USB_EemFsmState hostbState; // state of network bound operations + osMessageQueueId_t hostbFrameQ; // queue for hostbound message pointers + USB_EemFrame * hostbFrame; // hostbound frame + uint16_t hostbFrameLength; // hostbound frame length + uint16_t hostbFrameIndex; // read index in hostbound frame + bool echoRespQueued; // indicates if an EchoResponse was queued bool initialized; // EEM is initialized + osThreadId_t th; // EEM thread } USB_EemState; /** @@ -23,4 +72,10 @@ void usb_eem_init(uint8_t data_ep); */ int usb_eem_process_and_return(USB_CallbackEvent * cbevt); +/** + * Set Ethernet interface. + * @param pointer to Ethernet interface +*/ +void usb_eem_set_intf(EthInterface * intf); + #endif /* CLASS_EEM */ diff --git a/desc/usb_config.json b/desc/usb_config.json index a08fa28..1af0929 100644 --- a/desc/usb_config.json +++ b/desc/usb_config.json @@ -60,18 +60,41 @@ "direction": "in", "transfer_type": "bulk", "max_packet_size": 64, - "service_interval": 0 + "service_interval": 20 }, { "n": 1, "direction": "out", "transfer_type": "bulk", "max_packet_size": 64, - "service_interval": 0 + "service_interval": 20 + } + ] + }, + { + "id": 2, + "class": "0x02", + "subclass": "0x0C", + "protocol_code": "0x07", + "additional_interfaces": [], + "endpoints": [ + { + "n": 3, + "direction": "in", + "transfer_type": "bulk", + "max_packet_size": 64, + "service_interval": 10 + }, + { + "n": 3, + "direction": "out", + "transfer_type": "bulk", + "max_packet_size": 64, + "service_interval": 10 } ] } ] } ] -} +} \ No newline at end of file diff --git a/desc/usb_desc.c b/desc/usb_desc.c index 6f548db..6bc6593 100644 --- a/desc/usb_desc.c +++ b/desc/usb_desc.c @@ -1,189 +1,176 @@ #include "usb_desc.h" -const USB_StrDesc_lang_id lang_id = { - /* String descriptor */ - 0x4, // bLength - UD_String, // bDescriptorType - 0x409, // bString +const USB_DeviceDesc devDesc = { /* Device Descriptor */ + 0x12, //bLength + UD_Device, //bDescriptorType + 0x200, //bcdUSB + 0x2, //bDeviceClass + 0x0, //bDeviceSubclass + 0x0, //bDeviceProtocol + 0x40, //bMaxPacketSize0 + 0x925, //idVendor + 0x9050, //idProduct + 0x100, //bcdDevice + 0x1, //iManufacturer + 0x2, //iProduct + 0x3, //iSerialNumber + 0x1, //bNumConfigurations }; -const USB_StrDesc_vendor_string vendor_string = { - /* String descriptor */ - 0x10, // bLength - UD_String, // bDescriptorType - { - 'E', - 'p', - 'a', - 'g', - 'r', - 'i', - 's', - }, // bString -}; - -const USB_StrDesc_product_string product_string = { - /* String descriptor */ - 0x16, // bLength - UD_String, // bDescriptorType - { - 'T', - 'e', - 's', - 't', - 'd', - 'e', - 'v', - 'i', - 'c', - 'e', - }, // bString -}; - -const USB_StrDesc_serial_number serial_number = { - /* String descriptor */ - 0xa, // bLength - UD_String, // bDescriptorType - { - '1', - '5', - '5', - '2', - }, // bString -}; - -const StringDescs strDescs = { - (USB_StringDesc *)&lang_id, // lang_id - (USB_StringDesc *)&vendor_string, // vendor_string - (USB_StringDesc *)&product_string, // product_string - (USB_StringDesc *)&serial_number, // serial_number -}; - -const USB_DeviceDesc devDesc = { - /* Device Descriptor */ - 0x12, // bLength - UD_Device, // bDescriptorType - 0x200, // bcdUSB - 0x2, // bDeviceClass - 0x0, // bDeviceSubclass - 0x0, // bDeviceProtocol - 0x40, // bMaxPacketSize0 - 0x925, // idVendor - 0x9050, // idProduct - 0x100, // bcdDevice - 0x1, // iManufacturer - 0x2, // iProduct - 0x3, // iSerialNumber - 0x1, // bNumConfigurations -}; - -const USB_DeviceQualifierDesc devQualDesc = { - /* Device Qualifier descriptor */ - 0x9, // bLength - UD_DeviceQualifier, // bDescriptorType - 0x200, // bcdUSB - 0x2, // bDeviceClass - 0x0, // bDeviceSubclass - 0x0, // bDeviceProtocol - 0x40, // bMaxPacketSize - 0x1, // bNumConfigurations +const USB_DeviceQualifierDesc devQualDesc = { /* Device Qualifier descriptor */ + 0x9, //bLength + UD_DeviceQualifier, //bDescriptorType + 0x200, //bcdUSB + 0x2, //bDeviceClass + 0x0, //bDeviceSubclass + 0x0, //bDeviceProtocol + 0x40, //bMaxPacketSize + 0x1, //bNumConfigurations }; const FullConfigurations0 fullConfDescs0 = { - { - /* Configuration descriptor */ - 0x9, // bLength - UD_Configuration, // bDescriptorType - sizeof(struct _FullConfigurations0), // wTotalLength - 0x2, // bNumInterfaces - 0x1, // bConfigurationValue - 0x0, // iConfiguration - USB_CONFIG_ATTR_USB1_1_FLAG, // bmAttributes - 0x32, // bMaxPower - }, // config - { - /* Interface descriptor : 0 */ - 0x9, // bLength - UD_Interface, // bDescriptorType - 0x0, // bInterfaceNumber - 0x0, // bAlternateSetting - 0x1, // bNumEndpoints - 0x2, // bInterfaceClass - 0x2, // bInterfaceSubclass - 0x1, // bInterfaceProtocol - 0x0, // iInterface - }, // intf0 - { - /* Header Functional descriptor */ - 0x5, // bLength - 0x24, // bDescriptorType - 0x0, // bDescriptorSubType - 0x110, // bcdCDC - }, // intf0hfd - { - /* Abstract Control Management Functional descriptor */ - 0x4, // bLength - 0x24, // bDescriptorType - 0x2, // bDescriptorSubType - 0x2, // bmCapabilities - }, // intf0acmfd - { - /* Abstract Control Management Functional descriptor */ - 0x5, // bLength - 0x24, // bDescriptorType - 0x6, // bDescriptorSubType - 0x0, // bMasterInterface - 0x1, // bSlaveInterface0 - }, // intf0ufd - { - /* Call Management Functional descriptor */ - 0x5, // bLength - 0x24, // bDescriptorType - 0x1, // bDescriptorSubType - 0x0, // bmCapabilities - 0x1, // dDataInterface - }, // intf0cmfd - { - /* Endpoint descriptor : 2 in */ - 0x7, // bLength - UD_Endpoint, // bDescriptorType - (USB_IN << 7) | (2), // bEndpointAddress - UT_Interrupt, // bmAttributes - 0x40, // wMaxPacketSize - 0x2, // bInterval - }, // ep2in - { - /* Interface descriptor : 1 */ - 0x9, // bLength - UD_Interface, // bDescriptorType - 0x1, // bInterfaceNumber - 0x0, // bAlternateSetting - 0x2, // bNumEndpoints - 0xa, // bInterfaceClass - 0x0, // bInterfaceSubclass - 0x0, // bInterfaceProtocol - 0x0, // iInterface - }, // intf1 - { - /* Endpoint descriptor : 1 in */ - 0x7, // bLength - UD_Endpoint, // bDescriptorType - (USB_IN << 7) | (1), // bEndpointAddress - UT_Bulk, // bmAttributes - 0x40, // wMaxPacketSize - 0x0, // bInterval - }, // ep1in - { - /* Endpoint descriptor : 1 out */ - 0x7, // bLength - UD_Endpoint, // bDescriptorType - (USB_OUT << 7) | (1), // bEndpointAddress - UT_Bulk, // bmAttributes - 0x40, // wMaxPacketSize - 0x0, // bInterval - }, // ep1out + { /* Configuration descriptor */ + 0x9, //bLength + UD_Configuration, //bDescriptorType + sizeof(struct _FullConfigurations0), //wTotalLength + 0x3, //bNumInterfaces + 0x1, //bConfigurationValue + 0x0, //iConfiguration + USB_CONFIG_ATTR_USB1_1_FLAG, //bmAttributes + 0x32, //bMaxPower + }, //config + { /* Interface descriptor : 0 */ + 0x9, //bLength + UD_Interface, //bDescriptorType + 0x0, //bInterfaceNumber + 0x0, //bAlternateSetting + 0x1, //bNumEndpoints + 0x2, //bInterfaceClass + 0x2, //bInterfaceSubclass + 0x1, //bInterfaceProtocol + 0x0, //iInterface + }, //intf0 + { /* Header Functional descriptor */ + 0x5, //bLength + 0x24, //bDescriptorType + 0x0, //bDescriptorSubType + 0x110, //bcdCDC + }, //intf0hfd + { /* Abstract Control Management Functional descriptor */ + 0x4, //bLength + 0x24, //bDescriptorType + 0x2, //bDescriptorSubType + 0x2, //bmCapabilities + }, //intf0acmfd + { /* Union Functional descriptor */ + 0x5, //bLength + 0x24, //bDescriptorType + 0x6, //bDescriptorSubType + 0x0, //bMasterInterface + 0x1, //bSlaveInterface0 + }, //intf0ufd + { /* Call Management Functional descriptor */ + 0x5, //bLength + 0x24, //bDescriptorType + 0x1, //bDescriptorSubType + 0x0, //bmCapabilities + 0x1, //dDataInterface + }, //intf0cmfd + { /* Endpoint descriptor : 2 in */ + 0x7, //bLength + UD_Endpoint, //bDescriptorType + (USB_IN << 7) | (2), //bEndpointAddress + UT_Interrupt, //bmAttributes + 0x40, //wMaxPacketSize + 0x2, //bInterval + }, //ep2in + { /* Interface descriptor : 1 */ + 0x9, //bLength + UD_Interface, //bDescriptorType + 0x1, //bInterfaceNumber + 0x0, //bAlternateSetting + 0x2, //bNumEndpoints + 0xa, //bInterfaceClass + 0x0, //bInterfaceSubclass + 0x0, //bInterfaceProtocol + 0x0, //iInterface + }, //intf1 + { /* Endpoint descriptor : 1 in */ + 0x7, //bLength + UD_Endpoint, //bDescriptorType + (USB_IN << 7) | (1), //bEndpointAddress + UT_Bulk, //bmAttributes + 0x40, //wMaxPacketSize + 0x14, //bInterval + }, //ep1in + { /* Endpoint descriptor : 1 out */ + 0x7, //bLength + UD_Endpoint, //bDescriptorType + (USB_OUT << 7) | (1), //bEndpointAddress + UT_Bulk, //bmAttributes + 0x40, //wMaxPacketSize + 0x14, //bInterval + }, //ep1out + { /* Interface descriptor : 2 */ + 0x9, //bLength + UD_Interface, //bDescriptorType + 0x2, //bInterfaceNumber + 0x0, //bAlternateSetting + 0x2, //bNumEndpoints + 0x2, //bInterfaceClass + 0xc, //bInterfaceSubclass + 0x7, //bInterfaceProtocol + 0x0, //iInterface + }, //intf2 + { /* Endpoint descriptor : 3 in */ + 0x7, //bLength + UD_Endpoint, //bDescriptorType + (USB_IN << 7) | (3), //bEndpointAddress + UT_Bulk, //bmAttributes + 0x40, //wMaxPacketSize + 0xa, //bInterval + }, //ep3in + { /* Endpoint descriptor : 3 out */ + 0x7, //bLength + UD_Endpoint, //bDescriptorType + (USB_OUT << 7) | (3), //bEndpointAddress + UT_Bulk, //bmAttributes + 0x40, //wMaxPacketSize + 0xa, //bInterval + }, //ep3out }; const ConfDescs confDescs = { - (USB_ConfigurationDesc *)&fullConfDescs0, // fullConfDescs0 + (USB_ConfigurationDesc *) & fullConfDescs0, //fullConfDescs0 +}; + +const USB_StrDesc_lang_id lang_id = { /* String descriptor */ + 0x4, //bLength + UD_String, //bDescriptorType + 0x409, //bString +}; + +const USB_StrDesc_vendor_string vendor_string = { /* String descriptor */ + 0x10, //bLength + UD_String, //bDescriptorType + {'E', 'p', 'a', 'g', 'r', 'i', 's',}, //bString +}; + +const USB_StrDesc_product_string product_string = { /* String descriptor */ + 0x16, //bLength + UD_String, //bDescriptorType + {'T', 'e', 's', 't', 'd', 'e', 'v', 'i', 'c', 'e',}, //bString +}; + +const USB_StrDesc_serial_number serial_number = { /* String descriptor */ + 0xa, //bLength + UD_String, //bDescriptorType + {'1', '5', '5', '2',}, //bString +}; + +const StringDescs strDescs = { + (USB_StringDesc *) & lang_id, //lang_id + (USB_StringDesc *) & vendor_string, //vendor_string + (USB_StringDesc *) & product_string, //product_string + (USB_StringDesc *) & serial_number, //serial_number }; diff --git a/desc/usb_desc.h b/desc/usb_desc.h index 2b526e8..70f24b8 100644 --- a/desc/usb_desc.h +++ b/desc/usb_desc.h @@ -1,5 +1,5 @@ -#ifndef CORE_USB_DESC_USB_DESC -#define CORE_USB_DESC_USB_DESC +#ifndef _USB_DESC_H +#define _USB_DESC_H #include "../usb_device_types.h" @@ -7,39 +7,6 @@ // --------------------------- -typedef struct _USB_StrDesc_lang_id { - uint8_t bLength; - uint8_t bDescriptorType; - uint16_t bString; -} __attribute__((packed)) USB_StrDesc_lang_id; - -extern const USB_StrDesc_lang_id lang_id; /* String descriptor */ -typedef struct _USB_StrDesc_vendor_string { - uint8_t bLength; - uint8_t bDescriptorType; - uint16_t bString[7]; -} __attribute__((packed)) USB_StrDesc_vendor_string; - -extern const USB_StrDesc_vendor_string vendor_string; /* String descriptor */ -typedef struct _USB_StrDesc_product_string { - uint8_t bLength; - uint8_t bDescriptorType; - uint16_t bString[10]; -} __attribute__((packed)) USB_StrDesc_product_string; - -extern const USB_StrDesc_product_string product_string; /* String descriptor */ -typedef struct _USB_StrDesc_serial_number { - uint8_t bLength; - uint8_t bDescriptorType; - uint16_t bString[4]; -} __attribute__((packed)) USB_StrDesc_serial_number; - -extern const USB_StrDesc_serial_number serial_number; /* String descriptor */ - -// --------------------------- - -typedef USB_StringDesc *StringDescs[4]; -extern const StringDescs strDescs; extern const USB_DeviceDesc devDesc; /* Device Descriptor */ extern const USB_DeviceQualifierDesc devQualDesc; /* Device Qualifier descriptor */ typedef struct _USB_HeaderFunctionalDescriptor { @@ -73,12 +40,15 @@ typedef struct _FullConfigurations0 { USB_InterfaceDesc intf0; /* Interface descriptor : 0 */ USB_HeaderFunctionalDescriptor intf0hfd; /* Header Functional descriptor */ USB_AbstractControlManagementFunctionalDescriptor intf0acmfd; /* Abstract Control Management Functional descriptor */ - USB_UnionFunctionalDescriptor intf0ufd; /* Abstract Control Management Functional descriptor */ + USB_UnionFunctionalDescriptor intf0ufd; /* Union Functional descriptor */ USB_CallManagementFunctionalDescriptor intf0cmfd; /* Call Management Functional descriptor */ USB_EndpointDesc ep2in; /* Endpoint descriptor : 2 in */ USB_InterfaceDesc intf1; /* Interface descriptor : 1 */ USB_EndpointDesc ep1in; /* Endpoint descriptor : 1 in */ USB_EndpointDesc ep1out; /* Endpoint descriptor : 1 out */ + USB_InterfaceDesc intf2; /* Interface descriptor : 2 */ + USB_EndpointDesc ep3in; /* Endpoint descriptor : 3 in */ + USB_EndpointDesc ep3out; /* Endpoint descriptor : 3 out */ } __attribute__((packed)) FullConfigurations0; extern const FullConfigurations0 fullConfDescs0; @@ -87,5 +57,38 @@ extern const FullConfigurations0 fullConfDescs0; typedef USB_ConfigurationDesc *ConfDescs[1]; extern const ConfDescs confDescs; +typedef struct _USB_StrDesc_lang_id { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bString; +} __attribute__((packed)) USB_StrDesc_lang_id; -#endif /* CORE_USB_DESC_USB_DESC */ +extern const USB_StrDesc_lang_id lang_id; /* String descriptor */ +typedef struct _USB_StrDesc_vendor_string { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bString[7]; +} __attribute__((packed)) USB_StrDesc_vendor_string; + +extern const USB_StrDesc_vendor_string vendor_string; /* String descriptor */ +typedef struct _USB_StrDesc_product_string { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bString[10]; +} __attribute__((packed)) USB_StrDesc_product_string; + +extern const USB_StrDesc_product_string product_string; /* String descriptor */ +typedef struct _USB_StrDesc_serial_number { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bString[4]; +} __attribute__((packed)) USB_StrDesc_serial_number; + +extern const USB_StrDesc_serial_number serial_number; /* String descriptor */ + +// --------------------------- + +typedef USB_StringDesc *StringDescs[4]; +extern const StringDescs strDescs; + +#endif // _USB_DESC_H diff --git a/usb.c b/usb.c index b80573d..40d7a63 100644 --- a/usb.c +++ b/usb.c @@ -69,40 +69,40 @@ void usbcore_init() { void usbcore_process_setup_pckt(const uint8_t *data, uint16_t size, uint8_t stage) { - //MSG("STUP: %u, %s\n", size, stage == UST_SETUP ? "SETUP" : "DATA"); + // MSG("STUP: %u, %s\n", size, stage == UST_SETUP ? "SETUP" : "DATA"); // #define USB_PREPARE_DESC_VAR(var, size) sz = usb_prepare_descriptor((const uint8_t *)&(var), sizeof((var)), (size)) switch (stups.next_stage) { - case UST_SETUP: { // expecting SETUP stage - if (stage == UST_SETUP) { // SETUP received - // save setup request - stups.setup_req = *((USB_SetupRequest *)data); + case UST_SETUP: { // expecting SETUP stage + if (stage == UST_SETUP) { // SETUP received + // save setup request + stups.setup_req = *((USB_SetupRequest *)data); - // if data direction is IN, then don't expect an OUT transaction - if ((stups.setup_req.bmRequestType.fields.dir == USB_IN) || - ((stups.setup_req.bmRequestType.fields.dir == USB_OUT) && (stups.setup_req.wLength == 0))) { - stups.out_complete = true; // DATA stage is IN - } else { - stups.out_complete = false; // OUT transaction are not exhausted yet (i.e. DATA stage is OUT) - stups.next_stage = UST_DATA; // next stage is DATA OUT stage - } + // if data direction is IN, then don't expect an OUT transaction + if ((stups.setup_req.bmRequestType.fields.dir == USB_IN) || + ((stups.setup_req.bmRequestType.fields.dir == USB_OUT) && (stups.setup_req.wLength == 0))) { + stups.out_complete = true; // DATA stage is IN + } else { + stups.out_complete = false; // OUT transaction are not exhausted yet (i.e. DATA stage is OUT) + stups.next_stage = UST_DATA; // next stage is DATA OUT stage } - break; + } + break; + } + + case UST_DATA: { // expecting DATA stage + if (stage == UST_DATA) { // DATA received + stups.out_complete = true; } - case UST_DATA: { // expecting DATA stage - if (stage == UST_DATA) { // DATA received - stups.out_complete = true; - } + // step into SETUP state + stups.next_stage = UST_SETUP; + break; + } - // step into SETUP state - stups.next_stage = UST_SETUP; - break; - } - - default: - break; + default: + break; } // expect status transaction to follow the data phase @@ -117,117 +117,132 @@ void usbcore_process_setup_pckt(const uint8_t *data, uint16_t size, uint8_t stag #define SET_TRANSMISSION_POINTER(ptr) data = (const uint8_t *)(ptr) switch (stups.setup_req.bRequest) { - case UREQ_GetDescriptor: // GET DESCRIPTOR - { - // which descriptor? - uint8_t desc_type = (stups.setup_req.wValue >> 8); // get descriptor type - uint8_t desc_index = (stups.setup_req.wValue & 0xFF); // get descriptor index - uint8_t sz = 0; - const uint8_t *data = NULL; + case UREQ_GetDescriptor: // GET DESCRIPTOR + { + // which descriptor? + uint8_t desc_type = (stups.setup_req.wValue >> 8); // get descriptor type + uint8_t desc_index = (stups.setup_req.wValue & 0xFF); // get descriptor index + uint8_t sz = 0; + const uint8_t *data = NULL; - switch (desc_type) { - case UD_Device: // DEVICE DESCRIPTOR - DETERMINE_TRANSFER_SIZE(devDesc.bLength); - SET_TRANSMISSION_POINTER(&devDesc); - break; - case UD_Configuration: // CONFIGURATION DESCRIPTOR - DETERMINE_TRANSFER_SIZE(confDescs[desc_index]->wTotalLength); - SET_TRANSMISSION_POINTER(confDescs[desc_index]); - break; - case UD_String: // STRING DESCRIPTOR - DETERMINE_TRANSFER_SIZE(strDescs[desc_index]->bLength); - SET_TRANSMISSION_POINTER(strDescs[desc_index]); - break; - case UD_DeviceQualifier: - DETERMINE_TRANSFER_SIZE(devQualDesc.bLength); - SET_TRANSMISSION_POINTER(&devQualDesc); - break; - // Descriptors not implemented - default: - usbdrv_stall_endpoint(0, USB_IN, true); // stall IN, since request in unsupported - break; - } - - // arm data transmission - usbdrv_arm_IN_endpoint(0, data, sz); + switch (desc_type) { + case UD_Device: // DEVICE DESCRIPTOR + DETERMINE_TRANSFER_SIZE(devDesc.bLength); + SET_TRANSMISSION_POINTER(&devDesc); + break; + case UD_Configuration: // CONFIGURATION DESCRIPTOR + DETERMINE_TRANSFER_SIZE(confDescs[desc_index]->wTotalLength); + SET_TRANSMISSION_POINTER(confDescs[desc_index]); + break; + case UD_String: // STRING DESCRIPTOR + DETERMINE_TRANSFER_SIZE(strDescs[desc_index]->bLength); + SET_TRANSMISSION_POINTER(strDescs[desc_index]); + break; + case UD_DeviceQualifier: + DETERMINE_TRANSFER_SIZE(devQualDesc.bLength); + SET_TRANSMISSION_POINTER(&devQualDesc); + break; + // Descriptors not implemented + default: + usbdrv_stall_endpoint(0, USB_IN, true); // stall IN, since request in unsupported break; } - case UREQ_SetAddress: { // SET ADDRESS - uint8_t addr = stups.setup_req.wValue & 0x7F; - usbdrv_set_address(addr); - usbdrv_arm_OUT_endpoint(0, 64); // prepare for data OUT stage - USBDRV_ARM_IN_ZLP(0); // ZLP IN - break; - } - case UREQ_SetConfiguration: // SET CONFIGURATION - { - uint8_t config_id = stups.setup_req.wValue & 0xFF; - usbdrv_fetch_endpoint_configuration(config_id - 1); - USBDRV_ARM_IN_ZLP(0); - break; - } - case UREQ_SetInterface: // SET INTERFACE - { - // TODO: set configuration - USBDRV_ARM_IN_ZLP(0); - break; - } - default: { // UNKNOWN REQUEST, pass processing to user application - USB_CallbackEvent cbevt = { 0 }; - cbevt.type = USB_CBEVT_UNKNOWN_REQ; - cbevt.setup_request = &stups.setup_req; - cbevt.data = (const uint8_t *)data; - cbevt.size = size; - cbevt.ep = 0; - cbevt.dir = USB_OUT; - cbevt.reply_valid = false; - usb_event_callback(&cbevt); + // arm data transmission + usbdrv_arm_IN_endpoint(0, data, sz); + break; + } - if (cbevt.reply_valid) { - usbdrv_arm_IN_endpoint(0, cbevt.reply_data, cbevt.reply_size); - } - break; + case UREQ_SetAddress: { // SET ADDRESS + uint8_t addr = stups.setup_req.wValue & 0x7F; + usbdrv_set_address(addr); + usbdrv_arm_OUT_endpoint(0, 64); // prepare for data OUT stage + USBDRV_ARM_IN_ZLP(0); // ZLP IN + break; + } + case UREQ_SetConfiguration: // SET CONFIGURATION + { + uint8_t config_id = stups.setup_req.wValue & 0xFF; + usbdrv_fetch_endpoint_configuration(config_id - 1); + USBDRV_ARM_IN_ZLP(0); + break; + } + case UREQ_SetInterface: // SET INTERFACE + { + // TODO: set configuration + USBDRV_ARM_IN_ZLP(0); + break; + } + default: { // UNKNOWN REQUEST, pass processing to user application + USB_CallbackEvent cbevt = {0}; + cbevt.type = USB_CBEVT_UNKNOWN_REQ; + cbevt.setup_request = &stups.setup_req; + cbevt.data = (const uint8_t *)data; + cbevt.size = size; + cbevt.ep = 0; + cbevt.dir = USB_OUT; + cbevt.reply_valid = false; + usb_event_callback(&cbevt); + + if (cbevt.reply_valid) { + usbdrv_arm_IN_endpoint(0, cbevt.reply_data, cbevt.reply_size); } + break; + } } stups.out_complete = false; } void usbcore_process_nonsetup_event(USBDRV_CallbackCompound *cbcpd) { - USB_CallbackEvent cbevt = { 0 }; // allocate and clear structure... - cbevt.ep = cbcpd->ep; // later only fill nonzero fields + USB_CallbackEvent cbevt = {0}; // allocate and clear structure... + cbevt.ep = cbcpd->ep; // later only fill nonzero fields bool discard_event = false; // only discard if event does not fit any category above switch (cbcpd->code) { - case USB_CBC_OUT: { - cbevt.type = USB_CBEVT_OUT; - cbevt.data = cbcpd->data; - cbevt.size = cbcpd->size; - cbevt.dir = USB_OUT; - break; - } - case USB_CBC_IN_FIFOEMPTY: - case USB_CBC_IN_DONE: { - cbevt.type = USB_CBEVT_IN; - if (cbcpd->code == USB_CBC_IN_FIFOEMPTY) { // signals a failed in transfer - cbevt.subtype = USB_CBEVST_IN_REQ; - } - cbevt.dir = USB_IN; - break; + case USB_CBC_OUT: { + cbevt.type = USB_CBEVT_OUT; + cbevt.data = cbcpd->data; + cbevt.size = cbcpd->size; + cbevt.dir = USB_OUT; + cbevt.arm_out_endpoint = true; + break; + } + case USB_CBC_IN_FIFOEMPTY: + case USB_CBC_IN_DONE: { + cbevt.type = USB_CBEVT_IN; + if (cbcpd->code == USB_CBC_IN_FIFOEMPTY) { // signals a failed in transfer + cbevt.subtype = USB_CBEVST_IN_REQ; } + cbevt.dir = USB_IN; + cbevt.enable_autozlp = true; // by default, enable auto ZLP + break; + } - default: - discard_event = true; - break; + default: + discard_event = true; + break; } // if event is not ment to be discarded, then invoke event callback if (!discard_event) { usb_event_callback(&cbevt); + + // for OUT events, check the autoarm flag + if (cbevt.dir == USB_OUT) { + if (cbevt.arm_out_endpoint) { + usbdrv_autoarm_OUT_endpoint(cbcpd->ep); + } + } else if (cbevt.dir == USB_IN) { + usbdrv_enable_autozlp(cbcpd->ep, cbevt.enable_autozlp); // handle auto ZLP setting + } } } -uint32_t usbcore_write(uint8_t ep, const uint8_t *data, uint16_t size) { +uint32_t usbcore_schedule_transmission(uint8_t ep, const uint8_t *data, uint16_t size) { return usbdrv_arm_IN_endpoint(ep, data, size); +} + +uint32_t usbcore_schedule_reception(uint8_t ep, uint16_t size) { + return usbdrv_arm_OUT_endpoint(ep, size); } \ No newline at end of file diff --git a/usb.h b/usb.h index 72f0b02..9a52c6b 100644 --- a/usb.h +++ b/usb.h @@ -4,6 +4,7 @@ #include "usb_callback_event.h" void usbcore_init(); // initialize USB core -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 +uint32_t usbcore_schedule_transmission(uint8_t ep, const uint8_t *data, uint16_t size); // write data to endpoint, return with number of bytes actually written +uint32_t usbcore_schedule_reception(uint8_t ep, uint16_t size); // expect data coming from the endpoint #endif /* CORE_USB_USB */ diff --git a/usb_callback_event.h b/usb_callback_event.h index 6a41473..e977eaf 100644 --- a/usb_callback_event.h +++ b/usb_callback_event.h @@ -28,6 +28,8 @@ typedef struct { bool reply_valid; // reply message is valid const uint8_t * reply_data; // reply data uint8_t reply_size; // reply size + bool arm_out_endpoint; // automatically arm OUT endpoint at the end of the current transmission + bool enable_autozlp; // enable automatic ZLP transmission based on transfer size } USB_CallbackEvent; #endif /* CORE_USB_USB_CALLBACK_EVENT */ diff --git a/usb_driver.c b/usb_driver.c index c1395cb..0a6c440 100644 --- a/usb_driver.c +++ b/usb_driver.c @@ -94,6 +94,10 @@ void usbdrv_reset() { // --------------- +#if USB_EVENT_PROCESSING_IN_OS_THREAD +static void usbdrv_thread(void *param); +#endif + // initialize global state void usbdrv_init_global_state() { // clear state @@ -103,8 +107,18 @@ void usbdrv_init_global_state() { gs.rx_buf = rx_buf; gs.rx_buf_level = 0; +#if USB_EVENT_PROCESSING_IN_OS_THREAD // initialize event queue - // gs.event_queue = Q_CREATE_T(USB_EVENT_QUEUE_LENGTH, USBDRV_EventCompound, event_queue_mem); + gs.event_queue = osMessageQueueNew(16, sizeof(USBDRV_EventCompound), NULL); + + // initialize event processing thread + osThreadAttr_t attr; + bzero(&attr, sizeof(osThreadAttr_t)); + attr.stack_size = 2048; + attr.name = "usb"; + attr.priority = osPriorityNormal; + gs.th = osThreadNew(usbdrv_thread, NULL, &attr); +#endif } // --------------- @@ -149,6 +163,9 @@ void usbdrv_periph_init() { usbdrv_flush_rx_fifo(); usbdrv_flush_tx_fifo(USB_FLUSH_TX_FIFO_ALL); + // make Tx FIFO empty interrupt fire when Tx FIFO is emtpy + // SET_BIT(USBG->GAHBCFG, USB_OTG_GAHBCFG_TXFELVL); + // set masks for endpoint interrupts SET_BIT(USBD->DIEPMSK, USB_OTG_DIEPMSK_XFRCM | USB_OTG_DIEPMSK_TOM | USB_OTG_DIEPMSK_ITTXFEMSK); // transfer complete, timeout and Tx FIFO empty while receiving IN for IN EPs SET_BIT(USBD->DOEPMSK, USB_OTG_DOEPMSK_XFRCM | USB_OTG_DOEPMSK_STUPM); // transfer complete and SETUP complete for OUT EPs @@ -244,7 +261,7 @@ void usbdrv_fetch_endpoint_configuration(uint8_t config_index) { #define USB_MIN_FIFO_SIZE (64) #define USB_FIFO_MARGIN (8) #define USB_RX_FIFO_SETUP_RESERVATION_DWORDS (10) -#define USB_MIN_GROSS_TX_FIFO_SIZE (USB_MIN_EP_FIFO_SIZE + USB_FIFO_MARGIN) +#define USB_MIN_GROSS_TX_FIFO_SIZE (2 * USB_MIN_EP_FIFO_SIZE) #define USB_MIN_GROSS_RX_FIFO_SIZE (2 * USB_MIN_EP_FIFO_SIZE + USB_RX_FIFO_SETUP_RESERVATION_DWORDS * 4) // build FIFO (compute addresses) @@ -265,10 +282,12 @@ void usbdrv_build_fifo() { for (uint8_t i = 0; i < USB_NUM_OF_ENDPOINTS; i++) { USBDRV_EpConfig *cfg = &gs.ep_IN[i]; if (cfg->is_configured) { - cfg->fifo_size = CEIL4(MAX(USB_MIN_GROSS_TX_FIFO_SIZE, 2 * cfg->max_packet_size + USB_FIFO_MARGIN)); // correct FIFO size if necessary - cfg->fifo_address = next_fifo_addr; // store FIFO address - cfg->zlp_next = false; // clear ZLP next - next_fifo_addr += cfg->fifo_size; // advance next address + cfg->fifo_size = CEIL4(MAX(USB_MIN_GROSS_TX_FIFO_SIZE, 2 * cfg->max_packet_size)); // correct FIFO size if necessary + cfg->fifo_address = next_fifo_addr; // store FIFO address + cfg->auto_zlp = true; // turn on automatic ZLP appending + cfg->zlp_next = false; + // cfg->txp = false; // no transfer is in progress + next_fifo_addr += cfg->fifo_size; // advance next address } } } @@ -355,6 +374,8 @@ void usbdrv_configure_endpoint(uint8_t ep, uint8_t dir, const USBDRV_EpConfig *c // NAK processing if (cfg->responding_NAK) { SET_BIT(USBINEP[ep].DIEPCTL, USB_OTG_DIEPCTL_SNAK); // send NAK + } else { + SET_BIT(USBINEP[ep].DIEPCTL, USB_OTG_DIEPCTL_CNAK); // clear sending NAK } } } @@ -470,44 +491,73 @@ 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);*/ - // transmission may only be commenced if last transmission has concluded - if (READ_BIT(USBINEP[ep].DIEPCTL, USB_OTG_DIEPCTL_EPENA)) { + // determine if a transmission is in progress or not + bool txp = READ_BIT(USBINEP[ep].DIEPCTL, USB_OTG_DIEPCTL_EPENA); + /*if (txp) { return 0; - } + }*/ + + uint16_t mps = gs.ep_IN[ep].max_packet_size; // fetch maximum packet size // determine final write size - uint32_t freeSize = 0; - // if (len > 0) { // only poll DTXFSTS if (len > 0) (see datasheet) - freeSize = USBINEP[ep].DTXFSTS * sizeof(uint32_t); // get free transmit buffer size - //} - uint16_t writeSize = MIN(freeSize, len); // limit transmit size to free size + uint32_t freeSize = USBINEP[ep].DTXFSTS * sizeof(uint32_t); // get free transmit buffer size + uint16_t writeSize = 0; - // 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 (writeSize > 0) { // if length is nonzero - packet_count = writeSize / mps + (((writeSize % mps) > 0) ? 1 : 0); + if (txp) { // transaction is in progress + uint32_t remainingBytes = USBINEP[ep].DIEPTSIZ & USB_OTG_DIEPTSIZ_XFRSIZ; // acquire remaining bytes + if (len >= mps) { // write only full-sized packets, or a short packet + uint16_t mws = MIN(freeSize, len); // maximum possible write size + uint16_t fpc = mws / mps; // full packet count + writeSize = fpc * mps; // form write size + } else { // length is less then a full packet size + if (len <= freeSize) { // packet can be written + writeSize = len; + } else { + writeSize = 0; + } + } + // adjust write size to remaining size + writeSize = MIN(remainingBytes, writeSize); + } else { // no transaction is in progress + writeSize = MIN(freeSize, len); } - // set zlp_next if transmission size is integer multiple of max packet size6 - gs.ep_IN[ep].zlp_next = (writeSize > 0) && ((writeSize % mps) == 0); + // if no transmission is in progress + if (!txp) { + // calculate packet count based on 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); // a transfer may contain multiple packets + } - // program DIEPTSIZ with transfer length - USBINEP[ep].DIEPTSIZ = (packet_count << USB_OTG_DIEPTSIZ_MULCNT_Pos) | (packet_count << USB_OTG_DIEPTSIZ_PKTCNT_Pos) | writeSize; + // set zlp_next if transfer size is integer multiple of max packet size and auto ZLP is on + gs.ep_IN[ep].zlp_next = (len > 0) && ((len % mps) == 0); - // enable endpoint and cancel responding NAK - SET_BIT(USBINEP[ep].DIEPCTL, USB_OTG_DIEPCTL_EPENA | USB_OTG_DIEPCTL_CNAK); + // program DIEPTSIZ with transfer length + USBINEP[ep].DIEPTSIZ = /*(packet_count << USB_OTG_DIEPTSIZ_MULCNT_Pos) |*/ (packet_count << USB_OTG_DIEPTSIZ_PKTCNT_Pos) | len; + + // enable endpoint and cancel responding NAK + SET_BIT(USBINEP[ep].DIEPCTL, USB_OTG_DIEPCTL_EPENA | USB_OTG_DIEPCTL_CNAK); + } + + // turn interrupt generation on only, if this is NOT the last FIFO write considering the current transfer + if (len > writeSize) { + USBD->DIEPEMPMSK |= ((uint32_t)(1 << ep)); + } // disable ALL USB interrupts to prevent access to specific registers (see errata) CLEAR_BIT(USBG->GAHBCFG, USB_OTG_GAHBCFG_GINT); + // https://github.com/iliasam/STM32F4_USB_MICROPHONE/blob/master/Libraries/STM32_USB_OTG_Driver/src/usb_dcd_int.c#L655 + // https://github.com/iliasam/STM32F4_USB_MICROPHONE/blob/master/Libraries/STM32_USB_OTG_Driver/src/usb_core.c#L168 + // push full dwords volatile uint32_t *p = (uint32_t *)USBFIFO(ep); 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); - p[i] = dword; + *p = dword; } // push the remaining partial dword @@ -529,9 +579,29 @@ uint32_t usbdrv_arm_IN_endpoint(uint8_t ep, const uint8_t *data, uint16_t len) { } // arm OUT endpoint -void usbdrv_arm_OUT_endpoint(uint8_t ep, uint8_t size) { +uint32_t usbdrv_arm_OUT_endpoint(uint8_t ep, uint8_t size) { + // arm endpoint only if it was not armed before + if (READ_BIT(USBOUTEP[ep].DOEPCTL, USB_OTG_DOEPCTL_EPENA)) { + return 0; + } + + // cap size at max packet size defined for this endpoint + size = MIN(gs.ep_OUT[ep].max_packet_size, size); + + // write registers USBOUTEP[ep].DOEPTSIZ |= USB_OTG_DOEPTSIZ_PKTCNT | size; // program DIEPTSIZ with maximum (expected) transfer length and set PCKTCNT to make ready for reception SET_BIT(USBOUTEP[ep].DOEPCTL, USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK); // enable endpoint and clear NAK + + // return with armed size + return size; +} + +void usbdrv_autoarm_OUT_endpoint(uint8_t ep) { + gs.ep_OUT[ep].autoarm = true; +} + +void usbdrv_enable_autozlp(uint8_t ep, bool en) { + gs.ep_IN[ep].auto_zlp = en; } // ---------------- @@ -543,25 +613,25 @@ void usbdrv_set_address(uint8_t addr) { // ---------------- +#if USB_EVENT_PROCESSING_IN_OS_THREAD // push event onto the event queue -// void usbdrv_push_event(uint32_t evt_code, USBDRV_EventData *evt_data) { -// USBDRV_EventCompound evt_cpd; -// if (evt_data != NULL) { -// evt_cpd.data = *evt_data; -// } -// evt_cpd.code = evt_code; -// q_push(gs.event_queue, &evt_cpd); -// } +void usbdrv_push_event(uint32_t evt_code, USBDRV_EventData *evt_data) { + USBDRV_EventCompound evt_cpd; + if (evt_data != NULL) { + evt_cpd.data = *evt_data; + } + evt_cpd.code = evt_code; + osMessageQueuePut(gs.event_queue, &evt_cpd, 0, 0); +} -// // call this to process incoming events -// void usbdrv_periodic_processing() { -// if ((gs.event_queue != NULL) && (q_avail(gs.event_queue))) { -// USBDRV_EventCompound evt_cpd; -// q_top(gs.event_queue, &evt_cpd); -// q_pop(gs.event_queue); -// usbdrv_process_event(evt_cpd.code, NULL); -// } -// } +static void usbdrv_thread(void *param) { + for (;;) { + USBDRV_EventCompound evt_cpd; + osMessageQueueGet(gs.event_queue, &evt_cpd, 0, osWaitForever); + usbdrv_process_event(evt_cpd.code, NULL); + } +} +#endif // ---------------- @@ -664,7 +734,10 @@ void usbdrv_process_event(uint8_t evt_code, USBDRV_EventData *evt_data) { SET_BIT(USBOUTEP[ep].DOEPINT, USB_OTG_DOEPINT_XFRC); USBMSG("OUT\n"); - usbdrv_arm_OUT_endpoint(ep, gs.ep_OUT[ep].max_packet_size); + if ((ep == 0) || (gs.ep_OUT[ep].autoarm)) { // EP0 must always be armed + usbdrv_arm_OUT_endpoint(ep, gs.ep_OUT[ep].max_packet_size); // arm endpoint + gs.ep_OUT[ep].autoarm = false; // clear autoarm flag + } } } } @@ -685,13 +758,23 @@ void usbdrv_process_event(uint8_t evt_code, USBDRV_EventData *evt_data) { if (READ_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_TOC)) { // timeout done SET_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_TOC); USBMSG("TO\n"); - } + } else if ((USBD->DIEPEMPMSK & (1 << ep)) && READ_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_TXFE)) { + // disable FIFO empty interrupt + USBD->DIEPEMPMSK &= ~((uint32_t)(1 << ep)); - if (READ_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_XFRC)) { // IN transaction done + cbcpd.code = USB_CBC_IN_FIFOEMPTY; + usbcore_process_nonsetup_event(&cbcpd); + } else if (READ_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_XFRC)) { // IN transaction done SET_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_XFRC); + // disable FIFO empty interrupt + USBD->DIEPEMPMSK &= ~((uint32_t)(1 << ep)); + + // transfer finished + // gs.ep_IN[ep].txp = false; + // see if a ZLP transmission was queued - if (gs.ep_IN[ep].zlp_next) { + if (/*gs.ep_IN[ep].auto_zlp && */ gs.ep_IN[ep].zlp_next) { usbdrv_arm_IN_endpoint(ep, NULL, 0); // send ZLP } else { // no ZLP USBMSG("IN [%d]\n", ep); @@ -699,13 +782,14 @@ void usbdrv_process_event(uint8_t evt_code, USBDRV_EventData *evt_data) { 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 + } else 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); // USBMSG("IN FIFOEMPTY [%d]\n", ep); + // transfer finished + // gs.ep_IN[ep].txp = false; + cbcpd.code = USB_CBC_IN_FIFOEMPTY; usbcore_process_nonsetup_event(&cbcpd); } @@ -737,7 +821,14 @@ bool usbdrv_get_endpoint_interrupt_flag(uint8_t ep, uint8_t dir) { return (USBD->DAINT & (1 << (ep + ((dir == USB_OUT) ? USB_OTG_DAINT_OEPINT_Pos : 0)))) != 0; } +#if USB_EVENT_PROCESSING_IN_OS_THREAD +#define PROCESS_EVENT(evt, data) usbdrv_push_event((evt), (data)) +#else +#define PROCESS_EVENT(evt, data) usbdrv_process_event((evt), (data)) +#endif + void OTG_FS_IRQHandler() { + uint32_t ints = USBG->GINTSTS; // USB reset @@ -753,15 +844,16 @@ void OTG_FS_IRQHandler() { // ST calls speed negotiation the enumeration, normally this // interrupt fires only before even communication is commenced) if (ints & USB_OTG_GINTSTS_ENUMDNE) { - SET_BIT(USBG->GINTSTS, USB_OTG_GINTSTS_ENUMDNE); // clear interrupt - usbdrv_process_event(USB_EVT_SPEEDNEG_DONE, NULL); // process event - // usbdrv_push_event(USB_EVT_SPEEDNEG_DONE, NULL); // push event + SET_BIT(USBG->GINTSTS, USB_OTG_GINTSTS_ENUMDNE); // clear interrupt + PROCESS_EVENT(USB_EVT_SPEEDNEG_DONE, NULL); // process event + // usbdrv_process_event(USB_EVT_SPEEDNEG_DONE, NULL); // process event + // usbdrv_push_event(USB_EVT_SPEEDNEG_DONE, NULL); // push event } // Start of Frame received (like Keep-Alive in LS mode) - // if (ints & USB_OTG_GINTSTS_SOF) { - // SET_BIT(USBG->GINTSTS, USB_OTG_GINTSTS_SOF); // clear interrupt - // } + if (ints & USB_OTG_GINTSTS_SOF) { + SET_BIT(USBG->GINTSTS, USB_OTG_GINTSTS_SOF); // clear interrupt + } // USB Suspend if (ints & USB_OTG_GINTSTS_USBSUSP) { @@ -771,7 +863,7 @@ void OTG_FS_IRQHandler() { // OUT endpoint interrupt if (ints & USB_OTG_GINTSTS_OEPINT) { - usbdrv_process_event(USB_EVT_OUT_DONE, NULL); + PROCESS_EVENT(USB_EVT_OUT_DONE, NULL); // usbdrv_push_event(USB_EVT_OUT_DONE, NULL); // if (USBD->DAINT & (1 << 16)) { // if (USBOUTEP[0].DOEPINT & USB_OTG_DOEPINT_STUP) { @@ -785,15 +877,182 @@ void OTG_FS_IRQHandler() { // 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 - usbdrv_process_event(USB_EVT_RECEPTION_DONE, NULL); // process event + PROCESS_EVENT(USB_EVT_RECEPTION_DONE, NULL); // process event // usbdrv_push_event(USB_EVT_RECEPTION_DONE, NULL); // push event } // IN endpoint interrupt if (ints & USB_OTG_GINTSTS_IEPINT) { - usbdrv_process_event(USB_EVT_IN_DONE, NULL); + PROCESS_EVENT(USB_EVT_IN_DONE, NULL); // usbdrv_push_event(USB_EVT_IN_DONE, NULL); } return; -} \ No newline at end of file +} + +// --------------- + +// 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);*/ + +// // determine if a transmission is in progress or not +// if (gs.ep_IN[ep].txp) { +// return 0; +// } + +// // determine if a transmission is in progress or not +// // bool txp = READ_BIT(USBINEP[ep].DIEPCTL, USB_OTG_DIEPCTL_EPENA); // transmission in progress + +// // transmission may only be commenced if last transmission has concluded +// /*if (READ_BIT(USBINEP[ep].DIEPCTL, USB_OTG_DIEPCTL_EPENA)) { +// return 0; +// }*/ + +// uint16_t mps = gs.ep_IN[ep].max_packet_size; // fetch maximum packet size + +// // determine final write size +// uint32_t freeSize = 0; + +// // if (len > 0) { // only poll DTXFSTS if (len > 0) (see datasheet) +// freeSize = USBINEP[ep].DTXFSTS * sizeof(uint32_t); // get free transmit buffer size +// //} + +// // if (gs.ep_IN[ep].txp && (freeSize == 0)) { +// // return 0; +// // } + +// uint16_t writeSize = MIN(freeSize, len); // limit transmit size to free size + +// // if transmission is in progress, then remaining transmission bytes also limits transmission size +// if (gs.ep_IN[ep].txp) { +// uint16_t bytesLeft = USBINEP[ep].DIEPTSIZ & USB_OTG_DIEPTSIZ_XFRSIZ; +// writeSize = MIN(writeSize, bytesLeft); + +// // if this was the last write, then disable Tx empty interrupt +// if (bytesLeft <= mps) { +// USBD->DIEPEMPMSK &= ~((uint32_t)(1 << ep)); +// } else { // if not, turn it back on +// USBD->DIEPEMPMSK |= ((uint32_t)(1 << ep)); +// } + +// } else { // if no transmission was in progress, then setup endpoint for transmission +// // calculate packet count based on max packet size +// uint16_t packet_count = 1; // for ZLPs +// if (writeSize > 0) { // if length is nonzero +// packet_count = len / mps + (((len % mps) > 0) ? 1 : 0); // a transfer may contain multiple packets +// } + +// // set zlp_next if transfer size is integer multiple of max packet size and auto ZLP is on +// gs.ep_IN[ep].zlp_next = gs.ep_IN[ep].auto_zlp && (len > 0) && ((len % mps) == 0); + +// // program DIEPTSIZ with transfer length +// USBINEP[ep].DIEPTSIZ = /*(packet_count << USB_OTG_DIEPTSIZ_MULCNT_Pos) |*/ (packet_count << USB_OTG_DIEPTSIZ_PKTCNT_Pos) | len; + +// // enable endpoint and cancel responding NAK +// SET_BIT(USBINEP[ep].DIEPCTL, USB_OTG_DIEPCTL_EPENA | USB_OTG_DIEPCTL_CNAK); + +// // enable FIFO empty interrupt, only if further write operations will follow +// // DO NOT enable this interrupt source when dealing with EP0! +// if ((ep != 0) && (writeSize < len)) { +// USBD->DIEPEMPMSK |= (1 << ep); +// } + +// // set transfer in progress +// gs.ep_IN[ep].txp = true; +// } + +// // disable ALL USB interrupts to prevent access to specific registers (see errata) +// CLEAR_BIT(USBG->GAHBCFG, USB_OTG_GAHBCFG_GINT); + +// // push full dwords +// volatile uint32_t *p = (uint32_t *)USBFIFO(ep); +// 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); +// p[i] = dword; +// } + +// // push the remaining partial dword +// uint8_t rem_bytes = writeSize & 0b11; +// if (rem_bytes > 0) { +// uint32_t rem_dword = 0; +// uint16_t rem_start = writeSize - rem_bytes; +// for (int16_t i = writeSize - 1; i >= rem_start; i--) { +// rem_dword = (rem_dword << 8) | data[i]; +// } +// *p = rem_dword; +// } + +// // unmask USB interrupts +// SET_BIT(USBG->GAHBCFG, USB_OTG_GAHBCFG_GINT); + +// // return with written size +// return writeSize; +// } + +// EZ MŰKÖDIK, DE CSAK RÖVID TRANSFEREKRE +// 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);*/ + +// // determine if a transmission is in progress or not +// bool txp = READ_BIT(USBINEP[ep].DIEPCTL, USB_OTG_DIEPCTL_EPENA); +// if (txp) { +// return 0; +// } + +// uint16_t mps = gs.ep_IN[ep].max_packet_size; // fetch maximum packet size + +// // 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 packet_count = 1; // for ZLPs +// if (writeSize > 0) { // if length is nonzero +// packet_count = writeSize / mps + (((writeSize % mps) > 0) ? 1 : 0); // a transfer may contain multiple packets +// } + +// // set zlp_next if transfer size is integer multiple of max packet size and auto ZLP is on +// gs.ep_IN[ep].zlp_next = gs.ep_IN[ep].auto_zlp && (writeSize > 0) && ((writeSize % mps) == 0); + +// // program DIEPTSIZ with transfer length +// USBINEP[ep].DIEPTSIZ = /*(packet_count << USB_OTG_DIEPTSIZ_MULCNT_Pos) |*/ (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); + +// // set transfer in progress +// //gs.ep_IN[ep].txp = true; + +// // disable ALL USB interrupts to prevent access to specific registers (see errata) +// CLEAR_BIT(USBG->GAHBCFG, USB_OTG_GAHBCFG_GINT); + +// // push full dwords +// volatile uint32_t *p = (uint32_t *)USBFIFO(ep); +// 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); +// p[i] = dword; +// } + +// // push the remaining partial dword +// uint8_t rem_bytes = writeSize & 0b11; +// if (rem_bytes > 0) { +// uint32_t rem_dword = 0; +// uint16_t rem_start = writeSize - rem_bytes; +// for (int16_t i = writeSize - 1; i >= rem_start; i--) { +// rem_dword = (rem_dword << 8) | data[i]; +// } +// *p = rem_dword; +// } + +// // unmask USB interrupts +// SET_BIT(USBG->GAHBCFG, USB_OTG_GAHBCFG_GINT); + +// // return with written size +// return writeSize; +// } \ No newline at end of file diff --git a/usb_driver.h b/usb_driver.h index 93c9a41..77baa56 100644 --- a/usb_driver.h +++ b/usb_driver.h @@ -1,11 +1,11 @@ #ifndef CORE_USB_USB_DRIVER #define CORE_USB_USB_DRIVER -//#include "utils/gen_queue.h" +// #include "utils/gen_queue.h" #include "usb_common_types.h" -//#define USBDBGMSG +// #define USBDBGMSG #define USB_NUM_OF_ENDPOINTS (4) @@ -14,6 +14,15 @@ #define USB_MIN_EP_FIFO_SIZE (64) +// Unfortunately this cannot be enabled, since the USB controller +// generates such enormous amounts of interrupts, so that no other +// task can run along the USB processing. +#define USB_EVENT_PROCESSING_IN_OS_THREAD (0) + +#if USB_EVENT_PROCESSING_IN_OS_THREAD +#include +#endif + // endpoint configuration structure typedef struct { uint16_t max_packet_size; // maximum packet size @@ -26,7 +35,10 @@ typedef struct { uint8_t is_configured; // the endpoint is in use uint16_t fifo_address; // address in the FIFO uint16_t fifo_size; // FIFO size - bool8_t zlp_next; // indicates, that ZLP should be transmitted at the end of the current transfer + bool8_t auto_zlp; // automatically insert ZLP packets + bool8_t zlp_next; // indicates, that ZLP should be transmitted at the end of the current transfer (for IN endpoints) + bool8_t autoarm; // automatically arm endpoint (for OUT endpoints) + //bool8_t txp; // transfer in progress } USBDRV_EpConfig; typedef enum { @@ -58,8 +70,11 @@ typedef struct { uint16_t state; // FSM state uint8_t *rx_buf; // pointer to the receive buffer (this way declaration can be separated) uint16_t rx_buf_level; // fill level of the rx buffer - // Queue *event_queue; // event queue uint8_t address; // device address +#if USB_EVENT_PROCESSING_IN_OS_THREAD + osMessageQueueId_t event_queue; // event queue + osThreadId_t th; // event processing thread +#endif } USBDRV_GlobalState; // USB received packet status @@ -90,8 +105,8 @@ typedef struct { // callback codes typedef enum { - USB_CBC_OUT, // OUT done! - USB_CBC_IN_DONE, // IN done! + USB_CBC_OUT, // OUT done! + USB_CBC_IN_DONE, // IN done! USB_CBC_IN_FIFOEMPTY, // could not serve IN request, since Tx FIFO was empty } USBDRV_CallbackCode; @@ -138,7 +153,10 @@ void usbdrv_process_rx_fifo_top(USBDRV_EventData *evt_data); // see what's on to 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 +uint32_t usbdrv_arm_OUT_endpoint(uint8_t ep, uint8_t size); // arm OUT endpoint, returned with the actual armed size (capped by the max packet size on that endpoint) +void usbdrv_autoarm_OUT_endpoint(uint8_t ep); // automatically re-arm OUT endpoint at the and of the current OUT transfer +void usbdrv_enable_autozlp(uint8_t ep, bool en); // enable or disable automatic ZLP appending to the transaction + bool usbdrv_get_endpoint_interrupt_flag(uint8_t ep, uint8_t dir); // get endpoint interrupt flag void usbdrv_set_address(uint8_t addr); // set device address