#include #include #include "usbd_ctlreq.h" #include "usbd_ioreq.h" #include "usbd_conf.h" #include "usbd_helper.h" #include "pcan_protocol.h" #include "pcan_led.h" #include "pcan_usb.h" 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; }