From f620f29e0a803ed942134ac8a801d5204cebe034 Mon Sep 17 00:00:00 2001 From: gdisirio Date: Wed, 13 Jun 2012 16:13:00 +0000 Subject: git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@4273 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/hal/include/io_channel.h | 2 +- os/hal/include/serial_usb.h | 1 + os/hal/include/usb.h | 14 ++ os/hal/platforms/STM32/OTGv1/usb_lld.c | 278 ++++++++++++++++++++------------- os/hal/platforms/STM32/OTGv1/usb_lld.h | 12 +- os/hal/src/serial_usb.c | 26 +++ testhal/STM32F4xx/USB_CDC/Makefile | 2 +- testhal/STM32F4xx/USB_CDC/main.c | 11 +- 8 files changed, 226 insertions(+), 120 deletions(-) diff --git a/os/hal/include/io_channel.h b/os/hal/include/io_channel.h index 3357bf6bd..2f2809bf8 100644 --- a/os/hal/include/io_channel.h +++ b/os/hal/include/io_channel.h @@ -275,7 +275,7 @@ typedef struct { /** @brief No pending conditions.*/ #define CHN_NO_ERROR 0 /** @brief Connection happened.*/ -#define ICHN_CONNECTED 1 +#define CHN_CONNECTED 1 /** @brief Disconnection happened.*/ #define CHN_DISCONNECTED 2 /** @brief Data available in the input queue.*/ diff --git a/os/hal/include/serial_usb.h b/os/hal/include/serial_usb.h index c5b118e70..4ad65d76d 100644 --- a/os/hal/include/serial_usb.h +++ b/os/hal/include/serial_usb.h @@ -160,6 +160,7 @@ extern "C" { void sduObjectInit(SerialUSBDriver *sdp); void sduStart(SerialUSBDriver *sdup, const SerialUSBConfig *config); void sduStop(SerialUSBDriver *sdup); + void sduConfigureHookI(USBDriver *usbp); bool_t sduRequestsHook(USBDriver *usbp); void sduDataTransmitted(USBDriver *usbp, usbep_t ep); void sduDataReceived(USBDriver *usbp, usbep_t ep); diff --git a/os/hal/include/usb.h b/os/hal/include/usb.h index d2a154949..751128c7c 100644 --- a/os/hal/include/usb.h +++ b/os/hal/include/usb.h @@ -321,9 +321,21 @@ typedef const USBDescriptor * (*usbgetdescriptor_t)(USBDriver *usbp, * @name Macro Functions * @{ */ +/** + * @brief Returns the sriver state. + * + * @param[in] usbp pointer to the @p USBDriver object + * @return The driver state. + * + * @iclass + */ +#define usbGetDriverStateI(usbp) ((usbp)->state) + /** * @brief Connects the USB device. * + * @param[in] usbp pointer to the @p USBDriver object + * * @api */ #define usbConnectBus(usbp) usb_lld_connect_bus(usbp) @@ -331,6 +343,8 @@ typedef const USBDescriptor * (*usbgetdescriptor_t)(USBDriver *usbp, /** * @brief Disconnect the USB device. * + * @param[in] usbp pointer to the @p USBDriver object + * * @api */ #define usbDisconnectBus(usbp) usb_lld_disconnect_bus(usbp) diff --git a/os/hal/platforms/STM32/OTGv1/usb_lld.c b/os/hal/platforms/STM32/OTGv1/usb_lld.c index 33cb571ec..5dc5a1052 100644 --- a/os/hal/platforms/STM32/OTGv1/usb_lld.c +++ b/os/hal/platforms/STM32/OTGv1/usb_lld.c @@ -144,7 +144,7 @@ static void otg_rxfifo_flush(void) { OTG->GRSTCTL = GRSTCTL_RXFFLSH; while ((OTG->GRSTCTL & GRSTCTL_RXFFLSH) != 0) ; - /* Wait for 3 PHY Clocks*/ + /* Wait for 3 PHY Clocks.*/ halPolledDelay(12); } @@ -153,7 +153,7 @@ static void otg_txfifo_flush(uint32_t fifo) { OTG->GRSTCTL = GRSTCTL_TXFNUM(fifo) | GRSTCTL_TXFFLSH; while ((OTG->GRSTCTL & GRSTCTL_TXFFLSH) != 0) ; - /* Wait for 3 PHY Clocks*/ + /* Wait for 3 PHY Clocks.*/ halPolledDelay(12); } @@ -186,6 +186,84 @@ static uint32_t otg_ram_alloc(USBDriver *usbp, size_t size) { "otg_fifo_alloc(), #1", "FIFO memory overflow"); return next; } +/** + * @brief Prepares for a transmit transaction on an IN endpoint. + * @post The endpoint is ready for @p usbStartTransmitI(). + * @note The transmit transaction size is equal to the data contained + * in the queue. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * @param[in] n maximum number of bytes to copy + * + * @special + */ +static void otg_prepare_transmit(USBDriver *usbp, usbep_t ep, size_t n) { + uint32_t pcnt; + USBInEndpointState *isp = usbp->epc[ep]->in_state; + + isp->txsize = n; + isp->txcnt = 0; + + /* Transfer initialization.*/ + if (n == 0) { + /* Special case, sending zero size packet.*/ + OTG->ie[ep].DIEPTSIZ = DIEPTSIZ_PKTCNT(1) | DIEPTSIZ_XFRSIZ(0); + } + else { + /* Normal case.*/ + pcnt = (n + usbp->epc[ep]->in_maxsize - 1) / usbp->epc[ep]->in_maxsize; + OTG->ie[ep].DIEPTSIZ = DIEPTSIZ_PKTCNT(pcnt) | + DIEPTSIZ_XFRSIZ(usbp->epc[ep]->in_state->txsize); + } +} + +/** + * @brief Prepares for a receive transaction on an OUT endpoint. + * @post The endpoint is ready for @p usbStartReceiveI(). + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * @param[in] n maximum number of bytes to copy + * + * @special + */ +static void otg_prepare_receive(USBDriver *usbp, usbep_t ep, size_t n) { + uint32_t pcnt; + USBOutEndpointState *osp = usbp->epc[ep]->out_state; + + osp->rxsize = n; + osp->rxcnt = 0; + + /* Transfer initialization.*/ + pcnt = (n + usbp->epc[ep]->out_maxsize - 1) / usbp->epc[ep]->out_maxsize; + OTG->oe[ep].DOEPTSIZ = DOEPTSIZ_STUPCNT(3) | DOEPTSIZ_PKTCNT(pcnt) | + DOEPTSIZ_XFRSIZ(usbp->epc[ep]->out_maxsize); +} + +/** + * @brief Pushes a series of words into a FIFO. + * + * @param[in] fifop pointer to the FIFO register + * @param[in] buf pointer to the words buffer, not necessarily word + * aligned + * @param[in] n number of words to push + * + * @return A pointer after the last word pushed. + * + * @notapi + */ +static uint8_t *otg_do_push(volatile uint32_t *fifop, uint8_t *buf, size_t n) { + + while (n > 0) { + /* Note, this line relies on the Cortex-M3/M4 ability to perform + unaligned word accesses and on the LSB-first memory organization.*/ + *fifop = *((uint32_t *)buf); + buf += 4; + n--; + } + return buf; +} /** * @brief Writes to a TX FIFO. @@ -199,19 +277,8 @@ static uint32_t otg_ram_alloc(USBDriver *usbp, size_t size) { static void otg_fifo_write_from_buffer(usbep_t ep, const uint8_t *buf, size_t n) { - volatile uint32_t *fifop; - fifop = OTG_FIFO(ep); - n = (n + 3) / 4; - while (n) { - uint32_t dw = (uint32_t)buf[0] | - ((uint32_t)buf[1] << 8) | - ((uint32_t)buf[2] << 16) | - ((uint32_t)buf[3] << 24); - *fifop = dw; - n--; - buf += 4; - } + otg_do_push(OTG_FIFO(ep), (uint8_t *)buf, (n + 3) / 4); } /** @@ -226,43 +293,75 @@ static void otg_fifo_write_from_buffer(usbep_t ep, static void otg_fifo_write_from_queue(usbep_t ep, OutputQueue *oqp, size_t n) { - size_t nw; - uint8_t *bp; + size_t ntogo; volatile uint32_t *fifop; fifop = OTG_FIFO(ep); - /* Fetching data from the queue buffer directly.*/ - bp = oqp->q_rdptr; - nw = (n + 3) / 4; - do { - uint32_t dw; - dw = (uint32_t)*bp++; - if (bp >= oqp->q_top) - bp = oqp->q_buffer; - dw |= (uint32_t)*bp++ << 8; - if (bp >= oqp->q_top) - bp = oqp->q_buffer; - dw |= (uint32_t)*bp++ << 16; - if (bp >= oqp->q_top) - bp = oqp->q_buffer; - dw |= (uint32_t)*bp++ << 24; - if (bp >= oqp->q_top) - bp = oqp->q_buffer; + ntogo = n; + while (ntogo > 0) { + uint32_t dw, i; + size_t streak, nw = ntogo / 4; + uint32_t nw2end = (oqp->q_top - oqp->q_rdptr) / 4; + + ntogo -= (streak = nw <= nw2end ? nw : nw2end) * 4; + oqp->q_rdptr = otg_do_push(fifop, oqp->q_rdptr, streak); + if (oqp->q_rdptr >= oqp->q_top) { + oqp->q_rdptr = oqp->q_buffer; + continue; + } + + /* If this condition is not satisfied then there is a word lying across + queue circular buffer boundary.*/ + if (ntogo <= 0) + break; + + /* One byte at time.*/ + dw = 0; + i = 0; + while ((ntogo > 0) && (i < 4)) { + dw |= (uint32_t)*oqp->q_rdptr++ << (i * 8); + if (oqp->q_rdptr >= oqp->q_top) + oqp->q_rdptr = oqp->q_buffer; + ntogo--; + i++; + } *fifop = dw; - } while (--nw > 0); + } /* Updating queue.*/ chSysLockFromIsr(); - oqp->q_rdptr += n; - if (oqp->q_rdptr >= oqp->q_top) - oqp->q_rdptr -= chQSizeI(oqp); oqp->q_counter += n; while (notempty(&oqp->q_waiting)) chSchReadyI(fifo_remove(&oqp->q_waiting))->p_u.rdymsg = Q_OK; chSysUnlockFromIsr(); } +/** + * @brief Pops a series of words from a FIFO. + * + * @param[in] fifop pointer to the FIFO register + * @param[in] buf pointer to the words buffer, not necessarily word + * aligned + * @param[in] n number of words to push + * + * @return A pointer after the last word pushed. + * + * @notapi + */ +static uint8_t *otg_do_pop(volatile uint32_t *fifop, uint8_t *buf, size_t n) { + + while (n > 0) { + uint32_t dw = *fifop; + /* Note, this line relies on the Cortex-M3/M4 ability to perform + unaligned word accesses and on the LSB-first memory organization.*/ + *((uint32_t *)buf) = dw; + buf += 4; + n--; + } + return buf; +} + /** * @brief Reads a packet from the RXFIFO. * @@ -281,10 +380,10 @@ static void otg_fifo_read_to_buffer(uint8_t *buf, size_t n, size_t max) { while (n) { uint32_t dw = *fifop; if (max) { - *buf++ = (uint8_t)dw; - *buf++ = (uint8_t)(dw >> 8); - *buf++ = (uint8_t)(dw >> 16); - *buf++ = (uint8_t)(dw >> 24); + /* Note, this line relies on the Cortex-M3/M4 ability to perform + unaligned word accesses and on the LSB-first memory organization.*/ + *((uint32_t *)buf) = dw; + buf += 4; max--; } n--; @@ -300,37 +399,40 @@ static void otg_fifo_read_to_buffer(uint8_t *buf, size_t n, size_t max) { * @notapi */ static void otg_fifo_read_to_queue(InputQueue *iqp, size_t n) { - size_t nw, nb; + size_t ntogo; volatile uint32_t *fifop; fifop = OTG_FIFO(0); - nb = n; - nw = (n + 3) / 4; - do { - uint32_t dw = *fifop; - *iqp->q_wrptr++ = (uint8_t)dw; - if (iqp->q_wrptr >= iqp->q_top) - iqp->q_wrptr = iqp->q_buffer; - if (--nb > 0) { - *iqp->q_wrptr++ = (uint8_t)(dw >> 8); - if (iqp->q_wrptr >= iqp->q_top) - iqp->q_wrptr = iqp->q_buffer; - if (--nb > 0) { - *iqp->q_wrptr++ = (uint8_t)(dw >> 16); - if (iqp->q_wrptr >= iqp->q_top) - iqp->q_wrptr = iqp->q_buffer; + ntogo = n; + while (ntogo > 0) { + uint32_t dw, i; + size_t streak, nw = ntogo / 4; + uint32_t nw2end = (iqp->q_wrptr - iqp->q_wrptr) / 4; - if (--nb > 0) { - *iqp->q_wrptr++ = (uint8_t)(dw >> 24); - if (iqp->q_wrptr >= iqp->q_top) - iqp->q_wrptr = iqp->q_buffer; + ntogo -= (streak = nw <= nw2end ? nw : nw2end) * 4; + iqp->q_wrptr = otg_do_pop(fifop, iqp->q_wrptr, streak); + if (iqp->q_wrptr >= iqp->q_top) { + iqp->q_wrptr = iqp->q_buffer; + continue; + } + + /* If this condition is not satisfied then there is a word lying across + queue circular buffer boundary.*/ + if (ntogo <= 0) + break; - --nb; - } - } + /* One byte at time.*/ + dw = *fifop; + i = 0; + while ((ntogo > 0) && (i < 4)) { + *iqp->q_wrptr++ = (uint8_t)dw >> (i * 8); + if (iqp->q_wrptr >= iqp->q_top) + iqp->q_wrptr = iqp->q_buffer; + ntogo--; + i++; } - } while (--nw > 0); + } /* Updating queue.*/ chSysLockFromIsr(); @@ -846,18 +948,11 @@ void usb_lld_read_setup(USBDriver *usbp, usbep_t ep, uint8_t *buf) { */ void usb_lld_prepare_receive(USBDriver *usbp, usbep_t ep, uint8_t *buf, size_t n) { - uint32_t pcnt; USBOutEndpointState *osp = usbp->epc[ep]->out_state; osp->rxqueued = FALSE; - osp->rxsize = n; - osp->rxcnt = 0; osp->mode.linear.rxbuf = buf; - - /* Transfer initialization.*/ - pcnt = (n + usbp->epc[ep]->out_maxsize - 1) / usbp->epc[ep]->out_maxsize; - OTG->oe[ep].DOEPTSIZ = DOEPTSIZ_STUPCNT(3) | DOEPTSIZ_PKTCNT(pcnt) | - DOEPTSIZ_XFRSIZ(usbp->epc[ep]->out_maxsize); + otg_prepare_receive(usbp, ep, n); } /** @@ -872,30 +967,15 @@ void usb_lld_prepare_receive(USBDriver *usbp, usbep_t ep, */ void usb_lld_prepare_transmit(USBDriver *usbp, usbep_t ep, const uint8_t *buf, size_t n) { - uint32_t pcnt; USBInEndpointState *isp = usbp->epc[ep]->in_state; isp->txqueued = FALSE; - isp->txsize = n; - isp->txcnt = 0; isp->mode.linear.txbuf = buf; - - if (n == 0) { - /* Special case, sending zero size packet.*/ - OTG->ie[ep].DIEPTSIZ = DIEPTSIZ_PKTCNT(1) | DIEPTSIZ_XFRSIZ(0); - } - else { - /* Transfer initialization.*/ - pcnt = (n + usbp->epc[ep]->in_maxsize - 1) / usbp->epc[ep]->in_maxsize; - OTG->ie[ep].DIEPTSIZ = DIEPTSIZ_PKTCNT(pcnt) | - DIEPTSIZ_XFRSIZ(usbp->epc[ep]->in_state->txsize); - } + otg_prepare_transmit(usbp, ep, n); } /** * @brief Prepares for a receive transaction on an OUT endpoint. - * @pre In order to use this function the endpoint must have been - * initialized in transaction mode. * @post The endpoint is ready for @p usbStartReceiveI(). * @note The receive transaction size is equal to the space in the queue * rounded to the lower multiple of a packet size. So make sure there @@ -911,24 +991,15 @@ void usb_lld_prepare_transmit(USBDriver *usbp, usbep_t ep, */ void usb_lld_prepare_queued_receive(USBDriver *usbp, usbep_t ep, InputQueue *iq, size_t n) { - uint32_t pcnt; USBOutEndpointState *osp = usbp->epc[ep]->out_state; osp->rxqueued = TRUE; - osp->rxsize = n; - osp->rxcnt = 0; osp->mode.queue.rxqueue = iq; - - /* Transfer initialization.*/ - pcnt = (n + usbp->epc[ep]->out_maxsize - 1) / usbp->epc[ep]->out_maxsize; - OTG->oe[ep].DOEPTSIZ = DOEPTSIZ_STUPCNT(3) | DOEPTSIZ_PKTCNT(pcnt) | - DOEPTSIZ_XFRSIZ(usbp->epc[ep]->out_maxsize); + otg_prepare_receive(usbp, ep, n); } /** * @brief Prepares for a transmit transaction on an IN endpoint. - * @pre In order to use this function the endpoint must have been - * initialized in transaction mode. * @post The endpoint is ready for @p usbStartTransmitI(). * @note The transmit transaction size is equal to the data contained * in the queue. @@ -942,18 +1013,11 @@ void usb_lld_prepare_queued_receive(USBDriver *usbp, usbep_t ep, */ void usb_lld_prepare_queued_transmit(USBDriver *usbp, usbep_t ep, OutputQueue *oq, size_t n) { - uint32_t pcnt; USBInEndpointState *isp = usbp->epc[ep]->in_state; isp->txqueued = TRUE; - isp->txsize = n; - isp->txcnt = 0; isp->mode.queue.txqueue = oq; - - /* Transfer initialization.*/ - pcnt = (n + usbp->epc[ep]->in_maxsize - 1) / usbp->epc[ep]->in_maxsize; - OTG->ie[ep].DIEPTSIZ = DIEPTSIZ_PKTCNT(pcnt) | - DIEPTSIZ_XFRSIZ(usbp->epc[ep]->in_state->txsize); + otg_prepare_transmit(usbp, ep, n); } /** diff --git a/os/hal/platforms/STM32/OTGv1/usb_lld.h b/os/hal/platforms/STM32/OTGv1/usb_lld.h index ea0def924..9dc2a0eec 100644 --- a/os/hal/platforms/STM32/OTGv1/usb_lld.h +++ b/os/hal/platforms/STM32/OTGv1/usb_lld.h @@ -126,7 +126,7 @@ typedef struct { union { struct { /** - * @brief Pointer to the transmission buffer. + * @brief Pointer to the transmission linear buffer. */ const uint8_t *txbuf; } linear; @@ -158,7 +158,7 @@ typedef struct { union { struct { /** - * @brief Pointer to the receive buffer. + * @brief Pointer to the receive linear buffer. */ uint8_t *rxbuf; } linear; @@ -217,16 +217,12 @@ typedef struct { uint16_t out_maxsize; /** * @brief @p USBEndpointState associated to the IN endpoint. - * @details This structure maintains the state of the IN endpoint when - * the endpoint is not in packet mode. Endpoints configured in - * packet mode must set this field to @p NULL. + * @details This structure maintains the state of the IN endpoint. */ USBInEndpointState *in_state; /** * @brief @p USBEndpointState associated to the OUT endpoint. - * @details This structure maintains the state of the OUT endpoint when - * the endpoint is not in packet mode. Endpoints configured in - * packet mode must set this field to @p NULL. + * @details This structure maintains the state of the OUT endpoint. */ USBOutEndpointState *out_state; /* End of the mandatory fields.*/ diff --git a/os/hal/src/serial_usb.c b/os/hal/src/serial_usb.c index 52be6e975..2b02ae02f 100644 --- a/os/hal/src/serial_usb.c +++ b/os/hal/src/serial_usb.c @@ -120,6 +120,11 @@ static void inotify(GenericQueue *qp) { size_t n, maxsize; SerialUSBDriver *sdup = chQGetLink(qp); + /* If the USB driver is not in the appropriate state then transactions + must not be started.*/ + if (usbGetDriverStateI(sdup->config->usbp) != USB_ACTIVE) + return; + /* If there is in the queue enough space to hold at least one packet and a transaction is not yet started then a new transaction is started for the available space.*/ @@ -145,6 +150,11 @@ static void onotify(GenericQueue *qp) { size_t n; SerialUSBDriver *sdup = chQGetLink(qp); + /* If the USB driver is not in the appropriate state then transactions + must not be started.*/ + if (usbGetDriverStateI(sdup->config->usbp) != USB_ACTIVE) + return; + /* If there is not an ongoing transaction and the output queue contains data then a new transaction is started.*/ if (!usbGetTransmitStatusI(sdup->config->usbp, USB_CDC_DATA_REQUEST_EP) && @@ -238,6 +248,22 @@ void sduStop(SerialUSBDriver *sdup) { usbStop(sdup->config->usbp); } +/** + * @brief USB device configured handler. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @iclass + */ +void sduConfigureHookI(USBDriver *usbp) { + SerialUSBDriver *sdup = usbp->param; + + sdup->flags = CHN_NO_ERROR; + chIQResetI(&sdup->iqueue); + chOQResetI(&sdup->oqueue); + chnAddFlagsI(sdup, CHN_CONNECTED); +} + /** * @brief Default requests hook. * @details Applications wanting to use the Serial over USB driver can use diff --git a/testhal/STM32F4xx/USB_CDC/Makefile b/testhal/STM32F4xx/USB_CDC/Makefile index 233e47d0e..1a6ac2d74 100644 --- a/testhal/STM32F4xx/USB_CDC/Makefile +++ b/testhal/STM32F4xx/USB_CDC/Makefile @@ -5,7 +5,7 @@ # Compiler options here. ifeq ($(USE_OPT),) - USE_OPT = -O0 -ggdb -fomit-frame-pointer -falign-functions=16 + USE_OPT = -O2 -ggdb -fomit-frame-pointer -falign-functions=16 endif # C specific options here (added to USE_OPT). diff --git a/testhal/STM32F4xx/USB_CDC/main.c b/testhal/STM32F4xx/USB_CDC/main.c index 0ae9c81e0..99acdc894 100644 --- a/testhal/STM32F4xx/USB_CDC/main.c +++ b/testhal/STM32F4xx/USB_CDC/main.c @@ -296,13 +296,18 @@ static void usb_event(USBDriver *usbp, usbevent_t event) { case USB_EVENT_ADDRESS: return; case USB_EVENT_CONFIGURED: + chSysLockFromIsr(); + /* Enables the endpoints specified into the configuration. Note, this callback is invoked from an ISR so I-Class functions must be used.*/ - chSysLockFromIsr(); usbInitEndpointI(usbp, USB_CDC_DATA_REQUEST_EP, &ep1config); usbInitEndpointI(usbp, USB_CDC_INTERRUPT_REQUEST_EP, &ep2config); usbInitEndpointI(usbp, USB_CDC_DATA_AVAILABLE_EP, &ep3config); + + /* Resetting the state of the CDC subsystem.*/ + sduConfigureHookI(usbp); + chSysUnlockFromIsr(); return; case USB_EVENT_SUSPEND: @@ -437,10 +442,10 @@ int main(void) { /* * Activates the USB driver and then the USB bus pull-up on D+. */ + usbDisconnectBus(serusbcfg.usbp); + chThdSleepMilliseconds(1000); sduObjectInit(&SDU1); sduStart(&SDU1, &serusbcfg); - usbDisconnectBus(serusbcfg.usbp); - chThdSleepMilliseconds(100); usbConnectBus(serusbcfg.usbp); /* -- cgit v1.2.3