aboutsummaryrefslogtreecommitdiffstats
path: root/os/hal/ports
diff options
context:
space:
mode:
authorGiovanni Di Sirio <gdisirio@gmail.com>2015-11-26 14:30:16 +0000
committerGiovanni Di Sirio <gdisirio@gmail.com>2015-11-26 14:30:16 +0000
commitec598016954ebb12ee9c2adcbe4f0788859b0c06 (patch)
tree0998557ad7ac81d038179003b037cb0cb9d0e8a0 /os/hal/ports
parent88247bf5e9b1f3c73894c5c10347416ad201fe0a (diff)
downloadChibiOS-ec598016954ebb12ee9c2adcbe4f0788859b0c06.tar.gz
ChibiOS-ec598016954ebb12ee9c2adcbe4f0788859b0c06.tar.bz2
ChibiOS-ec598016954ebb12ee9c2adcbe4f0788859b0c06.zip
Fixed bug #662.
git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@8531 35acf78f-673a-0410-8e92-d51de3d6d3f4
Diffstat (limited to 'os/hal/ports')
-rw-r--r--os/hal/ports/STM32/LLD/USBv1/usb_lld.c95
1 files changed, 80 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);