diff options
Diffstat (limited to 'src/portable')
| -rw-r--r-- | src/portable/st/synopsys/dcd_synopsys.c | 384 |
1 files changed, 331 insertions, 53 deletions
diff --git a/src/portable/st/synopsys/dcd_synopsys.c b/src/portable/st/synopsys/dcd_synopsys.c index cc7181c3d..5a89bb386 100644 --- a/src/portable/st/synopsys/dcd_synopsys.c +++ b/src/portable/st/synopsys/dcd_synopsys.c @@ -4,6 +4,7 @@ * Copyright (c) 2018 Scott Shawcroft, 2019 William D. Jones for Adafruit Industries * Copyright (c) 2019 Ha Thach (tinyusb.org) * Copyright (c) 2020 Jan Duempelmann + * Copyright (c) 2020 Reinhard Panhuber * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -135,8 +136,18 @@ typedef struct { uint8_t * buffer; uint16_t total_len; uint16_t max_size; + uint8_t interval; } xfer_ctl_t; +// EP size and transfer type report +typedef struct TU_ATTR_PACKED { + // The following format may look complicated but it is the most elegant way of addressing the required fields: EP number, EP direction, and EP transfer type. + // The codes assigned to those fields, according to the USB specification, can be neatly used as indices. + uint16_t ep_size[EP_MAX][2]; ///< dim 1: EP number, dim 2: EP direction denoted by TUSB_DIR_OUT (= 0) and TUSB_DIR_IN (= 1) + bool ep_transfer_type[EP_MAX][2][4]; ///< dim 1: EP number, dim 2: EP direction, dim 3: transfer type, where 0 = Control, 1 = Isochronous, 2 = Bulk, and 3 = Interrupt + ///< I know very well that EP0 can only be used as control EP and we waste space here but for the sake of simplicity we accept that. It is used in a non-persistent way anyway! +} ep_sz_tt_report_t; + typedef volatile uint32_t * usb_fifo_t; xfer_ctl_t xfer_status[EP_MAX][2]; @@ -156,7 +167,7 @@ static void bus_reset(uint8_t rhport) USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport); USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport); USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE(rhport); - USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE(rhport); + // USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE(rhport); tu_memclr(xfer_status, sizeof(xfer_status)); @@ -206,28 +217,93 @@ static void bus_reset(uint8_t rhport) // are enabled at least "2 x (Largest-EPsize/4) + 1" are recommended. Maybe provide a macro for application to // overwrite this. -#if TUD_OPT_HIGH_SPEED - _allocated_fifo_words = 271 + 2*EP_MAX; -#else - _allocated_fifo_words = 47 + 2*EP_MAX; -#endif + // We rework this here and initialize the FIFOs here only for the USB reset case. The rest is done once a + // configuration was set from the host. For this initialization phase we use 64 bytes as FIFO size. - usb_otg->GRXFSIZ = _allocated_fifo_words; + // Found by trial: 10 + 2 + CFG_TUD_ENDPOINT0_SIZE/4 + 1 + 6 - not quite sure where 1 + 6 comes from but this works for 8/16/32/64 EP0 size + _allocated_fifo_words = 10 + 2 + CFG_TUD_ENDPOINT0_SIZE/4 + 1 + 6; // 64 bytes max packet size + 2 words (for the status of the control OUT data packet) + 10 words (for setup packets) - // Control IN uses FIFO 0 with 64 bytes ( 16 32-bit word ) - usb_otg->DIEPTXF0_HNPTXFSIZ = (16 << USB_OTG_TX0FD_Pos) | _allocated_fifo_words; + // _allocated_fifo_words = 47 + 2*EP_MAX; // 64 bytes max packet size + 2 words (for the status of the control OUT data packet) + 10 words (for setup packets) - _allocated_fifo_words += 16; + usb_otg->GRXFSIZ = _allocated_fifo_words; - // TU_LOG2_INT(_allocated_fifo_words); + // Control IN uses FIFO 0 with 64 bytes ( 16 32-bit word ) + usb_otg->DIEPTXF0_HNPTXFSIZ = (CFG_TUD_ENDPOINT0_SIZE/4 << USB_OTG_TX0FD_Pos) | _allocated_fifo_words; - // Fixed control EP0 size to 64 bytes - in_ep[0].DIEPCTL &= ~(0x03 << USB_OTG_DIEPCTL_MPSIZ_Pos); - xfer_status[0][TUSB_DIR_OUT].max_size = xfer_status[0][TUSB_DIR_IN].max_size = 64; + _allocated_fifo_words += CFG_TUD_ENDPOINT0_SIZE/4; + // Set SETUP packet count to 3 out_ep[0].DOEPTSIZ |= (3 << USB_OTG_DOEPTSIZ_STUPCNT_Pos); usb_otg->GINTMSK |= USB_OTG_GINTMSK_OEPINT | USB_OTG_GINTMSK_IEPINT; + + + //#if TUD_OPT_HIGH_SPEED + // _allocated_fifo_words = 271 + 2*EP_MAX; + //#else + // _allocated_fifo_words = 47 + 2*EP_MAX; + //#endif + // + // usb_otg->GRXFSIZ = _allocated_fifo_words; + // + // // Control IN uses FIFO 0 with 64 bytes ( 16 32-bit word ) + // usb_otg->DIEPTXF0_HNPTXFSIZ = (16 << USB_OTG_TX0FD_Pos) | _allocated_fifo_words; + // + // _allocated_fifo_words += 16; + // + // // TU_LOG2_INT(_allocated_fifo_words); + // + // // Fixed control EP0 size to 64 bytes + // in_ep[0].DIEPCTL &= ~(0x03 << USB_OTG_DIEPCTL_MPSIZ_Pos); + // xfer_status[0][TUSB_DIR_OUT].max_size = xfer_status[0][TUSB_DIR_IN].max_size = 64; + // + // // Set SETUP packet count to 3 + // out_ep[0].DOEPTSIZ |= (3 << USB_OTG_DOEPTSIZ_STUPCNT_Pos); + // + // usb_otg->GINTMSK |= USB_OTG_GINTMSK_OEPINT | USB_OTG_GINTMSK_IEPINT; +} + +// Required after new configuration received in case EP0 max packet size has changed +static void set_EP0_max_pkt_size(void) +{ + USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport); + USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE(rhport); + + uint32_t enum_spd = (dev->DSTS & USB_OTG_DSTS_ENUMSPD_Msk) >> USB_OTG_DSTS_ENUMSPD_Pos; + + // Maximum packet size for EP 0 is set for both directions by writing DIEPCTL. + switch (enum_spd) + { + case 0x00: // High speed - always 64 byte + in_ep[0].DIEPCTL &= ~(0x03 << USB_OTG_DIEPCTL_MPSIZ_Pos); + xfer_status[0][TUSB_DIR_OUT].max_size = xfer_status[0][TUSB_DIR_IN].max_size = 64; + break; + + case 0x03: // Full speed +#if CFG_TUD_ENDPOINT0_SIZE == 64 + in_ep[0].DIEPCTL &= ~(0x03 << USB_OTG_DIEPCTL_MPSIZ_Pos); + xfer_status[0][TUSB_DIR_OUT].max_size = xfer_status[0][TUSB_DIR_IN].max_size = 64; +#elif CFG_TUD_ENDPOINT0_SIZE == 32 + in_ep[0].DIEPCTL &= ~(0x03 << USB_OTG_DIEPCTL_MPSIZ_Pos); + in_ep[0].DIEPCTL |= (0x01 << USB_OTG_DIEPCTL_MPSIZ_Pos); + xfer_status[0][TUSB_DIR_OUT].max_size = xfer_status[0][TUSB_DIR_IN].max_size = 32; +#elif CFG_TUD_ENDPOINT0_SIZE == 16 + in_ep[0].DIEPCTL &= ~(0x03 << USB_OTG_DIEPCTL_MPSIZ_Pos); + in_ep[0].DIEPCTL |= (0x02 << USB_OTG_DIEPCTL_MPSIZ_Pos); + xfer_status[0][TUSB_DIR_OUT].max_size = xfer_status[0][TUSB_DIR_IN].max_size = 16; +#elif CFG_TUD_ENDPOINT0_SIZE == 8 + in_ep[0].DIEPCTL |= (0x03 << USB_OTG_DIEPCTL_MPSIZ_Pos); + xfer_status[0][TUSB_DIR_OUT].max_size = xfer_status[0][TUSB_DIR_IN].max_size = 8; +#else +# error CFG_TUD_ENDPOINT0_SIZE MUST be 8, 16, 32, or 64! +#endif + break; + + default: // Low speed - always 8 bytes + in_ep[0].DIEPCTL |= (0x03 << USB_OTG_DIEPCTL_MPSIZ_Pos); + xfer_status[0][TUSB_DIR_OUT].max_size = 8; + xfer_status[0][TUSB_DIR_IN].max_size = 8; + } } // Set turn-around timeout according to link speed @@ -300,7 +376,6 @@ static void set_speed(uint8_t rhport, tusb_speed_t speed) dev->DCFG |= (bitvalue << USB_OTG_DCFG_DSPD_Pos); } - #if defined(USB_HS_PHYC) static bool USB_HS_PHYCInit(void) { @@ -366,7 +441,7 @@ static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t c in_ep[epnum].DIEPCTL |= USB_OTG_DIEPCTL_EPENA | USB_OTG_DIEPCTL_CNAK; // For ISO endpoint set correct odd/even bit for next frame. - if ((in_ep[epnum].DIEPCTL & USB_OTG_DIEPCTL_EPTYP) == USB_OTG_DIEPCTL_EPTYP_0) + if ((in_ep[epnum].DIEPCTL & USB_OTG_DIEPCTL_EPTYP) == USB_OTG_DIEPCTL_EPTYP_0 && (XFER_CTL_BASE(epnum, dir))->interval == 1) { // Take odd/even bit from frame counter. uint32_t const odd_frame_now = (dev->DSTS & (1u << USB_OTG_DSTS_FNSOF_Pos)); @@ -383,6 +458,12 @@ static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t c ((total_bytes << USB_OTG_DOEPTSIZ_XFRSIZ_Pos) & USB_OTG_DOEPTSIZ_XFRSIZ_Msk); out_ep[epnum].DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; + if ((out_ep[epnum].DOEPCTL & USB_OTG_DOEPCTL_EPTYP) == USB_OTG_DOEPCTL_EPTYP_0 && (XFER_CTL_BASE(epnum, dir))->interval == 1) + { + // Take odd/even bit from frame counter. + uint32_t const odd_frame_now = (dev->DSTS & (1u << USB_OTG_DSTS_FNSOF_Pos)); + out_ep[epnum].DOEPCTL |= (odd_frame_now ? USB_OTG_DOEPCTL_SD0PID_SEVNFRM_Msk : USB_OTG_DOEPCTL_SODDFRM_Msk); + } } } @@ -513,7 +594,7 @@ void dcd_disconnect(uint8_t rhport) bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) { - USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport); +// USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport); USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport); USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE(rhport); USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE(rhport); @@ -534,6 +615,7 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir); xfer->max_size = desc_edpt->wMaxPacketSize.size; + xfer->interval = desc_edpt->bInterval; if(dir == TUSB_DIR_OUT) { @@ -545,6 +627,8 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) } else { + // FIFO allocation done in dcd_alloc_mem_for_conf() + // "USB Data FIFOs" section in reference manual // Peripheral FIFO architecture // @@ -570,29 +654,29 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) // - Interrupt is EPSize // - Bulk/ISO is max(EPSize, remaining-fifo / non-opened-EPIN) - uint16_t const fifo_remaining = EP_FIFO_SIZE/4 - _allocated_fifo_words; - uint16_t fifo_size = desc_edpt->wMaxPacketSize.size / 4; - - if ( desc_edpt->bmAttributes.xfer != TUSB_XFER_INTERRUPT ) - { - uint8_t opened = 0; - for(uint8_t i = 0; i < EP_MAX; i++) - { - if ( (i != epnum) && (xfer_status[i][TUSB_DIR_IN].max_size > 0) ) opened++; - } - - // EP Size or equally divided of remaining whichever is larger - fifo_size = tu_max16(fifo_size, fifo_remaining / (EP_MAX - opened)); - } - - // FIFO overflows, we probably need a better allocating scheme - TU_ASSERT(fifo_size <= fifo_remaining); - - // DIEPTXF starts at FIFO #1. - // Both TXFD and TXSA are in unit of 32-bit words. - usb_otg->DIEPTXF[epnum - 1] = (fifo_size << USB_OTG_DIEPTXF_INEPTXFD_Pos) | _allocated_fifo_words; - - _allocated_fifo_words += fifo_size; +// uint16_t const fifo_remaining = EP_FIFO_SIZE/4 - _allocated_fifo_words; +// uint16_t fifo_size = desc_edpt->wMaxPacketSize.size / 4; +// +// if ( desc_edpt->bmAttributes.xfer != TUSB_XFER_INTERRUPT ) +// { +// uint8_t opened = 0; +// for(uint8_t i = 0; i < EP_MAX; i++) +// { +// if ( (i != epnum) && (xfer_status[i][TUSB_DIR_IN].max_size > 0) ) opened++; +// } +// +// // EP Size or equally divided of remaining whichever is larger +// fifo_size = tu_max16(fifo_size, fifo_remaining / (EP_MAX - opened)); +// } +// +// // FIFO overflows, we probably need a better allocating scheme +// TU_ASSERT(fifo_size <= fifo_remaining); +// +// // DIEPTXF starts at FIFO #1. +// // Both TXFD and TXSA are in unit of 32-bit words. +// usb_otg->DIEPTXF[epnum - 1] = (fifo_size << USB_OTG_DIEPTXF_INEPTXFD_Pos) | _allocated_fifo_words; +// +// _allocated_fifo_words += fifo_size; in_ep[epnum].DIEPCTL |= (1 << USB_OTG_DIEPCTL_USBAEP_Pos) | (epnum << USB_OTG_DIEPCTL_TXFNUM_Pos) | @@ -696,20 +780,7 @@ static void dcd_edpt_disable (uint8_t rhport, uint8_t ep_addr, bool stall) */ void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr) { - USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport); - - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - dcd_edpt_disable(rhport, ep_addr, false); - if (dir == TUSB_DIR_IN) - { - uint16_t const fifo_size = (usb_otg->DIEPTXF[epnum - 1] & USB_OTG_DIEPTXF_INEPTXFD_Msk) >> USB_OTG_DIEPTXF_INEPTXFD_Pos; - uint16_t const fifo_start = (usb_otg->DIEPTXF[epnum - 1] & USB_OTG_DIEPTXF_INEPTXSA_Msk) >> USB_OTG_DIEPTXF_INEPTXSA_Pos; - // For now only endpoint that has FIFO at the end of FIFO memory can be closed without fuss. - TU_ASSERT(fifo_start + fifo_size == _allocated_fifo_words,); - _allocated_fifo_words -= fifo_size; - } } void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr) @@ -978,6 +1049,9 @@ void dcd_int_handler(uint8_t rhport) tusb_speed_t const speed = get_speed(rhport); set_turnaround(usb_otg, speed); + + set_EP0_max_pkt_size(); + dcd_event_bus_reset(rhport, speed, true); } @@ -1040,6 +1114,210 @@ void dcd_int_handler(uint8_t rhport) // IEPINT bit read-only handle_epin_ints(rhport, dev, in_ep); } + + // Check for Incomplete isochronous IN transfer + if(int_status & USB_OTG_GINTSTS_IISOIXFR) { + TU_LOG2(" IISOIXFR!\r\n"); + } +} + +// Helper function which parses through the current configuration descriptors to find the biggest EPs in size. +static bool get_ep_size_report(uint8_t rhport, tusb_desc_configuration_t const * desc_cfg, ep_sz_tt_report_t * p_report) +{ + (void) rhport; + + // tu_memclr(p_report, sizeof(ep_sz_tt_report_t)); // This does not initialize the first two entries ... i do not know why! + + // EP0 sizes and usages are fixed + p_report->ep_size[0][TUSB_DIR_OUT] = p_report->ep_size[0][TUSB_DIR_IN] = CFG_TUD_ENDPOINT0_SIZE; + p_report->ep_transfer_type[0][TUSB_DIR_OUT][TUSB_XFER_CONTROL] = p_report->ep_transfer_type[0][TUSB_DIR_IN][TUSB_XFER_CONTROL] = true; + + // Parse interface descriptor + uint8_t const * p_desc = ((uint8_t const*) desc_cfg) + sizeof(tusb_desc_configuration_t); + uint8_t const * desc_end = ((uint8_t const*) desc_cfg) + desc_cfg->wTotalLength; + + uint8_t addr; + + while( p_desc < desc_end ) + { + if (TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)) + { + + addr = ((tusb_desc_endpoint_t const*) p_desc)->bEndpointAddress; + + // Verify values - this checks may be omitted in case we trust the descriptors to be okay + TU_VERIFY(tu_edpt_number(addr) < EP_MAX); + TU_VERIFY(tu_edpt_dir(addr) <= TUSB_DIR_IN); + TU_VERIFY(((tusb_desc_endpoint_t const*) p_desc)->bmAttributes.xfer <= TUSB_XFER_INTERRUPT); + + p_report->ep_size[tu_edpt_number(addr)][tu_edpt_dir(addr)] = tu_max16(p_report->ep_size[tu_edpt_number(addr)][tu_edpt_dir(addr)], ((tusb_desc_endpoint_t const*) p_desc)->wMaxPacketSize.size); + p_report->ep_transfer_type[tu_edpt_number(addr)][tu_edpt_dir(addr)][((tusb_desc_endpoint_t const*) p_desc)->bmAttributes.xfer] = true; + } + p_desc = tu_desc_next(p_desc); // Proceed + } + return true; +} + +// Setup FIFO buffers at configuration time. +// The idea is to use this information such that the FIFOs need to be configured only once +// at configuration time and no more later. This makes it easy in case you want to close and open EPs for different +// purposes at any time (without taking care of other active EPs). You also avoid the nasty need of defragmenting +// the TX buffers, which is likely to happen. +// Certainly, this function does not allow for the highest possible flexibility as it only works in the worst case +// (all biggest EPs can be active at the same time). However, this should not be a problem for almost all applications. + +// Spare space is assigned equally divided to bulk and interrupt EPs. +// Pure isochronous EPs do not get any spare space as it does not make any sense. +TU_ATTR_WEAK bool dcd_alloc_mem_for_conf(uint8_t rhport, tusb_desc_configuration_t const * desc_cfg) +{ + (void) rhport; + + USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport); + USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport); + USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE(rhport); + + // for(uint8_t n = 0; n < EP_MAX; n++) { + // out_ep[n].DOEPCTL |= USB_OTG_DOEPCTL_SNAK; + // } + + out_ep[0].DOEPCTL |= USB_OTG_DOEPCTL_SNAK; + + // Deactivate Interrupts? + dev->DAINTMSK &= ~((1 << USB_OTG_DAINTMSK_OEPM_Pos) | (1 << USB_OTG_DAINTMSK_IEPM_Pos)); + dev->DOEPMSK &= ~(USB_OTG_DOEPMSK_STUPM | USB_OTG_DOEPMSK_XFRCM); + dev->DIEPMSK &= ~(USB_OTG_DIEPMSK_TOM | USB_OTG_DIEPMSK_XFRCM); + + // usb_otg->GINTMSK &= ~(USB_OTG_GINTMSK_OEPINT | USB_OTG_GINTMSK_IEPINT); + + // Determine maximum required spaces for individual EPs and what kind of usage (control, bulk, etc.) they are used for + ep_sz_tt_report_t report = {0}; // dim 1: EP number, dim 2: EP direction, dim 3: transfer type + TU_VERIFY(get_ep_size_report(rhport, desc_cfg, &report)); + + // With that information, set the following up: + // The RX buffer size (as it is a shared buffer here) is set to the sum of the two biggest out EPs plus all the required extra words used for setup packets etc. + // This should work well for all kinds of applications + + // Determine number of used out EPs of current configuration and size of two biggest out EPs + uint8_t nUsedOutEPs = 0, cnt_ep, cnt_tt; + uint16_t sz[2] = {0, 0}; + uint16_t fifo_depth; + + for (cnt_ep = 0; cnt_ep < EP_MAX; cnt_ep++) + { + for (cnt_tt = 0; cnt_tt <= TUSB_XFER_INTERRUPT; cnt_tt++) + { + if (report.ep_transfer_type[cnt_ep][TUSB_DIR_OUT][cnt_tt]) + { + nUsedOutEPs++; + break; + } + } + + fifo_depth = report.ep_size[cnt_ep][TUSB_DIR_OUT] / 4 + 1; + if (sz[0] < fifo_depth) + { + sz[1] = sz[0]; + sz[0] = fifo_depth; + } + else if (sz[1] < report.ep_size[cnt_ep][TUSB_DIR_OUT]) + { + sz[1] = fifo_depth; + } + } + + // For configuration use the approach as explained in bus_reset() + _allocated_fifo_words = 13 + 1 + 1 + 2 * nUsedOutEPs + sz[0] + sz[1] + 2; // again, i do not really know why we need + 2 but otherwise it does not work + + usb_otg->GRXFSIZ = _allocated_fifo_words; + + // Control IN uses FIFO 0 with report.ep_size[0][TUSB_DIR_IN] bytes ( report.ep_size[0][TUSB_DIR_IN]/4 32-bit word ) + fifo_depth = report.ep_size[0][TUSB_DIR_IN] / 4; + fifo_depth = tu_max16(16, fifo_depth); + usb_otg->DIEPTXF0_HNPTXFSIZ = (fifo_depth << USB_OTG_TX0FD_Pos) | _allocated_fifo_words; + + _allocated_fifo_words += fifo_depth; + + // For configuration of remaining in EPs use the approach as explained in dcd_edpt_open() except that: + // - ISO EPs only get EP size as FIFO size. More makes no sense since within one frame precisely EP size bytes are transfered and not more. + // Furthermore, double buffering is not possible (for this silicon) since once FIFO was written to and transmit bit was set you are + // not allowed to write to the FIFO any more until transmit was done. So you can not send something and buffer the next frame in the + // meantime into the buffer. TODO: check for high speed and uC types which can do this! + // - Interrupt EPs only get EP size as FIFO size + // - Bulk and control (other than EP0 - this is possible) get spare space equally divided - those profit the most from extra space + + // "USB Data FIFOs" section in reference manual + // Peripheral FIFO architecture + // + // --------------- 320 or 1024 ( 1280 or 4096 bytes ) + // | IN FIFO MAX | + // --------------- + // | ... | + // --------------- y + x + w + GRXFSIZ + // | IN FIFO 2 | + // --------------- x + w + GRXFSIZ + // | IN FIFO 1 | + // --------------- w + GRXFSIZ + // | IN FIFO 0 | + // --------------- GRXFSIZ + // | OUT FIFO | + // | ( Shared ) | + // --------------- 0 + // + // In FIFO is allocated by following rules: + // - IN EP 1 gets FIFO 1, IN EP "n" gets FIFO "n". + // - Offset: allocated so far + // - Size - as described above + + // Determine required numbers + // Remaining space available in bytes + uint16_t const fifo_remaining = EP_FIFO_SIZE/4 - _allocated_fifo_words; + + // Required space by EPs in words, number of bulk and control EPs + uint16_t ep_sz_total = 0; + // Number of bulk and control EPs + uint8_t nbc = 0; + // EP0 is already taken care of so exclude that here + for (cnt_ep = 1; cnt_ep < EP_MAX; cnt_ep++) + { + fifo_depth = (report.ep_size[cnt_ep][TUSB_DIR_IN] + 3) / 4; // Since we need full words take care of remainders! + if (fifo_depth > 0 && fifo_depth < 16) fifo_depth = 16; // Minimum FIFO depth is 16 + ep_sz_total += fifo_depth; + nbc += (report.ep_transfer_type[cnt_ep][TUSB_DIR_IN][TUSB_XFER_BULK] | report.ep_transfer_type[cnt_ep][TUSB_DIR_IN][TUSB_XFER_CONTROL]); + } + + if (ep_sz_total > fifo_remaining) + { + // Too less space available to apply this allocation scheme - return false and set a flag such that a different approach may be used TODO: introduce flag + return false; + } + + uint16_t extra_space = nbc > 0 ? (fifo_remaining - ep_sz_total) / nbc : 0; // If no bulk or control EPs are used we just leave the rest of the memory unused + + // Setup FIFOs + for (cnt_ep = 1; cnt_ep < EP_MAX; cnt_ep++) + { + // If EP is used + if (report.ep_size[cnt_ep][TUSB_DIR_IN] > 0) + { + fifo_depth = (report.ep_size[cnt_ep][TUSB_DIR_IN] + 3) / 4 + ((report.ep_transfer_type[cnt_ep][TUSB_DIR_IN][TUSB_XFER_BULK] || report.ep_transfer_type[cnt_ep][TUSB_DIR_IN][TUSB_XFER_CONTROL]) ? extra_space : 0); + fifo_depth = tu_max16(16, fifo_depth); + usb_otg->DIEPTXF[cnt_ep - 1] = (fifo_depth << USB_OTG_DIEPTXF_INEPTXFD_Pos) | _allocated_fifo_words; + _allocated_fifo_words += fifo_depth; + } + } + + // usb_otg->GINTMSK |= USB_OTG_GINTMSK_OEPINT | USB_OTG_GINTMSK_IEPINT; + + // Enable interrupts + dev->DAINTMSK |= (1 << USB_OTG_DAINTMSK_OEPM_Pos) | (1 << USB_OTG_DAINTMSK_IEPM_Pos); + dev->DOEPMSK |= USB_OTG_DOEPMSK_STUPM | USB_OTG_DOEPMSK_XFRCM; + dev->DIEPMSK |= USB_OTG_DIEPMSK_TOM | USB_OTG_DIEPMSK_XFRCM; + + // USB_OTG_FS->GINTMSK |= USB_OTG_GINTMSK_OEPINT | USB_OTG_GINTMSK_IEPINT; + + out_ep[0].DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + + return true; } #endif |
