From: arighi Date: Fri, 17 Sep 2010 16:06:52 +0000 (+0000) Subject: STM32: USB full-speed device driver X-Git-Tag: 2.6.0~161 X-Git-Url: https://codewiz.org/gitweb?a=commitdiff_plain;h=7713a5e329a770f53d53e39e9b3b8dae55c71614;p=bertos.git STM32: USB full-speed device driver Add the low-level driver for the STM32 USB controller. TODO: implement an intermediate hardware-agnostic USB layer to register low-level USB constroller drivers and high-level USB device drivers: _________________________________ | High-level USB layer | | (usb-serial, mass-storage, ...) | +---------------------------------+ | Middle-level USB layer | | (hardware-agnostic interface) | +---------------------------------+ | Low-level USB layer | | (hardware-specific driver) | +---------------------------------+ At the moment the "Middle-level" USB layer is merged into the "Low-level" USB layer. git-svn-id: https://src.develer.com/svnoss/bertos/trunk@4240 38d2e660-2303-0410-9eaa-f027e97ec537 --- diff --git a/bertos/cfg/cfg_usb.h b/bertos/cfg/cfg_usb.h new file mode 100644 index 00000000..9875fc09 --- /dev/null +++ b/bertos/cfg/cfg_usb.h @@ -0,0 +1,57 @@ +/** + * \file + * + * + * \author Andrea Righi + * + * \brief Configuration file for the USB driver module + */ + +#ifndef CFG_USB_H +#define CFG_USB_H + +/** + * Module logging level. + * + * $WIZ$ type = "enum" + * $WIZ$ value_list = "log_level" + */ +#define USB_LOG_LEVEL LOG_LVL_INFO + +/** + * module logging format. + * + * $WIZ$ type = "enum" + * $WIZ$ value_list = "log_format" + */ +#define USB_LOG_FORMAT LOG_FMT_TERSE + +#endif /* CFG_USB_H */ diff --git a/bertos/cpu/cortex-m3/drv/usb_stm32.c b/bertos/cpu/cortex-m3/drv/usb_stm32.c new file mode 100644 index 00000000..ef1abda2 --- /dev/null +++ b/bertos/cpu/cortex-m3/drv/usb_stm32.c @@ -0,0 +1,1820 @@ +/** + * \file + * + * + * \brief STM32 USB driver + * + * \author Andrea Righi + */ + +#include "cfg/cfg_usb.h" + +#define LOG_LEVEL USB_LOG_LEVEL +#define LOG_FORMAT USB_LOG_FORMAT + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include /* memcpy() */ + +#include "usb_stm32.h" + +#define ALIGN_UP(value, align) (((value) & ((align) - 1)) ? \ + (((value) + ((align) - 1)) & ~((align) - 1)) : \ + (value)) +/* STM32 USB registers */ +struct stm32_usb +{ + reg32_t EP0R; + reg32_t EP1R; + reg32_t EP2R; + reg32_t EP3R; + reg32_t EP4R; + reg32_t EP5R; + reg32_t EP6R; + reg32_t EP7R; + reg32_t __reserved[8]; + reg32_t CNTR; + reg32_t ISTR; + reg32_t FNR; + reg32_t DADDR; + reg32_t BTABLE; +}; + +/* Hardware registers */ +static struct stm32_usb *usb = (struct stm32_usb *)USB_BASE_ADDR; + +/* Endpoint descriptors: used for handling requests to use with endpoints */ +static stm32_usb_ep_t ep_cnfg[ENP_MAX_NUMB]; + +/* USB EP0 control descriptor */ +static const usb_endpoint_descriptor_t USB_CtrlEpDescr0 = +{ + .bLength = sizeof(USB_CtrlEpDescr0), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT | 0, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + .wMaxPacketSize = USB_EP0_MAX_SIZE, + .bInterval = 0, +}; + +/* USB EP1 control descriptor */ +static const usb_endpoint_descriptor_t USB_CtrlEpDescr1 = +{ + .bLength = sizeof(USB_CtrlEpDescr1), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN | 0, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + .wMaxPacketSize = USB_EP0_MAX_SIZE, + .bInterval = 0, +}; + +/* USB setup packet */ +static usb_ctrlrequest_t setup_packet; + +/* USB device controller: max supported interfaces */ +#define USB_MAX_INTERFACE 1 + +/* USB device controller features */ +#define STM32_UDC_FEATURE_SELFPOWERED BV(0) +#define STM32_UDC_FEATURE_REMOTE_WAKEUP BV(1) + +/* Hardware-specific USB device controller structure */ +typedef struct stm32_udc +{ + uint8_t state; + uint32_t cfg_id; + const usb_config_descriptor_t *cfg; + uint32_t interfaces; + uint32_t alt[USB_MAX_INTERFACE]; + uint32_t address; + uint8_t feature; +} PACKED stm32_udc_t; + +/* Hardware-specific USB Device Controller */ +static stm32_udc_t udc; + +/* Generic USB Device Controller structure */ +static struct usb_device *usb_dev; + +/* USB packet memory management: list of allocated chunks */ +static pack_mem_slot_t *pPacketMemUse; + +/* USB packet memory management: memory buffer metadata */ +#define EP_MAX_SLOTS 16 +static pack_mem_slot_t PacketMemBuff[EP_MAX_SLOTS]; + +/* Allocate a free block of the packet memory */ +static pack_mem_slot_t *usb_malloc(void) +{ + unsigned int i; + + for (i = 0; i < countof(PacketMemBuff); i++) + if (PacketMemBuff[i].Size == 0) + return &PacketMemBuff[i]; + return NULL; +} + +/* Release a block of the packet memory */ +static void usb_free(pack_mem_slot_t *pPntr) +{ + pPntr->Size = 0; +} + +/* Allocate a free chunk of the packet memory (inside a block) */ +static bool USB_AllocateBuffer(uint16_t *pOffset, uint32_t *pPacketSize, + int EndPoint) +{ + pack_mem_slot_t *pPacketMem = pPacketMemUse, + *pPacketMemNext, *pPacketMemUseNew; + uint32_t MaxPacketSize = *pPacketSize; + + /* + * Packet size alignment: + * - fine-granularity allocation: size alignment by 2; + * - coarse-granularity allocation: size alignment by 32. + */ + if (MaxPacketSize < 62) + MaxPacketSize = ALIGN_UP(MaxPacketSize, 2); + else + MaxPacketSize = ALIGN_UP(MaxPacketSize, 32); + /* + * Finding free memory chunks from the allocated blocks of the USB + * packet memory. + */ + *pOffset = 0; + while (pPacketMem != NULL) + { + /* Offset alignment by 4 */ + *pOffset = ALIGN_UP(pPacketMem->Start + pPacketMem->Size, 4); + pPacketMemNext = pPacketMem->next; + if ((pPacketMem->next == NULL) || + (pPacketMemNext->Start >= + *pOffset + MaxPacketSize)) + break; + pPacketMem = pPacketMem->next; + } + /* Check for out-of-memory condition */ + if ((*pOffset + MaxPacketSize) >= USB_BDT_OFFSET) + return false; + /* + * Allocate a new memory block, next to the last allocated block. + */ + pPacketMemUseNew = usb_malloc(); + if (pPacketMemUseNew == NULL) + return false; + /* Insert the block to the list of allocated blocks */ + if (pPacketMemUse == NULL) + { + pPacketMemUse = pPacketMemUseNew; + pPacketMemUse->next = NULL; + } + else + { + pPacketMemUseNew->next = pPacketMem->next; + pPacketMem->next = pPacketMemUseNew; + } + /* Update block's metadata */ + pPacketMemUseNew->ep_addr = EndPoint; + pPacketMemUseNew->Start = *pOffset; + pPacketMemUseNew->Size = MaxPacketSize; + + *pPacketSize = MaxPacketSize; + + return true; +} + +/* Release a chunk of the packet memory (inside a block) */ +static void USB_ReleaseBuffer(int EndPoint) +{ + pack_mem_slot_t *pPacketMem, *pPacketMemPrev = NULL; + pPacketMem = pPacketMemUse; + + while (pPacketMem != NULL) + { + if (pPacketMem->ep_addr == EndPoint) + { + if (UNLIKELY(pPacketMemPrev == NULL)) + { + /* Free the first element of the list */ + pPacketMemUse = pPacketMemUse->next; + usb_free(pPacketMem); + pPacketMem = pPacketMemUse; + continue; + } + pPacketMemPrev->next = pPacketMem->next; + usb_free(pPacketMem); + } + else + pPacketMemPrev = pPacketMem; + pPacketMem = pPacketMemPrev->next; + } +} + +/*-------------------------------------------------------------------------*/ + +/* Connect USB controller */ +static void usb_connect(void) +{ + stm32_gpioPinWrite((struct stm32_gpio *)GPIOC_BASE, 1 << 11, 0); +} + +/* Set USB device address */ +static void usb_set_address(uint32_t addr) +{ + usb->DADDR = addr | 0x80; +} + +/* Suspend USB controller */ +static void usb_suspend(void) +{ + usb->CNTR |= bmFSUSP | bmLPMODE; +} + +/* Resume USB controller */ +static void usb_resume(void) +{ + uint32_t line_status; + + line_status = usb->FNR & 0xc000; + if (!line_status) + return; + /* check for noise and eventually return to sleep */ + if (line_status == 0xc000) + usb_suspend(); + else + usb->CNTR &= ~(bmFSUSP | bmLPMODE); +} + +/* Convert logical EP address to physical EP address */ +static int USB_EpLogToPhysAdd(uint8_t ep_addr) +{ + int addr = (ep_addr & 0x0f) << 1; + return (ep_addr & 0x80) ? addr + 1 : addr; +} + +/* Set EP address */ +static void EpCtrlSet_EA(reg32_t *reg, uint32_t val) +{ + val &= 0x0f; + val |= *reg & 0x0700; + val |= USB_CTRL_CLEAR_ONLY_MASK; + *reg = val; +} + +/* Get EP IN status */ +static uint32_t EpCtrlGet_STAT_TX(reg32_t *reg) +{ + return (*reg & (0x3UL << 4)) >> 4; +} + +/* Set EP IN state */ +static void EpCtrlSet_STAT_TX(reg32_t *reg, ep_state_t val) +{ + uint32_t state; + int i; + + /* + * The EP can change state between read and write operations from VALID + * to NAK and result of set operation will be invalid. + */ + for (i = 0; i < 2; i++) + { + if (EpCtrlGet_STAT_TX(reg) == val) + return; + state = val; + state <<= 4; + state ^= *reg; + state |= USB_CTRL_CLEAR_ONLY_MASK; + /* Clear the toggle bits without STAT_TX (4,5) */ + state &= ~0x7040; + *reg = state; + } +} + +/* Set EP DTOG_TX bit (IN) */ +static void EpCtrlSet_DTOG_TX(reg32_t *reg, uint32_t val) +{ + val = val ? (*reg ^ (1UL << 6)) : *reg; + /* Clear the toggle bits without DTOG_TX (6) */ + val &= ~0x7030; + val |= USB_CTRL_CLEAR_ONLY_MASK; + *reg = val; +} + +/* Clear EP CTR_TX bit (IN) */ +static void EpCtrlClr_CTR_TX(reg32_t *reg) +{ + uint32_t val = *reg; + + val &= ~(USB_CTRL_TOGGLE_MASK | 1UL << 7); + /* Set RX_CTR */ + val |= 1UL << 15; + *reg = val; +} + +/* Clear EP CTR_RX bit (OUT) */ +static void EpCtrlClr_CTR_RX(reg32_t *reg) +{ + uint32_t val = *reg; + val &= ~(USB_CTRL_TOGGLE_MASK | 1UL << 15); + /* Set TX_CTR */ + val |= 1UL << 7; + *reg = val; +} + +/* Set EP KIND bit */ +static void EpCtrlSet_EP_KIND(reg32_t *reg, uint32_t val) +{ + val = val ? (1UL << 8) : 0; + val |= *reg & ~(USB_CTRL_TOGGLE_MASK | (1UL << 8)); + val |= USB_CTRL_CLEAR_ONLY_MASK; + *reg = val; +} + +/* Set EP type */ +static int EpCtrlSet_EP_TYPE(reg32_t *reg, uint8_t val) +{ + uint32_t type; + + if (UNLIKELY(val >= EP_TYPE_MAX)) + { + ASSERT(0); + return USB_INVAL_ERROR; + } + type = val; + type <<= 9; + type |= *reg & ~(USB_CTRL_TOGGLE_MASK | (0x3UL << 9)); + type |= USB_CTRL_CLEAR_ONLY_MASK; + *reg = type; + + return USB_OK; +} + +/* Get EP STAT_RX (OUT) */ +static uint32_t EpCtrlGet_STAT_RX(reg32_t *reg) +{ + uint32_t val = *reg & (0x3UL << 12); + return val >> 12; +} + +/* Set EP STAT_RX (OUT) */ +static void EpCtrlSet_STAT_RX(reg32_t *reg, ep_state_t val) +{ + uint32_t state; + int i; + + /* + * The EP can change state between read and write operations from VALID + * to NAK and result of set operation will be invalid. + */ + for (i = 0; i < 2; i++) + { + if (EpCtrlGet_STAT_RX(reg) == val) + return; + state = val; + state <<= 12; + state ^= *reg; + state |= USB_CTRL_CLEAR_ONLY_MASK; + /* Clear the toggle bits without STAT_RX (12,13) */ + state &= ~0x4070; + *reg = state; + } +} + +/* Set DTOG_RX bit */ +static void EpCtrlSet_DTOG_RX(reg32_t *reg, uint32_t val) +{ + val = val ? (*reg ^ (1UL << 14)) : *reg; + /* Clear the toggle bits without DTOG_RX (14) */ + val &= ~0x3070; + val |= USB_CTRL_CLEAR_ONLY_MASK; + *reg = val; +} + +/* Get EP SETUP bit */ +static uint32_t EpCtrlGet_SETUP(reg32_t *reg) +{ + uint32_t val = *reg & (1UL << 11); + return val ? 1 : 0; +} + +/* Core endpoint I/O function */ +static void __usb_ep_io(int EP) +{ + ssize_t Count, CountHold, Offset; + uint32_t *pDst, *pSrc, Data; + bool CurrentBuffer; + stm32_usb_ep_t *epd = &ep_cnfg[EP]; + + ASSERT(epd->hw); + if (epd->status != BEGIN_SERVICED && epd->status != NO_SERVICED) + return; + + if (EP & 0x01) + { + /* EP IN */ + Count = epd->size - epd->offset; + while (epd->avail_data) + { + if (!Count && !(epd->flags & STM32_USB_EP_ZERO_PACKET)) + break; + + /* Set Status */ + epd->status = BEGIN_SERVICED; + /* Get data size */ + if ((epd->flags & STM32_USB_EP_ZERO_PACKET) && + (Count == epd->max_size)) + epd->flags |= STM32_USB_EP_ZERO_PACKET | + STM32_USB_EP_ZERO_POSSIBLE; + + CountHold = Count = MIN(Count, epd->max_size); + if (!Count) + epd->flags |= STM32_USB_EP_ZERO_PACKET; + Offset = epd->offset; + epd->offset += Count; + CurrentBuffer = true; + switch (epd->type) + { + case USB_ENDPOINT_XFER_CONTROL: + case USB_ENDPOINT_XFER_INT: + pDst = (uint32_t *)addr2usbmem(ReadEpDTB_AddrTx(EP >> 1)); + break; + case USB_ENDPOINT_XFER_BULK: + pDst = (uint32_t *)addr2usbmem(ReadEpDTB_AddrTx(EP >> 1)); + break; + case USB_ENDPOINT_XFER_ISOC: + LOG_ERR("%s: isochronous transfer not supported\n", + __func__); + /* Fallback to default */ + default: + ASSERT(0); + return; + } + + /* Write data to packet memory buffer */ + while (Count) + { + Data = *(epd->write_buffer + Offset++); + if (--Count) + { + Data |= (uint32_t)(*(epd->write_buffer + Offset++)) << 8; + --Count; + } + *pDst++ = Data; + } + + if (CurrentBuffer) + WriteEpDTB_CountTx(EP >> 1, CountHold); + else + WriteEpDTB_CountRx(EP >> 1, CountHold); + + EpCtrlSet_STAT_TX(epd->hw, EP_VALID); + + --ep_cnfg[EP].avail_data; + Count = epd->size - epd->offset; + } + if (!Count && !(epd->flags & STM32_USB_EP_ZERO_PACKET)) + { + epd->status = COMPLETE; + /* call callback function */ + if (epd->complete) + epd->complete(EP); + } + } + else + { + /* EP OUT */ + while (epd->avail_data) + { + /* Get data size and buffer pointer */ + switch (epd->type) + { + case USB_ENDPOINT_XFER_CONTROL: + case USB_ENDPOINT_XFER_INT: + /* Get received bytes number */ + Count = ReadEpDTB_CountRx(EP >> 1) & 0x3FF; + /* Get address of the USB packet buffer for corresponding EP */ + pSrc = (uint32_t *)addr2usbmem(ReadEpDTB_AddrRx(EP >> 1)); + break; + case USB_ENDPOINT_XFER_BULK: + /* Get received bytes number */ + Count = ReadEpDTB_CountRx(EP >> 1) & 0x3FF; + /* Get address of the USB packet buffer for corresponding EP */ + pSrc = (uint32_t *)addr2usbmem(ReadEpDTB_AddrRx(EP >> 1)); + break; + case USB_ENDPOINT_XFER_ISOC: + LOG_ERR("%s: isochronous transfer not supported\n", + __func__); + /* Fallback to default */ + default: + ASSERT(0); + return; + } + + if (Count > (epd->size - epd->offset)) + { + epd->status = BUFFER_OVERRUN; + epd->size = ep_cnfg[EP].offset; + break; + } + else if (Count < ep_cnfg[EP].max_size) + { + epd->status = BUFFER_UNDERRUN; + epd->size = ep_cnfg[EP].offset + Count; + } + else + epd->status = BEGIN_SERVICED; + + Offset = epd->offset; + epd->offset += Count; + + /* Read data from packet memory buffer */ + while (Count) + { + Data = *pSrc++; + *(epd->read_buffer + Offset++) = Data; + if (--Count) + { + Data >>= 8; + *(epd->read_buffer + Offset++) = Data; + --Count; + } + } + + EpCtrlSet_STAT_RX(epd->hw, EP_VALID); + + --ep_cnfg[EP].avail_data; + + if (*epd->hw & (1UL << 11)) + { + ep_cnfg[EP].status = SETUP_OVERWRITE; + return; + } + if (!(Count = (epd->size - epd->offset))) + { + epd->status = COMPLETE; + break; + } + } + if (epd->status != BEGIN_SERVICED && epd->status != NO_SERVICED) + { + /* call callback function */ + if (epd->complete) + epd->complete(EP); + } + } +} + +/* Configure an EP descriptor before performing a I/O operation */ +#define USB_EP_IO(__EP, __op, __buf, __size, __complete) \ +({ \ + cpu_flags_t flags; \ + stm32_usb_io_status_t ret; \ + \ + /* NOTE: buffer must be 4-bytes aligned */ \ + ASSERT(!((size_t)__buf & 0x03)); \ + \ + /* Fill EP descriptor */ \ + IRQ_SAVE_DISABLE(flags); \ + if (__size < 0) \ + { \ + ep_cnfg[__EP].status = NOT_READY; \ + ep_cnfg[__EP].complete = NULL; \ + ret = NOT_READY; \ + goto out; \ + } \ + if (ep_cnfg[__EP].status == BEGIN_SERVICED) \ + { \ + ret = NOT_READY; \ + goto out; \ + } \ + /* \ + * NOTE: the write_buffer and read_buffer are actually the \ + * same location in memory (it's a union). \ + * \ + * We have to do this trick to silent a build warning by \ + * casting the I/O buffer to (void *) or (const void *). \ + */ \ + ep_cnfg[__EP].__op ## _buffer = __buf; \ + ep_cnfg[__EP].offset = 0; \ + ep_cnfg[__EP].size = __size; \ + ep_cnfg[__EP].complete = __complete; \ + if (!size) \ + ep_cnfg[__EP].flags = STM32_USB_EP_ZERO_PACKET; \ + else \ + ep_cnfg[__EP].flags = 0; \ + ep_cnfg[__EP].status = NO_SERVICED; \ + \ + /* Perform the I/O operation */ \ + __usb_ep_io(__EP); \ + \ + ret = ep_cnfg[__EP].status; \ +out: \ + IRQ_RESTORE(flags); \ + ret; \ +}) + +/* Configure and endponint and perform a read operation */ +static stm32_usb_io_status_t +__usb_ep_read(int ep, void *buffer, ssize_t size, void (*complete)(int)) +{ + if (UNLIKELY(ep >= ENP_MAX_NUMB)) + { + ASSERT(0); + return STALLED; + } + ASSERT(!(ep & 0x01)); + return USB_EP_IO(ep, read, buffer, size, complete); +} + +/* Configure and endponint and perform a write operation */ +static stm32_usb_io_status_t +__usb_ep_write(int ep, const void *buffer, ssize_t size, void (*complete)(int)) +{ + if (UNLIKELY(ep >= ENP_MAX_NUMB)) + { + ASSERT(0); + return STALLED; + } + ASSERT(ep & 0x01); + return USB_EP_IO(ep, write, buffer, size, complete); +} + +static bool rx_done; +static size_t rx_size; + +static void usb_ep_read_complete(int ep) +{ + if (UNLIKELY(ep >= ENP_MAX_NUMB)) + { + ASSERT(0); + return; + } + ASSERT(!(ep & 0x01)); + + rx_done = true; + rx_size = ep_cnfg[ep].size; +} + +ssize_t usb_ep_read(int ep, void *buffer, ssize_t size) +{ + if (UNLIKELY(!size)) + return 0; + size = MIN(size, USB_RX_MAX_SIZE); + rx_done = false; + rx_size = 0; + + /* Blocking read */ + __usb_ep_read(USB_EpLogToPhysAdd(ep), buffer, size, + usb_ep_read_complete); + while (!rx_done) + cpu_relax(); + + return rx_size; +} + +static bool tx_done; +static size_t tx_size; + +static void usb_ep_write_complete(int ep) +{ + if (UNLIKELY(ep >= ENP_MAX_NUMB)) + { + ASSERT(0); + return; + } + ASSERT(ep & 0x01); + + tx_done = true; + tx_size = ep_cnfg[ep].size; +} + +ssize_t usb_ep_write(int ep, const void *buffer, ssize_t size) +{ + if (UNLIKELY(!size)) + return 0; + size = MIN(size, USB_TX_MAX_SIZE); + tx_done = false; + tx_size = 0; + + /* Blocking write */ + __usb_ep_write(USB_EpLogToPhysAdd(ep), buffer, size, + usb_ep_write_complete); + while (!tx_done) + cpu_relax(); + + return tx_size; +} + +static void usb_ep_low_level_config(int ep, uint16_t offset, uint16_t size) +{ + stm32_usb_ep_t *epc = &ep_cnfg[ep]; + + /* IN EP */ + if (ep & 0x01) + { + /* Disable EP */ + EpCtrlSet_STAT_TX(epc->hw, EP_DISABLED); + /* Clear Tx toggle */ + EpCtrlSet_DTOG_TX(epc->hw, 0); + /* Clear Correct Transfer for transmission flag */ + EpCtrlClr_CTR_TX(epc->hw); + + /* Update EP description table */ + WriteEpDTB_AddrTx(ep >> 1, offset); + WriteEpDTB_CountTx(ep >> 1, 0); + } + /* OUT EP */ + else + { + uint16_t rx_count = 0; + + /* Disable EP */ + EpCtrlSet_STAT_RX(epc->hw, EP_DISABLED); + /* Clear Rx toggle */ + EpCtrlSet_DTOG_RX(epc->hw, 0); + /* Clear Correct Transfer for reception flag */ + EpCtrlClr_CTR_RX(epc->hw); + /* Descriptor block size field */ + rx_count |= (size > 62) << 15; + /* Descriptor number of blocks field */ + rx_count |= (((size > 62) ? (size >> 5) - 1 : size >> 1) & + 0x1f) << 10; + /* Update EP description table */ + WriteEpDTB_AddrRx(ep >> 1, offset); + WriteEpDTB_CountRx(ep >> 1, rx_count); + } +} + +/* Enable/Disable an endpoint */ +static int usb_ep_configure(const usb_endpoint_descriptor_t *epd, bool enable) +{ + int EP; + stm32_usb_ep_t *ep_hw; + reg32_t *hw; + uint16_t Offset; + uint32_t MaxPacketSizeTmp; + + EP = USB_EpLogToPhysAdd(epd->bEndpointAddress); + ep_hw = &ep_cnfg[EP]; + + if (enable) + { + /* + * Allocate packet memory for EP buffer/s calculate actual size + * only for the OUT EPs. + */ + MaxPacketSizeTmp = epd->wMaxPacketSize; + if (!USB_AllocateBuffer(&Offset, &MaxPacketSizeTmp, EP)) + return -USB_MEMORY_FULL; + + /* Set EP status */ + ep_hw->status = NOT_READY; + /* Init EP flags */ + ep_hw->flags = 0; + + /* Set endpoint type */ + ep_hw->type = usb_endpoint_type(epd); + /* Init EP max packet size */ + ep_hw->max_size = epd->wMaxPacketSize; + + if (EP & 0x01) + ep_hw->avail_data = 1; + else + ep_hw->avail_data = 0; + hw = (reg32_t *)&usb->EP0R; + hw += EP >> 1; + + /* Set Ep Address */ + EpCtrlSet_EA(hw, EP >> 1); + ep_hw->hw = hw; + + /* Low-level endpoint configuration */ + usb_ep_low_level_config(EP, Offset, MaxPacketSizeTmp); + + /* Set EP Kind & enable */ + switch (ep_hw->type) + { + case USB_ENDPOINT_XFER_CONTROL: + LOG_INFO("EP%d: CONTROL IN\n", EP >> 1); + EpCtrlSet_EP_TYPE(hw, EP_CTRL); + EpCtrlSet_EP_KIND(hw, 0); + break; + case USB_ENDPOINT_XFER_INT: + LOG_INFO("EP%d: INTERRUPT IN\n", EP >> 1); + EpCtrlSet_EP_TYPE(hw, EP_INTERRUPT); + EpCtrlSet_EP_KIND(hw, 0); + break; + case USB_ENDPOINT_XFER_BULK: + LOG_INFO("EP%d: BULK IN\n", EP >> 1); + EpCtrlSet_EP_TYPE(hw, EP_BULK); + EpCtrlSet_EP_KIND(hw, 0); + break; + case USB_ENDPOINT_XFER_ISOC: + LOG_ERR("EP%d: ISOCHRONOUS IN: not supported\n", EP >> 1); + /* Fallback to default */ + default: + ASSERT(0); + return -USB_NODEV_ERROR; + } + if (EP & 0x01) + { + /* Enable EP */ + EpCtrlSet_STAT_TX(hw, EP_NAK); + /* Clear Correct Transfer for transmission flag */ + EpCtrlClr_CTR_TX(hw); + } + else + { + /* Enable EP */ + EpCtrlSet_STAT_RX(hw, EP_VALID); + } + } + else if (ep_cnfg[EP].hw) + { + hw = (reg32_t *)&usb->EP0R; + hw += EP >> 1; + + /* IN EP */ + if (EP & 0x01) + { + /* Disable IN EP */ + EpCtrlSet_STAT_TX(hw, EP_DISABLED); + /* Clear Correct Transfer for reception flag */ + EpCtrlClr_CTR_TX(hw); + } + /* OUT EP */ + else + { + /* Disable OUT EP */ + EpCtrlSet_STAT_RX(hw, EP_DISABLED); + /* Clear Correct Transfer for reception flag */ + EpCtrlClr_CTR_RX(hw); + } + /* Release buffer */ + USB_ReleaseBuffer(EP); + ep_cnfg[EP].hw = NULL; + } + return 0; +} + +/* Get EP stall/unstall */ +static int USB_GetStallEP(int EP, bool *pStall) +{ + if (ep_cnfg[EP].hw == NULL) + return -USB_NODEV_ERROR; + + *pStall = (EP & 0x01) ? + (EpCtrlGet_STAT_TX(ep_cnfg[EP].hw) == EP_STALL): /* IN EP */ + (EpCtrlGet_STAT_RX(ep_cnfg[EP].hw) == EP_STALL); /* OUT EP */ + + return USB_OK; +} + +/* Set EP stall/unstall */ +static int USB_SetStallEP(int EP, bool Stall) +{ + if (ep_cnfg[EP].hw == NULL) + return -USB_NODEV_ERROR; + + if (Stall) + { + ep_cnfg[EP].status = STALLED; + if (EP & 0x01) + { + /* IN EP */ + EpCtrlSet_STAT_TX(ep_cnfg[EP].hw, EP_STALL); + ep_cnfg[EP].avail_data = 1; + } + else + { + /* OUT EP */ + EpCtrlSet_STAT_RX(ep_cnfg[EP].hw, EP_STALL); + ep_cnfg[EP].avail_data = 0; + } + } + else + { + ep_cnfg[EP].status = NOT_READY; + if(EP & 0x01) + { + /* IN EP */ + ep_cnfg[EP].avail_data = 1; + /* reset Data Toggle bit */ + EpCtrlSet_DTOG_TX(ep_cnfg[EP].hw, 0); + EpCtrlSet_STAT_TX(ep_cnfg[EP].hw, EP_NAK); + } + else + { + /* OUT EP */ + ep_cnfg[EP].avail_data = 0; + /* reset Data Toggle bit */ + EpCtrlSet_DTOG_RX(ep_cnfg[EP].hw, 0); + EpCtrlSet_STAT_RX(ep_cnfg[EP].hw, EP_VALID); + } + } + return USB_OK; +} + +/* Stall both directions of the control EP */ +static void USB_StallCtrlEP(void) +{ + ep_cnfg[CTRL_ENP_IN].avail_data = 1; + ep_cnfg[CTRL_ENP_IN].status = STALLED; + ep_cnfg[CTRL_ENP_OUT].avail_data = 0; + ep_cnfg[CTRL_ENP_OUT].status = STALLED; + + USB_SetStallEP(CTRL_ENP_IN, true); + USB_SetStallEP(CTRL_ENP_OUT, true); +} + +/* + * Find the position of an interface descriptor inside the configuration + * descriptor. + */ +static int usb_find_interface(uint32_t num, uint32_t alt) +{ + usb_interface_descriptor_t *id; + int i; + + for (i = 0; ; i++) + { + /* TODO: support more than one configuration per device */ + id = (usb_interface_descriptor_t *)usb_dev->config[i]; + if (id == NULL) + break; + if (id->bDescriptorType != USB_DT_INTERFACE) + continue; + if ((id->bInterfaceNumber == num) && + (id->bAlternateSetting == alt)) + return i; + } + return -USB_NODEV_ERROR; +} + +/* + * Configure/deconfigure EPs of a certain interface. + */ +static void +usb_configure_ep_interface(unsigned int num, unsigned int alt, bool enable) +{ + usb_endpoint_descriptor_t *epd; + int i, start; + + /* + * Find the position of the interface descriptor (inside the + * configuration descriptor). + */ + start = usb_find_interface(num, alt); + if (start < 0) + { + LOG_ERR("%s: interface (%u,%u) not found\n", + __func__, num, alt); + return; + } + /* + * Cycle over endpoint descriptors. + * + * NOTE: the first endpoint descriptor is placed next to the interface + * descriptor, so we need to add +1 to the position of the interface + * descriptor to find it. + */ + for (i = start + 1; ; i++) + { + epd = (usb_endpoint_descriptor_t *)usb_dev->config[i]; + if ((epd == NULL) || (epd->bDescriptorType != USB_DT_ENDPOINT)) + break; + if (UNLIKELY(usb_ep_configure(epd, enable) < 0)) + { + LOG_ERR("%s: out of memory, can't initialize EP\n", + __func__); + return; + } + } +} + +/* Set device state */ +static void usb_set_device_state(int state) +{ + unsigned int i; + + LOG_INFO("%s: new state %d\n", __func__, state); + + if (udc.state == USB_STATE_CONFIGURED) + { + /* Deconfigure device */ + for (i = 0; i < udc.interfaces; ++i) + usb_configure_ep_interface(i, + udc.alt[i], false); + } + switch (state) + { + case USB_STATE_ATTACHED: + case USB_STATE_POWERED: + case USB_STATE_DEFAULT: + usb_set_address(0); + usb_dev->configured = false; + udc.address = udc.cfg_id = 0; + break; + case USB_STATE_ADDRESS: + udc.cfg_id = 0; + break; + case USB_STATE_CONFIGURED: + /* Configure device */ + for (i = 0; i < udc.interfaces; ++i) + usb_configure_ep_interface(i, + udc.alt[i], true); + break; + default: + /* Unknown state: disconnected or connection in progress */ + usb_dev->configured = false; + udc.address = 0; + udc.cfg_id = 0; + break; + } + udc.state = state; +} + +/* Setup packet: set address status phase end handler */ +static void USB_AddStatusEndHandler(UNUSED_ARG(int, EP)) +{ + uint16_t w_value; + + w_value = usb_le16_to_cpu(setup_packet.wValue); + udc.address = w_value & 0xff; + usb_set_address(udc.address); + + if (udc.address) + usb_set_device_state(USB_STATE_ADDRESS); + else + usb_set_device_state(USB_STATE_DEFAULT); + + __usb_ep_write(CTRL_ENP_IN, NULL, -1, NULL); + __usb_ep_read(CTRL_ENP_OUT, NULL, -1, NULL); +} + +/* Prepare status phase */ +static void USB_StatusPhase(bool in) +{ + if (in) + __usb_ep_write(CTRL_ENP_IN, NULL, 0, NULL); +} + +/* Setup packet: status phase end handler */ +static void USB_StatusEndHandler(UNUSED_ARG(int, EP)) +{ + __usb_ep_write(CTRL_ENP_IN, NULL, -1, NULL); + __usb_ep_read(CTRL_ENP_OUT, NULL, -1, NULL); +} + +/* Address status handler */ +static void USB_StatusHandler(UNUSED_ARG(int, EP)) +{ + if (setup_packet.mRequestType & USB_DIR_IN) + { + USB_StatusPhase(false); + ep_cnfg[CTRL_ENP_OUT].complete = USB_StatusEndHandler; + } + else + { + USB_StatusPhase(true); + ep_cnfg[CTRL_ENP_IN].complete = + (setup_packet.bRequest == USB_REQ_SET_ADDRESS) ? + USB_AddStatusEndHandler : + USB_StatusEndHandler; + } +} + +/* Global variable to handle the following non-blocking I/O operations */ +static uint32_t InData; + +/* Get device status */ +static int UsbDevStatus(uint16_t index) +{ + if (index) + return -USB_NODEV_ERROR; + + InData = ((uint32_t)udc.feature) & 0xff; + __usb_ep_write(CTRL_ENP_IN, (uint8_t *)&InData, 2, USB_StatusHandler); + + return 0; +} + +/* Get interface status */ +static int UsbInterfaceStatus(UNUSED_ARG(uint16_t, index)) +{ + InData = 0; + __usb_ep_write(CTRL_ENP_IN, (uint8_t *)&InData, 2, USB_StatusHandler); + + return 0; +} + +/* Get endpoint status */ +static int UsbEpStatus(uint16_t index) +{ + if ((index & 0x7F) > 16) + return -USB_NODEV_ERROR; + + InData = 0; + USB_GetStallEP(USB_EpLogToPhysAdd(index), (bool *)&InData); + __usb_ep_write(CTRL_ENP_IN, (uint8_t *)&InData, 2, USB_StatusHandler); + + return 0; +} + +/* USB setup packet: GET_STATUS request handler */ +static void USB_GetStatusHandler(void) +{ + uint16_t w_value = usb_le16_to_cpu(setup_packet.wValue); + uint16_t w_index = usb_le16_to_cpu(setup_packet.wIndex); + uint16_t w_length = usb_le16_to_cpu(setup_packet.wLength); + + /* GET_STATUS sanity checks */ + if (udc.state < USB_STATE_ADDRESS) + { + LOG_WARN("%s: bad GET_STATUS request (State=%02x)\n", + __func__, udc.state); + ep_cnfg[CTRL_ENP_OUT].status = STALLED; + return; + } + if (w_length != 2) + { + LOG_WARN("%s: bad GET_STATUS request (wLength.Word=%02x)\n", + __func__, w_length); + ep_cnfg[CTRL_ENP_OUT].status = STALLED; + return; + } + if (!(setup_packet.mRequestType & USB_DIR_IN)) + { + LOG_WARN("%s: bad GET_STATUS request (mRequestType=%02x)\n", + __func__, setup_packet.mRequestType); + ep_cnfg[CTRL_ENP_OUT].status = STALLED; + return; + } + if (w_value) + { + LOG_WARN("%s: bad GET_STATUS request (wValue=%02x)\n", + __func__, w_value); + ep_cnfg[CTRL_ENP_OUT].status = STALLED; + return; + } + + /* Process GET_STATUS request */ + switch (setup_packet.mRequestType & USB_RECIP_MASK) + { + case USB_RECIP_DEVICE: + if (UsbDevStatus(w_index) < 0) + { + LOG_WARN("%s: GET_STATUS: invalid UsbRecipientDevice\n", + __func__); + ep_cnfg[CTRL_ENP_OUT].status = STALLED; + return; + } + LOG_INFO("%s: GET_STATUS: mRequestType=%02x (UsbRecipientDevice)\n", + __func__, setup_packet.mRequestType); + break; + case USB_RECIP_INTERFACE: + if (UsbInterfaceStatus(w_index) < 0) + { + LOG_WARN("%s: GET_STATUS: invalid UsbRecipientInterface\n", + __func__); + ep_cnfg[CTRL_ENP_OUT].status = STALLED; + return; + } + LOG_INFO("%s: GET_STATUS: mRequestType=%02x (UsbRecipientInterface)\n", + __func__, setup_packet.mRequestType); + break; + case USB_RECIP_ENDPOINT: + if (UsbEpStatus(w_index) < 0) + { + LOG_WARN("%s: GET_STATUS: invalid UsbRecipientEndpoint\n", + __func__); + ep_cnfg[CTRL_ENP_OUT].status = STALLED; + return; + } + LOG_INFO("%s: GET_STATUS: mRequestType=%02x (UsbRecipientEndpoint)\n", + __func__, setup_packet.mRequestType); + break; + default: + LOG_WARN("%s: GET_STATUS: invalid UsbRecipientEndpoint\n", + __func__); + ep_cnfg[CTRL_ENP_OUT].status = STALLED; + break; + } +} + +/* + * Return the lower value from Host expected size and size and set a flag + * STM32_USB_EP_ZERO_POSSIBLE when size is lower that host expected size. + */ +static size_t usb_size(size_t size, size_t host_size) +{ + if (size < host_size) + { + ep_cnfg[CTRL_ENP_IN].flags |= STM32_USB_EP_ZERO_POSSIBLE; + return size; + } + return host_size; +} + +static int usb_get_device_descriptor(int id) +{ + if (id) + return -USB_NODEV_ERROR; + + usb_dev->device->bMaxPacketSize0 = USB_EP0_MAX_SIZE; + __usb_ep_write(CTRL_ENP_IN, (const uint8_t *)usb_dev->device, + usb_size(usb_dev->device->bLength, + setup_packet.wLength), + USB_StatusHandler); + return 0; +} + +#define USB_BUFSIZE (128) +static uint8_t usb_cfg_buffer[USB_BUFSIZE]; + +static int usb_get_configuration_descriptor(int id) +{ + const usb_config_descriptor_t **config = + (const usb_config_descriptor_t **)usb_dev->config; + uint8_t *p = usb_cfg_buffer; + int i; + + /* TODO: support more than one configuration per device */ + if (UNLIKELY(id > 0)) + return -USB_NODEV_ERROR; + + for (i = 0; config[i]; i++) + { + memcpy(p, config[i], config[i]->bLength); + p += config[i]->bLength; + + if (UNLIKELY((p - usb_cfg_buffer) > USB_BUFSIZE)) + { + ASSERT(0); + return -USB_BUF_OVERFLOW; + } + } + ((usb_config_descriptor_t *)usb_cfg_buffer)->wTotalLength = + usb_cpu_to_le16(p - usb_cfg_buffer); + __usb_ep_write(CTRL_ENP_IN, + usb_cfg_buffer, + usb_size(p - usb_cfg_buffer, + setup_packet.wLength), + USB_StatusHandler); + return 0; +} + +static int usb_get_string_descriptor(unsigned int id) +{ + usb_string_descriptor_t *lang_str; + unsigned int lang_id, str_id; + uint16_t w_index_lo = usb_le16_to_cpu(setup_packet.wIndex) & 0x00ff; + uint16_t w_index_hi = (usb_le16_to_cpu(setup_packet.wIndex) & 0xff00) >> 8; + + ASSERT(usb_dev->strings != NULL); + ASSERT(usb_dev->strings[0] != NULL); + + lang_str = usb_dev->strings[0]; + if (id) + { + /* Find Language index */ + for (lang_id = 0; ; lang_id++) + { + usb_string_descriptor_t *str = usb_dev->strings[lang_id]; + + if (UNLIKELY(str == NULL)) + return -USB_NODEV_ERROR; + if ((str->data[0] == w_index_lo) && + (str->data[1] == w_index_hi)) + break; + } + /* Check buffer overflow to find string index */ + for (str_id = 0; str_id < id; str_id++) + { + lang_str = usb_dev->strings[lang_id + 1 + str_id]; + if (lang_str == NULL) + return -USB_NODEV_ERROR; + } + } + __usb_ep_write(CTRL_ENP_IN, + lang_str, + usb_size(lang_str->bLength, setup_packet.wLength), + USB_StatusHandler); + return 0; +} + +static void UsbGetDescriptor(void) +{ + uint16_t w_value_lo = usb_le16_to_cpu(setup_packet.wValue) & 0x00ff; + uint16_t w_value_hi = (usb_le16_to_cpu(setup_packet.wValue) & 0xff00) >> 8; + + if (udc.state < USB_STATE_DEFAULT) + { + ep_cnfg[CTRL_ENP_OUT].status = STALLED; + return; + } + switch (w_value_hi) + { + case USB_DT_DEVICE: + LOG_INFO("%s: GET_DEVICE_DESCRIPTOR: id=%d, state=%d\n", + __func__, + w_value_lo, + udc.state); + if (usb_get_device_descriptor(w_value_lo) < 0) + ep_cnfg[CTRL_ENP_OUT].status = STALLED; + break; + case USB_DT_CONFIG: + LOG_INFO("%s: GET_CONFIG_DESCRIPTOR: id=%d, state=%d\n", + __func__, w_value_lo, udc.state); + if (usb_get_configuration_descriptor(w_value_lo) < 0) + ep_cnfg[CTRL_ENP_OUT].status = STALLED; + break; + case USB_DT_STRING: + LOG_INFO("%s: GET_STRING_DESCRIPTOR: id=%d, state=%d\n", + __func__, w_value_lo, udc.state); + if (usb_get_string_descriptor(w_value_lo) < 0) + ep_cnfg[CTRL_ENP_OUT].status = STALLED; + break; + default: + LOG_WARN("%s: GET_UNKNOWN_DESCRIPTOR: id=%d, state=%d\n", + __func__, w_value_lo, udc.state); + ep_cnfg[CTRL_ENP_OUT].status = STALLED; + break; + } +} + +/* USB setup packet: GET_DESCRIPTOR handler */ +static void UBS_GetDescriptorHandler(void) +{ + LOG_INFO("%s: GET_DESCRIPTOR: RECIP = %d\n", + __func__, + setup_packet.mRequestType & USB_RECIP_MASK); + if ((setup_packet.mRequestType & USB_RECIP_MASK) == + USB_RECIP_DEVICE) + UsbGetDescriptor(); + else + ep_cnfg[CTRL_ENP_OUT].status = STALLED; +} + +/* USB setup packet: SET_ADDRESS handler */ +static void USB_SetAddressHandler(void) +{ + uint16_t w_value = usb_le16_to_cpu(setup_packet.wValue); + uint16_t w_index = usb_le16_to_cpu(setup_packet.wIndex); + uint16_t w_length = usb_le16_to_cpu(setup_packet.wLength); + + LOG_INFO("%s: SET_ADDRESS: %d\n", + __func__, usb_le16_to_cpu(setup_packet.wValue)); + if ((udc.state >= USB_STATE_DEFAULT) && + ((setup_packet.mRequestType & USB_RECIP_MASK) == + USB_RECIP_DEVICE) && + (w_index == 0) && (w_length == 0) && (w_value < 128)) + USB_StatusHandler(CTRL_ENP_IN); + else + ep_cnfg[CTRL_ENP_OUT].status = STALLED; +} + +/* USB setup packet: GET_CONFIGURATION handler */ +static void USB_GetConfigurationHandler(void) +{ + uint16_t w_value = usb_le16_to_cpu(setup_packet.wValue); + uint16_t w_index = usb_le16_to_cpu(setup_packet.wIndex); + + LOG_INFO("%s: GET_CONFIGURATION\n", __func__); + if ((udc.state >= USB_STATE_ADDRESS) && + (w_value == 0) && (w_index == 0) && (w_value == 1)) + { + InData = udc.cfg_id; + __usb_ep_write(CTRL_ENP_IN, (uint8_t *)&InData, 1, USB_StatusHandler); + } + else + ep_cnfg[CTRL_ENP_OUT].status = STALLED; +} + +static const usb_config_descriptor_t *usb_find_configuration(int num) +{ + const usb_config_descriptor_t *cfg; + int i; + + for (i = 0; ; i++) + { + cfg = (const usb_config_descriptor_t *)usb_dev->config[i]; + if (cfg == NULL) + break; + if (cfg->bDescriptorType != USB_DT_CONFIG) + continue; + if (cfg->bConfigurationValue == num) + return cfg; + } + return NULL; +} + +static int UsbSetConfigurationState(uint32_t Configuration) +{ + const usb_config_descriptor_t *pCnfg; + unsigned int i; + + if (Configuration) + { + /* Find configuration descriptor */ + pCnfg = usb_find_configuration(Configuration); + if (pCnfg == NULL) + return -USB_NODEV_ERROR; + + /* Reset current configuration */ + usb_set_device_state(USB_STATE_ADDRESS); + usb_dev->configured = false; + udc.cfg = pCnfg; + + /* Set Interface and Alternative Setting */ + udc.cfg_id = Configuration; + /* Set self-powered state */ + if (pCnfg->bmAttributes & USB_CONFIG_ATT_SELFPOWER) + udc.feature |= STM32_UDC_FEATURE_SELFPOWERED; + + /* Configure all existing interfaces to alternative setting 0 */ + ASSERT(pCnfg->bNumInterfaces <= USB_MAX_INTERFACE); + udc.interfaces = pCnfg->bNumInterfaces; + for (i = 0; i < udc.interfaces; i++) + udc.alt[i] = 0; + usb_set_device_state(USB_STATE_CONFIGURED); + usb_dev->configured = true; + } + else + { + usb_dev->configured = false; + usb_set_device_state(USB_STATE_ADDRESS); + } + return 0; +} + +/* USB setup packet: SET_CONFIGURATION handler */ +static void USB_SetConfigurationHandler(void) +{ + uint16_t w_value = usb_le16_to_cpu(setup_packet.wValue); + uint16_t w_index = usb_le16_to_cpu(setup_packet.wIndex); + uint16_t w_length = usb_le16_to_cpu(setup_packet.wLength); + + LOG_INFO("%s: SET_CONFIGURATION: %d\n", + __func__, w_value); + if ((udc.state >= USB_STATE_ADDRESS) && + (w_index == 0) && (w_length == 0) && + (UsbSetConfigurationState(w_value & 0xff) == 0)) + USB_StatusHandler(CTRL_ENP_OUT); + else + ep_cnfg[CTRL_ENP_OUT].status = STALLED; +} + +/* USB setup packet: standard request handler */ +static void USB_StandardRequestHandler(void) +{ + switch (setup_packet.bRequest) + { + case USB_REQ_GET_STATUS: + USB_GetStatusHandler(); + break; + case USB_REQ_CLEAR_FEATURE: + LOG_INFO("%s: bRequest=%d (CLEAR_FEATURE)\n", + __func__, setup_packet.bRequest); + break; + case USB_REQ_SET_FEATURE: + LOG_INFO("%s: bRequest=%d (SET_FEATURE)\n", + __func__, setup_packet.bRequest); + break; + case USB_REQ_SET_ADDRESS: + USB_SetAddressHandler(); + break; + case USB_REQ_GET_DESCRIPTOR: + UBS_GetDescriptorHandler(); + break; + case USB_REQ_SET_DESCRIPTOR: + LOG_INFO("%s: bRequest=%d (SET_DESCRIPTOR)\n", + __func__, setup_packet.bRequest); + break; + case USB_REQ_GET_CONFIGURATION: + USB_GetConfigurationHandler(); + break; + case USB_REQ_SET_CONFIGURATION: + USB_SetConfigurationHandler(); + break; + case USB_REQ_GET_INTERFACE: + LOG_INFO("%s: bRequest=%d (GET_INTERFACE)\n", + __func__, setup_packet.bRequest); + break; + case USB_REQ_SET_INTERFACE: + LOG_INFO("%s: bRequest=%d (SET_INTERFACE)\n", + __func__, setup_packet.bRequest); + break; + case USB_REQ_SYNCH_FRAME: + LOG_INFO("%s: bRequest=%d (SYNCH_FRAME)\n", + __func__, setup_packet.bRequest); + break; + default: + LOG_WARN("%s: bRequest=%d (Unknown)\n", + __func__, setup_packet.bRequest); + ep_cnfg[CTRL_ENP_OUT].status = STALLED; + break; + } +} + +/* USB setup packet handler */ +static void USB_SetupHandler(void) +{ + switch (setup_packet.mRequestType & USB_TYPE_MASK) + { + /* Standard */ + case USB_TYPE_STANDARD: + LOG_INFO("%s: bmRequestType=%02x (Standard)\n", + __func__, setup_packet.mRequestType); + USB_StandardRequestHandler(); + break; + /* Class */ + case USB_TYPE_CLASS: + LOG_INFO("%s: bmRequestType=%02x (Class)\n", + __func__, setup_packet.mRequestType); + break; + /* Vendor */ + case USB_TYPE_VENDOR: + LOG_INFO("%s: bmRequestType=%02x (Vendor)\n", + __func__, setup_packet.mRequestType); + break; + case USB_TYPE_RESERVED: + LOG_INFO("%s: bmRequestType=%02x (Reserved)\n", + __func__, setup_packet.mRequestType); + break; + /* Other */ + default: + LOG_WARN("%s: bmRequestType=%02x (Unknown)\n", + __func__, setup_packet.mRequestType); + ep_cnfg[CTRL_ENP_OUT].status = STALLED; + break; + } +} + +static void usb_hw_reset(void) +{ + unsigned int i; + int ret; + + /* Initialize endpoint descriptors */ + for (i = 0; i < countof(ep_cnfg); i++) + ep_cnfg[i].hw = NULL; + + /* Initialize USB memory */ + for (i = 0; i < countof(PacketMemBuff); i++) + PacketMemBuff[i].Size = 0; + usb->BTABLE = USB_BDT_OFFSET; + pPacketMemUse = NULL; + + /* Endpoint initialization */ + ret = usb_ep_configure(&USB_CtrlEpDescr0, true); + if (UNLIKELY(ret < 0)) + { + LOG_WARN("%s: out of memory, cannot initialize EP0\n", + __func__); + return; + } + ret = usb_ep_configure(&USB_CtrlEpDescr1, true); + if (UNLIKELY(ret < 0)) + { + LOG_WARN("%s: out of memory, cannot initialize EP1\n", + __func__); + return; + } + + /* Set default address */ + usb_set_address(0); + + /* Enable all the device interrupts */ +#if 0 + usb->CNTR = bmCTRM | bmRESETM | bmSOFM | bmERRM | bmPMAOVRM | + bmSUSPM | bmWKUPM; +#else + /* XXX: disable frame interrupts for now (too much noise!) */ + usb->CNTR = bmCTRM | bmRESETM | bmERRM | bmPMAOVRM | bmSUSPM | bmWKUPM; +#endif +} + +/* Handle a correct transfer under ISR */ +static void usb_isr_correct_transfer(stm32_usb_irq_status_t interrupt) +{ + int EP; + reg32_t *pReg = (reg32_t *)&usb->EP0R; + + /* Find corresponding EP */ + pReg += interrupt.EP_ID; + EP = (int)(((*pReg & 0x0f) << 1) + (interrupt.DIR ? 0 : 1)); + ep_cnfg[EP].avail_data = 1; + + ASSERT(ep_cnfg[EP].hw); + /* IN EP */ + if (EP & 0x01) + EpCtrlClr_CTR_TX(ep_cnfg[EP].hw); + else + EpCtrlClr_CTR_RX(ep_cnfg[EP].hw); + if (EP == CTRL_ENP_OUT) + { + /* Determinate type of packet (only for control EP) */ + bool SetupPacket = EpCtrlGet_SETUP(ep_cnfg[CTRL_ENP_OUT].hw); + + if (SetupPacket) + { + ep_cnfg[CTRL_ENP_IN].avail_data = 1; + /* init IO to receive Setup packet */ + __usb_ep_write(CTRL_ENP_IN, NULL, -1, NULL); + __usb_ep_read(CTRL_ENP_OUT, &setup_packet, + sizeof(setup_packet), NULL); + + /* reset EP IO ctrl */ + if (setup_packet.mRequestType & USB_DIR_IN) + USB_StatusHandler(CTRL_ENP_OUT); + USB_SetupHandler(); + if (ep_cnfg[CTRL_ENP_OUT].status == STALLED) + USB_StallCtrlEP(); + } + else + { + if (ep_cnfg[CTRL_ENP_OUT].complete && + setup_packet.mRequestType & USB_DIR_IN) + ep_cnfg[CTRL_ENP_OUT].complete(CTRL_ENP_OUT); + else + __usb_ep_io(EP); + } + } + else if (EP == CTRL_ENP_IN) + { + if (ep_cnfg[CTRL_ENP_IN].complete && + !(setup_packet.mRequestType & USB_DIR_IN)) + ep_cnfg[CTRL_ENP_IN].complete(CTRL_ENP_IN); + else + __usb_ep_io(EP); + + } + else + __usb_ep_io(EP); +} + +/* USB: interrupt service routine */ +static void usb_isr(void) +{ + stm32_usb_irq_status_t interrupt; + + /* Get masked interrupt flags */ + interrupt.status = usb->ISTR; + interrupt.status &= usb->CNTR | 0x1f; + + if (interrupt.PMAOVR) + { + LOG_WARN("%s: DMA overrun / underrun\n", __func__); + usb->ISTR = ~bmPMAOVRM; + } + if (interrupt.ERR) + { + LOG_WARN("%s: engine error\n", __func__); + usb->ISTR = ~bmERRM; + } + if (interrupt.RESET) + { + LOG_INFO("%s: device reset\n", __func__); + usb->ISTR = ~bmRESETM; + usb_hw_reset(); + usb_set_device_state(USB_STATE_DEFAULT); + } + if (interrupt.SOF) + { + uint16_t frame_nr = usb->FNR & 0x0fff; + + LOG_INFO("%s: frame %#x\n", __func__, frame_nr); + usb->ISTR = ~bmSOFM; + } + if (interrupt.WKUP) + { + LOG_INFO("%s: wake-up\n", __func__); + usb->ISTR = ~(bmSUSPM | bmWKUPM); + usb_resume(); + } + if (interrupt.SUSP) + { + LOG_INFO("%s: suspend\n", __func__); + usb_suspend(); + usb->ISTR = ~(bmSUSPM | bmWKUPM); + } + if (interrupt.ESOF) + { + LOG_INFO("%s: expected frame\n", __func__); + usb->ISTR = ~bmESOFM; + } + if (interrupt.CTR) + { + usb_isr_correct_transfer(interrupt); + } +} + +/* USB: hardware initialization */ +static void usb_hw_init(void) +{ + /* Enable clocking on the required GPIO pins */ + RCC->APB2ENR |= RCC_APB2_GPIOA | RCC_APB2_GPIOC; + + /* Make sure that the CAN controller is disabled and held in reset */ + RCC->APB1ENR &= ~RCC_APB1_CAN; + + /* Configure USB_DM and USB_DP to work as USB lines */ + stm32_gpioPinConfig((struct stm32_gpio *)GPIOA_BASE, + USB_DM_PIN | USB_DP_PIN, + GPIO_MODE_AF_PP, GPIO_SPEED_50MHZ); + /* Configure USB_DISC to work as USB disconnect */ + stm32_gpioPinConfig((struct stm32_gpio *)GPIOC_BASE, + USB_DISC_PIN, + GPIO_MODE_OUT_PP, GPIO_SPEED_50MHZ); + stm32_gpioPinWrite((struct stm32_gpio *)GPIOC_BASE, + USB_DISC_PIN, 1); + + /* Ensure the USB clock is disabled before setting the prescaler */ + RCC->APB1ENR &= ~RCC_APB1_USB; + + /* Configure USB clock (48MHz) */ + *CFGR_USBPRE_BB &= ~RCC_USBCLK_PLLCLK_1DIV5; + + /* Activate USB clock */ + RCC->APB1ENR |= RCC_APB1_USB; + + /* Force USB reset and disable USB interrupts */ + usb->CNTR = bmFRES; + timer_delayHp(1); + + /* Issue a USB reset */ + usb_hw_reset(); + + /* Clear spurious pending interrupt */ + usb->ISTR = 0; + + /* Register interrupt handler */ + sysirq_setHandler(USB_LP_CAN_RX0_IRQHANDLER, usb_isr); + + /* Software connection enable */ + usb_connect(); +} + +/* Initialize the USB controller */ +static void usb_init(void) +{ + udc.state = USB_STATE_NOTATTACHED; + udc.feature = 0; + + usb_hw_init(); +} + +/* Register an upper layer USB device into the driver */ +int usb_device_register(struct usb_device *dev) +{ +#if CONFIG_KERN + MOD_CHECK(proc); +#endif + usb_dev = dev; + usb_init(); + while (!usb_dev->configured) + cpu_relax(); + return 0; +} diff --git a/bertos/cpu/cortex-m3/drv/usb_stm32.h b/bertos/cpu/cortex-m3/drv/usb_stm32.h new file mode 100644 index 00000000..b34619a7 --- /dev/null +++ b/bertos/cpu/cortex-m3/drv/usb_stm32.h @@ -0,0 +1,240 @@ +/** + * \file + * + * + * \author Andrea Righi + * + * \brief STM32: USB full-speed device driver + * + * Low-level USB device driver for the STM32 architecture. + */ + +#ifndef USB_STM32_H +#define USB_STM32_H + +#include +#include + +#define USB_BASE_ADDR 0x40005C00 + +#define USB_DM_PIN (1 << 11) +#define USB_DP_PIN (1 << 12) +#define USB_DISC_PIN (1 << 11) + +#define USB_EP0_MAX_SIZE 8 +#define USB_RX_MAX_SIZE 64 +#define USB_TX_MAX_SIZE 64 + +/* USB packet memory organization */ +#define USB_PACKET_MEMORY_BASE 0x40006000 +#define USB_PACKET_MEMORY_SIZE 512 + +/* Offset of the buffer descriptor table inside the packet memory */ +#define USB_BDT_OFFSET \ + ((USB_PACKET_MEMORY_SIZE - (sizeof(stm32_usb_bd_t) * ENP_MAX_NUMB)) & ~7) + +#define addr2usbmem(Offset) \ + (USB_PACKET_MEMORY_BASE + ((Offset << 1) & ~3) + (Offset & 1)) + +#define ReadEpDTB(Slot, Offset) \ + (*((uint16_t *)(addr2usbmem((USB_BDT_OFFSET + \ + (Slot) * sizeof(stm32_usb_bd_t) + \ + (Offset)))))) + +#define WriteEpDTB(Slot, Offset, Data) (ReadEpDTB(Slot, Offset) = Data) + +#define AddrTxOffset 0 +#define CountTxOffset 2 +#define AddrRxOffset 4 +#define CountRxOffset 6 + +#define ReadEpDTB_AddrRx(Slot) ReadEpDTB(Slot,AddrRxOffset) +#define ReadEpDTB_CountRx(Slot) ReadEpDTB(Slot,CountRxOffset) +#define ReadEpDTB_AddrTx(Slot) ReadEpDTB(Slot,AddrTxOffset) +#define ReadEpDTB_CountTx(Slot) ReadEpDTB(Slot,CountTxOffset) + +#define WriteEpDTB_AddrRx(Slot,Data) WriteEpDTB(Slot,AddrRxOffset,Data) +#define WriteEpDTB_CountRx(Slot,Data) WriteEpDTB(Slot,CountRxOffset,Data) +#define WriteEpDTB_AddrTx(Slot,Data) WriteEpDTB(Slot,AddrTxOffset,Data) +#define WriteEpDTB_CountTx(Slot,Data) WriteEpDTB(Slot,CountTxOffset,Data) + +#define USB_CTRL_RW_MASK 0x070F +#define USB_CTRL_CLEAR_ONLY_MASK 0x8080 +#define USB_CTRL_TOGGLE_MASK 0x7070 + +/* CNTR register flags */ +#define bmCTRM 0x8000 +#define bmPMAOVRM 0x4000 +#define bmERRM 0x2000 +#define bmWKUPM 0x1000 +#define bmSUSPM 0x0800 +#define bmRESETM 0x0400 +#define bmSOFM 0x0200 +#define bmESOFM 0x0100 + +#define bmRESUME 0x0010 +#define bmFSUSP 0x0008 +#define bmLPMODE 0x0004 +#define bmPDWN 0x0002 +#define bmFRES 0x0001 + +/* USB error codes */ +enum stm32_usb_error +{ + USB_OK = 0, + USB_INTR_ERROR, + USB_INVAL_ERROR, + USB_NODEV_ERROR, + USB_MEMORY_FULL, + USB_BUF_OVERFLOW, + USB_EP_STALLED, + USB_FATAL_ERROR, +}; + +/* STM32 USB endpoint types */ +enum stm32_usb_ep_type +{ + EP_BULK = 0, + EP_CTRL, + EP_ISO, + EP_INTERRUPT, + + EP_TYPE_MAX +}; + +/* STM32 USB interrupt status register bits */ +typedef union +{ + uint32_t status; + struct { + uint8_t EP_ID : 4; + uint8_t DIR : 1; + uint8_t : 2; + uint8_t SZDPR : 1; + uint8_t ESOF : 1; + uint8_t SOF : 1; + uint8_t RESET : 1; + uint8_t SUSP : 1; + uint8_t WKUP : 1; + uint8_t ERR : 1; + uint8_t PMAOVR : 1; + uint8_t CTR : 1; + }; +} PACKED stm32_usb_irq_status_t; + +/* Endpoint state */ +typedef enum +{ + EP_DISABLED = 0, + EP_STALL, + EP_NAK, + EP_VALID +} ep_state_t; + +/* STM32 USB supported endpoints */ +typedef enum +{ + CTRL_ENP_OUT = 0, CTRL_ENP_IN, + ENP1_OUT, ENP1_IN, + ENP2_OUT, ENP2_IN, + ENP3_OUT, ENP3_IN, + ENP4_OUT, ENP4_IN, + ENP5_OUT, ENP5_IN, + ENP6_OUT, ENP6_IN, + ENP7_OUT, ENP7_IN, + ENP8_OUT, ENP8_IN, + ENP9_OUT, ENP9_IN, + ENP10_OUT, ENP10_IN, + ENP11_OUT, ENP11_IN, + ENP12_OUT, ENP12_IN, + ENP13_OUT, ENP13_IN, + ENP14_OUT, ENP14_IN, + ENP15_OUT, ENP15_IN, + + ENP_MAX_NUMB +} stm32_usb_endpoint_t; + +/* STM32 USB packet memory slot */ +typedef struct pack_mem_slot_t +{ + stm32_usb_endpoint_t ep_addr; + uint16_t Start; + uint16_t Size; + struct pack_mem_slot_t *next; +} pack_mem_slot_t; + +/* STM32 USB buffer descriptor (packet memory) */ +typedef struct +{ + uint16_t AddrTx; + uint16_t CountTx; + uint16_t AddrRx; + uint16_t CountRx; +} PACKED stm32_usb_bd_t; + +/* STM32 USB endpoint I/O status */ +typedef enum +{ + NOT_READY = 0, + NO_SERVICED, + BEGIN_SERVICED, + COMPLETE, + BUFFER_UNDERRUN, + BUFFER_OVERRUN, + SETUP_OVERWRITE, + STALLED, +} stm32_usb_io_status_t; + +/* STM32 USB hardware endpoint descriptor */ +typedef struct +{ + reg32_t *hw; + uint8_t type; + void (*complete)(int); + ssize_t max_size; + ssize_t offset; + ssize_t size; + stm32_usb_io_status_t status; + union + { + uint8_t *read_buffer; + const uint8_t *write_buffer; + }; + int32_t avail_data; + uint8_t flags; +} stm32_usb_ep_t; + +/* STM32 USB hardware endpoint flags */ +#define STM32_USB_EP_AVAIL_DATA BV(0) +#define STM32_USB_EP_ZERO_PACKET BV(1) +#define STM32_USB_EP_ZERO_POSSIBLE BV(2) + +#endif /* USB_STM32_H */ diff --git a/bertos/drv/usb.h b/bertos/drv/usb.h new file mode 100644 index 00000000..b68d9bf6 --- /dev/null +++ b/bertos/drv/usb.h @@ -0,0 +1,459 @@ +/** + * \file + * + * + * \author Andrea Righi + * + * \brief USB 2.0 standard descriptors + * + * This file holds USB constants and structures that are needed for USB device + * APIs, as defined in the USB 2.0 specification. + * + * $WIZ$ module_name = "usb" + * $WIZ$ module_configuration = "bertos/cfg/cfg_usb.h" + * $WIZ$ module_supports = "stm32" + */ + +#ifndef USB_H +#define USB_H + +#include + +/* + * Handle CPU endianess + * + * TODO: consider to move this stuff in compiler.h + */ +#define usb_bswap16(x) (((x & 0xff) << 8) | (x >> 8)) +#define usb_bswap32(x) ((usb_bswap16(x & 0xffff) << 16) | usb_bswap16(x >> 16)) + +#if CPU_BYTE_ORDER == CPU_LITTLE_ENDIAN +#define usb_cpu_to_le16(x) (x) +#define usb_le16_to_cpu(x) (x) +#define usb_cpu_to_le32(x) (x) +#define usb_le32_to_cpu(x) (x) +#elif CPU_BYTE_ORDER == CPU_BIG_ENDIAN +#define usb_cpu_to_le16(x) usb_bswap16(x) +#define usb_le16_to_cpu(x) usb_bswap16(x) +#define usb_cpu_to_le32(x) usb_bswap32(x) +#define usb_le32_to_cpu(x) usb_bswap32(x) +#else +#error "unrecognized CPU endianness" +#endif + +/* State of a USB device */ +enum usb_device_state { + USB_STATE_NOTATTACHED = 0, + + /* chapter 9 device states */ + USB_STATE_ATTACHED, + USB_STATE_POWERED, /* wired */ + USB_STATE_DEFAULT, /* limited function */ + USB_STATE_ADDRESS, + USB_STATE_CONFIGURED, /* most functions */ +}; + +/* + * USB directions + * + * This bit flag is used in endpoint descriptors' bEndpointAddress field. + * It's also one of three fields in control requests bRequestType. + */ +#define USB_DIR_OUT 0 /* to device */ +#define USB_DIR_IN 0x80 /* to host */ + +/* + * USB types, the second of three bRequestType fields + */ +#define USB_TYPE_MASK (0x03 << 5) +#define USB_TYPE_STANDARD (0x00 << 5) +#define USB_TYPE_CLASS (0x01 << 5) +#define USB_TYPE_VENDOR (0x02 << 5) +#define USB_TYPE_RESERVED (0x03 << 5) + +/* + * USB recipients, the third of three bRequestType fields + */ +#define USB_RECIP_MASK 0x1f +#define USB_RECIP_DEVICE 0x00 +#define USB_RECIP_INTERFACE 0x01 +#define USB_RECIP_ENDPOINT 0x02 +#define USB_RECIP_OTHER 0x03 + +/* + * USB standard requests, for the bRequest field of a SETUP packet. + */ +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +#define USB_REQ_SET_FEATURE 0x03 +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C + +/* + * Descriptor types ... USB 2.0 spec table 9.5 + */ +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 +#define USB_DT_DEVICE_QUALIFIER 0x06 +#define USB_DT_OTHER_SPEED_CONFIG 0x07 +#define USB_DT_INTERFACE_POWER 0x08 + +/* + * Conventional codes for class-specific descriptors. The convention is + * defined in the USB "Common Class" Spec (3.11). Individual class specs + * are authoritative for their usage, not the "common class" writeup. + */ +#define USB_DT_CS_DEVICE (USB_TYPE_CLASS | USB_DT_DEVICE) +#define USB_DT_CS_CONFIG (USB_TYPE_CLASS | USB_DT_CONFIG) +#define USB_DT_CS_STRING (USB_TYPE_CLASS | USB_DT_STRING) +#define USB_DT_CS_INTERFACE (USB_TYPE_CLASS | USB_DT_INTERFACE) +#define USB_DT_CS_ENDPOINT (USB_TYPE_CLASS | USB_DT_ENDPOINT) + +/* + * This structure is used to send control requests to a USB device. + * + * It matches the different fields of the USB 2.0 spec. section 9.3, table 9-2. + */ +typedef struct usb_ctrlrequest +{ + uint8_t mRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +} PACKED usb_ctrlrequest_t; + +/* All standard descriptors have these 2 fields at the beginning */ +typedef struct usb_descriptor_header +{ + uint8_t bLength; + uint8_t bDescriptorType; +} PACKED usb_descriptor_header_t; + +/* Device descriptor */ +typedef struct usb_device_descriptor +{ + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdUSB; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize0; + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + uint8_t iManufacturer; + uint8_t iProduct; + uint8_t iSerialNumber; + uint8_t bNumConfigurations; +} PACKED usb_device_descriptor_t; + +/* USB string descriptor */ +typedef struct usb_string_descriptor +{ + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t data[0]; +} PACKED usb_string_descriptor_t; + +/* + * Macros to define USB strings + * + * TODO: add comment. + */ +#define USB_STRING_1(__a, ...) __a "\x00" +#define USB_STRING_2(__a, ...) __a "\x00" USB_STRING_1(__VA_ARGS__) +#define USB_STRING_3(__a, ...) __a "\x00" USB_STRING_2(__VA_ARGS__) +#define USB_STRING_4(__a, ...) __a "\x00" USB_STRING_3(__VA_ARGS__) +#define USB_STRING_5(__a, ...) __a "\x00" USB_STRING_4(__VA_ARGS__) +#define USB_STRING_6(__a, ...) __a "\x00" USB_STRING_5(__VA_ARGS__) +#define USB_STRING_7(__a, ...) __a "\x00" USB_STRING_6(__VA_ARGS__) +#define USB_STRING_8(__a, ...) __a "\x00" USB_STRING_7(__VA_ARGS__) +#define USB_STRING_9(__a, ...) __a "\x00" USB_STRING_8(__VA_ARGS__) +#define USB_STRING_10(__a, ...) __a "\x00" USB_STRING_9(__VA_ARGS__) +#define USB_STRING_11(__a, ...) __a "\x00" USB_STRING_10(__VA_ARGS__) +#define USB_STRING_12(__a, ...) __a "\x00" USB_STRING_11(__VA_ARGS__) +#define USB_STRING_13(__a, ...) __a "\x00" USB_STRING_12(__VA_ARGS__) +#define USB_STRING_14(__a, ...) __a "\x00" USB_STRING_13(__VA_ARGS__) +#define USB_STRING_15(__a, ...) __a "\x00" USB_STRING_14(__VA_ARGS__) +#define USB_STRING_16(__a, ...) __a "\x00" USB_STRING_15(__VA_ARGS__) + +#define USB_STRING(...) PP_CAT(USB_STRING_, PP_COUNT(__VA_ARGS__))(__VA_ARGS__) + +#define DEFINE_USB_STRING(__name, __text) \ + struct { \ + usb_descriptor_header_t __header; \ + uint8_t __body[sizeof(__text)]; \ + } PACKED __name = { \ + .__header = { \ + .bLength = sizeof(__name), \ + .bDescriptorType = USB_DT_STRING, \ + }, \ + .__body = {__text}, \ + } + +/* + * Device and/or Interface Class codes as found in bDeviceClass or + * bInterfaceClass and defined by www.usb.org documents. + */ +#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */ +#define USB_CLASS_AUDIO 1 +#define USB_CLASS_COMM 2 +#define USB_CLASS_HID 3 +#define USB_CLASS_PHYSICAL 5 +#define USB_CLASS_STILL_IMAGE 6 +#define USB_CLASS_PRINTER 7 +#define USB_CLASS_MASS_STORAGE 8 +#define USB_CLASS_HUB 9 +#define USB_CLASS_CDC_DATA 0x0a +#define USB_CLASS_CSCID 0x0b /* chip+ smart card */ +#define USB_CLASS_CONTENT_SEC 0x0d /* content security */ +#define USB_CLASS_VIDEO 0x0e +#define USB_CLASS_WIRELESS_CONTROLLER 0xe0 +#define USB_CLASS_MISC 0xef +#define USB_CLASS_APP_SPEC 0xfe +#define USB_CLASS_VENDOR_SPEC 0xff + +/* Device configuration descriptor */ +typedef struct usb_config_descriptor +{ + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumInterfaces; + uint8_t bConfigurationValue; + uint8_t iConfiguration; + uint8_t bmAttributes; + uint8_t bMaxPower; +} PACKED usb_config_descriptor_t; + +/* from config descriptor bmAttributes */ +#define USB_CONFIG_ATT_ONE (1 << 7) /* must be set */ +#define USB_CONFIG_ATT_SELFPOWER (1 << 6) /* self powered */ +#define USB_CONFIG_ATT_WAKEUP (1 << 5) /* can wakeup */ +#define USB_CONFIG_ATT_BATTERY (1 << 4) /* battery powered */ + +/* Device interface descriptor */ +typedef struct usb_interface_descriptor +{ + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bNumEndpoints; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + uint8_t iInterface; +} PACKED usb_interface_descriptor_t; + +/* Endpoint descriptor */ +typedef struct usb_endpoint_descriptor +{ + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bEndpointAddress; + uint8_t bmAttributes; + uint16_t wMaxPacketSize; + uint8_t bInterval; +} PACKED usb_endpoint_descriptor_t; + +/* + * Endpoints + */ +#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */ +#define USB_ENDPOINT_DIR_MASK 0x80 + +#define USB_ENDPOINT_SYNCTYPE 0x0c +#define USB_ENDPOINT_SYNC_NONE (0 << 2) +#define USB_ENDPOINT_SYNC_ASYNC (1 << 2) +#define USB_ENDPOINT_SYNC_ADAPTIVE (2 << 2) +#define USB_ENDPOINT_SYNC_SYNC (3 << 2) + +#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */ +#define USB_ENDPOINT_XFER_CONTROL 0 +#define USB_ENDPOINT_XFER_ISOC 1 +#define USB_ENDPOINT_XFER_BULK 2 +#define USB_ENDPOINT_XFER_INT 3 +#define USB_ENDPOINT_MAX_ADJUSTABLE 0x80 + +/* USB: generic device descriptor */ +struct usb_device +{ + usb_device_descriptor_t *device; + usb_descriptor_header_t **config; + usb_string_descriptor_t **strings; + /* Private data */ + bool configured; +}; + +/* + * usb_endpoint_num - get the endpoint's number + */ +INLINE int usb_endpoint_num(const usb_endpoint_descriptor_t *epd) +{ + return epd->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; +} + +/* + * usb_endpoint_type - get the endpoint's transfer type + */ +INLINE int usb_endpoint_type(const struct usb_endpoint_descriptor *epd) +{ + return epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; +} + +/* + * usb_endpoint_dir_in - check if the endpoint has IN direction + */ +INLINE int usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd) +{ + return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN); +} + +/* + * usb_endpoint_dir_out - check if the endpoint has OUT direction + */ +INLINE int usb_endpoint_dir_out(const struct usb_endpoint_descriptor *epd) +{ + return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT); +} + +/* + * usb_endpoint_xfer_bulk - check if the endpoint has bulk transfer type + */ +INLINE int usb_endpoint_xfer_bulk(const struct usb_endpoint_descriptor *epd) +{ + return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_BULK); +} + +/* + * usb_endpoint_xfer_control - check if the endpoint has control transfer type + */ +INLINE int usb_endpoint_xfer_control(const struct usb_endpoint_descriptor *epd) +{ + return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_CONTROL); +} + +/* + * usb_endpoint_xfer_int - check if the endpoint has interrupt transfer type + */ +INLINE int usb_endpoint_xfer_int(const struct usb_endpoint_descriptor *epd) +{ + return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_INT); +} + +/* + * usb_endpoint_xfer_isoc - check if the endpoint has isochronous transfer type + */ +INLINE int usb_endpoint_xfer_isoc(const struct usb_endpoint_descriptor *epd) +{ + return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_ISOC); +} + +/* + * usb_endpoint_is_bulk_in - check if the endpoint is bulk IN + */ +INLINE int usb_endpoint_is_bulk_in(const struct usb_endpoint_descriptor *epd) +{ + return usb_endpoint_xfer_bulk(epd) && usb_endpoint_dir_in(epd); +} + +/* + * usb_endpoint_is_bulk_out - check if the endpoint is bulk OUT + */ +INLINE int usb_endpoint_is_bulk_out(const struct usb_endpoint_descriptor *epd) +{ + return usb_endpoint_xfer_bulk(epd) && usb_endpoint_dir_out(epd); +} + +/* + * usb_endpoint_is_int_in - check if the endpoint is interrupt IN + */ +INLINE int usb_endpoint_is_int_in(const struct usb_endpoint_descriptor *epd) +{ + return usb_endpoint_xfer_int(epd) && usb_endpoint_dir_in(epd); +} + +/* + * usb_endpoint_is_int_out - check if the endpoint is interrupt OUT + */ +INLINE int usb_endpoint_is_int_out(const struct usb_endpoint_descriptor *epd) +{ + return usb_endpoint_xfer_int(epd) && usb_endpoint_dir_out(epd); +} + +/* + * usb_endpoint_is_isoc_in - check if the endpoint is isochronous IN + */ +INLINE int usb_endpoint_is_isoc_in(const struct usb_endpoint_descriptor *epd) +{ + return usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_in(epd); +} + +/* + * usb_endpoint_is_isoc_out - check if the endpoint is isochronous OUT + */ +INLINE int usb_endpoint_is_isoc_out(const struct usb_endpoint_descriptor *epd) +{ + return usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_out(epd); +} + +/* + * usb_ep_read - configure endponint and perform the read operation + */ +ssize_t usb_ep_read(int ep, void *buffer, ssize_t size); + +/* + * usb_ep_write - configure endponint and perform the write operation + */ +ssize_t usb_ep_write(int ep, const void *buffer, ssize_t size); + +/* + * usb_device_register - register a generic USB device driver + */ +int usb_device_register(struct usb_device *dev); + +#endif /* USB_H */