#include "pcan_usb.h" #include "pcan_led.h" #include "pcan_protocol.h" #include "usbd_conf.h" #include "usbd_ctlreq.h" #include "usbd_helper.h" #include "usbd_ioreq.h" #include #include USBD_HandleTypeDef hUsbDeviceFS; extern USBD_DescriptorsTypeDef FS_Desc; static struct t_class_data pcan_data = { 0 }; struct t_pcan_description { USB_CONFIGURATION_DESCRIPTOR con0; USB_INTERFACE_DESCRIPTOR if0; USB_ENDPOINT_DESCRIPTOR ep1; USB_ENDPOINT_DESCRIPTOR ep2; USB_ENDPOINT_DESCRIPTOR ep3; USB_ENDPOINT_DESCRIPTOR ep4; }; __ALIGN_BEGIN static const USB_DEVICE_QUALIFIER_DESCRIPTOR dev_qua __ALIGN_END = { .bLength = sizeof(USB_DEVICE_QUALIFIER_DESCRIPTOR), .bDescriptorType = USB_QUALIFIER_DESCRIPTOR_TYPE, .bcdUSB = 0x0100, /* 1.0 */ .bDeviceClass = 0, .bDeviceSubClass = 0, .bDeviceProtocol = 0, .bMaxPacketSize0 = 64, .bNumConfigurations = 1, .bReserved = 0, }; __ALIGN_BEGIN static struct t_pcan_description pcan_usb_dev __ALIGN_END = { .con0 = { .bLength = sizeof( USB_CONFIGURATION_DESCRIPTOR ), .bDescriptorType = USB_CONFIGURATION_DESCRIPTOR_TYPE, .wTotalLength = sizeof( struct t_pcan_description ), .bNumInterfaces = 1, .bConfigurationValue = 1, .iConfiguration = 0, .bmAttributes = USB_CONFIG_BUS_POWERED, .MaxPower = 100, /* = 200mA */ }, .if0 = { .bLength = sizeof( USB_INTERFACE_DESCRIPTOR ), .bDescriptorType = USB_INTERFACE_DESCRIPTOR_TYPE, .bInterfaceNumber = 0, .bAlternateSetting = 0, .bNumEndpoints = 4, .bInterfaceClass = 0, .bInterfaceSubClass = 0, .bInterfaceProtocol = 0, .iInterface = 0, }, .ep1 = { .bLength = sizeof( USB_ENDPOINT_DESCRIPTOR ), .bDescriptorType = USB_ENDPOINT_DESCRIPTOR_TYPE, .bEndpointAddress = PCAN_USB_EP_CMDIN, /* PC IN cmd resp */ .bmAttributes = USB_ENDPOINT_TYPE_BULK, .wMaxPacketSize = 16, .bInterval = 0, }, .ep2 = { .bLength = sizeof( USB_ENDPOINT_DESCRIPTOR ), .bDescriptorType = USB_ENDPOINT_DESCRIPTOR_TYPE, .bEndpointAddress = PCAN_USB_EP_CMDOUT, /* PC OUT cmd */ .bmAttributes = USB_ENDPOINT_TYPE_BULK, .wMaxPacketSize = 16, .bInterval = 0, }, .ep3 = { .bLength = sizeof( USB_ENDPOINT_DESCRIPTOR ), .bDescriptorType = USB_ENDPOINT_DESCRIPTOR_TYPE, .bEndpointAddress = PCAN_USB_EP_MSGIN, /* PC IN frames */ .bmAttributes = USB_ENDPOINT_TYPE_BULK, .wMaxPacketSize = 64, .bInterval = 0, }, .ep4 = { .bLength = sizeof( USB_ENDPOINT_DESCRIPTOR ), .bDescriptorType = USB_ENDPOINT_DESCRIPTOR_TYPE, .bEndpointAddress = PCAN_USB_EP_MSGOUT, .bmAttributes = USB_ENDPOINT_TYPE_BULK,/* PC OUT frames */ .wMaxPacketSize = 64, .bInterval = 0, }, }; static uint8_t device_init(USBD_HandleTypeDef *pdev, uint8_t cfgidx) { USB_ENDPOINT_DESCRIPTOR *p_ep = &pcan_usb_dev.ep1; UNUSED(cfgidx); for (int i = 0; i < pcan_usb_dev.if0.bNumEndpoints; i++) { uint8_t ep_addr = p_ep[i].bEndpointAddress; if (p_ep[i].bmAttributes == USB_ENDPOINT_TYPE_BULK) { if (pdev->dev_speed == USBD_SPEED_FULL) ; else if (pdev->dev_speed == USBD_SPEED_HIGH) ; else assert(0); } USBD_LL_OpenEP(pdev, ep_addr, p_ep[i].bmAttributes, p_ep[i].wMaxPacketSize); if ((ep_addr & 0x80) != 0) pdev->ep_in[ep_addr & EP_ADDR_MSK].is_used = 1; else pdev->ep_out[ep_addr & EP_ADDR_MSK].is_used = 1; } pdev->pClassData = (void *)&pcan_data; USBD_LL_PrepareReceive(pdev, PCAN_USB_EP_CMDOUT, pcan_data.buffer_cmd, sizeof(pcan_data.buffer_cmd)); USBD_LL_PrepareReceive(pdev, PCAN_USB_EP_MSGOUT, pcan_data.buffer_data, sizeof(pcan_data.buffer_data)); return USBD_OK; } static uint8_t device_deinit(USBD_HandleTypeDef *pdev, uint8_t cfgidx) { USB_ENDPOINT_DESCRIPTOR const *p_ep = &pcan_usb_dev.ep1; UNUSED(cfgidx); for (int i = 0; i < pcan_usb_dev.if0.bNumEndpoints; i++) { uint8_t ep_addr = p_ep[i].bEndpointAddress; USBD_LL_CloseEP(pdev, ep_addr); if ((ep_addr & 0x80) != 0) pdev->ep_in[ep_addr & EP_ADDR_MSK].is_used = 0; else pdev->ep_out[ep_addr & EP_ADDR_MSK].is_used = 0; } pdev->pClassData = (void *)0; return USBD_OK; } uint16_t pcan_usb_send_command_buffer(const void *p, uint16_t size) { USBD_HandleTypeDef *pdev = &hUsbDeviceFS; if (pdev->ep_in[PCAN_USB_EP_CMDIN & 0xFU].total_length) return 0; pdev->ep_in[PCAN_USB_EP_CMDIN & 0xFU].total_length = size; if (USBD_LL_Transmit(pdev, PCAN_USB_EP_CMDIN, (void *)p, size) == USBD_OK) return size; return 0; } uint16_t pcan_usb_send_data_buffer(const void *p, uint16_t size) { USBD_HandleTypeDef *pdev = &hUsbDeviceFS; if (pdev->ep_in[PCAN_USB_EP_MSGIN & 0xFU].total_length) return 0; pdev->ep_in[PCAN_USB_EP_MSGIN & 0xFU].total_length = size; if (USBD_LL_Transmit(pdev, PCAN_USB_EP_MSGIN, (void *)p, size) == USBD_OK) return size; return 0; } void pcan_usb_poll(void) { HAL_PCD_IRQHandler(&hpcd_USB_FS); } static uint8_t device_setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) { switch (req->bRequest) { /* get info */ case 0: switch (req->wValue) { case 0: /* bootloader info */ { static const uint8_t bootloader_info[] = { 0x00, 0x00, 0x08, 0x04, 0x00, 0x08, 0x07, 0x00, 0x04, 0x02, 0xe0, 0x07, 0x01, 0x00, 0x00, 0x00 }; USBD_CtlSendData(pdev, (void *)bootloader_info, sizeof(bootloader_info)); pcan_led_set_mode(LED_CH0_RX, LED_MODE_ON, 0); pcan_led_set_mode(LED_CH0_TX, LED_MODE_ON, 0); /* reset endpoints */ pcan_flush_ep(PCAN_USB_EP_MSGIN); pcan_flush_ep(PCAN_USB_EP_CMDIN); } break; } break; } return USBD_OK; } static uint8_t device_ep0_rx_ready(USBD_HandleTypeDef *pdev) { UNUSED(pdev); return USBD_OK; } /* data was sent to PC */ static uint8_t device_data_in(USBD_HandleTypeDef *pdev, uint8_t epnum) { struct t_class_data *p_data = (void *)pdev->pClassData; if (pdev->pClassData == 0) return USBD_FAIL; /* use ZLP */ #if 0 PCD_HandleTypeDef *hpcd = pdev->pData; uint32_t len = pdev->ep_in[epnum].total_length; /* packet is multiple of maxpacket, so tell host what all transfer is done */ if( len && ( len % hpcd->IN_ep[epnum].maxpacket ) == 0 ) { /* update the packet total length */ pdev->ep_in[epnum].total_length = 0; /* send ZLP */ if( !p_data->ep_tx_data_pending[epnum] ) { USBD_LL_Transmit( pdev, epnum, NULL, 0 ); } else { p_data->ep_tx_in_use[epnum] = 0; } } else { /* tx done, no active transfer */ p_data->ep_tx_in_use[epnum] = 0; } #else pdev->ep_in[epnum].total_length = 0U; p_data->ep_tx_in_use[epnum] = 0; #endif return USBD_OK; } /* data was received from PC */ static uint8_t device_data_out(USBD_HandleTypeDef *pdev, uint8_t epnum) { int size; if (pdev->pClassData == 0) return USBD_FAIL; size = USBD_LL_GetRxDataSize(pdev, epnum); (void)size; if (epnum == PCAN_USB_EP_CMDOUT) { pcan_protocol_process_command(pcan_data.buffer_cmd, size); USBD_LL_PrepareReceive(pdev, PCAN_USB_EP_CMDOUT, pcan_data.buffer_cmd, sizeof(pcan_data.buffer_cmd)); } else if (epnum == PCAN_USB_EP_MSGOUT) { pcan_protocol_process_data(pcan_data.buffer_data, size); USBD_LL_PrepareReceive(pdev, PCAN_USB_EP_MSGOUT, pcan_data.buffer_data, sizeof(pcan_data.buffer_data)); } else { return USBD_FAIL; } return USBD_OK; } static uint8_t *device_get_hs_cfg(uint16_t *length) { *length = sizeof(struct t_pcan_description); return (void *)&pcan_usb_dev; } static uint8_t *device_get_fs_cfg(uint16_t *length) { *length = sizeof(struct t_pcan_description); return (void *)&pcan_usb_dev; } static uint8_t *device_get_other_speed_cfg(uint16_t *length) { *length = sizeof(struct t_pcan_description); return (void *)&pcan_usb_dev; } static uint8_t *device_get_device_qualifier(uint16_t *length) { *length = sizeof(USB_DEVICE_QUALIFIER_DESCRIPTOR); return (void *)&dev_qua; } static uint8_t *device_get_user_string(USBD_HandleTypeDef *pdev, uint8_t index, uint16_t *length) { UNUSED(pdev); if (index == 10) { __ALIGN_BEGIN static const uint16_t vendor_descriptor[1 + 24] __ALIGN_END = { 0x0332, 'P', 'E', 'A', 'K', '-', 'S', 'y', 's', 't', 'e', 'm', ' ', 'T', 'e', 'c', 'h', 'n', 'i', 'k', ' ', 'G', 'm', 'b', 'H' }; *length = sizeof(vendor_descriptor); return (uint8_t *)vendor_descriptor; } return 0; } USBD_ClassTypeDef usbd_pcan = { .Init = device_init, .DeInit = device_deinit, .Setup = device_setup, .EP0_TxSent = 0, .EP0_RxReady = device_ep0_rx_ready, .DataIn = device_data_in, .DataOut = device_data_out, .SOF = 0, .IsoINIncomplete = 0, .IsoOUTIncomplete = 0, .GetHSConfigDescriptor = device_get_hs_cfg, .GetFSConfigDescriptor = device_get_fs_cfg, .GetOtherSpeedConfigDescriptor = device_get_other_speed_cfg, .GetDeviceQualifierDescriptor = device_get_device_qualifier, #if (USBD_SUPPORT_USER_STRING_DESC == 1U) .GetUsrStrDescriptor = device_get_user_string, #endif }; void pcan_usb_init(void) { if (USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS) != USBD_OK) { assert(0); } if (USBD_RegisterClass(&hUsbDeviceFS, &usbd_pcan) != USBD_OK) { assert(0); } if (USBD_Start(&hUsbDeviceFS) != USBD_OK) { assert(0); } } int pcan_flush_ep(uint8_t ep) { USBD_HandleTypeDef *pdev = &hUsbDeviceFS; struct t_class_data *p_data = (void *)pdev->pClassData; p_data->ep_tx_in_use[ep & 0x0F] = 0; return USBD_LL_FlushEP(pdev, ep) == USBD_OK; } int pcan_flush_data(struct t_m2h_fsm *pfsm, void *src, int size) { USBD_HandleTypeDef *pdev = &hUsbDeviceFS; struct t_class_data *p_data = (void *)pdev->pClassData; if (!p_data) return 0; switch (pfsm->state) { case 1: if (p_data->ep_tx_in_use[pfsm->ep_addr & 0x0F]) { p_data->ep_tx_data_pending[pfsm->ep_addr & 0x0F] = size; return 0; } pfsm->state = 0; /* fall through */ case 0: assert(p_data->ep_tx_in_use[pfsm->ep_addr & 0x0F] == 0); if (size > pfsm->dbsize) break; memcpy(pfsm->pdbuf, src, size); p_data->ep_tx_in_use[pfsm->ep_addr & 0x0F] = 1; /* prepare data transmit */ pdev->ep_in[pfsm->ep_addr & EP_ADDR_MSK].total_length = size; /* no pending data */ p_data->ep_tx_data_pending[pfsm->ep_addr & 0x0F] = 0; USBD_LL_Transmit(pdev, pfsm->ep_addr, pfsm->pdbuf, size); pfsm->total_tx += size; pfsm->state = 1; return 1; } return 0; }