/** * @brief Prepares for a transmit operation. * * @param[in] usbp pointer to the @p USBDriver object * @param[in] ep endpoint number * * @notapi */ 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); else usb_packet_write_from_buffer(USB_GET_DESCRIPTOR(ep), isp->mode.linear.txbuf, n); }
/** * @brief Prepares for a transmit operation. * * @param[in] usbp pointer to the @p USBDriver object * @param[in] ep endpoint number * * @notapi */ void usb_lld_prepare_transmit(USBDriver *usbp, usbep_t ep) { size_t n; USBInEndpointState *isp = usbp->epc[ep]->in_state; /* Transfer initialization.*/ n = isp->txsize; if (n > (size_t)usbp->epc[ep]->in_maxsize) n = (size_t)usbp->epc[ep]->in_maxsize; if (isp->txqueued) usb_packet_write_from_queue(USB_GET_DESCRIPTOR(ep), isp->mode.queue.txqueue, n); else usb_packet_write_from_buffer(USB_GET_DESCRIPTOR(ep), isp->mode.linear.txbuf, n); }
/** * @brief Reads a setup packet from the dedicated packet buffer. * @details This function must be invoked in the context of the @p setup_cb * callback in order to read the received setup packet. * @pre In order to use this function the endpoint must have been * initialized as a control endpoint. * @post The endpoint is ready to accept another packet. * * @param[in] usbp pointer to the @p USBDriver object * @param[in] ep endpoint number * @param[out] buf buffer where to copy the packet data * * @notapi */ void usb_lld_read_setup(USBDriver *usbp, usbep_t ep, uint8_t *buf) { stm32_usb_pma_t *pmap; stm32_usb_descriptor_t *udp; uint32_t n; (void)usbp; udp = USB_GET_DESCRIPTOR(ep); pmap = USB_ADDR2PTR(udp->RXADDR0); for (n = 0; n < 4; n++) { *(uint16_t *)buf = (uint16_t)*pmap++; buf += 2; } }
/** * @brief Enables an endpoint. * * @param[in] usbp pointer to the @p USBDriver object * @param[in] ep endpoint number * * @notapi */ void usb_lld_init_endpoint(USBDriver *usbp, usbep_t ep) { uint16_t nblocks, epr; stm32_usb_descriptor_t *dp; const USBEndpointConfig *epcp = usbp->epc[ep]; /* Setting the endpoint type.*/ switch (epcp->ep_mode & USB_EP_MODE_TYPE) { case USB_EP_MODE_TYPE_ISOC: epr = EPR_EP_TYPE_ISO; break; case USB_EP_MODE_TYPE_BULK: epr = EPR_EP_TYPE_BULK; break; case USB_EP_MODE_TYPE_INTR: epr = EPR_EP_TYPE_INTERRUPT; break; default: epr = EPR_EP_TYPE_CONTROL; } /* IN endpoint settings, always in NAK mode initially.*/ if (epcp->in_cb != NULL) epr |= EPR_STAT_TX_NAK; /* OUT endpoint settings. If the endpoint is in packet mode then it must start ready to accept data else it must start in NAK mode.*/ if (epcp->out_cb != NULL) { if (epcp->ep_mode & USB_EP_MODE_PACKET) { usbp->receiving |= (1 << ep); epr |= EPR_STAT_RX_VALID; } else 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->TXCOUNT = 0; dp->RXCOUNT = nblocks; dp->TXADDR = pm_alloc(usbp, epcp->in_maxsize); dp->RXADDR = pm_alloc(usbp, epcp->out_maxsize); }
/** * @brief Copies a packet from memory into a packet buffer. * * @param[in] ep endpoint number * @param[in] buf buffer where to fetch the endpoint data * @param[in] n maximum number of bytes to copy */ static void write_packet(usbep_t ep, const uint8_t *buf, size_t n){ uint32_t *pmap; stm32_usb_descriptor_t *udp; size_t count; udp = USB_GET_DESCRIPTOR(ep); pmap = USB_ADDR2PTR(udp->TXADDR); udp->TXCOUNT = n; count = (n + 1) / 2; while (count) { *pmap++ = *(uint16_t *)buf; buf += 2; count--; } EPR_SET_STAT_TX(ep, EPR_STAT_TX_VALID); }
/** * @brief Writes to a dedicated packet buffer. * @pre In order to use this function he endpoint must have been * initialized in packet mode. * @note This function can be invoked both in thread and IRQ context. * * @param[in] usbp pointer to the @p USBDriver object * @param[in] ep endpoint number * @param[in] buf buffer where to fetch the packet data * @param[in] n maximum number of bytes to copy. This value must * not exceed the maximum packet size for this endpoint. * * @notapi */ void usb_lld_write_packet_buffer(USBDriver *usbp, usbep_t ep, const uint8_t *buf, size_t n) { uint32_t *pmap; stm32_usb_descriptor_t *udp; (void)usbp; udp = USB_GET_DESCRIPTOR(ep); pmap = USB_ADDR2PTR(udp->TXADDR0); udp->TXCOUNT0 = (uint16_t)n; n = (n + 1) / 2; while (n > 0) { *pmap++ = *(uint16_t *)buf; buf += 2; n--; } }
/** * @brief Writes a packet to the dedicated packet buffer. * @pre In order to use this function he endpoint must have been * initialized in packet mode. * @post The endpoint is ready to transmit the packet. * * @param[in] usbp pointer to the @p USBDriver object * @param[in] ep endpoint number * @param[in] buf buffer where to fetch the packet data * @param[in] n maximum number of bytes to copy. This value must * not exceed the maximum packet size for this endpoint. * * @notapi */ void usb_lld_write_packet(USBDriver *usbp, usbep_t ep, const uint8_t *buf, size_t n) { uint32_t *pmap; stm32_usb_descriptor_t *udp; (void)usbp; udp = USB_GET_DESCRIPTOR(ep); pmap = USB_ADDR2PTR(udp->TXADDR); udp->TXCOUNT = n; n = (n + 1) / 2; while (n > 0) { *pmap++ = *(uint16_t *)buf; buf += 2; n--; } EPR_SET_STAT_TX(ep, EPR_STAT_TX_VALID); }
/** * @brief Copies a packet from a packet buffer into memory. * * @param[in] ep endpoint number * @param[in] buf buffer where to copy the endpoint data * @param[in] n maximum number of bytes to copy * @return The packet size. * @retval 0 Special case, zero sized packet. */ static size_t read_packet(usbep_t ep, uint8_t *buf, size_t n){ uint32_t *pmap; stm32_usb_descriptor_t *udp; size_t count; udp = USB_GET_DESCRIPTOR(ep); pmap = USB_ADDR2PTR(udp->RXADDR); count = udp->RXCOUNT & RXCOUNT_COUNT_MASK; if (n > count) n = count; count = (n + 1) / 2; while (count) { *(uint16_t *)buf = (uint16_t)*pmap++; buf += 2; count--; } return n; }
/** * @brief Enables an endpoint. * * @param[in] usbp pointer to the @p USBDriver object * @param[in] ep endpoint number * * @notapi */ void usb_lld_init_endpoint(USBDriver *usbp, usbep_t ep) { uint16_t nblocks, epr; stm32_usb_descriptor_t *dp; const USBEndpointConfig *epcp = usbp->epc[ep]; /* Setting the endpoint type.*/ switch (epcp->ep_mode & USB_EP_MODE_TYPE) { case USB_EP_MODE_TYPE_ISOC: epr = EPR_EP_TYPE_ISO; break; case USB_EP_MODE_TYPE_BULK: epr = EPR_EP_TYPE_BULK; break; case USB_EP_MODE_TYPE_INTR: epr = EPR_EP_TYPE_INTERRUPT; break; default: 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); }
/** * @brief Reads from a dedicated packet buffer. * @pre In order to use this function he endpoint must have been * initialized in packet mode. * @note This function can be invoked both in thread and IRQ context. * * @param[in] usbp pointer to the @p USBDriver object * @param[in] ep endpoint number * @param[out] buf buffer where to copy the packet data * @param[in] n maximum number of bytes to copy. This value must * not exceed the maximum packet size for this endpoint. * @return The received packet size regardless the specified * @p n parameter. * @retval 0 Zero size packet received. * * @notapi */ size_t usb_lld_read_packet_buffer(USBDriver *usbp, usbep_t ep, uint8_t *buf, size_t n) { uint32_t *pmap; stm32_usb_descriptor_t *udp; size_t count; (void)usbp; udp = USB_GET_DESCRIPTOR(ep); pmap = USB_ADDR2PTR(udp->RXADDR0); count = (size_t)udp->RXCOUNT0 & RXCOUNT_COUNT_MASK; if (n > count) n = count; n = (n + 1) / 2; while (n > 0) { *(uint16_t *)buf = (uint16_t)*pmap++; buf += 2; n--; } return count; }
/** * @brief Enables an endpoint. * * @param[in] usbp pointer to the @p USBDriver object * @param[in] ep endpoint number * * @notapi */ void usb_lld_init_endpoint(USBDriver *usbp, usbep_t ep) { uint16_t epr; stm32_usb_descriptor_t *dp; const USBEndpointConfig *epcp = usbp->epc[ep]; /* 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: #if STM32_USB_USE_ISOCHRONOUS osalDbgAssert((epcp->in_state == NULL) || (epcp->out_state == NULL), "isochronous EP cannot be IN and OUT"); epr = EPR_EP_TYPE_ISO; break; #else osalDbgAssert(false, "isochronous support disabled"); #endif /* Falls through.*/ case USB_EP_MODE_TYPE_BULK: epr = EPR_EP_TYPE_BULK; break; case USB_EP_MODE_TYPE_INTR: epr = EPR_EP_TYPE_INTERRUPT; break; default: epr = EPR_EP_TYPE_CONTROL; } dp = USB_GET_DESCRIPTOR(ep); /* IN endpoint handling.*/ if (epcp->in_state != NULL) { dp->TXCOUNT0 = 0; dp->TXADDR0 = usb_pm_alloc(usbp, epcp->in_maxsize); #if STM32_USB_USE_ISOCHRONOUS if (epr == EPR_EP_TYPE_ISO) { epr |= EPR_STAT_TX_VALID; dp->TXCOUNT1 = dp->TXCOUNT0; dp->TXADDR1 = dp->TXADDR0; /* Both buffers overlapped.*/ } else { epr |= EPR_STAT_TX_NAK; } #else epr |= EPR_STAT_TX_NAK; #endif } /* OUT endpoint handling.*/ if (epcp->out_state != NULL) { uint16_t nblocks; /* 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->RXCOUNT0 = nblocks; dp->RXADDR0 = usb_pm_alloc(usbp, epcp->out_maxsize); #if STM32_USB_USE_ISOCHRONOUS if (epr == EPR_EP_TYPE_ISO) { epr |= EPR_STAT_RX_VALID; dp->RXCOUNT1 = dp->RXCOUNT0; dp->RXADDR1 = dp->RXADDR0; /* Both buffers overlapped.*/ } else { epr |= EPR_STAT_RX_NAK; } #else epr |= EPR_STAT_RX_NAK; #endif } /* EPxR register setup.*/ EPR_SET(ep, epr | ep); EPR_TOGGLE(ep, epr); }
/** * @brief Writes to a dedicated packet buffer. * * @param[in] ep endpoint number * @param[in] buf buffer where to fetch the packet data * @param[in] n maximum number of bytes to copy. This value must * not exceed the maximum packet size for this endpoint. * * @notapi */ static void usb_packet_write_from_buffer(usbep_t ep, const uint8_t *buf, size_t n) { stm32_usb_descriptor_t *udp = USB_GET_DESCRIPTOR(ep); stm32_usb_pma_t *pmap = USB_ADDR2PTR(udp->TXADDR0); int i = (int)n; #if STM32_USB_USE_ISOCHRONOUS uint32_t epr = STM32_USB->EPR[ep]; /* 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.*/ if (EPR_EP_TYPE_IS_ISO(epr) && (epr & EPR_DTOG_TX)) udp->TXCOUNT1 = (stm32_usb_pma_t)n; else udp->TXCOUNT0 = (stm32_usb_pma_t)n; #else udp->TXCOUNT0 = (stm32_usb_pma_t)n; #endif #if STM32_USB_USE_FAST_COPY while (i >= 16) { uint32_t w; w = *(buf + 0); w |= *(buf + 1) << 8; *(pmap + 0) = (stm32_usb_pma_t)w; w = *(buf + 2); w |= *(buf + 3) << 8; *(pmap + 1) = (stm32_usb_pma_t)w; w = *(buf + 4); w |= *(buf + 5) << 8; *(pmap + 2) = (stm32_usb_pma_t)w; w = *(buf + 6); w |= *(buf + 7) << 8; *(pmap + 3) = (stm32_usb_pma_t)w; w = *(buf + 8); w |= *(buf + 9) << 8; *(pmap + 4) = (stm32_usb_pma_t)w; w = *(buf + 10); w |= *(buf + 11) << 8; *(pmap + 5) = (stm32_usb_pma_t)w; w = *(buf + 12); w |= *(buf + 13) << 8; *(pmap + 6) = (stm32_usb_pma_t)w; w = *(buf + 14); w |= *(buf + 15) << 8; *(pmap + 7) = (stm32_usb_pma_t)w; i -= 16; buf += 16; pmap += 8; } #endif /* STM32_USB_USE_FAST_COPY */ while (i > 0) { uint32_t w; w = *buf++; w |= *buf++ << 8; *pmap++ = (stm32_usb_pma_t)w; i -= 2; } }
/** * @brief Reads from a dedicated packet buffer. * * @param[in] ep endpoint number * @param[out] buf buffer where to copy the packet data * @return The size of the receivee packet. * * @notapi */ static size_t usb_packet_read_to_buffer(usbep_t ep, uint8_t *buf) { size_t i, n; stm32_usb_descriptor_t *udp = USB_GET_DESCRIPTOR(ep); stm32_usb_pma_t *pmap = USB_ADDR2PTR(udp->RXADDR0); #if STM32_USB_USE_ISOCHRONOUS uint32_t epr = STM32_USB->EPR[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.*/ if (EPR_EP_TYPE_IS_ISO(epr) && !(epr & EPR_DTOG_RX)) n = (size_t)udp->RXCOUNT1 & RXCOUNT_COUNT_MASK; else n = (size_t)udp->RXCOUNT0 & RXCOUNT_COUNT_MASK; #else n = (size_t)udp->RXCOUNT0 & RXCOUNT_COUNT_MASK; #endif i = n; #if STM32_USB_USE_FAST_COPY while (i >= 16) { uint32_t w; w = *(pmap + 0); *(buf + 0) = (uint8_t)w; *(buf + 1) = (uint8_t)(w >> 8); w = *(pmap + 1); *(buf + 2) = (uint8_t)w; *(buf + 3) = (uint8_t)(w >> 8); w = *(pmap + 2); *(buf + 4) = (uint8_t)w; *(buf + 5) = (uint8_t)(w >> 8); w = *(pmap + 3); *(buf + 6) = (uint8_t)w; *(buf + 7) = (uint8_t)(w >> 8); w = *(pmap + 4); *(buf + 8) = (uint8_t)w; *(buf + 9) = (uint8_t)(w >> 8); w = *(pmap + 5); *(buf + 10) = (uint8_t)w; *(buf + 11) = (uint8_t)(w >> 8); w = *(pmap + 6); *(buf + 12) = (uint8_t)w; *(buf + 13) = (uint8_t)(w >> 8); w = *(pmap + 7); *(buf + 14) = (uint8_t)w; *(buf + 15) = (uint8_t)(w >> 8); i -= 16; buf += 16; pmap += 8; } #endif /* STM32_USB_USE_FAST_COPY */ while (i >= 2) { uint32_t w = *pmap++; *buf++ = (uint8_t)w; *buf++ = (uint8_t)(w >> 8); i -= 2; } if (i >= 1) { *buf = (uint8_t)*pmap; } return n; }
/** * @brief Enables an endpoint. * * @param[in] usbp pointer to the @p USBDriver object * @param[in] ep endpoint number * * @notapi */ void usb_lld_init_endpoint(USBDriver *usbp, usbep_t ep) { uint16_t nblocks, epr; stm32_usb_descriptor_t *dp; const USBEndpointConfig *epcp = usbp->epc[ep]; /* 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: epr = EPR_EP_TYPE_BULK; break; case USB_EP_MODE_TYPE_INTR: epr = EPR_EP_TYPE_INTERRUPT; break; default: epr = EPR_EP_TYPE_CONTROL; } /* 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); }