diff --git a/hal/m258ke/nuvoton/usbd.c b/hal/m258ke/nuvoton/usbd.c new file mode 100644 index 0000000..abbe61c --- /dev/null +++ b/hal/m258ke/nuvoton/usbd.c @@ -0,0 +1,794 @@ +/**************************************************************************//** + * @file usbd.c + * @version V0.10 + * @brief M251 series USBD driver source file + * + * SPDX-License-Identifier: Apache-2.0 + * @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved. +*****************************************************************************/ + +#include +#include "NuMicro.h" + +#if 0 + #define DBG_PRINTF printf +#else + #define DBG_PRINTF(...) +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** @addtogroup Standard_Driver Standard Driver + @{ +*/ + +/** @addtogroup USBD_Driver USBD Driver + @{ +*/ + + +/** @addtogroup USBD_EXPORTED_FUNCTIONS USBD Exported Functions + @{ +*/ + +/* Global variables for Control Pipe */ +uint8_t g_USBD_au8SetupPacket[8] = {0u}; /*!< Setup packet buffer */ +volatile uint8_t g_USBD_u8RemoteWakeupEn = 0u; /*!< Remote wake up function enable flag */ + +/** + * @cond HIDDEN_SYMBOLS + */ +static volatile uint8_t *s_USBD_pu8CtrlInPointer = 0ul; +static volatile uint8_t *s_USBD_pu8CtrlOutPointer = 0ul; +static volatile uint32_t s_USBD_u32CtrlInSize = 0ul; +static volatile uint32_t s_USBD_u32CtrlOutSize = 0ul; +static volatile uint32_t s_USBD_u32CtrlOutSizeLimit = 0ul; +static volatile uint32_t s_USBD_u32UsbAddr = 0ul; +static volatile uint32_t s_USBD_u32UsbConfig = 0ul; +static volatile uint32_t s_USBD_u32CtrlMaxPktSize = 8ul; +static volatile uint32_t s_USBD_u32UsbAltInterface = 0ul; +static volatile uint8_t s_USBD_u8CtrlInZeroFlag = 0ul; +static volatile uint32_t s_USBD_u32CtrlOutToggle = 0ul; +/** + * @endcond + */ + +const S_USBD_INFO_T *g_USBD_sINFO; /*!< A pointer for USB information structure */ + +VENDOR_REQ g_USBD_pfnVendorRequest = NULL; /*!< USB Vendor Request Functional Pointer */ +CLASS_REQ g_USBD_pfnClassRequest = NULL; /*!< USB Class Request Functional Pointer */ +SET_INTERFACE_REQ g_USBD_pfnSetInterface = NULL; /*!< USB Set Interface Functional Pointer */ +SET_CONFIG_CB g_USBD_pfnSetConfigCallback = NULL; /*!< USB Set configuration callback function pointer */ +uint32_t g_USBD_u32EpStallLock = 0ul; /*!< Bit map flag to lock specified EP when SET_FEATURE */ + +/** + * @brief This function makes USBD module to be ready to use + * + * @param[in] param The structure of USBD information. + * @param[in] pfnClassReq USB Class request callback function. + * @param[in] pfnSetInterface USB Set Interface request callback function. + * + * + * @details This function will enable USB controller, USB PHY transceiver and pull-up resistor of USB_D+ pin. USB PHY will drive SE0 to bus. + */ +void USBD_Open(const S_USBD_INFO_T *param, CLASS_REQ pfnClassReq, SET_INTERFACE_REQ pfnSetInterface) +{ + g_USBD_sINFO = param; + g_USBD_pfnClassRequest = pfnClassReq; + g_USBD_pfnSetInterface = pfnSetInterface; + + /* get EP0 maximum packet size */ + s_USBD_u32CtrlMaxPktSize = g_USBD_sINFO->gu8DevDesc[7]; + + /* Initial USB engine */ +#ifdef SUPPORT_LPM + USBD->ATTR = 0x7D0UL | USBD_LPMACK; +#else + USBD->ATTR = 0x7D0UL; +#endif + /* Force SE0 */ + USBD_SET_SE0(); +} + +/** + * @brief This function makes USB host to recognize the device + * + * + * + * @details Enable WAKEUP, FLDET, USB and BUS interrupts. Disable software-disconnect function after 100ms delay with SysTick timer. + */ +void USBD_Start(void) +{ + /* Disable software-disconnect function */ + USBD_CLR_SE0(); + + /* Clear USB-related interrupts before enable interrupt */ + USBD_CLR_INT_FLAG(USBD_INT_BUS | USBD_INT_USB | USBD_INT_FLDET | USBD_INT_WAKEUP); + + /* Enable USB-related interrupts. */ + USBD_ENABLE_INT(USBD_INT_BUS | USBD_INT_USB | USBD_INT_FLDET | USBD_INT_WAKEUP); +} + +/** + * @brief Get the received SETUP packet + * + * @param[in] buf A buffer pointer used to store 8-byte SETUP packet. + * + * + * @details Store SETUP packet to a user-specified buffer. + * + */ +void USBD_GetSetupPacket(uint8_t *buf) +{ + USBD_MemCopy(buf, g_USBD_au8SetupPacket, 8ul); +} + +/** + * @brief Process SETUP packet + * + * + * + * @details Parse SETUP packet and perform the corresponding action. + * + */ +void USBD_ProcessSetupPacket(void) +{ + s_USBD_u32CtrlOutToggle = 0; + /* Get SETUP packet from USB buffer */ + USBD_MemCopy(g_USBD_au8SetupPacket, (uint8_t *)USBD_BUF_BASE, 8ul); + + /* Check the request type */ + switch (g_USBD_au8SetupPacket[0] & 0x60ul) + { + case REQ_STANDARD: /* Standard */ + { + USBD_StandardRequest(); + break; + } + + case REQ_CLASS: /* Class */ + { + if (g_USBD_pfnClassRequest != NULL) + { + g_USBD_pfnClassRequest(); + } + + break; + } + + case REQ_VENDOR: /* Vendor */ + { + if (g_USBD_pfnVendorRequest != NULL) + { + g_USBD_pfnVendorRequest(); + } + + break; + } + + default: /* reserved */ + { + /* Setup error, stall the device */ + USBD_SET_EP_STALL(EP0); + USBD_SET_EP_STALL(EP1); + break; + } + } +} + +/** + * @brief Process GetDescriptor request + * + * + * + * @details Parse GetDescriptor request and perform the corresponding action. + * + */ +void USBD_GetDescriptor(void) +{ + uint32_t u32Len; + + s_USBD_u8CtrlInZeroFlag = (uint8_t)0ul; + + u32Len = 0ul; + u32Len = g_USBD_au8SetupPacket[7]; + u32Len <<= 8ul; + u32Len += g_USBD_au8SetupPacket[6]; + + switch (g_USBD_au8SetupPacket[3]) + { + /* Get Device Descriptor */ + case DESC_DEVICE: + { + u32Len = USBD_Minimum(u32Len, LEN_DEVICE); + DBG_PRINTF("Get device desc, %d\n", u32Len); + + USBD_PrepareCtrlIn((uint8_t *)g_USBD_sINFO->gu8DevDesc, u32Len); + + break; + } + + /* Get Configuration Descriptor */ + case DESC_CONFIG: + { + uint32_t u32TotalLen; + + DBG_PRINTF("Get config desc len %d, acture len %d\n", u32Len, u32TotalLen); + u32TotalLen = g_USBD_sINFO->gu8ConfigDesc[3]; + u32TotalLen = g_USBD_sINFO->gu8ConfigDesc[2] + (u32TotalLen << 8U); + + DBG_PRINTF("Get config desc len %d, acture len %d\n", u32Len, u32TotalLen); + + if (u32Len > u32TotalLen) + { + u32Len = u32TotalLen; + + if ((u32Len % s_USBD_u32CtrlMaxPktSize) == 0ul) + { + s_USBD_u8CtrlInZeroFlag = (uint8_t)1ul; + } + } + + DBG_PRINTF("Minimum len %d\n", u32Len); + + USBD_PrepareCtrlIn((uint8_t *)g_USBD_sINFO->gu8ConfigDesc, u32Len); + + break; + } + + + /* Get BOS Descriptor */ + case DESC_BOS: + { + if (g_USBD_sINFO->gu8BosDesc) + { + u32Len = USBD_Minimum(u32Len, LEN_BOS + LEN_DEVCAP); + USBD_PrepareCtrlIn((uint8_t *)g_USBD_sINFO->gu8BosDesc, u32Len); + } + else + { + USBD_SET_EP_STALL(EP0); + USBD_SET_EP_STALL(EP1); + } + + break; + } + + /* Get HID Descriptor */ + case DESC_HID: + { + /* CV3.0 HID Class Descriptor Test, + Need to indicate index of the HID Descriptor within gu8ConfigDescriptor, specifically HID Composite device. */ + uint32_t u32ConfigDescOffset; /* u32ConfigDescOffset is configuration descriptor offset (HID descriptor start index) */ + u32Len = USBD_Minimum(u32Len, LEN_HID); + DBG_PRINTF("Get HID desc, %d\n", u32Len); + + u32ConfigDescOffset = g_USBD_sINFO->gu32ConfigHidDescIdx[g_USBD_au8SetupPacket[4]]; + USBD_PrepareCtrlIn((uint8_t *)&g_USBD_sINFO->gu8ConfigDesc[u32ConfigDescOffset], u32Len); + + break; + } + + /* Get Report Descriptor */ + case DESC_HID_RPT: + { + DBG_PRINTF("Get HID report, %d\n", u32Len); + + if (u32Len > g_USBD_sINFO->gu32HidReportSize[g_USBD_au8SetupPacket[4]]) + { + u32Len = g_USBD_sINFO->gu32HidReportSize[g_USBD_au8SetupPacket[4]]; + + if ((u32Len % s_USBD_u32CtrlMaxPktSize) == 0ul) + { + s_USBD_u8CtrlInZeroFlag = (uint8_t)1ul; + } + } + + USBD_PrepareCtrlIn((uint8_t *)g_USBD_sINFO->gu8HidReportDesc[g_USBD_au8SetupPacket[4]], u32Len); + break; + } + + /* Get String Descriptor */ + case DESC_STRING: + { + /* Get String Descriptor */ + if (g_USBD_au8SetupPacket[2] < 4ul) + { + if (u32Len > g_USBD_sINFO->gu8StringDesc[g_USBD_au8SetupPacket[2]][0]) + { + u32Len = g_USBD_sINFO->gu8StringDesc[g_USBD_au8SetupPacket[2]][0]; + + if ((u32Len % s_USBD_u32CtrlMaxPktSize) == 0ul) + { + s_USBD_u8CtrlInZeroFlag = (uint8_t)1ul; + } + } + + DBG_PRINTF("Get string desc %d\n", u32Len); + + USBD_PrepareCtrlIn((uint8_t *)g_USBD_sINFO->gu8StringDesc[g_USBD_au8SetupPacket[2]], u32Len); + + + break; + } + else + { + /* Not support. Reply STALL. */ + USBD_SET_EP_STALL(EP0); + USBD_SET_EP_STALL(EP1); + + DBG_PRINTF("Unsupported string desc (%d). Stall ctrl pipe.\n", g_USBD_au8SetupPacket[2]); + + break; + } + } + + default: + /* Not support. Reply STALL. */ + USBD_SET_EP_STALL(EP0); + USBD_SET_EP_STALL(EP1); + + DBG_PRINTF("Unsupported get desc type. stall ctrl pipe\n"); + + break; + } +} + +/** + * @brief Process standard request + * + * + * + * @details Parse standard request and perform the corresponding action. + * + */ +void USBD_StandardRequest(void) +{ + DBG_PRINTF("USBD_StandardRequest\n"); + + /* clear global variables for new request */ + s_USBD_pu8CtrlInPointer = 0ul; + s_USBD_u32CtrlInSize = 0ul; + + if (g_USBD_au8SetupPacket[0] & 0x80ul) /* request data transfer direction */ + { + /* Device to host */ + switch (g_USBD_au8SetupPacket[1]) + { + case GET_CONFIGURATION: + { + /* Return current configuration setting */ + /* Data stage */ + M8(USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP0)) = (uint8_t)s_USBD_u32UsbConfig; + USBD_SET_DATA1(EP0); + USBD_SET_PAYLOAD_LEN(EP0, 1ul); + /* Status stage */ + USBD_PrepareCtrlOut(0, 0ul); + + DBG_PRINTF("Get configuration\n"); + + break; + } + + case GET_DESCRIPTOR: + { + USBD_GetDescriptor(); + USBD_PrepareCtrlOut(0, 0ul); /* For status stage */ + break; + } + + case GET_INTERFACE: + { + /* Return current interface setting */ + /* Data stage */ + M8(USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP0)) = (uint8_t)s_USBD_u32UsbAltInterface; + USBD_SET_DATA1(EP0); + USBD_SET_PAYLOAD_LEN(EP0, 1ul); + /* Status stage */ + USBD_PrepareCtrlOut(0, 0ul); + + DBG_PRINTF("Get interface\n"); + + break; + } + + case GET_STATUS: + { + /* Device */ + if (g_USBD_au8SetupPacket[0] == 0x80ul) + { + uint8_t u8Tmp; + + u8Tmp = (uint8_t)0ul; + + if (g_USBD_sINFO->gu8ConfigDesc[7] & 0x40ul) u8Tmp |= (uint8_t)1ul; /* Self-Powered/Bus-Powered. */ + + if (g_USBD_sINFO->gu8ConfigDesc[7] & 0x20) u8Tmp |= (uint8_t)(g_USBD_u8RemoteWakeupEn << 1ul); /* Remote wake up */ + + M8(USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP0)) = u8Tmp; + + } + /* Interface */ + else if (g_USBD_au8SetupPacket[0] == 0x81ul) + M8(USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP0)) = (uint8_t)0ul; + /* Endpoint */ + else if (g_USBD_au8SetupPacket[0] == 0x82ul) + { + uint8_t ep = (uint8_t)g_USBD_au8SetupPacket[4] & 0xFul; + M8(USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP0)) = (uint8_t)(USBD_GetStall(ep) ? 1ul : 0ul); + } + + M8(USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP0) + 1) = (uint8_t)0ul; + /* Data stage */ + USBD_SET_DATA1(EP0); + USBD_SET_PAYLOAD_LEN(EP0, 2ul); + /* Status stage */ + USBD_PrepareCtrlOut(0, 0ul); + + DBG_PRINTF("Get status\n"); + + break; + } + + default: + { + /* Setup error, stall the device */ + USBD_SET_EP_STALL(EP0); + USBD_SET_EP_STALL(EP1); + + DBG_PRINTF("Unknown request. stall ctrl pipe.\n"); + + break; + } + } + } + else + { + /* Host to device */ + switch (g_USBD_au8SetupPacket[1]) + { + case CLEAR_FEATURE: + { + if (g_USBD_au8SetupPacket[2] == FEATURE_ENDPOINT_HALT) + { + uint32_t epNum, i; + + /* EP number stall is not allow to be clear in MSC class "Error Recovery Test". + a flag: g_USBD_u32EpStallLock is added to support it */ + epNum = (uint8_t)(g_USBD_au8SetupPacket[4] & 0xFul); + + for (i = 0ul; i < USBD_MAX_EP; i++) + { + if (((USBD->EP[i].CFG & 0xFul) == epNum) && ((g_USBD_u32EpStallLock & (1ul << i)) == 0ul)) + { + USBD->EP[i].CFGP &= ~USBD_CFGP_SSTALL_Msk; + USBD->EP[i].CFG &= ~USBD_CFG_DSQSYNC_Msk; + DBG_PRINTF("Clr stall ep%d %x\n", i, USBD->EP[i].CFGP); + } + } + } + else if (g_USBD_au8SetupPacket[2] == FEATURE_DEVICE_REMOTE_WAKEUP) + g_USBD_u8RemoteWakeupEn = (uint8_t)0; + + /* Status stage */ + USBD_SET_DATA1(EP0); + USBD_SET_PAYLOAD_LEN(EP0, 0ul); + + DBG_PRINTF("Clear feature op %d\n", g_USBD_au8SetupPacket[2]); + + break; + } + + case SET_ADDRESS: + { + s_USBD_u32UsbAddr = g_USBD_au8SetupPacket[2]; + DBG_PRINTF("Set addr to %d\n", s_USBD_u32UsbAddr); + + /* DATA IN for end of setup */ + /* Status Stage */ + USBD_SET_DATA1(EP0); + USBD_SET_PAYLOAD_LEN(EP0, 0ul); + + break; + } + + case SET_CONFIGURATION: + { + s_USBD_u32UsbConfig = g_USBD_au8SetupPacket[2]; + + if (g_USBD_pfnSetConfigCallback) + g_USBD_pfnSetConfigCallback(); + + /* Status stage */ + USBD_SET_DATA1(EP0); + USBD_SET_PAYLOAD_LEN(EP0, 0ul); + + DBG_PRINTF("Set config to %d\n", s_USBD_u32UsbConfig); + + break; + } + + case SET_FEATURE: + { + if (g_USBD_au8SetupPacket[2] == FEATURE_ENDPOINT_HALT) + { + USBD_SetStall((uint8_t)(g_USBD_au8SetupPacket[4] & 0xFul)); + DBG_PRINTF("Set feature. stall ep %d\n", g_USBD_au8SetupPacket[4] & 0xFul); + } + else if (g_USBD_au8SetupPacket[2] == FEATURE_DEVICE_REMOTE_WAKEUP) + { + g_USBD_u8RemoteWakeupEn = (uint8_t)1ul; + DBG_PRINTF("Set feature. enable remote wakeup\n"); + } + + /* Status stage */ + USBD_SET_DATA1(EP0); + USBD_SET_PAYLOAD_LEN(EP0, 0ul); + + break; + } + + case SET_INTERFACE: + { + s_USBD_u32UsbAltInterface = g_USBD_au8SetupPacket[2]; + + if (g_USBD_pfnSetInterface != NULL) + { + g_USBD_pfnSetInterface(s_USBD_u32UsbAltInterface); + } + + /* Status stage */ + USBD_SET_DATA1(EP0); + USBD_SET_PAYLOAD_LEN(EP0, 0ul); + + DBG_PRINTF("Set interface to %d\n", s_USBD_u32UsbAltInterface); + + break; + } + + default: + { + + /* Setup error, stall the device */ + USBD_SET_EP_STALL(EP0); + USBD_SET_EP_STALL(EP1); + + DBG_PRINTF("Unsupported request. stall ctrl pipe.\n"); + + break; + } + } + } +} + +/** + * @brief Prepare the first Control IN pipe + * + * @param[in] pu8Buf The pointer of data sent to USB host. + * @param[in] u32Size The IN transfer size. + * + * + * @details Prepare data for Control IN transfer. + * + */ +void USBD_PrepareCtrlIn(uint8_t pu8Buf[], uint32_t u32Size) +{ + DBG_PRINTF("Prepare Ctrl In %d\n", u32Size); + + if (u32Size > s_USBD_u32CtrlMaxPktSize) + { + // Data size > MXPLD + s_USBD_pu8CtrlInPointer = pu8Buf + s_USBD_u32CtrlMaxPktSize; + s_USBD_u32CtrlInSize = u32Size - s_USBD_u32CtrlMaxPktSize; + USBD_SET_DATA1(EP0); + USBD_MemCopy((uint8_t *)USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP0), pu8Buf, s_USBD_u32CtrlMaxPktSize); + USBD_SET_PAYLOAD_LEN(EP0, s_USBD_u32CtrlMaxPktSize); + } + else + { + // Data size <= MXPLD + s_USBD_pu8CtrlInPointer = 0ul; + s_USBD_u32CtrlInSize = 0ul; + + USBD_SET_DATA1(EP0); + USBD_MemCopy((uint8_t *)USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP0), pu8Buf, u32Size); + USBD_SET_PAYLOAD_LEN(EP0, u32Size); + } +} + +/** + * @brief Repeat Control IN pipe + * + * + * + * @details This function processes the remained data of Control IN transfer. + * + */ +void USBD_CtrlIn(void) +{ + + DBG_PRINTF("Ctrl In Ack. residue %d\n", s_USBD_u32CtrlInSize); + + if (s_USBD_u32CtrlInSize) + { + // Process remained data + if (s_USBD_u32CtrlInSize > s_USBD_u32CtrlMaxPktSize) + { + // Data size > MXPLD + USBD_MemCopy((uint8_t *)USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP0), (uint8_t *)s_USBD_pu8CtrlInPointer, s_USBD_u32CtrlMaxPktSize); + USBD_SET_PAYLOAD_LEN(EP0, s_USBD_u32CtrlMaxPktSize); + s_USBD_pu8CtrlInPointer += s_USBD_u32CtrlMaxPktSize; + s_USBD_u32CtrlInSize -= s_USBD_u32CtrlMaxPktSize; + } + else + { + // Data size <= MXPLD + USBD_MemCopy((uint8_t *)USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP0), (uint8_t *)s_USBD_pu8CtrlInPointer, s_USBD_u32CtrlInSize); + USBD_SET_PAYLOAD_LEN(EP0, s_USBD_u32CtrlInSize); + + s_USBD_pu8CtrlInPointer = 0ul; + s_USBD_u32CtrlInSize = 0ul; + } + } + else // No more data for IN token + { + // In ACK for Set address + if ((g_USBD_au8SetupPacket[0] == REQ_STANDARD) && (g_USBD_au8SetupPacket[1] == SET_ADDRESS)) + { + if ((USBD_GET_ADDR() != s_USBD_u32UsbAddr) && (USBD_GET_ADDR() == 0ul)) + { + USBD_SET_ADDR(s_USBD_u32UsbAddr); + } + } + + /* For the case of data size is integral times maximum packet size */ + if (s_USBD_u8CtrlInZeroFlag) + { + USBD_SET_PAYLOAD_LEN(EP0, 0ul); + s_USBD_u8CtrlInZeroFlag = (uint8_t)0ul; + } + + DBG_PRINTF("Ctrl In done.\n"); + + } +} + +/** + * @brief Prepare the first Control OUT pipe + * + * @param[in] pu8Buf The pointer of data received from USB host. + * @param[in] u32Size The OUT transfer size. + * + * + * @details This function is used to prepare the first Control OUT transfer. + * + */ +void USBD_PrepareCtrlOut(uint8_t *pu8Buf, uint32_t u32Size) +{ + s_USBD_pu8CtrlOutPointer = pu8Buf; + s_USBD_u32CtrlOutSize = 0ul; + s_USBD_u32CtrlOutSizeLimit = u32Size; + USBD_SET_PAYLOAD_LEN(EP1, s_USBD_u32CtrlMaxPktSize); +} + +/** + * @brief Repeat Control OUT pipe + * + * + * + * @details This function processes the successive Control OUT transfer. + * + */ +void USBD_CtrlOut(void) +{ + if (s_USBD_u32CtrlOutToggle != (USBD->EPSTS0 & 0xf0)) + { + s_USBD_u32CtrlOutToggle = USBD->EPSTS0 & 0xf0; //keep H/W EP1 toggle status + + if (s_USBD_u32CtrlOutSize < s_USBD_u32CtrlOutSizeLimit) + { + uint32_t u32Size; + uint32_t addr; + + u32Size = USBD_GET_PAYLOAD_LEN(EP1); + addr = USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP1); + USBD_MemCopy((uint8_t *)s_USBD_pu8CtrlOutPointer, (uint8_t *)addr, u32Size); + s_USBD_pu8CtrlOutPointer += u32Size; + s_USBD_u32CtrlOutSize += u32Size; + + if (s_USBD_u32CtrlOutSize < s_USBD_u32CtrlOutSizeLimit) + { + USBD_SET_PAYLOAD_LEN(EP1, s_USBD_u32CtrlMaxPktSize); + } + } + } + else + { + USBD_SET_PAYLOAD_LEN(EP1, s_USBD_u32CtrlMaxPktSize); + } +} + +/** + * @brief Reset software flags + * + * + * + * @details This function resets all variables for protocol and resets USB device address to 0. + * + */ +void USBD_SwReset(void) +{ + uint32_t i; + + // Reset all variables for protocol + s_USBD_pu8CtrlInPointer = 0ul; + s_USBD_u32CtrlInSize = 0ul; + s_USBD_pu8CtrlOutPointer = 0ul; + s_USBD_u32CtrlOutSize = 0ul; + s_USBD_u32CtrlOutSizeLimit = 0ul; + g_USBD_u32EpStallLock = 0ul; + memset(g_USBD_au8SetupPacket, 0, 8ul); + + /* Reset PID DATA0 */ + for (i = 0ul; i < USBD_MAX_EP; i++) + USBD->EP[i].CFG &= ~USBD_CFG_DSQSYNC_Msk; + + // Reset USB device address + USBD_SET_ADDR(0ul); +} + +/** + * @brief USBD Set Vendor Request + * + * @param[in] pfnVendorReq Vendor Request Callback Function + * + * + * @details This function is used to set USBD vendor request callback function + */ +void USBD_SetVendorRequest(VENDOR_REQ pfnVendorReq) +{ + g_USBD_pfnVendorRequest = pfnVendorReq; +} + +/** + * @brief The callback function which called when get SET CONFIGURATION request + * + * @param[in] pfnSetConfigCallback Callback function pointer for SET CONFIGURATION request + * + * + * @details This function is used to set the callback function which will be called at SET CONFIGURATION request. + */ +void USBD_SetConfigCallback(SET_CONFIG_CB pfnSetConfigCallback) +{ + g_USBD_pfnSetConfigCallback = pfnSetConfigCallback; +} + + +/** + * @brief EP stall lock function to avoid stall clear by USB SET FEATURE request. + * + * @param[in] u32EpBitmap Use bitmap to select which endpoints will be locked + * + * + * @details This function is used to lock relative endpoint to avoid stall clear by SET FEATURE request. + * If ep stall locked, user needs to reset USB device or re-configure device to clear it. + */ +void USBD_LockEpStall(uint32_t u32EpBitmap) +{ + g_USBD_u32EpStallLock = u32EpBitmap; +} + +/** @} end of group USBD_EXPORTED_FUNCTIONS */ + +/** @} end of group USBD_Driver */ + +/** @} end of group Standard_Driver */ + +#ifdef __cplusplus +} +#endif + +/*** (C) COPYRIGHT 2019 Nuvoton Technology Corp. ***/ diff --git a/hal/m258ke/nuvoton/usbd.h b/hal/m258ke/nuvoton/usbd.h new file mode 100644 index 0000000..37cae15 --- /dev/null +++ b/hal/m258ke/nuvoton/usbd.h @@ -0,0 +1,671 @@ +/****************************************************************************** + * @file usbd.h + * @version V0.10 + * @brief M251 series USB driver header file + * + * SPDX-License-Identifier: Apache-2.0 + * @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved. + ******************************************************************************/ + +#ifndef __USBD_H__ +#define __USBD_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*!< Definition for enabling Link Power Management(LPM) function. + LPM related handler will raise after LPM event happen. + if bcdUSB >= 0x0201, USB version is equal or higher than 2.1, + OS(Windows) will issue "get BOS descriptor" request to check the support of LPM. + + Notice: + If bcdUSB >= 0x0201, where USB version is equal or higher than 2.1, + WIN8 ~ WIN10 will fail to enumerate the device if device stalls the "get BOS descriptor" request. + WIN7 can still enumerate this device even though the "get BOS descriptor" request been stalled. + +*/ +//#define SUPPORT_LPM + +/** @addtogroup Standard_Driver Standard Driver + @{ +*/ + +/** @addtogroup USBD_Driver USBD Driver + @{ +*/ + +/** @addtogroup USBD_EXPORTED_STRUCTS USBD Exported Structs + @{ +*/ +typedef struct s_usbd_info +{ + uint8_t *gu8DevDesc; /*!< Pointer for USB Device Descriptor */ + uint8_t *gu8ConfigDesc; /*!< Pointer for USB Configuration Descriptor */ + uint8_t **gu8StringDesc; /*!< Pointer for USB String Descriptor pointers */ + uint8_t **gu8HidReportDesc; /*!< Pointer for USB HID Report Descriptor */ + uint8_t *gu8BosDesc; /*!< Pointer for USB BOS Descriptor */ + uint32_t *gu32HidReportSize; /*!< Pointer for HID Report descriptor Size */ + uint32_t *gu32ConfigHidDescIdx; /*!< Pointer for HID Descriptor start index */ +} S_USBD_INFO_T; /*!< Device description structure */ + +extern const S_USBD_INFO_T gsInfo; + +/** @} end of group USBD_EXPORTED_STRUCTS */ + + + + +/** @addtogroup USBD_EXPORTED_CONSTANTS USBD Exported Constants + @{ +*/ +#define USBD_BUF_BASE (USBD_BASE+0x100ul) /*!< USBD buffer base address \hideinitializer */ +#define USBD_MAX_EP 12ul /*!< Total EP number \hideinitializer */ + +#define EP0 0ul /*!< Endpoint 0 \hideinitializer */ +#define EP1 1ul /*!< Endpoint 1 \hideinitializer */ +#define EP2 2ul /*!< Endpoint 2 \hideinitializer */ +#define EP3 3ul /*!< Endpoint 3 \hideinitializer */ +#define EP4 4ul /*!< Endpoint 4 \hideinitializer */ +#define EP5 5ul /*!< Endpoint 5 \hideinitializer */ +#define EP6 6ul /*!< Endpoint 6 \hideinitializer */ +#define EP7 7ul /*!< Endpoint 7 \hideinitializer */ +#define EP8 8ul /*!< Endpoint 8 \hideinitializer */ +#define EP9 9ul /*!< Endpoint 9 \hideinitializer */ +#define EP10 10ul /*!< Endpoint 10 \hideinitializer */ +#define EP11 11ul /*!< Endpoint 11 \hideinitializer */ + +/*! b, then return a. Otherwise, return b. + */ +#define USBD_Maximum(a,b) ((a)>(b) ? (a) : (b)) + + +/** + * @brief Compare two input numbers and return minimum one + * + * @param[in] a First number to be compared + * @param[in] b Second number to be compared + * + * @return Minimum value between a and b + * + * @details If a < b, then return a. Otherwise, return b. + */ +#define USBD_Minimum(a,b) ((a)<(b) ? (a) : (b)) + + +/** + * @brief Enable USB + * + * + * + * @details To set USB ATTR control register to enable USB and PHY. + * + */ +#define USBD_ENABLE_USB() ((uint32_t)(USBD->ATTR |= 0x7D0)) + +/** + * @brief Disable USB + * + * + * + * @details To set USB ATTR control register to disable USB. + * + */ +#define USBD_DISABLE_USB() ((uint32_t)(USBD->ATTR &= ~USBD_USB_EN)) + +/** + * @brief Enable USB PHY + * + * + * + * @details To set USB ATTR control register to enable USB PHY. + * + */ +#define USBD_ENABLE_PHY() ((uint32_t)(USBD->ATTR |= USBD_PHY_EN)) + +/** + * @brief Disable USB PHY + * + * + * + * @details To set USB ATTR control register to disable USB PHY. + * + */ +#define USBD_DISABLE_PHY() ((uint32_t)(USBD->ATTR &= ~USBD_PHY_EN)) + +/** + * @brief Enable SE0. Force USB PHY transceiver to drive SE0. + * + * + * + * @details Set DRVSE0 bit of USB_DRVSE0 register to enable software-disconnect function. Force USB PHY transceiver to drive SE0 to bus. + * + */ +#define USBD_SET_SE0() ((uint32_t)(USBD->SE0 |= USBD_DRVSE0)) + +/** + * @brief Disable SE0 + * + * + * + * @details Clear DRVSE0 bit of USB_DRVSE0 register to disable software-disconnect function. + * + */ +#define USBD_CLR_SE0() ((uint32_t)(USBD->SE0 &= ~USBD_DRVSE0)) + +/** + * @brief Set USB device address + * + * @param[in] addr The USB device address. + * + * + * @details Write USB device address to USB_FADDR register. + * + */ +#define USBD_SET_ADDR(addr) (USBD->FADDR = (addr)) + +/** + * @brief Get USB device address + * + * + * @return USB device address + * + * @details Read USB_FADDR register to get USB device address. + * + */ +#define USBD_GET_ADDR() ((uint32_t)(USBD->FADDR)) + +/** + * @brief Enable USB interrupt function + * + * @param[in] intr The combination of the specified interrupt enable bits. + * Each bit corresponds to a interrupt enable bit. + * This parameter decides which interrupts will be enabled. + * (USBD_INT_WAKEUP, USBD_INT_FLDET, USBD_INT_USB, USBD_INT_BUS) + * + * + * @details Enable USB related interrupt functions specified by intr parameter. + * + */ +#define USBD_ENABLE_INT(intr) (USBD->INTEN |= (intr)) + +/** + * @brief Get interrupt status + * + * + * @return The value of USB_INTSTS register + * + * @details Return all interrupt flags of USB_INTSTS register. + * + */ +#define USBD_GET_INT_FLAG() ((uint32_t)(USBD->INTSTS)) + +/** + * @brief Clear USB interrupt flag + * + * @param[in] flag The combination of the specified interrupt flags. + * Each bit corresponds to a interrupt source. + * This parameter decides which interrupt flags will be cleared. + * (USBD_INTSTS_WAKEUP, USBD_INTSTS_FLDET, USBD_INTSTS_BUS, USBD_INTSTS_USB) + * + * + * @details Clear USB related interrupt flags specified by flag parameter. + * + */ +#define USBD_CLR_INT_FLAG(flag) (USBD->INTSTS = (flag)) + +/** + * @brief Get endpoint status + * + * + * @return The value of USB_EPSTS register. + * + * @details Return all endpoint status. + * + */ +#define USBD_GET_EP_FLAG() ((uint32_t)(USBD->EPSTS)) + +/** + * @brief Get USB bus state + * + * + * @return The value of USB_ATTR[3:0] and USB_ATTR[13:12]. + * Bit 0 indicates USB bus reset status. + * Bit 1 indicates USB bus suspend status. + * Bit 2 indicates USB bus resume status. + * Bit 3 indicates USB bus time-out status. + * Bit 12 indicates USB bus LPM L1 suspend status. + * Bit 13 indicates USB bus LPM L1 resume status. + * + * @details Return USB_ATTR[3:0] and USB_ATTR[13:12] for USB bus events. + * + * \hideinitializer + */ +#define USBD_GET_BUS_STATE() ((uint32_t)(USBD->ATTR & 0x300f)) + + +/** + * @brief Check cable connection state + * + * + * @retval 0 USB cable is not attached. + * @retval 1 USB cable is attached. + * + * @details Check the connection state by FLDET bit of USB_FLDET register. + * + */ +#define USBD_IS_ATTACHED() ((uint32_t)(USBD->VBUSDET & USBD_VBUSDET_VBUSDET_Msk)) + +/** + * @brief Stop USB transaction of the specified endpoint ID + * + * @param[in] ep The USB endpoint ID. M251 series supports 12 hardware endpoint ID. This parameter could be 0 ~ 11. + * + * + * @details Write 1 to CLRRDY bit of USB_CFGPx register to stop USB transaction of the specified endpoint ID. + * + */ +#define USBD_STOP_TRANSACTION(ep) (*((__IO uint32_t *) ((uint32_t)&USBD->EP[0].CFGP + (uint32_t)((ep) << 4))) |= USBD_CFGP_CLRRDY_Msk) + +/** + * @brief Set USB DATA1 PID for the specified endpoint ID + * + * @param[in] ep The USB endpoint ID. M251 series supports 12 hardware endpoint ID. This parameter could be 0 ~ 11. + * + * + * @details Set DSQ_SYNC bit of USB_CFGx register to specify the DATA1 PID for the following IN token transaction. + * Base on this setting, hardware will toggle PID between DATA0 and DATA1 automatically for IN token transactions. + * + */ +#define USBD_SET_DATA1(ep) (*((__IO uint32_t *) ((uint32_t)&USBD->EP[0].CFG + (uint32_t)((ep) << 4))) |= USBD_CFG_DSQSYNC_Msk) + +/** + * @brief Set USB DATA0 PID for the specified endpoint ID + * + * @param[in] ep The USB endpoint ID. M251 series supports 12 hardware endpoint ID. This parameter could be 0 ~ 11. + * + * + * @details Clear DSQ_SYNC bit of USB_CFGx register to specify the DATA0 PID for the following IN token transaction. + * Base on this setting, hardware will toggle PID between DATA0 and DATA1 automatically for IN token transactions. + * + */ +#define USBD_SET_DATA0(ep) (*((__IO uint32_t *) ((uint32_t)&USBD->EP[0].CFG + (uint32_t)((ep) << 4))) &= (~USBD_CFG_DSQSYNC_Msk)) + +/** + * @brief Set USB payload size (IN data) + * + * @param[in] ep The USB endpoint ID. M251 series supports 12 hardware endpoint ID. This parameter could be 0 ~ 11. + * + * @param[in] size The transfer length. + * + * + * @details This macro will write the transfer length to USB_MXPLDx register for IN data transaction. + * + */ +#define USBD_SET_PAYLOAD_LEN(ep, size) (*((__IO uint32_t *) ((uint32_t)&USBD->EP[0].MXPLD + (uint32_t)((ep) << 4))) = (size)) + +/** + * @brief Get USB payload size (OUT data) + * + * @param[in] ep The USB endpoint ID. M251 series supports 12 endpoint ID. This parameter could be 0 ~ 11. + * + * @return The value of USB_MXPLDx register. + * + * @details Get the data length of OUT data transaction by reading USB_MXPLDx register. + * + */ +#define USBD_GET_PAYLOAD_LEN(ep) ((uint32_t)*((__IO uint32_t *) ((uint32_t)&USBD->EP[0].MXPLD + (uint32_t)((ep) << 4)))) + +/** + * @brief Configure endpoint + * + * @param[in] ep The USB endpoint ID. M251 series supports 12 hardware endpoint ID. This parameter could be 0 ~ 11. + * + * @param[in] config The USB configuration. + * + * + * @details This macro will write config parameter to USB_CFGx register of specified endpoint ID. + * + */ +#define USBD_CONFIG_EP(ep, config) (*((__IO uint32_t *) ((uint32_t)&USBD->EP[0].CFG + (uint32_t)((ep) << 4))) = (config)) + +/** + * @brief Set USB endpoint buffer + * + * @param[in] ep The USB endpoint ID. M251 series supports 12 hardware endpoint ID. This parameter could be 0 ~ 11. + * + * @param[in] offset The SRAM offset. + * + * + * @details This macro will set the SRAM offset for the specified endpoint ID. + * + */ +#define USBD_SET_EP_BUF_ADDR(ep, offset) (*((__IO uint32_t *) ((uint32_t)&USBD->EP[0].BUFSEG + (uint32_t)((ep) << 4))) = (offset)) + +/** + * @brief Get the offset of the specified USB endpoint buffer + * + @param[in] ep The USB endpoint ID. M251 series supports 12 hardware endpoint ID. This parameter could be 0 ~ 11. + * + * @return The offset of the specified endpoint buffer. + * + * @details This macro will return the SRAM offset of the specified endpoint ID. + * + */ +#define USBD_GET_EP_BUF_ADDR(ep) ((uint32_t)*((__IO uint32_t *) ((uint32_t)&USBD->EP[0].BUFSEG + (uint32_t)((ep) << 4)))) + +/** + * @brief Set USB endpoint stall state + * + * @param[in] ep The USB endpoint ID. M251 series supports 12 hardware endpoint ID. This parameter could be 0 ~ 11. + * + * + * @details Set USB endpoint stall state for the specified endpoint ID. Endpoint will respond STALL token automatically. + * + */ +#define USBD_SET_EP_STALL(ep) (*((__IO uint32_t *) ((uint32_t)&USBD->EP[0].CFGP + (uint32_t)((ep) << 4))) |= USBD_CFGP_SSTALL_Msk) + +/** + * @brief Clear USB endpoint stall state + * + * @param[in] ep The USB endpoint ID. M251 series supports 12 hardware endpoint ID. This parameter could be 0 ~ 11. + * + * + * @details Clear USB endpoint stall state for the specified endpoint ID. Endpoint will respond ACK/NAK token. + */ +#define USBD_CLR_EP_STALL(ep) (*((__IO uint32_t *) ((uint32_t)&USBD->EP[0].CFGP + (uint32_t)((ep) << 4))) &= ~USBD_CFGP_SSTALL_Msk) + +/** + * @brief Get USB endpoint stall state + * + * @param[in] ep The USB endpoint ID. M251 series supports 12 hardware endpoint ID. This parameter could be 0 ~ 11. + * + * @retval 0 USB endpoint is not stalled. + * @retval Others USB endpoint is stalled. + * + * @details Get USB endpoint stall state of the specified endpoint ID. + * + */ +#define USBD_GET_EP_STALL(ep) (*((__IO uint32_t *) ((uint32_t)&USBD->EP[0].CFGP + (uint32_t)((ep) << 4))) & USBD_CFGP_SSTALL_Msk) + +/** + * @brief To support byte access between USB SRAM and system SRAM + * + * @param[in] dest Destination pointer. + * + * @param[in] src Source pointer. + * + * @param[in] size Byte count. + * + * + * @details This function will copy the number of data specified by size and src parameters to the address specified by dest parameter. + * + */ +__STATIC_INLINE void USBD_MemCopy(uint8_t *dest, uint8_t *src, uint32_t size) +{ + while (size--) *dest++ = *src++; +} + + +/** + * @brief Set USB endpoint stall state + * + * @param[in] epnum USB endpoint number + * + * + * @details Set USB endpoint stall state. Endpoint will respond STALL token automatically. + * + */ +__STATIC_INLINE void USBD_SetStall(uint8_t epnum) +{ + uint32_t i; + + for (i = 0ul; i < USBD_MAX_EP; i++) + { + uint32_t u32CfgAddr; + uint32_t u32Cfg; + u32CfgAddr = (uint32_t)(i << 4) + (uint32_t)&USBD->EP[0].CFG; /* USBD_CFG0 */ + u32Cfg = *((__IO uint32_t *)(u32CfgAddr)); + + if ((u32Cfg & 0xful) == epnum) + { + u32CfgAddr = (uint32_t)(i << 4) + (uint32_t)&USBD->EP[0].CFGP; /* USBD_CFGP0 */ + u32Cfg = *((__IO uint32_t *)(u32CfgAddr)); + + *((__IO uint32_t *)(u32CfgAddr)) = (u32Cfg | USBD_CFGP_SSTALL); + break; + } + } +} + +/** + * @brief Clear USB endpoint stall state + * + * @param[in] epnum USB endpoint number + * + * + * @details Clear USB endpoint stall state. Endpoint will respond ACK/NAK token. + */ +__STATIC_INLINE void USBD_ClearStall(uint8_t epnum) +{ + uint32_t i; + + for (i = 0ul; i < USBD_MAX_EP; i++) + { + uint32_t u32CfgAddr; + uint32_t u32Cfg; + u32CfgAddr = (uint32_t)(i << 4) + (uint32_t)&USBD->EP[0].CFG; /* USBD_CFG0 */ + u32Cfg = *((__IO uint32_t *)(u32CfgAddr)); + + if ((u32Cfg & 0xful) == epnum) + { + u32CfgAddr = (uint32_t)(i << 4) + (uint32_t)&USBD->EP[0].CFGP; /* USBD_CFGP0 */ + u32Cfg = *((__IO uint32_t *)(u32CfgAddr)); + + *((__IO uint32_t *)(u32CfgAddr)) = (u32Cfg & ~USBD_CFGP_SSTALL); + break; + } + } +} + +/** + * @brief Get USB endpoint stall state + * + * @param[in] epnum USB endpoint number + * + * @retval 0 USB endpoint is not stalled. + * @retval Others USB endpoint is stalled. + * + * @details Get USB endpoint stall state. + * + */ +__STATIC_INLINE uint32_t USBD_GetStall(uint8_t epnum) +{ + uint32_t u32CfgAddr; + uint32_t u32Cfg; + uint32_t i; + + for (i = 0ul; i < USBD_MAX_EP; i++) + { + u32CfgAddr = (uint32_t)(i << 4ul) + (uint32_t)&USBD->EP[0].CFG; /* USBD_CFG0 */ + u32Cfg = *((__IO uint32_t *)(u32CfgAddr)); + + if ((u32Cfg & 0xful) == epnum) + { + u32CfgAddr = (uint32_t)(i << 4ul) + (uint32_t)&USBD->EP[0].CFGP; /* USBD_CFGP0 */ + break; + } + } + + return ((*((__IO uint32_t *)(u32CfgAddr))) & USBD_CFGP_SSTALL); +} + + +extern volatile uint8_t g_USBD_u8RemoteWakeupEn; + + +typedef void (*VENDOR_REQ)(void); /*!< Functional pointer type definition for Vendor class */ +typedef void (*CLASS_REQ)(void); /*!< Functional pointer type declaration for USB class request callback handler */ +typedef void (*SET_INTERFACE_REQ)(uint32_t u32AltInterface); /*!< Functional pointer type declaration for USB set interface request callback handler */ +typedef void (*SET_CONFIG_CB)(void); /*!< Functional pointer type declaration for USB set configuration request callback handler */ + + +/*--------------------------------------------------------------------*/ +void USBD_Open(const S_USBD_INFO_T *param, CLASS_REQ pfnClassReq, SET_INTERFACE_REQ pfnSetInterface); +void USBD_Start(void); +void USBD_GetSetupPacket(uint8_t *buf); +void USBD_ProcessSetupPacket(void); +void USBD_StandardRequest(void); +void USBD_PrepareCtrlIn(uint8_t pu8Buf[], uint32_t u32Size); +void USBD_CtrlIn(void); +void USBD_PrepareCtrlOut(uint8_t *pu8Buf, uint32_t u32Size); +void USBD_CtrlOut(void); +void USBD_SwReset(void); +void USBD_SetVendorRequest(VENDOR_REQ pfnVendorReq); +void USBD_SetConfigCallback(SET_CONFIG_CB pfnSetConfigCallback); +void USBD_LockEpStall(uint32_t u32EpBitmap); + +/** @} end of group USBD_EXPORTED_FUNCTIONS */ + +/** @} end of group USBD_Driver */ + +/** @} end of group Standard_Driver */ + +#ifdef __cplusplus +} +#endif + +#endif /* __USBD_H__ */ + +/*** (C) COPYRIGHT 2020 Nuvoton Technology Corp. ***/