Merge branch 'master' of github.com:hathach/tinyusb into kkitayam-impl_close_all_for_khci
This commit is contained in:
		| @@ -214,14 +214,14 @@ bool btd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t c | ||||
|     } | ||||
|     else return false; | ||||
|  | ||||
|     return tud_control_xfer(rhport, request, &_btd_itf.hci_cmd, request->wLength); | ||||
|     return tud_control_xfer(rhport, request, &_btd_itf.hci_cmd, sizeof(_btd_itf.hci_cmd)); | ||||
|   } | ||||
|   else if ( stage == CONTROL_STAGE_DATA ) | ||||
|   { | ||||
|     // Handle class request only | ||||
|     TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS); | ||||
|  | ||||
|     if (tud_bt_hci_cmd_cb) tud_bt_hci_cmd_cb(&_btd_itf.hci_cmd, request->wLength); | ||||
|     if (tud_bt_hci_cmd_cb) tud_bt_hci_cmd_cb(&_btd_itf.hci_cmd, tu_min16(request->wLength, sizeof(_btd_itf.hci_cmd))); | ||||
|   } | ||||
|  | ||||
|   return true; | ||||
|   | ||||
| @@ -372,7 +372,7 @@ typedef struct | ||||
|  | ||||
| static inline const char* tu_lookup_find(tu_lookup_table_t const* p_table, uint32_t key) | ||||
| { | ||||
|   static char not_found[10]; | ||||
|   static char not_found[11]; | ||||
|  | ||||
|   for(uint16_t i=0; i<p_table->count; i++) | ||||
|   { | ||||
| @@ -380,7 +380,7 @@ static inline const char* tu_lookup_find(tu_lookup_table_t const* p_table, uint3 | ||||
|   } | ||||
|  | ||||
|   // not found return the key value in hex | ||||
|   sprintf(not_found, "0x%08lX", key); | ||||
|   sprintf(not_found, "0x%08lX", (unsigned long) key); | ||||
|  | ||||
|   return not_found; | ||||
| } | ||||
|   | ||||
| @@ -372,7 +372,7 @@ typedef struct TU_ATTR_PACKED | ||||
|  | ||||
|   uint8_t  bNumInterfaces      ; ///< Number of interfaces supported by this speed configuration | ||||
|   uint8_t  bConfigurationValue ; ///< Value to use to select configuration | ||||
|   uint8_t  IConfiguration      ; ///< Index of string descriptor | ||||
|   uint8_t  iConfiguration      ; ///< Index of string descriptor | ||||
|   uint8_t  bmAttributes        ; ///< Same as Configuration descriptor | ||||
|   uint8_t  bMaxPower           ; ///< Same as Configuration descriptor | ||||
| } tusb_desc_other_speed_t; | ||||
| @@ -387,11 +387,14 @@ typedef struct TU_ATTR_PACKED | ||||
|   uint8_t  bDeviceClass       ; ///< Class Code | ||||
|   uint8_t  bDeviceSubClass    ; ///< SubClass Code | ||||
|   uint8_t  bDeviceProtocol    ; ///< Protocol Code | ||||
|  | ||||
|   uint8_t  bMaxPacketSize0    ; ///< Maximum packet size for other speed | ||||
|   uint8_t  bNumConfigurations ; ///< Number of Other-speed Configurations | ||||
|   uint8_t  bReserved          ; ///< Reserved for future use, must be zero | ||||
| } tusb_desc_device_qualifier_t; | ||||
|  | ||||
| TU_VERIFY_STATIC( sizeof(tusb_desc_device_qualifier_t) == 10, "size is not correct"); | ||||
|  | ||||
| /// USB Interface Association Descriptor (IAD ECN) | ||||
| typedef struct TU_ATTR_PACKED | ||||
| { | ||||
|   | ||||
| @@ -1002,10 +1002,22 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const | ||||
|     break; | ||||
|  | ||||
|     case TUSB_DESC_CONFIGURATION: | ||||
|     case TUSB_DESC_OTHER_SPEED_CONFIG: | ||||
|     { | ||||
|       TU_LOG2(" Configuration[%u]\r\n", desc_index); | ||||
|       tusb_desc_configuration_t const* desc_config; | ||||
|  | ||||
|       if ( desc_type == TUSB_DESC_CONFIGURATION ) | ||||
|       { | ||||
|         TU_LOG2(" Configuration[%u]\r\n", desc_index); | ||||
|         desc_config = (tusb_desc_configuration_t const*) tud_descriptor_configuration_cb(desc_index); | ||||
|       }else | ||||
|       { | ||||
|         // Host only request this after getting Device Qualifier descriptor | ||||
|         TU_LOG2(" Other Speed Configuration\r\n"); | ||||
|         TU_VERIFY( tud_descriptor_other_speed_configuration_cb ); | ||||
|         desc_config = (tusb_desc_configuration_t const*) tud_descriptor_other_speed_configuration_cb(desc_index); | ||||
|       } | ||||
|  | ||||
|       tusb_desc_configuration_t const* desc_config = (tusb_desc_configuration_t const*) tud_descriptor_configuration_cb(desc_index); | ||||
|       TU_ASSERT(desc_config); | ||||
|  | ||||
|       // Use offsetof to avoid pointer to the odd/misaligned address | ||||
| @@ -1029,29 +1041,17 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const | ||||
|     break; | ||||
|  | ||||
|     case TUSB_DESC_DEVICE_QUALIFIER: | ||||
|     { | ||||
|       TU_LOG2(" Device Qualifier\r\n"); | ||||
|  | ||||
|       // Host sends this request to ask why our device with USB BCD from 2.0 | ||||
|       // but is running at Full/Low Speed. If not highspeed capable stall this request, | ||||
|       // otherwise return the descriptor that could work in highspeed mode | ||||
|       if ( tud_descriptor_device_qualifier_cb ) | ||||
|       { | ||||
|         uint8_t const* desc_qualifier = tud_descriptor_device_qualifier_cb(); | ||||
|         TU_ASSERT(desc_qualifier); | ||||
|       TU_VERIFY( tud_descriptor_device_qualifier_cb ); | ||||
|  | ||||
|         // first byte of descriptor is its size | ||||
|         return tud_control_xfer(rhport, p_request, (void*) desc_qualifier, desc_qualifier[0]); | ||||
|       }else | ||||
|       { | ||||
|         return false; | ||||
|       } | ||||
|     break; | ||||
|       uint8_t const* desc_qualifier = tud_descriptor_device_qualifier_cb(); | ||||
|       TU_VERIFY(desc_qualifier); | ||||
|  | ||||
|     case TUSB_DESC_OTHER_SPEED_CONFIG: | ||||
|       TU_LOG2(" Other Speed Configuration\r\n"); | ||||
|  | ||||
|       // After Device Qualifier descriptor is received host will ask for this descriptor | ||||
|       return false; // not supported | ||||
|       // first byte of descriptor is its size | ||||
|       return tud_control_xfer(rhport, p_request, (void*) desc_qualifier, desc_qualifier[0]); | ||||
|     } | ||||
|     break; | ||||
|  | ||||
|     default: return false; | ||||
| @@ -1066,15 +1066,11 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr) | ||||
|   switch (event->event_id) | ||||
|   { | ||||
|     case DCD_EVENT_UNPLUGGED: | ||||
|       // UNPLUGGED event can be bouncing, only processing if we are currently connected | ||||
|       if ( _usbd_dev.connected ) | ||||
|       { | ||||
|         _usbd_dev.connected  = 0; | ||||
|         _usbd_dev.addressed  = 0; | ||||
|         _usbd_dev.cfg_num    = 0; | ||||
|         _usbd_dev.suspended  = 0; | ||||
|         osal_queue_send(_usbd_q, event, in_isr); | ||||
|       } | ||||
|       _usbd_dev.connected  = 0; | ||||
|       _usbd_dev.addressed  = 0; | ||||
|       _usbd_dev.cfg_num    = 0; | ||||
|       _usbd_dev.suspended  = 0; | ||||
|       osal_queue_send(_usbd_q, event, in_isr); | ||||
|     break; | ||||
|  | ||||
|     case DCD_EVENT_SUSPEND: | ||||
| @@ -1383,7 +1379,13 @@ void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr) | ||||
|   TU_ASSERT(dcd_edpt_close, /**/); | ||||
|   TU_LOG2("  CLOSING Endpoint: 0x%02X\r\n", ep_addr); | ||||
|  | ||||
|   uint8_t const epnum = tu_edpt_number(ep_addr); | ||||
|   uint8_t const dir   = tu_edpt_dir(ep_addr); | ||||
|  | ||||
|   dcd_edpt_close(rhport, ep_addr); | ||||
|   _usbd_dev.ep_status[epnum][dir].stalled = false; | ||||
|   _usbd_dev.ep_status[epnum][dir].busy = false; | ||||
|   _usbd_dev.ep_status[epnum][dir].claimed = false; | ||||
|  | ||||
|   return; | ||||
| } | ||||
|   | ||||
| @@ -113,9 +113,16 @@ uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid); | ||||
| TU_ATTR_WEAK uint8_t const * tud_descriptor_bos_cb(void); | ||||
|  | ||||
| // Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request | ||||
| // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete | ||||
| // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete. | ||||
| // device_qualifier descriptor describes information about a high-speed capable device that would | ||||
| // change if the device were operating at the other speed. If not highspeed capable stall this request. | ||||
| TU_ATTR_WEAK uint8_t const* tud_descriptor_device_qualifier_cb(void); | ||||
|  | ||||
| // Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request | ||||
| // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete | ||||
| // Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa | ||||
| TU_ATTR_WEAK uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index); | ||||
|  | ||||
| // Invoked when device is mounted (configured) | ||||
| TU_ATTR_WEAK void tud_mount_cb(void); | ||||
|  | ||||
|   | ||||
| @@ -42,10 +42,6 @@ | ||||
|  | ||||
| #include "device/dcd.h" | ||||
|  | ||||
| // Since TinyUSB doesn't use SOF for now, and this interrupt too often (1ms interval) | ||||
| // We disable SOF for now until needed later on | ||||
| #define USE_SOF     0 | ||||
|  | ||||
| // Max number of bi-directional endpoints including EP0 | ||||
| // Note: ESP32S2 specs say there are only up to 5 IN active endpoints include EP0 | ||||
| // We should probably prohibit enabling Endpoint IN > 4 (not done yet) | ||||
| @@ -92,11 +88,12 @@ static void bus_reset(void) | ||||
|     USB0.out_ep_reg[ep_num].doepctl |= USB_DO_SNAK0_M; // DOEPCTL0_SNAK | ||||
|   } | ||||
|  | ||||
|   USB0.dcfg &= ~USB_DEVADDR_M; // reset address | ||||
|   // clear device address | ||||
|   USB0.dcfg &= ~USB_DEVADDR_M; | ||||
|  | ||||
|   USB0.daintmsk |= USB_OUTEPMSK0_M | USB_INEPMSK0_M; | ||||
|   USB0.doepmsk |= USB_SETUPMSK_M | USB_XFERCOMPLMSK; | ||||
|   USB0.diepmsk |= USB_TIMEOUTMSK_M | USB_DI_XFERCOMPLMSK_M /*| USB_INTKNTXFEMPMSK_M*/; | ||||
|   USB0.daintmsk = USB_OUTEPMSK0_M | USB_INEPMSK0_M; | ||||
|   USB0.doepmsk  = USB_SETUPMSK_M | USB_XFERCOMPLMSK; | ||||
|   USB0.diepmsk  = USB_TIMEOUTMSK_M | USB_DI_XFERCOMPLMSK_M /*| USB_INTKNTXFEMPMSK_M*/; | ||||
|  | ||||
|   // "USB Data FIFOs" section in reference manual | ||||
|   // Peripheral FIFO architecture | ||||
| @@ -193,9 +190,6 @@ void dcd_init(uint8_t rhport) | ||||
|   USB0.gintsts = ~0U; //clear pending ints | ||||
|   USB0.gintmsk = USB_OTGINTMSK_M   | | ||||
|                  USB_MODEMISMSK_M  | | ||||
|           #if USE_SOF | ||||
|                  USB_SOFMSK_M      | | ||||
|           #endif | ||||
|                  USB_RXFLVIMSK_M   | | ||||
|                  USB_ERLYSUSPMSK_M | | ||||
|                  USB_USBSUSPMSK_M  | | ||||
| @@ -220,8 +214,17 @@ void dcd_remote_wakeup(uint8_t rhport) | ||||
| { | ||||
|   (void)rhport; | ||||
|  | ||||
|   // TODO must manually clear this bit after 1-15 ms | ||||
|   // USB0.DCTL |= USB_RMTWKUPSIG_M; | ||||
|   // set remote wakeup | ||||
|   USB0.dctl |= USB_RMTWKUPSIG_M; | ||||
|  | ||||
|   // enable SOF to detect bus resume | ||||
|   USB0.gintsts = USB_SOF_M; | ||||
|   USB0.gintmsk |= USB_SOFMSK_M; | ||||
|  | ||||
|   // Per specs: remote wakeup signal bit must be clear within 1-15ms | ||||
|   vTaskDelay(pdMS_TO_TICKS(1)); | ||||
|  | ||||
|   USB0.dctl &= ~USB_RMTWKUPSIG_M; | ||||
| } | ||||
|  | ||||
| // connect by enabling internal pull-up resistor on D+/D- | ||||
| @@ -260,9 +263,10 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_edpt) | ||||
|   xfer->max_size = desc_edpt->wMaxPacketSize.size; | ||||
|  | ||||
|   if (dir == TUSB_DIR_OUT) { | ||||
|     out_ep[epnum].doepctl |= USB_USBACTEP0_M | | ||||
|                              desc_edpt->bmAttributes.xfer << USB_EPTYPE0_S | | ||||
|                              desc_edpt->wMaxPacketSize.size << USB_MPS0_S; | ||||
|     out_ep[epnum].doepctl |= USB_USBACTEP1_M | | ||||
|                              desc_edpt->bmAttributes.xfer << USB_EPTYPE1_S | | ||||
|                              (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? USB_DO_SETD0PID1_M : 0) | | ||||
|                              desc_edpt->wMaxPacketSize.size << USB_MPS1_S; | ||||
|     USB0.daintmsk |= (1 << (16 + epnum)); | ||||
|   } else { | ||||
|     // "USB Data FIFOs" section in reference manual | ||||
| @@ -315,7 +319,25 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_edpt) | ||||
| void dcd_edpt_close_all(uint8_t rhport) | ||||
| { | ||||
|   (void) rhport; | ||||
|   // TODO implement dcd_edpt_close_all() | ||||
|  | ||||
|   usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]); | ||||
|   usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]); | ||||
|  | ||||
|   // Disable non-control interrupt | ||||
|   USB0.daintmsk = USB_OUTEPMSK0_M | USB_INEPMSK0_M; | ||||
|  | ||||
|   for(uint8_t n = 1; n < EP_MAX; n++) | ||||
|   { | ||||
|     // disable OUT endpoint | ||||
|     out_ep[n].doepctl = 0; | ||||
|     xfer_status[n][TUSB_DIR_OUT].max_size = 0; | ||||
|  | ||||
|     // disable IN endpoint | ||||
|     in_ep[n].diepctl = 0; | ||||
|     xfer_status[n][TUSB_DIR_IN].max_size = 0; | ||||
|   } | ||||
|  | ||||
|   _allocated_fifos = 1; | ||||
| } | ||||
|  | ||||
| bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) | ||||
| @@ -367,49 +389,6 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to | ||||
| bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes) | ||||
| { | ||||
|   (void)rhport; | ||||
|  | ||||
|   // USB buffers always work in bytes so to avoid unnecessary divisions we demand item_size = 1 | ||||
|   TU_ASSERT(ff->item_size == 1); | ||||
|  | ||||
|   uint8_t const epnum = tu_edpt_number(ep_addr); | ||||
|   uint8_t const dir   = tu_edpt_dir(ep_addr); | ||||
|  | ||||
|   xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir); | ||||
|   xfer->buffer       = NULL; | ||||
|   xfer->ff           = ff; | ||||
|   xfer->total_len    = total_bytes; | ||||
|   xfer->queued_len   = 0; | ||||
|   xfer->short_packet = false; | ||||
|  | ||||
|   uint16_t num_packets = (total_bytes / xfer->max_size); | ||||
|   uint8_t short_packet_size = total_bytes % xfer->max_size; | ||||
|  | ||||
|   // Zero-size packet is special case. | ||||
|   if (short_packet_size > 0 || (total_bytes == 0)) { | ||||
|     num_packets++; | ||||
|   } | ||||
|  | ||||
|   ESP_LOGV(TAG, "Transfer <-> EP%i, %s, pkgs: %i, bytes: %i", | ||||
|            epnum, ((dir == TUSB_DIR_IN) ? "USB0.HOST (in)" : "HOST->DEV (out)"), | ||||
|            num_packets, total_bytes); | ||||
|  | ||||
|   // IN and OUT endpoint xfers are interrupt-driven, we just schedule them | ||||
|   // here. | ||||
|   if (dir == TUSB_DIR_IN) { | ||||
|     // A full IN transfer (multiple packets, possibly) triggers XFRC. | ||||
|     USB0.in_ep_reg[epnum].dieptsiz = (num_packets << USB_D_PKTCNT0_S) | total_bytes; | ||||
|     USB0.in_ep_reg[epnum].diepctl |= USB_D_EPENA1_M | USB_D_CNAK1_M; // Enable | CNAK | ||||
|  | ||||
|     // Enable fifo empty interrupt only if there are something to put in the fifo. | ||||
|     if(total_bytes != 0) { | ||||
|       USB0.dtknqr4_fifoemptymsk |= (1 << epnum); | ||||
|     } | ||||
|   } else { | ||||
|     // Each complete packet for OUT xfers triggers XFRC. | ||||
|     USB0.out_ep_reg[epnum].doeptsiz |= USB_PKTCNT0_M | ((xfer->max_size & USB_XFERSIZE0_V) << USB_XFERSIZE0_S); | ||||
|     USB0.out_ep_reg[epnum].doepctl  |= USB_EPENA0_M | USB_CNAK0_M; | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| @@ -754,8 +733,8 @@ static void _dcd_int_handler(void* arg) | ||||
|   (void) arg; | ||||
|   uint8_t const rhport = 0; | ||||
|  | ||||
|   const uint32_t int_status = USB0.gintsts; | ||||
|   //const uint32_t int_msk = USB0.gintmsk; | ||||
|   const uint32_t int_msk = USB0.gintmsk; | ||||
|   const uint32_t int_status = USB0.gintsts & int_msk; | ||||
|  | ||||
|   if (int_status & USB_USBRST_M) { | ||||
|     // start of reset | ||||
| @@ -808,12 +787,15 @@ static void _dcd_int_handler(void* arg) | ||||
|     USB0.gotgint = otg_int; | ||||
|   } | ||||
|  | ||||
| #if USE_SOF | ||||
|   if (int_status & USB_SOF_M) { | ||||
|     USB0.gintsts = USB_SOF_M; | ||||
|     dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true); // do nothing actually | ||||
|  | ||||
|     // Disable SOF interrupt since currently only used for remote wakeup detection | ||||
|     USB0.gintmsk &= ~USB_SOFMSK_M; | ||||
|  | ||||
|     dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true); | ||||
|   } | ||||
| #endif | ||||
|  | ||||
|  | ||||
|   if (int_status & USB_RXFLVI_M) { | ||||
|     // RXFLVL bit is read-only | ||||
|   | ||||
| @@ -200,11 +200,10 @@ static void bus_reset(void) | ||||
| } | ||||
|  | ||||
| /* centralized location for USBD interrupt enable bit mask */ | ||||
| #if USE_SOF | ||||
| static const uint32_t enabled_irqs = USBD_INTSTS_VBDETIF_Msk | USBD_INTSTS_BUSIF_Msk | USBD_INTSTS_SETUP_Msk | USBD_INTSTS_USBIF_Msk | USBD_INTSTS_SOFIF_Msk; | ||||
| #else | ||||
| static const uint32_t enabled_irqs = USBD_INTSTS_VBDETIF_Msk | USBD_INTSTS_BUSIF_Msk | USBD_INTSTS_SETUP_Msk | USBD_INTSTS_USBIF_Msk; | ||||
| #endif | ||||
| enum { | ||||
|   ENABLED_IRQS = USBD_INTSTS_VBDETIF_Msk | USBD_INTSTS_BUSIF_Msk | USBD_INTSTS_SETUP_Msk | | ||||
|                  USBD_INTSTS_USBIF_Msk   | (USE_SOF ? USBD_INTSTS_SOFIF_Msk : 0) | ||||
| }; | ||||
|  | ||||
| /* | ||||
|   NUC121/NUC125/NUC126 TinyUSB API driver implementation | ||||
| @@ -226,8 +225,8 @@ void dcd_init(uint8_t rhport) | ||||
|  | ||||
|   usb_attach(); | ||||
|  | ||||
|   USBD->INTSTS = enabled_irqs; | ||||
|   USBD->INTEN  = enabled_irqs; | ||||
|   USBD->INTSTS = ENABLED_IRQS; | ||||
|   USBD->INTEN  = ENABLED_IRQS; | ||||
| } | ||||
|  | ||||
| void dcd_int_enable(uint8_t rhport) | ||||
| @@ -252,10 +251,23 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr) | ||||
|   // do it at dcd_edpt0_status_complete() | ||||
| } | ||||
|  | ||||
| static void remote_wakeup_delay(void) | ||||
| { | ||||
|   // try to delay for 1 ms | ||||
|   uint32_t count = SystemCoreClock / 1000; | ||||
|   while(count--) __NOP(); | ||||
| } | ||||
|  | ||||
| void dcd_remote_wakeup(uint8_t rhport) | ||||
| { | ||||
|   (void) rhport; | ||||
|   USBD->ATTR = USBD_ATTR_RWAKEUP_Msk; | ||||
|   // Enable PHY before sending Resume('K') state | ||||
|   USBD->ATTR |= USBD_ATTR_PHYEN_Msk; | ||||
|   USBD->ATTR |= USBD_ATTR_RWAKEUP_Msk; | ||||
|  | ||||
|   // Per specs: remote wakeup signal bit must be clear within 1-15ms | ||||
|   remote_wakeup_delay(); | ||||
|   USBD->ATTR &=~USBD_ATTR_RWAKEUP_Msk; | ||||
| } | ||||
|  | ||||
| bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) | ||||
| @@ -367,14 +379,16 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) | ||||
| { | ||||
|   (void) rhport; | ||||
|   USBD_EP_T *ep = ep_entry(ep_addr, false); | ||||
|   ep->CFG |= USBD_CFG_CSTALL_Msk; | ||||
|   ep->CFG = (ep->CFG & ~USBD_CFG_DSQSYNC_Msk) | USBD_CFG_CSTALL_Msk; | ||||
| } | ||||
|  | ||||
| void dcd_int_handler(uint8_t rhport) | ||||
| { | ||||
|   (void) rhport; | ||||
|  | ||||
|   uint32_t status = USBD->INTSTS; | ||||
|   // Mask non-enabled irqs, ex. SOF | ||||
|   uint32_t status = USBD->INTSTS & (ENABLED_IRQS | 0xffffff00); | ||||
|  | ||||
| #ifdef SUPPORT_LPM | ||||
|   uint32_t state = USBD->ATTR & 0x300f; | ||||
| #else | ||||
| @@ -505,7 +519,7 @@ void dcd_int_handler(uint8_t rhport) | ||||
|   } | ||||
|  | ||||
|   /* acknowledge all interrupts */ | ||||
|   USBD->INTSTS = status & enabled_irqs; | ||||
|   USBD->INTSTS = status & ENABLED_IRQS; | ||||
| } | ||||
|  | ||||
| // Invoked when a control transfer's status stage is complete. | ||||
|   | ||||
| @@ -57,11 +57,15 @@ | ||||
| // ENDPTCTRL | ||||
| enum { | ||||
|   ENDPTCTRL_STALL          = TU_BIT(0), | ||||
|   ENDPTCTRL_TOGGLE_INHIBIT = TU_BIT(5), ///< used for test only | ||||
|   ENDPTCTRL_TOGGLE_INHIBIT = TU_BIT(5), // used for test only | ||||
|   ENDPTCTRL_TOGGLE_RESET   = TU_BIT(6), | ||||
|   ENDPTCTRL_ENABLE         = TU_BIT(7) | ||||
| }; | ||||
|  | ||||
| enum { | ||||
|   ENDPTCTRL_TYPE_POS  = 2, // Endpoint type is 2-bit field | ||||
| }; | ||||
|  | ||||
| // USBSTS, USBINTR | ||||
| enum { | ||||
|   INTR_USB         = TU_BIT(0), | ||||
| @@ -91,12 +95,15 @@ typedef struct | ||||
|   uint32_t                      : 3  ; | ||||
|   uint32_t int_on_complete      : 1  ; | ||||
|   volatile uint32_t total_bytes : 15 ; | ||||
|   uint32_t                      : 0  ; | ||||
|   uint32_t                      : 1  ; | ||||
|  | ||||
|   // Word 2-6: Buffer Page Pointer List, Each element in the list is a 4K page aligned, physical memory address. The lower 12 bits in each pointer are reserved (except for the first one) as each memory pointer must reference the start of a 4K page | ||||
|   uint32_t buffer[5]; ///< buffer1 has frame_n for TODO Isochronous | ||||
|  | ||||
|   //------------- DCD Area -------------// | ||||
|   //--------------------------------------------------------------------+ | ||||
|   // TD is 32 bytes aligned but occupies only 28 bytes | ||||
|   // Therefore there are 4 bytes padding that we can use. | ||||
|   //--------------------------------------------------------------------+ | ||||
|   uint16_t expected_bytes; | ||||
|   uint8_t reserved[2]; | ||||
| } dcd_qtd_t; | ||||
| @@ -109,11 +116,10 @@ typedef struct | ||||
|   // Word 0: Capabilities and Characteristics | ||||
|   uint32_t                         : 15 ; ///< Number of packets executed per transaction descriptor 00 - Execute N transactions as demonstrated by the USB variable length protocol where N is computed using Max_packet_length and the Total_bytes field in the dTD. 01 - Execute one transaction 10 - Execute two transactions 11 - Execute three transactions Remark: Non-isochronous endpoints must set MULT = 00. Remark: Isochronous endpoints must set MULT = 01, 10, or 11 as needed. | ||||
|   uint32_t int_on_setup            : 1  ; ///< Interrupt on setup This bit is used on control type endpoints to indicate if USBINT is set in response to a setup being received. | ||||
|   uint32_t max_package_size        : 11 ; ///< This directly corresponds to the maximum packet size of the associated endpoint (wMaxPacketSize) | ||||
|   uint32_t max_packet_size         : 11 ; ///< Endpoint's wMaxPacketSize | ||||
|   uint32_t                         : 2  ; | ||||
|   uint32_t zero_length_termination : 1  ; ///< This bit is used for non-isochronous endpoints to indicate when a zero-length packet is received to terminate transfers in case the total transfer length is “multiple”. 0 - Enable zero-length packet to terminate transfers equal to a multiple of Max_packet_length (default). 1 - Disable zero-length packet on transfers that are equal in length to a multiple Max_packet_length. | ||||
|   uint32_t iso_mult                : 2  ; ///< | ||||
|   uint32_t                         : 0  ; | ||||
|  | ||||
|   // Word 1: Current qTD Pointer | ||||
|   volatile uint32_t qtd_addr; | ||||
| @@ -125,10 +131,11 @@ typedef struct | ||||
|   volatile tusb_control_request_t setup_request; | ||||
|  | ||||
|   //--------------------------------------------------------------------+ | ||||
|   /// Due to the fact QHD is 64 bytes aligned but occupies only 48 bytes | ||||
|   /// thus there are 16 bytes padding free that we can make use of. | ||||
|   // QHD is 64 bytes aligned but occupies only 48 bytes | ||||
|   // Therefore there are 16 bytes padding that we can use. | ||||
|   //--------------------------------------------------------------------+ | ||||
|   uint8_t reserved[16]; | ||||
|   tu_fifo_t * ff; | ||||
|   uint8_t reserved[12]; | ||||
| } dcd_qhd_t; | ||||
|  | ||||
| TU_VERIFY_STATIC( sizeof(dcd_qhd_t) == 64, "size is not correct"); | ||||
| @@ -145,10 +152,6 @@ typedef struct | ||||
| }dcd_controller_t; | ||||
|  | ||||
| #if CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX | ||||
|   // Each endpoint with direction (IN/OUT) occupies a queue head | ||||
|   // Therefore QHD_MAX is 2 x max endpoint count | ||||
|   #define QHD_MAX  (8*2) | ||||
|  | ||||
|   static const dcd_controller_t _dcd_controller[] = | ||||
|   { | ||||
|     // RT1010 and RT1020 only has 1 USB controller | ||||
| @@ -161,8 +164,6 @@ typedef struct | ||||
|   }; | ||||
|  | ||||
| #else | ||||
|   #define QHD_MAX (6*2) | ||||
|  | ||||
|   static const dcd_controller_t _dcd_controller[] = | ||||
|   { | ||||
|     { .regs = (dcd_registers_t*) LPC_USB0_BASE, .irqnum = USB0_IRQn, .ep_count = 6 }, | ||||
| @@ -174,8 +175,10 @@ typedef struct | ||||
|  | ||||
| typedef struct { | ||||
|   // Must be at 2K alignment | ||||
|   dcd_qhd_t qhd[QHD_MAX] TU_ATTR_ALIGNED(64); | ||||
|   dcd_qtd_t qtd[QHD_MAX] TU_ATTR_ALIGNED(32); // for portability, TinyUSB only queue 1 TD for each Qhd | ||||
|   // Each endpoint with direction (IN/OUT) occupies a queue head | ||||
|   // for portability, TinyUSB only queue 1 TD for each Qhd | ||||
|   dcd_qhd_t qhd[DCD_ATTR_ENDPOINT_MAX][2] TU_ATTR_ALIGNED(64); | ||||
|   dcd_qtd_t qtd[DCD_ATTR_ENDPOINT_MAX][2] TU_ATTR_ALIGNED(32); | ||||
| }dcd_data_t; | ||||
|  | ||||
| CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(2048) | ||||
| @@ -195,9 +198,9 @@ static void bus_reset(uint8_t rhport) | ||||
|   // endpoint type of the unused direction must be changed from the control type to any other | ||||
|   // type (e.g. bulk). Leaving an un-configured endpoint control will cause undefined behavior | ||||
|   // for the data PID tracking on the active endpoint. | ||||
|   for( int i=1; i < _dcd_controller[rhport].ep_count; i++) | ||||
|   for( uint8_t i=1; i < _dcd_controller[rhport].ep_count; i++) | ||||
|   { | ||||
|     dcd_reg->ENDPTCTRL[i] = (TUSB_XFER_BULK << 2) | (TUSB_XFER_BULK << 18); | ||||
|     dcd_reg->ENDPTCTRL[i] = (TUSB_XFER_BULK << ENDPTCTRL_TYPE_POS) | (TUSB_XFER_BULK << (16+ENDPTCTRL_TYPE_POS)); | ||||
|   } | ||||
|  | ||||
|   //------------- Clear All Registers -------------// | ||||
| @@ -217,11 +220,11 @@ static void bus_reset(uint8_t rhport) | ||||
|   tu_memclr(&_dcd_data, sizeof(dcd_data_t)); | ||||
|  | ||||
|   //------------- Set up Control Endpoints (0 OUT, 1 IN) -------------// | ||||
|   _dcd_data.qhd[0].zero_length_termination = _dcd_data.qhd[1].zero_length_termination = 1; | ||||
|   _dcd_data.qhd[0].max_package_size = _dcd_data.qhd[1].max_package_size = CFG_TUD_ENDPOINT0_SIZE; | ||||
|   _dcd_data.qhd[0].qtd_overlay.next = _dcd_data.qhd[1].qtd_overlay.next = QTD_NEXT_INVALID; | ||||
|   _dcd_data.qhd[0][0].zero_length_termination = _dcd_data.qhd[0][1].zero_length_termination = 1; | ||||
|   _dcd_data.qhd[0][0].max_packet_size  = _dcd_data.qhd[0][1].max_packet_size  = CFG_TUD_ENDPOINT0_SIZE; | ||||
|   _dcd_data.qhd[0][0].qtd_overlay.next = _dcd_data.qhd[0][1].qtd_overlay.next = QTD_NEXT_INVALID; | ||||
|  | ||||
|   _dcd_data.qhd[0].int_on_setup = 1; // OUT only | ||||
|   _dcd_data.qhd[0][0].int_on_setup = 1; // OUT only | ||||
| } | ||||
|  | ||||
| void dcd_init(uint8_t rhport) | ||||
| @@ -238,14 +241,15 @@ void dcd_init(uint8_t rhport) | ||||
|   dcd_reg->USBMODE = USBMODE_CM_DEVICE; | ||||
|   dcd_reg->OTGSC = OTGSC_VBUS_DISCHARGE | OTGSC_OTG_TERMINATION; | ||||
|  | ||||
|   // TODO Force fullspeed on non-highspeed port | ||||
|   // dcd_reg->PORTSC1 = PORTSC1_FORCE_FULL_SPEED; | ||||
| #if !TUD_OPT_HIGH_SPEED | ||||
|   dcd_reg->PORTSC1 = PORTSC1_FORCE_FULL_SPEED; | ||||
| #endif | ||||
|  | ||||
|   CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t)); | ||||
|  | ||||
|   dcd_reg->ENDPTLISTADDR = (uint32_t) _dcd_data.qhd; // Endpoint List Address has to be 2K alignment | ||||
|   dcd_reg->USBSTS  = dcd_reg->USBSTS; | ||||
|   dcd_reg->USBINTR = INTR_USB | INTR_ERROR | INTR_PORT_CHANGE | INTR_RESET | INTR_SUSPEND /*| INTR_SOF*/; | ||||
|   dcd_reg->USBINTR = INTR_USB | INTR_ERROR | INTR_PORT_CHANGE | INTR_SUSPEND; | ||||
|  | ||||
|   dcd_reg->USBCMD &= ~0x00FF0000;     // Interrupt Threshold Interval = 0 | ||||
|   dcd_reg->USBCMD |= USBCMD_RUN_STOP; // Connect | ||||
| @@ -272,7 +276,8 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr) | ||||
|  | ||||
| void dcd_remote_wakeup(uint8_t rhport) | ||||
| { | ||||
|   (void) rhport; | ||||
|   dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs; | ||||
|   dcd_reg->PORTSC1 |= PORTSC1_FORCE_PORT_RESUME; | ||||
| } | ||||
|  | ||||
| void dcd_connect(uint8_t rhport) | ||||
| @@ -290,26 +295,33 @@ void dcd_disconnect(uint8_t rhport) | ||||
| //--------------------------------------------------------------------+ | ||||
| // HELPER | ||||
| //--------------------------------------------------------------------+ | ||||
| // index to bit position in register | ||||
| static inline uint8_t ep_idx2bit(uint8_t ep_idx) | ||||
| { | ||||
|   return ep_idx/2 + ( (ep_idx%2) ? 16 : 0); | ||||
| } | ||||
|  | ||||
| static void qtd_init(dcd_qtd_t* p_qtd, void * data_ptr, uint16_t total_bytes) | ||||
| { | ||||
|   // Force the CPU to flush the buffer. We increase the size by 31 because the call aligns the | ||||
|   // address to 32-byte boundaries. Buffer must be word aligned | ||||
|   CleanInvalidateDCache_by_Addr((uint32_t*) tu_align((uint32_t) data_ptr, 4), total_bytes + 31); | ||||
|  | ||||
|   tu_memclr(p_qtd, sizeof(dcd_qtd_t)); | ||||
|  | ||||
|   p_qtd->next        = QTD_NEXT_INVALID; | ||||
|   p_qtd->active      = 1; | ||||
|   p_qtd->total_bytes = p_qtd->expected_bytes = total_bytes; | ||||
|   p_qtd->next            = QTD_NEXT_INVALID; | ||||
|   p_qtd->active          = 1; | ||||
|   p_qtd->total_bytes     = p_qtd->expected_bytes = total_bytes; | ||||
|   p_qtd->int_on_complete = true; | ||||
|  | ||||
|   if (data_ptr != NULL) | ||||
|   { | ||||
|     p_qtd->buffer[0]   = (uint32_t) data_ptr; | ||||
|     p_qtd->buffer[0] = (uint32_t) data_ptr; | ||||
|  | ||||
|     uint32_t const bufend = p_qtd->buffer[0] + total_bytes; | ||||
|     for(uint8_t i=1; i<5; i++) | ||||
|     { | ||||
|       p_qtd->buffer[i] |= tu_align4k( p_qtd->buffer[i-1] ) + 4096; | ||||
|       uint32_t const next_page = tu_align4k( p_qtd->buffer[i-1] ) + 4096; | ||||
|       if ( bufend <= next_page ) break; | ||||
|  | ||||
|       p_qtd->buffer[i] = next_page; | ||||
|  | ||||
|       // TODO page[1] FRAME_N for ISO transfer | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @@ -324,12 +336,15 @@ void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) | ||||
|  | ||||
|   dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs; | ||||
|   dcd_reg->ENDPTCTRL[epnum] |= ENDPTCTRL_STALL << (dir ? 16 : 0); | ||||
|  | ||||
|   // flush to abort any primed buffer | ||||
|   dcd_reg->ENDPTFLUSH = TU_BIT(epnum + (dir ? 16 : 0)); | ||||
| } | ||||
|  | ||||
| void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) | ||||
| { | ||||
|   uint8_t const epnum  = tu_edpt_number(ep_addr); | ||||
|   uint8_t const dir    = tu_edpt_dir(ep_addr); | ||||
|   uint8_t const epnum = tu_edpt_number(ep_addr); | ||||
|   uint8_t const dir   = tu_edpt_dir(ep_addr); | ||||
|  | ||||
|   // data toggle also need to be reset | ||||
|   dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs; | ||||
| @@ -339,45 +354,87 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) | ||||
|  | ||||
| bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) | ||||
| { | ||||
|   // TODO not support ISO yet | ||||
|   TU_VERIFY ( p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS); | ||||
|  | ||||
|   uint8_t const epnum  = tu_edpt_number(p_endpoint_desc->bEndpointAddress); | ||||
|   uint8_t const dir    = tu_edpt_dir(p_endpoint_desc->bEndpointAddress); | ||||
|   uint8_t const ep_idx = 2*epnum + dir; | ||||
|   uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress); | ||||
|   uint8_t const dir   = tu_edpt_dir(p_endpoint_desc->bEndpointAddress); | ||||
|  | ||||
|   // Must not exceed max endpoint number | ||||
|   TU_ASSERT( epnum < _dcd_controller[rhport].ep_count ); | ||||
|  | ||||
|   //------------- Prepare Queue Head -------------// | ||||
|   dcd_qhd_t * p_qhd = &_dcd_data.qhd[ep_idx]; | ||||
|   dcd_qhd_t * p_qhd = &_dcd_data.qhd[epnum][dir]; | ||||
|   tu_memclr(p_qhd, sizeof(dcd_qhd_t)); | ||||
|  | ||||
|   p_qhd->zero_length_termination = 1; | ||||
|   p_qhd->max_package_size        = p_endpoint_desc->wMaxPacketSize.size; | ||||
|   p_qhd->max_packet_size         = p_endpoint_desc->wMaxPacketSize.size; | ||||
|   if (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) | ||||
|   { | ||||
|     p_qhd->iso_mult = 1; | ||||
|   } | ||||
|  | ||||
|   p_qhd->qtd_overlay.next        = QTD_NEXT_INVALID; | ||||
|  | ||||
|   CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t)); | ||||
|  | ||||
|   // Enable EP Control | ||||
|   dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs; | ||||
|   dcd_reg->ENDPTCTRL[epnum] |= ((p_endpoint_desc->bmAttributes.xfer << 2) | ENDPTCTRL_ENABLE | ENDPTCTRL_TOGGLE_RESET) << (dir ? 16 : 0); | ||||
|  | ||||
|   uint32_t const epctrl = (p_endpoint_desc->bmAttributes.xfer << ENDPTCTRL_TYPE_POS) | ENDPTCTRL_ENABLE | ENDPTCTRL_TOGGLE_RESET; | ||||
|  | ||||
|   if ( dir == TUSB_DIR_OUT ) | ||||
|   { | ||||
|     dcd_reg->ENDPTCTRL[epnum] = (dcd_reg->ENDPTCTRL[epnum] & 0xFFFF0000u) | epctrl; | ||||
|   }else | ||||
|   { | ||||
|     dcd_reg->ENDPTCTRL[epnum] = (dcd_reg->ENDPTCTRL[epnum] & 0x0000FFFFu) | (epctrl << 16); | ||||
|   } | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| void dcd_edpt_close_all (uint8_t rhport) | ||||
| { | ||||
|   (void) rhport; | ||||
|   // TODO implement dcd_edpt_close_all() | ||||
|   dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs; | ||||
|  | ||||
|   // Disable all non-control endpoints | ||||
|   for( uint8_t epnum=1; epnum < _dcd_controller[rhport].ep_count; epnum++) | ||||
|   { | ||||
|     _dcd_data.qhd[epnum][TUSB_DIR_OUT].qtd_overlay.halted = 1; | ||||
|     _dcd_data.qhd[epnum][TUSB_DIR_IN ].qtd_overlay.halted = 1; | ||||
|  | ||||
|     dcd_reg->ENDPTFLUSH = TU_BIT(epnum) |  TU_BIT(epnum+16); | ||||
|     dcd_reg->ENDPTCTRL[epnum] = (TUSB_XFER_BULK << ENDPTCTRL_TYPE_POS) | (TUSB_XFER_BULK << (16+ENDPTCTRL_TYPE_POS)); | ||||
|   } | ||||
| } | ||||
|  | ||||
| bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) | ||||
| void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) | ||||
| { | ||||
|   uint8_t const epnum  = tu_edpt_number(ep_addr); | ||||
|   uint8_t const dir    = tu_edpt_dir(ep_addr); | ||||
|  | ||||
|   dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs; | ||||
|  | ||||
|   _dcd_data.qhd[epnum][dir].qtd_overlay.halted = 1; | ||||
|  | ||||
|   // Flush EP | ||||
|   uint32_t const flush_mask = TU_BIT(epnum + (dir ? 16 : 0)); | ||||
|   dcd_reg->ENDPTFLUSH = flush_mask; | ||||
|   while(dcd_reg->ENDPTFLUSH & flush_mask); | ||||
|  | ||||
|   // Clear EP enable | ||||
|   dcd_reg->ENDPTCTRL[epnum] &=~(ENDPTCTRL_ENABLE << (dir ? 16 : 0)); | ||||
| } | ||||
|  | ||||
| static void qhd_start_xfer(uint8_t rhport, uint8_t epnum, uint8_t dir) | ||||
| { | ||||
|   dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs; | ||||
|   uint8_t const epnum = tu_edpt_number(ep_addr); | ||||
|   uint8_t const dir   = tu_edpt_dir(ep_addr); | ||||
|   uint8_t const ep_idx = 2*epnum + dir; | ||||
|   dcd_qhd_t* p_qhd = &_dcd_data.qhd[epnum][dir]; | ||||
|   dcd_qtd_t* p_qtd = &_dcd_data.qtd[epnum][dir]; | ||||
|  | ||||
|   p_qhd->qtd_overlay.halted = false;            // clear any previous error | ||||
|   p_qhd->qtd_overlay.next   = (uint32_t) p_qtd; // link qtd to qhd | ||||
|  | ||||
|   // flush cache | ||||
|   CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t)); | ||||
|  | ||||
|   if ( epnum == 0 ) | ||||
|   { | ||||
| @@ -386,23 +443,87 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t | ||||
|     while(dcd_reg->ENDPTSETUPSTAT & TU_BIT(0)) {} | ||||
|   } | ||||
|  | ||||
|   dcd_qhd_t * p_qhd = &_dcd_data.qhd[ep_idx]; | ||||
|   dcd_qtd_t * p_qtd = &_dcd_data.qtd[ep_idx]; | ||||
|  | ||||
|   // Force the CPU to flush the buffer. We increase the size by 32 because the call aligns the | ||||
|   // address to 32-byte boundaries. | ||||
|   // void* cast to suppress cast-align warning, buffer must be | ||||
|   CleanInvalidateDCache_by_Addr((uint32_t*) tu_align((uint32_t) buffer, 4), total_bytes + 31); | ||||
|  | ||||
|   //------------- Prepare qtd -------------// | ||||
|   qtd_init(p_qtd, buffer, total_bytes); | ||||
|   p_qtd->int_on_complete = true; | ||||
|   p_qhd->qtd_overlay.next = (uint32_t) p_qtd; // link qtd to qhd | ||||
|  | ||||
|   CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t)); | ||||
|  | ||||
|   // start transfer | ||||
|   dcd_reg->ENDPTPRIME = TU_BIT( ep_idx2bit(ep_idx) ) ; | ||||
|   dcd_reg->ENDPTPRIME = TU_BIT(epnum + (dir ? 16 : 0)); | ||||
| } | ||||
|  | ||||
| bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) | ||||
| { | ||||
|   uint8_t const epnum = tu_edpt_number(ep_addr); | ||||
|   uint8_t const dir   = tu_edpt_dir(ep_addr); | ||||
|  | ||||
|   dcd_qhd_t* p_qhd = &_dcd_data.qhd[epnum][dir]; | ||||
|   dcd_qtd_t* p_qtd = &_dcd_data.qtd[epnum][dir]; | ||||
|  | ||||
|   // Prepare qtd | ||||
|   qtd_init(p_qtd, buffer, total_bytes); | ||||
|  | ||||
|   // Start qhd transfer | ||||
|   p_qhd->ff = NULL; | ||||
|   qhd_start_xfer(rhport, epnum, dir); | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| // fifo has to be aligned to 4k boundary | ||||
| bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes) | ||||
| { | ||||
|   uint8_t const epnum = tu_edpt_number(ep_addr); | ||||
|   uint8_t const dir   = tu_edpt_dir(ep_addr); | ||||
|  | ||||
|   dcd_qhd_t * p_qhd = &_dcd_data.qhd[epnum][dir]; | ||||
|   dcd_qtd_t * p_qtd = &_dcd_data.qtd[epnum][dir]; | ||||
|  | ||||
|   tu_fifo_buffer_info_t fifo_info; | ||||
|  | ||||
|   if (dir) | ||||
|   { | ||||
|     tu_fifo_get_read_info(ff, &fifo_info); | ||||
|   } else | ||||
|   { | ||||
|     tu_fifo_get_write_info(ff, &fifo_info); | ||||
|   } | ||||
|  | ||||
|   if ( fifo_info.len_lin >= total_bytes ) | ||||
|   { | ||||
|     // Linear length is enough for this transfer | ||||
|     qtd_init(p_qtd, fifo_info.ptr_lin, total_bytes); | ||||
|   } | ||||
|   else | ||||
|   { | ||||
|     // linear part is not enough | ||||
|  | ||||
|     // prepare TD up to linear length | ||||
|     qtd_init(p_qtd, fifo_info.ptr_lin, fifo_info.len_lin); | ||||
|  | ||||
|     if ( !tu_offset4k((uint32_t) fifo_info.ptr_wrap) && !tu_offset4k(tu_fifo_depth(ff)) ) | ||||
|     { | ||||
|       // If buffer is aligned to 4K & buffer size is multiple of 4K | ||||
|       // We can make use of buffer page array to also combine the linear + wrapped length | ||||
|       p_qtd->total_bytes = p_qtd->expected_bytes = total_bytes; | ||||
|  | ||||
|       for(uint8_t i = 1, page = 0; i < 5; i++) | ||||
|       { | ||||
|         // pick up buffer array where linear ends | ||||
|         if (p_qtd->buffer[i] == 0) | ||||
|         { | ||||
|           p_qtd->buffer[i] = (uint32_t) fifo_info.ptr_wrap + 4096 * page; | ||||
|           page++; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       CleanInvalidateDCache_by_Addr((uint32_t*) tu_align((uint32_t) fifo_info.ptr_wrap, 4), total_bytes - fifo_info.len_wrap + 31); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|       // TODO we may need to carry the wrapped length after the linear part complete | ||||
|       // for now only transfer up to linear part | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Start qhd transfer | ||||
|   p_qhd->ff = ff; | ||||
|   qhd_start_xfer(rhport, epnum, dir); | ||||
|  | ||||
|   return true; | ||||
| } | ||||
| @@ -410,9 +531,42 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t | ||||
| //--------------------------------------------------------------------+ | ||||
| // ISR | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| static void process_edpt_complete_isr(uint8_t rhport, uint8_t epnum, uint8_t dir) | ||||
| { | ||||
|   dcd_qhd_t * p_qhd = &_dcd_data.qhd[epnum][dir]; | ||||
|   dcd_qtd_t * p_qtd = &_dcd_data.qtd[epnum][dir]; | ||||
|  | ||||
|   uint8_t result = p_qtd->halted ? XFER_RESULT_STALLED : | ||||
|       ( p_qtd->xact_err || p_qtd->buffer_err ) ? XFER_RESULT_FAILED : XFER_RESULT_SUCCESS; | ||||
|  | ||||
|   if ( result != XFER_RESULT_SUCCESS ) | ||||
|   { | ||||
|     dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs; | ||||
|     // flush to abort error buffer | ||||
|     dcd_reg->ENDPTFLUSH = TU_BIT(epnum + (dir ? 16 : 0)); | ||||
|   } | ||||
|  | ||||
|   uint16_t const xferred_bytes = p_qtd->expected_bytes - p_qtd->total_bytes; | ||||
|  | ||||
|   if (p_qhd->ff) | ||||
|   { | ||||
|     if (dir == TUSB_DIR_IN) | ||||
|     { | ||||
|       tu_fifo_advance_read_pointer(p_qhd->ff, xferred_bytes); | ||||
|     } else | ||||
|     { | ||||
|       tu_fifo_advance_write_pointer(p_qhd->ff, xferred_bytes); | ||||
|     } | ||||
|   } | ||||
|    | ||||
|   // only number of bytes in the IOC qtd | ||||
|   dcd_event_xfer_complete(rhport, tu_edpt_addr(epnum, dir), xferred_bytes, result, true); | ||||
| } | ||||
|  | ||||
| void dcd_int_handler(uint8_t rhport) | ||||
| { | ||||
|   dcd_registers_t* const dcd_reg = _dcd_controller[rhport].regs; | ||||
|   dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs; | ||||
|  | ||||
|   uint32_t const int_enable = dcd_reg->USBINTR; | ||||
|   uint32_t const int_status = dcd_reg->USBSTS & int_enable; | ||||
| @@ -421,18 +575,46 @@ void dcd_int_handler(uint8_t rhport) | ||||
|   // disabled interrupt sources | ||||
|   if (int_status == 0) return; | ||||
|  | ||||
|   if (int_status & INTR_RESET) | ||||
|   { | ||||
|     bus_reset(rhport); | ||||
|     uint32_t speed = (dcd_reg->PORTSC1 & PORTSC1_PORT_SPEED) >> PORTSC1_PORT_SPEED_POS; | ||||
|     dcd_event_bus_reset(rhport, (tusb_speed_t) speed, true); | ||||
|   } | ||||
|   // Set if the port controller enters the full or high-speed operational state. | ||||
|   // either from Bus Reset or Suspended state | ||||
| 	if (int_status & INTR_PORT_CHANGE) | ||||
| 	{ | ||||
| 	  // TU_LOG2("PortChange %08lx\r\n", dcd_reg->PORTSC1); | ||||
|  | ||||
| 	  // Reset interrupt is not enabled, we manually check if Port Change is due | ||||
| 	  // to connection / disconnection | ||||
| 	  if ( dcd_reg->USBSTS & INTR_RESET ) | ||||
| 	  { | ||||
| 	    dcd_reg->USBSTS = INTR_RESET; | ||||
|  | ||||
| 	    if (dcd_reg->PORTSC1 & PORTSC1_CURRENT_CONNECT_STATUS) | ||||
| 	    { | ||||
| 	      uint32_t const speed = (dcd_reg->PORTSC1 & PORTSC1_PORT_SPEED) >> PORTSC1_PORT_SPEED_POS; | ||||
| 	      bus_reset(rhport); | ||||
| 	      dcd_event_bus_reset(rhport, (tusb_speed_t) speed, true); | ||||
| 	    }else | ||||
| 	    { | ||||
| 	      dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true); | ||||
| 	    } | ||||
| 	  } | ||||
| 	  else | ||||
| 	  { | ||||
| 	    // Triggered by resuming from suspended state | ||||
| 	    if ( !(dcd_reg->PORTSC1 & PORTSC1_SUSPEND) ) | ||||
| 	    { | ||||
| 	      dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true); | ||||
| 	    } | ||||
| 	  } | ||||
| 	} | ||||
|  | ||||
|   if (int_status & INTR_SUSPEND) | ||||
|   { | ||||
|     // TU_LOG2("Suspend %08lx\r\n", dcd_reg->PORTSC1); | ||||
|  | ||||
|     if (dcd_reg->PORTSC1 & PORTSC1_SUSPEND) | ||||
|     { | ||||
|       // Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration. | ||||
|       // Skip suspend event if we are not addressed | ||||
|       if ((dcd_reg->DEVICEADDR >> 25) & 0x0f) | ||||
|       { | ||||
|         dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true); | ||||
| @@ -440,21 +622,11 @@ void dcd_int_handler(uint8_t rhport) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Make sure we read the latest version of _dcd_data. | ||||
|   CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t)); | ||||
|  | ||||
|   // TODO disconnection does not generate interrupt !!!!!! | ||||
| //	if (int_status & INTR_PORT_CHANGE) | ||||
| //	{ | ||||
| //	  if ( !(dcd_reg->PORTSC1 & PORTSC1_CURRENT_CONNECT_STATUS) ) | ||||
| //	  { | ||||
| //      dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_UNPLUGGED }; | ||||
| //      dcd_event_handler(&event, true); | ||||
| //	  } | ||||
| //	} | ||||
|  | ||||
|   if (int_status & INTR_USB) | ||||
|   { | ||||
|     // Make sure we read the latest version of _dcd_data. | ||||
|     CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t)); | ||||
|  | ||||
|     uint32_t const edpt_complete = dcd_reg->ENDPTCOMPLETE; | ||||
|     dcd_reg->ENDPTCOMPLETE = edpt_complete; // acknowledge | ||||
|  | ||||
| @@ -462,26 +634,21 @@ void dcd_int_handler(uint8_t rhport) | ||||
|     { | ||||
|       //------------- Set up Received -------------// | ||||
|       // 23.10.10.2 Operational model for setup transfers | ||||
|       dcd_reg->ENDPTSETUPSTAT = dcd_reg->ENDPTSETUPSTAT;// acknowledge | ||||
|       dcd_reg->ENDPTSETUPSTAT = dcd_reg->ENDPTSETUPSTAT; | ||||
|  | ||||
|       dcd_event_setup_received(rhport, (uint8_t*) &_dcd_data.qhd[0].setup_request, true); | ||||
|       dcd_event_setup_received(rhport, (uint8_t*) &_dcd_data.qhd[0][0].setup_request, true); | ||||
|     } | ||||
|  | ||||
|     // 23.10.12.3 Failed QTD also get ENDPTCOMPLETE set | ||||
|     // nothing to do, we will submit xfer as error to usbd | ||||
|     // if (int_status & INTR_ERROR) { } | ||||
|  | ||||
|     if ( edpt_complete ) | ||||
|     { | ||||
|       for(uint8_t ep_idx = 0; ep_idx < QHD_MAX; ep_idx++) | ||||
|       for(uint8_t epnum = 0; epnum < DCD_ATTR_ENDPOINT_MAX; epnum++) | ||||
|       { | ||||
|         if ( tu_bit_test(edpt_complete, ep_idx2bit(ep_idx)) ) | ||||
|         { | ||||
|           // 23.10.12.3 Failed QTD also get ENDPTCOMPLETE set | ||||
|           dcd_qtd_t * p_qtd = &_dcd_data.qtd[ep_idx]; | ||||
|  | ||||
|           uint8_t result = p_qtd->halted  ? XFER_RESULT_STALLED : | ||||
|               ( p_qtd->xact_err ||p_qtd->buffer_err ) ? XFER_RESULT_FAILED : XFER_RESULT_SUCCESS; | ||||
|  | ||||
|           uint8_t const ep_addr = (ep_idx/2) | ( (ep_idx & 0x01) ? TUSB_DIR_IN_MASK : 0 ); | ||||
|           dcd_event_xfer_complete(rhport, ep_addr, p_qtd->expected_bytes - p_qtd->total_bytes, result, true); // only number of bytes in the IOC qtd | ||||
|         } | ||||
|         if ( tu_bit_test(edpt_complete, epnum)    ) process_edpt_complete_isr(rhport, epnum, TUSB_DIR_OUT); | ||||
|         if ( tu_bit_test(edpt_complete, epnum+16) ) process_edpt_complete_isr(rhport, epnum, TUSB_DIR_IN); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| @@ -490,9 +657,6 @@ void dcd_int_handler(uint8_t rhport) | ||||
|   { | ||||
|     dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true); | ||||
|   } | ||||
|  | ||||
|   if (int_status & INTR_NAK) {} | ||||
|   if (int_status & INTR_ERROR) TU_ASSERT(false, ); | ||||
| } | ||||
|  | ||||
| #endif | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 hathach
					hathach