From ec598016954ebb12ee9c2adcbe4f0788859b0c06 Mon Sep 17 00:00:00 2001 From: Giovanni Di Sirio Date: Thu, 26 Nov 2015 14:30:16 +0000 Subject: [PATCH] Fixed bug #662. git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@8531 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/hal/ports/STM32/LLD/USBv1/usb_lld.c | 95 ++++++++++++++++++++++---- readme.txt | 2 + 2 files changed, 82 insertions(+), 15 deletions(-) diff --git a/os/hal/ports/STM32/LLD/USBv1/usb_lld.c b/os/hal/ports/STM32/LLD/USBv1/usb_lld.c index 40a5664fb..69054f6fc 100644 --- a/os/hal/ports/STM32/LLD/USBv1/usb_lld.c +++ b/os/hal/ports/STM32/LLD/USBv1/usb_lld.c @@ -34,6 +34,8 @@ #define BTABLE_ADDR 0x0000 +#define EPR_EP_TYPE_IS_ISO(epr) ((epr & EPR_EP_TYPE_MASK) == EPR_EP_TYPE_ISO) + /*===========================================================================*/ /* Driver exported variables. */ /*===========================================================================*/ @@ -211,7 +213,6 @@ static void usb_packet_write_from_buffer(stm32_usb_descriptor_t *udp, stm32_usb_pma_t *pmap; pmap = USB_ADDR2PTR(udp->TXADDR0); - udp->TXCOUNT0 = (stm32_usb_pma_t)n; /* Pushing all complete words.*/ i = 0; @@ -251,7 +252,6 @@ static void usb_packet_write_from_queue(stm32_usb_descriptor_t *udp, syssts_t sts; stm32_usb_pma_t *pmap = USB_ADDR2PTR(udp->TXADDR0); - udp->TXCOUNT0 = (stm32_usb_pma_t)n; nhw = n / 2; while (nhw > 0) { stm32_usb_pma_t w; @@ -368,7 +368,17 @@ OSAL_IRQ_HANDLER(STM32_USB1_LP_HANDLER) { /* IN endpoint, transmission.*/ EPR_CLEAR_CTR_TX(ep); + /* Double buffering is always enabled for isochronous endpoints, and + although we overlap the two buffers for simplicity, we still need + to read from the right counter. The DTOG_TX bit indicates the buffer + that is currently in use by the USB peripheral, that is, the buffer + from which the next packet will be sent, so we need to read the + transmitted bytes from the counter of the OTHER buffer, which is + where we stored the last transmitted packet.*/ transmitted = (size_t)USB_GET_DESCRIPTOR(ep)->TXCOUNT0; + if (EPR_EP_TYPE_IS_ISO(epr) && !(epr & EPR_DTOG_TX)) + transmitted = (size_t)USB_GET_DESCRIPTOR(ep)->TXCOUNT1; + epcp->in_state->txcnt += transmitted; n = epcp->in_state->txsize - epcp->in_state->txcnt; if (n > 0) { @@ -376,6 +386,16 @@ OSAL_IRQ_HANDLER(STM32_USB1_LP_HANDLER) { if (n > epcp->in_maxsize) n = epcp->in_maxsize; + /* Double buffering is always enabled for isochronous endpoints, and + although we overlap the two buffers for simplicity, we still need + to write to the right counter. The DTOG_TX bit indicates the buffer + that is currently in use by the USB peripheral, that is, the buffer + from which the next packet will be sent, so we need to write the + counter of that buffer.*/ + USB_GET_DESCRIPTOR(ep)->TXCOUNT0 = (stm32_usb_pma_t)n; + if (EPR_EP_TYPE_IS_ISO(epr) && (epr & EPR_DTOG_TX)) + USB_GET_DESCRIPTOR(ep)->TXCOUNT1 = (stm32_usb_pma_t)n; + if (epcp->in_state->txqueued) usb_packet_write_from_queue(USB_GET_DESCRIPTOR(ep), epcp->in_state->mode.queue.txqueue, @@ -405,7 +425,17 @@ OSAL_IRQ_HANDLER(STM32_USB1_LP_HANDLER) { } else { stm32_usb_descriptor_t *udp = USB_GET_DESCRIPTOR(ep); + + /* Double buffering is always enabled for isochronous endpoints, and + although we overlap the two buffers for simplicity, we still need + to read from the right counter. The DTOG_RX bit indicates the buffer + that is currently in use by the USB peripheral, that is, the buffer + in which the next received packet will be stored, so we need to + read the counter of the OTHER buffer, which is where the last + received packet was stored.*/ n = (size_t)udp->RXCOUNT0 & RXCOUNT_COUNT_MASK; + if (EPR_EP_TYPE_IS_ISO(epr) && !(epr & EPR_DTOG_RX)) + n = (size_t)udp->RXCOUNT1 & RXCOUNT_COUNT_MASK; /* Reads the packet into the defined buffer.*/ if (epcp->out_state->rxqueued) @@ -418,6 +448,7 @@ OSAL_IRQ_HANDLER(STM32_USB1_LP_HANDLER) { n); epcp->out_state->mode.linear.rxbuf += n; } + /* Transaction data updated.*/ epcp->out_state->rxcnt += n; epcp->out_state->rxsize -= n; @@ -569,9 +600,13 @@ void usb_lld_init_endpoint(USBDriver *usbp, usbep_t ep) { stm32_usb_descriptor_t *dp; const USBEndpointConfig *epcp = usbp->epc[ep]; - /* Setting the endpoint type.*/ + /* Setting the endpoint type. Note that isochronous endpoints cannot be + bidirectional because it uses double buffering and both transmit and + receive descriptor fields are used for either direction.*/ switch (epcp->ep_mode & USB_EP_MODE_TYPE) { case USB_EP_MODE_TYPE_ISOC: + osalDbgAssert((epcp->in_cb == NULL) || (epcp->out_cb == NULL), + "isochronous EP cannot be IN and OUT"); epr = EPR_EP_TYPE_ISO; break; case USB_EP_MODE_TYPE_BULK: @@ -584,29 +619,48 @@ void usb_lld_init_endpoint(USBDriver *usbp, usbep_t ep) { epr = EPR_EP_TYPE_CONTROL; } - /* IN endpoint initially in NAK mode.*/ - if (epcp->in_cb != NULL) - epr |= EPR_STAT_TX_NAK; - - /* OUT endpoint initially in NAK mode.*/ - if (epcp->out_cb != NULL) - epr |= EPR_STAT_RX_NAK; - - /* EPxR register setup.*/ - EPR_SET(ep, epr | ep); - EPR_TOGGLE(ep, epr); - /* Endpoint size and address initialization.*/ if (epcp->out_maxsize > 62) nblocks = (((((epcp->out_maxsize - 1) | 0x1f) + 1) / 32) << 10) | 0x8000; else nblocks = ((((epcp->out_maxsize - 1) | 1) + 1) / 2) << 10; + dp = USB_GET_DESCRIPTOR(ep); dp->TXCOUNT0 = 0; dp->RXCOUNT0 = nblocks; dp->TXADDR0 = usb_pm_alloc(usbp, epcp->in_maxsize); dp->RXADDR0 = usb_pm_alloc(usbp, epcp->out_maxsize); + + /* Initial status for isochronous enpoints is valid because disabled and + valid are the only legal values. Also since double buffering is used + we need to initialize both count/address sets depending on the direction, + but since we are not taking advantage of the double buffering, we set both + addresses to point to the same PMA.*/ + if ((epcp->ep_mode & USB_EP_MODE_TYPE) == USB_EP_MODE_TYPE_ISOC) { + if (epcp->in_cb != NULL) { + epr |= EPR_STAT_TX_VALID; + dp->TXCOUNT1 = dp->TXCOUNT0; + dp->TXADDR1 = dp->TXADDR0; /* Both buffers overlapped.*/ + } + if (epcp->out_cb != NULL) { + epr |= EPR_STAT_RX_VALID; + dp->RXCOUNT1 = dp->RXCOUNT0; + dp->RXADDR1 = dp->RXADDR0; /* Both buffers overlapped.*/ + } + } + else { + /* Initial status for other endpoint types is NAK.*/ + if (epcp->in_cb != NULL) + epr |= EPR_STAT_TX_NAK; + + if (epcp->out_cb != NULL) + epr |= EPR_STAT_RX_NAK; + } + + /* EPxR register setup.*/ + EPR_SET(ep, epr | ep); + EPR_TOGGLE(ep, epr); } /** @@ -737,12 +791,23 @@ void usb_lld_prepare_receive(USBDriver *usbp, usbep_t ep) { void usb_lld_prepare_transmit(USBDriver *usbp, usbep_t ep) { size_t n; USBInEndpointState *isp = usbp->epc[ep]->in_state; + uint32_t epr = STM32_USB->EPR[ep]; /* Transfer initialization.*/ n = isp->txsize; if (n > (size_t)usbp->epc[ep]->in_maxsize) n = (size_t)usbp->epc[ep]->in_maxsize; + /* Double buffering is always enabled for isochronous endpoints, and + although we overlap the two buffers for simplicity, we still need + to write to the right counter. The DTOG_TX bit indicates the buffer + that is currently in use by the USB peripheral, that is, the buffer + from which the next packet will be sent, so we need to write the + counter of that buffer.*/ + USB_GET_DESCRIPTOR(ep)->TXCOUNT0 = (stm32_usb_pma_t)n; + if (EPR_EP_TYPE_IS_ISO(epr) && (epr & EPR_DTOG_TX)) + USB_GET_DESCRIPTOR(ep)->TXCOUNT1 = (stm32_usb_pma_t)n; + if (isp->txqueued) usb_packet_write_from_queue(USB_GET_DESCRIPTOR(ep), isp->mode.queue.txqueue, n); diff --git a/readme.txt b/readme.txt index 674734afa..1e9b571bc 100644 --- a/readme.txt +++ b/readme.txt @@ -144,6 +144,8 @@ (backported to 3.0.3). - HAL: Fixed RTC module loses day of week when converting (bug #664) (backported to 3.0.3). +- HAL: Fixed STM32 USBv1 broken isochronous endpoints (bug #662) + (backported to 3.0.4). - HAL: Fixed STM32 USBv1 wrong multiplier when calculating descriptor address in BTABLE (bug #661)(backported to 3.0.4 and 2.6.10). - HAL: Fixed STM32 USBv1 does not make use of BTABLE_ADDR define (bug #660)