#include #include #include #include "macro.h" #include "stm32.h" #include "usb.h" #include "inc/usb_cdc.h" #define CDC_EP0_SIZE 0x08 #define CDC_RXD_EP 0x01 #define CDC_TXD_EP 0x82 #define CDC_DATA_SZ 0x40 #define CDC_NTF_EP 0x83 #define CDC_NTF_SZ 0x08 #define CDC_LOOPBACK struct cdc_config { struct usb_config_descriptor config; struct usb_interface_descriptor comm; struct usb_cdc_header_desc cdc_hdr; struct usb_cdc_call_mgmt_desc cdc_mgmt; struct usb_cdc_acm_desc cdc_acm; struct usb_cdc_union_desc cdc_union; struct usb_endpoint_descriptor comm_ep; struct usb_interface_descriptor data; struct usb_endpoint_descriptor data_eprx; struct usb_endpoint_descriptor data_eptx; } __attribute__((packed)); static const struct usb_device_descriptor device_desc = { .bLength = sizeof(struct usb_device_descriptor), .bDescriptorType = USB_DTYPE_DEVICE, .bcdUSB = VERSION_BCD(2,0,0), .bDeviceClass = USB_CLASS_CDC, .bDeviceSubClass = USB_SUBCLASS_NONE, .bDeviceProtocol = USB_PROTO_NONE, .bMaxPacketSize0 = CDC_EP0_SIZE, .idVendor = 0x0483, .idProduct = 0x5740, .bcdDevice = VERSION_BCD(1,0,0), .iManufacturer = 1, .iProduct = 2, .iSerialNumber = INTSERIALNO_DESCRIPTOR, .bNumConfigurations = 1, }; static const struct cdc_config config_desc = { .config = { .bLength = sizeof(struct usb_config_descriptor), .bDescriptorType = USB_DTYPE_CONFIGURATION, .wTotalLength = sizeof(struct cdc_config), .bNumInterfaces = 2, .bConfigurationValue = 1, .iConfiguration = NO_DESCRIPTOR, .bmAttributes = USB_CFG_ATTR_RESERVED | USB_CFG_ATTR_SELFPOWERED, .bMaxPower = USB_CFG_POWER_MA(100), }, .comm = { .bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType = USB_DTYPE_INTERFACE, .bInterfaceNumber = 0, .bAlternateSetting = 0, .bNumEndpoints = 1, .bInterfaceClass = USB_CLASS_CDC, .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, .bInterfaceProtocol = USB_CDC_PROTO_V25TER, .iInterface = NO_DESCRIPTOR, }, .cdc_hdr = { .bFunctionLength = sizeof(struct usb_cdc_header_desc), .bDescriptorType = USB_DTYPE_CS_INTERFACE, .bDescriptorSubType = USB_DTYPE_CDC_HEADER, .bcdCDC = VERSION_BCD(1,1,0), }, .cdc_mgmt = { .bFunctionLength = sizeof(struct usb_cdc_call_mgmt_desc), .bDescriptorType = USB_DTYPE_CS_INTERFACE, .bDescriptorSubType = USB_DTYPE_CDC_CALL_MANAGEMENT, .bmCapabilities = 0, .bDataInterface = 1, }, .cdc_acm = { .bFunctionLength = sizeof(struct usb_cdc_acm_desc), .bDescriptorType = USB_DTYPE_CS_INTERFACE, .bDescriptorSubType = USB_DTYPE_CDC_ACM, .bmCapabilities = 0, }, .cdc_union = { .bFunctionLength = sizeof(struct usb_cdc_union_desc), .bDescriptorType = USB_DTYPE_CS_INTERFACE, .bDescriptorSubType = USB_DTYPE_CDC_UNION, .bMasterInterface0 = 0, .bSlaveInterface0 = 1, }, .comm_ep = { .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = USB_DTYPE_ENDPOINT, .bEndpointAddress = CDC_NTF_EP, .bmAttributes = USB_EPTYPE_INTERRUPT, .wMaxPacketSize = CDC_NTF_SZ, .bInterval = 0xFF, }, .data = { .bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType = USB_DTYPE_INTERFACE, .bInterfaceNumber = 1, .bAlternateSetting = 0, .bNumEndpoints = 2, .bInterfaceClass = USB_CLASS_CDC_DATA, .bInterfaceSubClass = USB_SUBCLASS_NONE, .bInterfaceProtocol = USB_PROTO_NONE, .iInterface = NO_DESCRIPTOR, }, .data_eprx = { .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = USB_DTYPE_ENDPOINT, .bEndpointAddress = CDC_RXD_EP, .bmAttributes = USB_EPTYPE_BULK, .wMaxPacketSize = CDC_DATA_SZ, .bInterval = 0x01, }, .data_eptx = { .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = USB_DTYPE_ENDPOINT, .bEndpointAddress = CDC_TXD_EP, .bmAttributes = USB_EPTYPE_BULK, .wMaxPacketSize = CDC_DATA_SZ, .bInterval = 0x01, }, }; static const struct usb_string_descriptor lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US); static const struct usb_string_descriptor manuf_desc_en = USB_STRING_DESC("Open source USB stack for STM32"); static const struct usb_string_descriptor prod_desc_en = USB_STRING_DESC("CDC Loopback demo"); static const struct usb_string_descriptor *const dtable[] = { &lang_desc, &manuf_desc_en, &prod_desc_en, }; usbd_device udev; uint32_t ubuf[0x20]; uint8_t fifo[0x200]; uint32_t fpos = 0; static struct usb_cdc_line_coding cdc_line = { .dwDTERate = 38400, .bCharFormat = USB_CDC_1_STOP_BITS, .bParityType = USB_CDC_NO_PARITY, .bDataBits = 8, }; static usbd_respond cdc_getdesc (usbd_ctlreq *req, void **address, uint16_t *length) { const uint8_t dtype = req->wValue >> 8; const uint8_t dnumber = req->wValue & 0xFF; const void* desc; uint16_t len = 0; switch (dtype) { case USB_DTYPE_DEVICE: desc = &device_desc; break; case USB_DTYPE_CONFIGURATION: desc = &config_desc; len = sizeof(config_desc); break; case USB_DTYPE_STRING: if (dnumber < 3) { desc = dtable[dnumber]; } else { return usbd_fail; } break; default: return usbd_fail; } if (len == 0) { len = ((struct usb_header_descriptor*)desc)->bLength; } *address = (void*)desc; *length = len; return usbd_ack; }; static usbd_respond cdc_control(usbd_device *dev, usbd_ctlreq *req, usbd_rqc_callback *callback) { if (((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) != (USB_REQ_INTERFACE | USB_REQ_CLASS)) return usbd_fail; switch (req->bRequest) { case USB_CDC_SET_CONTROL_LINE_STATE: return usbd_ack; case USB_CDC_SET_LINE_CODING: memmove( req->data, &cdc_line, sizeof(cdc_line)); return usbd_ack; case USB_CDC_GET_LINE_CODING: dev->status.data_ptr = &cdc_line; dev->status.data_count = sizeof(cdc_line); return usbd_ack; default: return usbd_fail; } } static void cdc_rxonly (usbd_device *dev, uint8_t event, uint8_t ep) { usbd_ep_read(dev, ep, fifo, CDC_DATA_SZ); } static void cdc_txonly(usbd_device *dev, uint8_t event, uint8_t ep) { uint8_t _t = dev->driver->frame_no(); memset(fifo, _t, CDC_DATA_SZ); usbd_ep_write(dev, ep, fifo, CDC_DATA_SZ); } static void cdc_loopback(usbd_device *dev, uint8_t event, uint8_t ep) { int _t; switch (event) { case usbd_evt_eptx: _t = usbd_ep_write(dev, CDC_TXD_EP, &fifo[0], (fpos < CDC_DATA_SZ) ? fpos : CDC_DATA_SZ); if (_t > 0) { memmove(&fifo[0], &fifo[_t], fpos - _t); fpos -= _t; } case usbd_evt_eprx: if (fpos < (sizeof(fifo) - CDC_DATA_SZ)) { _t = usbd_ep_read(dev, CDC_RXD_EP, &fifo[fpos], CDC_DATA_SZ); if (_t > 0) { fpos += _t; } } default: break; } } static usbd_respond cdc_setconf (usbd_device *dev, uint8_t cfg) { switch (cfg) { case 0: /* deconfiguring device */ usbd_ep_deconfig(dev, CDC_NTF_EP); usbd_ep_deconfig(dev, CDC_TXD_EP); usbd_ep_deconfig(dev, CDC_RXD_EP); usbd_reg_endpoint(dev, CDC_RXD_EP, 0); usbd_reg_endpoint(dev, CDC_TXD_EP, 0); return usbd_ack; case 1: /* configuring device */ usbd_ep_config(dev, CDC_RXD_EP, USB_EPTYPE_BULK | USB_EPTYPE_DBLBUF, CDC_DATA_SZ); usbd_ep_config(dev, CDC_TXD_EP, USB_EPTYPE_BULK | USB_EPTYPE_DBLBUF, CDC_DATA_SZ); usbd_ep_config(dev, CDC_NTF_EP, USB_EPTYPE_INTERRUPT, CDC_NTF_SZ); #if defined(CDC_LOOPBACK) usbd_reg_endpoint(dev, CDC_RXD_EP, cdc_loopback); usbd_reg_endpoint(dev, CDC_TXD_EP, cdc_loopback); #else usbd_reg_endpoint(dev, CDC_RXD_EP, cdc_rxonly); usbd_reg_endpoint(dev, CDC_TXD_EP, cdc_txonly); #endif usbd_ep_write(dev, CDC_TXD_EP, 0, 0); return usbd_ack; default: return usbd_fail; } } static void cdc_init_usbd(void) { usbd_init(&udev, &usbd_hw, CDC_EP0_SIZE, ubuf, sizeof(ubuf)); usbd_reg_config(&udev, cdc_setconf); usbd_reg_control(&udev, cdc_control); usbd_reg_descr(&udev, cdc_getdesc); } static void cdc_init_rcc (void) { #if defined(STM32L0) _BST(RCC->APB1ENR, RCC_APB1ENR_PWREN); _BMD(PWR->CR, PWR_CR_VOS, PWR_CR_VOS_0); _WBC(PWR->CSR, PWR_CSR_VOSF); /* set FLASH latency to 1 */ _BST(FLASH->ACR, FLASH_ACR_LATENCY); /* set clock at 32MHz PLL 6/3 HSI */ _BMD(RCC->CFGR, RCC_CFGR_PLLDIV | RCC_CFGR_PLLMUL | RCC_CFGR_PLLSRC, RCC_CFGR_PLLDIV3 | RCC_CFGR_PLLMUL6); _BST(RCC->CR, RCC_CR_HSION); _WBS(RCC->CR, RCC_CR_HSIRDY); _BST(RCC->CR, RCC_CR_PLLON); _WBS(RCC->CR, RCC_CR_PLLRDY); /* switch clock to PLL */ _BMD(RCC->CFGR, RCC_CFGR_SW, RCC_CFGR_SW_PLL); _WVL(RCC->CFGR, RCC_CFGR_SWS, RCC_CFGR_SWS_PLL); #elif defined(STM32L1) _BST(RCC->APB1ENR, RCC_APB1ENR_PWREN); _BMD(PWR->CR, PWR_CR_VOS, PWR_CR_VOS_0); _WBC(PWR->CSR, PWR_CSR_VOSF); /* set FLASH latency to 1 */ _BST(FLASH->ACR, FLASH_ACR_ACC64); _BST(FLASH->ACR, FLASH_ACR_LATENCY); /* set clock at 32 MHz PLL 6/3 HSI */ _BMD(RCC->CFGR, RCC_CFGR_PLLDIV | RCC_CFGR_PLLMUL | RCC_CFGR_PLLSRC, RCC_CFGR_PLLDIV3 | RCC_CFGR_PLLMUL6); _BST(RCC->CR, RCC_CR_HSION); _WBS(RCC->CR, RCC_CR_HSIRDY); _BST(RCC->CR, RCC_CR_PLLON); _WBS(RCC->CR, RCC_CR_PLLRDY); /* switch clock to PLL */ _BMD(RCC->CFGR, RCC_CFGR_SW, RCC_CFGR_SW_PLL); _WVL(RCC->CFGR, RCC_CFGR_SWS, RCC_CFGR_SWS_PLL); #elif defined(STM32L4) _BST(RCC->APB1ENR1, RCC_APB1ENR1_PWREN); /* Set power Range 1 */ _BMD(PWR->CR1, PWR_CR1_VOS, PWR_CR1_VOS_1); _WBC(PWR->SR2, PWR_SR2_VOSF); /* Adjust Flash latency */ _BST(FLASH->ACR, FLASH_ACR_LATENCY); /* set clock 48Mhz MSI */ _BMD(RCC->CR, RCC_CR_MSIRANGE, RCC_CR_MSIRANGE_11 | RCC_CR_MSIRGSEL); /* set MSI as 48MHz USB */ _BMD(RCC->CCIPR, RCC_CCIPR_CLK48SEL, RCC_CCIPR_CLK48SEL_0 | RCC_CCIPR_CLK48SEL_1); /* enable GPIOA clock */ _BST(RCC->AHB2ENR, RCC_AHB2ENR_GPIOAEN); /* set GP11 and GP12 as USB data pins AF10 */ _BST(GPIOA->AFR[1], (0x0A << 12) | (0x0A << 16)); _BMD(GPIOA->MODER, (0x03 << 22) | (0x03 << 24), (0x02 << 22) | (0x02 << 24)); #else #error Not supported #endif } void __libc_init_array(void) { } void SystemInit(void) { cdc_init_rcc(); } void main(void) { cdc_init_usbd(); usbd_control(&udev, usbd_cmd_enable); usbd_control(&udev, usbd_cmd_connect); while(1) { usbd_poll(&udev); } }