2016-08-14 18:37:30 +02:00
/** library for USB CDC ACM communication (code)
2019-12-06 17:40:34 +01:00
* @ file
2016-08-14 18:37:30 +02:00
* @ author King Kévin < kingkevin @ cuvoodoo . info >
2020-06-06 14:35:55 +02:00
* @ copyright SPDX - License - Identifier : GPL - 3.0 - or - later
2020-11-27 16:41:19 +01:00
* @ date 2016 - 2020
2016-08-14 18:37:30 +02:00
*/
2016-01-18 16:15:23 +01:00
/* standard libraries */
# include <stdint.h> // standard integer types
# include <stdlib.h> // general utilities
/* STM32 (including CM3) libraries */
2018-04-03 16:59:02 +02:00
# include <libopencmsis/core_cm3.h> // Cortex M3 utilities
# include <libopencm3/cm3/scb.h> // reset utilities
# include <libopencm3/cm3/nvic.h> // interrupt handler
2016-01-18 16:15:23 +01:00
# include <libopencm3/stm32/rcc.h> // real-time control clock library
# include <libopencm3/stm32/gpio.h> // general purpose input output library
# include <libopencm3/usb/usbd.h> // USB library
# include <libopencm3/usb/cdc.h> // USB CDC library
2017-04-15 13:57:45 +02:00
# include <libopencm3/usb/dfu.h> // DFU definitions
2016-01-18 16:15:23 +01:00
2018-04-03 16:59:02 +02:00
/* own libraries */
2016-08-14 18:37:30 +02:00
# include "global.h" // global utilities
2016-01-18 16:15:23 +01:00
# include "usb_cdcacm.h" // USB CDC ACM header and definitions
2019-12-06 17:10:24 +01:00
# include "print.h" // strings utilities (large library, but the main application has space)
2016-01-18 16:15:23 +01:00
2018-04-03 16:59:02 +02:00
/** maximum packet size for USB data transfer */
# define USB_DATA_TRANSFER_SIZE 64 // 64 is the maximum for full speed devices
volatile bool usb_cdcacm_connecting = false ;
2017-04-15 13:57:45 +02:00
static uint8_t usbd_control_buffer [ 128 ] = { 0 } ; /**< buffer to be used for control requests */
static usbd_device * usb_device = NULL ; /**< structure holding all the info related to the USB device */
2018-04-03 16:59:02 +02:00
static volatile bool first_connection = false ; /**< used to detect when the first connection occurred */
/* output ring buffer, index, and available memory */
2020-12-14 13:34:18 +01:00
static uint8_t tx_buffer [ 1024 ] = { 0 } ; /**< ring buffer for data to transmit */
2018-04-03 16:59:02 +02:00
static volatile uint16_t tx_i = 0 ; /**< current position if transmitted data */
static volatile uint16_t tx_used = 0 ; /**< how much data needs to be transmitted */
static volatile bool tx_lock = false ; /**< if the transmit buffer is currently being written */
2017-04-15 13:57:45 +02:00
2016-08-14 18:37:30 +02:00
/** USB CDC ACM device descriptor
* @ note as defined in USB CDC specification section 5
2016-01-28 21:21:50 +01:00
*/
2017-04-15 13:57:45 +02:00
static const struct usb_device_descriptor usb_cdcacm_device_descriptor = {
. bLength = USB_DT_DEVICE_SIZE , /**< the size of this header in bytes, 18 */
. bDescriptorType = USB_DT_DEVICE , /**< a value of 1 indicates that this is a device descriptor */
. bcdUSB = 0x0200 , /**< this device supports USB 2.0 */
. bDeviceClass = USB_CLASS_CDC , /**< use the CDC device class */
. bDeviceSubClass = 0 , /**< unused */
. bDeviceProtocol = 0 , /**< unused */
. bMaxPacketSize0 = 64 , /**< packet size for endpoint zero in bytes */
2018-04-03 16:59:02 +02:00
. idVendor = 0x1209 , /**< pid.codes vendor ID */
2018-04-03 17:08:34 +02:00
. idProduct = 0x4356 , /**< CuVoodoo product ID within the Vendor ID space */
2018-04-03 16:59:02 +02:00
. bcdDevice = 0x0000 , /**< Device Release Number: board version number */
2017-04-15 13:57:45 +02:00
. iManufacturer = 1 , /**< the index of the string in the string table that represents the name of the manufacturer of this device */
. iProduct = 2 , /**< the index of the string in the string table that represents the name of the product */
2019-12-06 17:10:24 +01:00
. iSerialNumber = 3 , /**< the index of the string in the string table that represents the serial number of this item in string form */
2017-04-15 13:57:45 +02:00
. bNumConfigurations = 1 , /**< the number of possible configurations this device has */
2016-01-18 16:15:23 +01:00
} ;
2016-08-14 18:37:30 +02:00
/** USB CDC ACM data endpoints
* @ note as defined in USB CDC specification section 5
*/
2017-04-15 13:57:45 +02:00
static const struct usb_endpoint_descriptor usb_cdcacm_data_endpoints [ ] = { {
. bLength = USB_DT_ENDPOINT_SIZE , /**< the size of the endpoint descriptor in bytes */
. bDescriptorType = USB_DT_ENDPOINT , /**< a value of 5 indicates that this describes an endpoint */
. bEndpointAddress = 0x02 , /**< OUT (from host) direction (0<<7), endpoint 2 */
. bmAttributes = USB_ENDPOINT_ATTR_BULK , /**< bulk mode */
2018-04-03 16:59:02 +02:00
. wMaxPacketSize = USB_DATA_TRANSFER_SIZE , /**< maximum packet size */
2017-04-15 13:57:45 +02:00
. bInterval = 1 , /**< the frequency, in number of frames, that we're going to be sending data */
2016-01-18 16:15:23 +01:00
} , {
2017-04-15 13:57:45 +02:00
. bLength = USB_DT_ENDPOINT_SIZE , /**< the size of the endpoint descriptor in bytes */
. bDescriptorType = USB_DT_ENDPOINT , /**< a value of 5 indicates that this describes an endpoint */
. bEndpointAddress = 0x82 , /**< IN (to host) direction (1<<7), endpoint 2 */
. bmAttributes = USB_ENDPOINT_ATTR_BULK , /**< bulk mode */
2018-04-03 16:59:02 +02:00
. wMaxPacketSize = USB_DATA_TRANSFER_SIZE , /**< maximum packet size */
2017-04-15 13:57:45 +02:00
. bInterval = 1 , /**< the frequency, in number of frames, that we're going to be sending data */
2016-01-18 16:15:23 +01:00
} } ;
2016-08-14 18:37:30 +02:00
/** USB CDC ACM communication endpoints
2018-04-03 16:59:02 +02:00
* @ note This notification endpoint isn ' t implemented . According to CDC spec its optional , but its absence causes a NULL pointer dereference in Linux cdc_acm driver
2016-01-18 16:15:23 +01:00
*/
2017-04-15 13:57:45 +02:00
static const struct usb_endpoint_descriptor usb_cdcacm_communication_endpoints [ ] = { {
. bLength = USB_DT_ENDPOINT_SIZE , /**< the size of the endpoint descriptor in bytes */
. bDescriptorType = USB_DT_ENDPOINT , /**< a value of 5 indicates that this describes an endpoint */
. bEndpointAddress = 0x81 , /**< IN (to host) direction (1<<7), endpoint 1 */
. bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT , /**< interrupt mode */
. wMaxPacketSize = 16 , /**< maximum packet size */
. bInterval = 255 , /**< the frequency, in number of frames, that we're going to be sending data */
2016-01-18 16:15:23 +01:00
} } ;
2016-08-14 18:37:30 +02:00
/** USB CDC ACM functional descriptor
* @ note as defined in USB CDC specification section 5.2 .3
2018-04-03 16:59:02 +02:00
* @ return packed structure
2016-01-28 21:21:50 +01:00
*/
2016-01-18 16:15:23 +01:00
static const struct {
2017-04-03 13:03:48 +02:00
struct usb_cdc_header_descriptor header ; /**< header */
struct usb_cdc_call_management_descriptor call_mgmt ; /**< call management descriptor */
struct usb_cdc_acm_descriptor acm ; /**< descriptor */
struct usb_cdc_union_descriptor cdc_union ; /**< descriptor */
2017-04-15 13:57:45 +02:00
} __attribute__ ( ( packed ) ) usb_cdcacm_functional_descriptors = {
2016-01-18 16:15:23 +01:00
. header = {
2017-04-03 13:03:48 +02:00
. bFunctionLength = sizeof ( struct usb_cdc_header_descriptor ) , /**< descriptor length */
. bDescriptorType = CS_INTERFACE , /**< descriptor type */
. bDescriptorSubtype = USB_CDC_TYPE_HEADER , /**< descriptor subtype */
. bcdCDC = 0x0110 , /**< CDC value */
2016-01-18 16:15:23 +01:00
} ,
. call_mgmt = {
2017-04-03 13:03:48 +02:00
. bFunctionLength = sizeof ( struct usb_cdc_call_management_descriptor ) , /**< descriptor length */
. bDescriptorType = CS_INTERFACE , /**< descriptor type */
. bDescriptorSubtype = USB_CDC_TYPE_CALL_MANAGEMENT , /**< descriptor subtype */
. bmCapabilities = 0 , /**< capabilities */
2018-04-08 15:45:34 +02:00
. bDataInterface = 1 , /**< data interface (==usb_cdcacm_data_interface.bInterfaceNumber) */
2016-01-18 16:15:23 +01:00
} ,
. acm = {
2017-04-03 13:03:48 +02:00
. bFunctionLength = sizeof ( struct usb_cdc_acm_descriptor ) , /**< descriptor length */
. bDescriptorType = CS_INTERFACE , /**< descriptor type */
. bDescriptorSubtype = USB_CDC_TYPE_ACM , /**< descriptor subtype */
. bmCapabilities = 0 , /**< capabilities */
2016-01-18 16:15:23 +01:00
} ,
. cdc_union = {
2017-04-03 13:03:48 +02:00
. bFunctionLength = sizeof ( struct usb_cdc_union_descriptor ) , /**< descriptor length */
. bDescriptorType = CS_INTERFACE , /**< descriptor type */
. bDescriptorSubtype = USB_CDC_TYPE_UNION , /**< descriptor subtype */
2018-04-08 15:45:34 +02:00
. bControlInterface = 0 , /**< control interface (==usb_cdcacm_com_interface.bInterfaceNumber) */
. bSubordinateInterface0 = 1 , /**< subordinate interface (==usb_cdcacm_data_interface.bInterfaceNumber) */
2016-01-18 16:15:23 +01:00
} ,
} ;
2016-08-14 18:37:30 +02:00
/** USB CDC interface descriptor
* @ note as defined in USB CDC specification section 5.1 .3
2016-01-28 21:21:50 +01:00
*/
2017-04-15 13:57:45 +02:00
static const struct usb_interface_descriptor usb_cdcacm_communication_interface = {
2018-04-08 15:45:34 +02:00
. bLength = USB_DT_INTERFACE_SIZE , /**< size of descriptor in byte */
. bDescriptorType = USB_DT_INTERFACE , /**< interface descriptor type */
. bInterfaceNumber = 0 , /**< interface number in the list */
. bAlternateSetting = 0 , /**< no alternative settings */
. bNumEndpoints = 1 , /**< number of endpoints used (communication OUT)*/
. bInterfaceClass = USB_CLASS_CDC , /**< USB CDC interface class for CDC */
. bInterfaceSubClass = USB_CDC_SUBCLASS_ACM , /**< USB CDC ACM interface subclass */
. bInterfaceProtocol = USB_CDC_PROTOCOL_NONE , /**< USB CDC ACM none protocol */
. iInterface = 0 , /**< the index of the string in the string table that represents interface description */
. endpoint = usb_cdcacm_communication_endpoints , /**< pointer to endpoint descriptor */
. extra = & usb_cdcacm_functional_descriptors , /**< pointer to functional description (for the data interface) */
. extralen = sizeof ( usb_cdcacm_functional_descriptors ) , /**< number of functional descriptions */
2017-04-15 13:57:45 +02:00
} ;
2016-01-18 16:15:23 +01:00
2016-08-14 18:37:30 +02:00
/** USB CDC ACM data class interface descriptor
* @ note as defined in USB CDC specification section 5.1 .3
2016-01-28 21:21:50 +01:00
*/
2017-04-15 13:57:45 +02:00
static const struct usb_interface_descriptor usb_cdcacm_data_interface = {
2018-04-03 16:59:02 +02:00
. bLength = USB_DT_INTERFACE_SIZE , /**< size of descriptor in byte */
. bDescriptorType = USB_DT_INTERFACE , /**< interface descriptor type */
. bInterfaceNumber = 1 , /**< interface number in the list */
. bAlternateSetting = 0 , /**< no alternative settings */
2018-04-08 15:45:34 +02:00
. bNumEndpoints = 2 , /**< number of endpoints used (data IN and OUT) */
. bInterfaceClass = USB_CLASS_DATA , /**< USB CDC interface class for data */
2018-04-03 16:59:02 +02:00
. bInterfaceSubClass = 0 , /**< USB CDC ACM interface subclass */
. bInterfaceProtocol = 0 , /**< USB CDC ACM none protocol */
. iInterface = 0 , /**< the index of the string in the string table that represents interface description */
2016-01-18 16:15:23 +01:00
2018-04-08 15:45:34 +02:00
. endpoint = usb_cdcacm_data_endpoints , /**< pointer to endpoint descriptor */
2017-04-15 13:57:45 +02:00
} ;
/** USB DFU functional descriptor
* @ note as defined in USB Device Firmware Upgrade specification section 4.2 .4
*/
static const struct usb_dfu_descriptor usb_dfu_functional = {
. bLength = sizeof ( struct usb_dfu_descriptor ) , /**< provide own size */
. bDescriptorType = DFU_FUNCTIONAL , /**< functional descriptor type */
. bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH , /**< this DFU can download and will detach after download */
. wDetachTimeout = 200 , /**< maximum time (in milliseconds) needed to detach (else resume normal operation) */
. wTransferSize = sizeof ( usbd_control_buffer ) , /**< set max transfer size */
. bcdDFUVersion = 0x0110 , /**< DFU specification version 1.1 used */
} ;
/** USB DFU interface descriptor
* @ note as defined in USB Device Firmware Upgrade specification section 4.2 .3
*/
static const struct usb_interface_descriptor usb_dfu_interface = {
. bLength = USB_DT_INTERFACE_SIZE , /**< size of descriptor in byte */
. bDescriptorType = USB_DT_INTERFACE , /**< interface descriptor type */
. bInterfaceNumber = 2 , /**< interface number in the list */
. bAlternateSetting = 0 , /**< no alternative settings */
. bNumEndpoints = 0 , /**< only the control pipe at endpoint 0 is used */
. bInterfaceClass = 0xFE , /**< DFU interface class (not defined in libopencm3 dfu lib) */
. bInterfaceSubClass = 1 , /**< DFU interface subclass (not defined in libopencm3 dfu lib) */
. bInterfaceProtocol = 1 , /**< runtime protocol (not defined in libopencm3 dfu lib) */
2019-12-06 17:10:24 +01:00
. iInterface = 4 , /**< the index of the string in the string table that represents interface description */
2018-04-03 16:59:02 +02:00
2017-04-15 13:57:45 +02:00
. extra = & usb_dfu_functional , /**< point to functional descriptor */
. extralen = sizeof ( usb_dfu_functional ) , /**< size of functional descriptor */
} ;
2016-01-18 16:15:23 +01:00
2016-08-14 18:37:30 +02:00
/** USB CDC ACM interface descriptor */
2017-04-15 13:57:45 +02:00
static const struct usb_interface usb_cdcacm_interfaces [ ] = { {
2016-01-18 16:15:23 +01:00
. num_altsetting = 1 ,
2017-04-15 13:57:45 +02:00
. altsetting = & usb_cdcacm_communication_interface ,
2016-01-18 16:15:23 +01:00
} , {
. num_altsetting = 1 ,
2017-04-15 13:57:45 +02:00
. altsetting = & usb_cdcacm_data_interface ,
} , {
. num_altsetting = 1 ,
. altsetting = & usb_dfu_interface ,
2016-01-18 16:15:23 +01:00
} } ;
2016-08-14 18:37:30 +02:00
/** USB CDC ACM configuration descriptor */
2017-04-15 13:57:45 +02:00
static const struct usb_config_descriptor usb_cdcacm_configuration_descriptor = {
. bLength = USB_DT_CONFIGURATION_SIZE , /**< the length of this header in bytes */
. bDescriptorType = USB_DT_CONFIGURATION , /**< a value of 2 indicates that this is a configuration descriptor */
. wTotalLength = 0 , /**< this should hold the total size of the configuration descriptor including all sub interfaces. it is automatically filled in by the USB stack in libopencm3 */
. bNumInterfaces = LENGTH ( usb_cdcacm_interfaces ) , /**< the number of interfaces in this configuration */
. bConfigurationValue = 1 , /**< the index of this configuration */
. iConfiguration = 0 , /**< a string index describing this configuration (zero means not provided) */
. bmAttributes = 0x80 , /**< bus powered (1<<7) */
2018-04-03 16:59:02 +02:00
. bMaxPower = 0xfa , /**< the maximum amount of current that this device will draw in 2mA units */
2016-01-18 16:15:23 +01:00
// end of header
2017-04-15 13:57:45 +02:00
. interface = usb_cdcacm_interfaces , /**< pointer to an array of interfaces */
2016-01-18 16:15:23 +01:00
} ;
2019-12-06 17:40:34 +01:00
/** device ID used as serial number */
2020-11-27 16:41:19 +01:00
static char usb_serial [ ] = " 0123456789ab " ;
2019-12-06 17:10:24 +01:00
2016-08-14 18:37:30 +02:00
/** USB string table
* @ note starting with index 1
*/
2019-12-06 17:10:24 +01:00
static const char * usb_strings [ ] = {
2018-04-03 16:59:02 +02:00
" CuVoodoo " , /**< manufacturer string */
2020-11-27 16:41:19 +01:00
" CuVoodoo STM32F4xx firmware " , /**< product string */
2019-12-06 17:10:24 +01:00
( const char * ) usb_serial , /**< device ID used as serial number */
2018-04-03 16:59:02 +02:00
" DFU bootloader (runtime mode) " , /**< DFU interface string */
2016-01-18 16:15:23 +01:00
} ;
2017-04-15 13:57:45 +02:00
/** DFU detach (disconnect USB and perform core reset)
* @ param [ in ] usbd_dev USB device ( unused )
* @ param [ in ] req USB request ( unused )
*/
static void usb_dfu_detach ( usbd_device * usbd_dev , struct usb_setup_data * req )
{
( void ) usbd_dev ; // variable not used
( void ) req ; // variable not used
2020-11-27 16:43:57 +01:00
if ( usb_device ) {
usbd_disconnect ( usb_device , true ) ;
}
dfu_bootloader ( ) ; // start DFU bootloader
2017-04-15 13:57:45 +02:00
}
2016-08-14 18:37:30 +02:00
/** incoming USB CDC ACM control request
* @ param [ in ] usbd_dev USB device descriptor
* @ param [ in ] req control request information
* @ param [ in ] buf control request data
* @ param [ in ] len control request data length
2017-04-15 13:57:45 +02:00
* @ param [ in ] complete function to run after request completed
2018-04-28 12:21:32 +02:00
* @ return USBD_REQ_HANDLED if handled correctly , USBD_REQ_NOTSUPP else
2016-08-14 18:37:30 +02:00
* @ note resets device when configured with 5 bits
*/
2018-04-28 12:21:32 +02:00
static enum usbd_request_return_codes usb_cdcacm_control_request ( usbd_device * usbd_dev , struct usb_setup_data * req , uint8_t * * buf , uint16_t * len , void ( * * complete ) ( usbd_device * usbd_dev , struct usb_setup_data * req ) )
2016-01-18 16:15:23 +01:00
{
2020-11-27 16:44:17 +01:00
if ( usb_dfu_interface . bInterfaceNumber = = req - > wIndex ) { // check if request is for DFU
2017-04-15 13:57:45 +02:00
switch ( req - > bRequest ) {
case DFU_DETACH : // USB detach requested
2019-03-26 19:27:40 +01:00
* complete = usb_dfu_detach ; // detach after reply
2017-04-15 13:57:45 +02:00
break ;
case DFU_GETSTATUS : // get status
( * buf ) [ 0 ] = DFU_STATUS_OK ; ; // set OK status
( * buf ) [ 1 ] = 0 ; // set null poll timeout
( * buf ) [ 2 ] = 0 ; // set null poll timeout
( * buf ) [ 3 ] = 0 ; // set null poll timeout
( * buf ) [ 4 ] = STATE_APP_IDLE ; // application is running
( * buf ) [ 5 ] = 0 ; // string not used
* len = 6 ; // set length of buffer to return
break ;
default : // other requests are not supported
2018-04-28 12:21:32 +02:00
return USBD_REQ_NOTSUPP ;
2017-04-15 13:57:45 +02:00
}
2020-11-27 16:44:17 +01:00
} else if ( usb_cdcacm_communication_interface . bInterfaceNumber = = req - > wIndex ) { // check if request is for CDC
2017-04-15 13:57:45 +02:00
switch ( req - > bRequest ) {
case USB_CDC_REQ_SET_CONTROL_LINE_STATE :
2020-11-27 16:44:17 +01:00
usb_cdcacm_connecting = ( 0 ! = req - > wValue ) ; // check if terminal is open (windows set the control line state before a terminal opens the port, but with value 0)
2017-04-15 13:57:45 +02:00
//bool dtr = (req->wValue & (1 << 0)) ? true : false;
//bool rts = (req->wValue & (1 << 1)) ? true : false;
2018-04-03 16:59:02 +02:00
/* the Linux cdc_acm driver requires this to be implemented
2017-04-15 13:57:45 +02:00
* even though it ' s optional in the CDC spec , and we don ' t
* advertise it in the ACM functional descriptor .
*/
uint8_t reply [ 10 ] = { 0 } ;
struct usb_cdc_notification * notif = ( void * ) reply ;
/* we echo signals back to host as notification. */
notif - > bmRequestType = 0xA1 ;
notif - > bNotification = USB_CDC_NOTIFY_SERIAL_STATE ;
notif - > wValue = 0 ;
notif - > wIndex = 0 ;
notif - > wLength = 2 ;
reply [ 8 ] = req - > wValue & 3 ;
reply [ 9 ] = 0 ;
2018-04-08 15:45:34 +02:00
usbd_ep_write_packet ( usbd_dev , usb_cdcacm_communication_endpoints [ 0 ] . bEndpointAddress , reply , LENGTH ( reply ) ) ; // send the reply from communication endpoint (we can't use the complete callback because we are not using the current control endpoint)
2017-04-15 13:57:45 +02:00
break ;
case USB_CDC_REQ_SET_LINE_CODING :
// ignore if length is wrong
if ( * len < sizeof ( struct usb_cdc_line_coding ) ) {
2018-04-28 12:21:32 +02:00
return USBD_REQ_NOTSUPP ;
2017-04-15 13:57:45 +02:00
}
2018-04-08 15:45:34 +02:00
// line coding is ignored
// to get the line coding
// struct usb_cdc_line_coding *coding = (struct usb_cdc_line_coding *)*buf;
2017-04-15 13:57:45 +02:00
break ;
default :
2018-04-28 12:21:32 +02:00
return USBD_REQ_NOTSUPP ;
2017-04-15 13:57:45 +02:00
}
2016-01-18 16:15:23 +01:00
}
2018-04-28 12:21:32 +02:00
return USBD_REQ_HANDLED ;
2016-01-18 16:15:23 +01:00
}
2016-08-14 18:37:30 +02:00
/** USB CDC ACM data received callback
2018-04-03 16:59:02 +02:00
* @ note called when data has been received
2016-08-14 18:37:30 +02:00
* @ param [ in ] usbd_dev USB device descriptor
* @ param [ in ] ep endpoint where data came in
*/
2017-04-15 13:57:45 +02:00
static void usb_cdcacm_data_rx_cb ( usbd_device * usbd_dev , uint8_t ep )
2016-01-18 16:15:23 +01:00
{
2018-04-03 16:59:02 +02:00
( void ) ep ; // not used
2016-01-18 16:15:23 +01:00
2018-04-03 16:59:02 +02:00
char usb_data [ USB_DATA_TRANSFER_SIZE ] = { 0 } ; // buffer to read data
2016-01-18 16:15:23 +01:00
uint16_t usb_length = 0 ; // length of incoming data
2019-03-26 19:27:40 +01:00
2018-04-03 16:59:02 +02:00
// receive data
2018-04-08 15:45:34 +02:00
usb_length = usbd_ep_read_packet ( usbd_dev , usb_cdcacm_data_endpoints [ 0 ] . bEndpointAddress , usb_data , sizeof ( usb_data ) ) ;
2016-01-18 16:15:23 +01:00
if ( usb_length ) { // copy received data
2018-04-03 16:59:02 +02:00
for ( uint16_t i = 0 ; i < usb_length & & i < LENGTH ( usb_data ) ; i + + ) { // only until buffer is full
2018-02-18 15:20:47 +01:00
user_input_store ( usb_data [ i ] ) ; // store user data
2016-01-18 16:15:23 +01:00
}
}
}
2016-08-14 18:37:30 +02:00
/** USB CDC ACM data transmitted callback
2018-04-03 16:59:02 +02:00
* @ note called once transmission is completed
2016-08-14 18:37:30 +02:00
* @ param [ in ] usbd_dev USB device descriptor
* @ param [ in ] ep endpoint where data came in
*/
2017-04-15 13:57:45 +02:00
static void usb_cdcacm_data_tx_cb ( usbd_device * usbd_dev , uint8_t ep )
2016-01-28 21:21:50 +01:00
{
2018-04-03 16:59:02 +02:00
( void ) ep ; // not used
2018-04-30 18:08:20 +02:00
static bool usb_tx_ongoing = false ; // if USB transmission is already ongoing
2016-01-28 21:21:50 +01:00
2018-04-30 18:08:20 +02:00
if ( ! usbd_dev & & usb_tx_ongoing ) { // putchar is trying to send data but a transmission is already ongoing
return ;
}
2020-11-27 16:44:17 +01:00
if ( 0 = = tx_used | | ! first_connection ) { // verify if we can send and there is something to send
2018-04-03 16:59:02 +02:00
usb_tx_ongoing = false ; // transmission ended
2016-08-14 18:37:30 +02:00
return ;
}
2018-04-30 18:08:20 +02:00
if ( ! tx_lock ) { // ensure no data is in being put in the buffer
tx_lock = true ; // get the lock (no dead lock should occur since putchar should not be used in interrupts)
2018-04-28 13:54:49 +02:00
usb_tx_ongoing = true ; // remember we started transmission
2018-04-03 16:59:02 +02:00
uint16_t usb_length = ( tx_used > USB_DATA_TRANSFER_SIZE ? USB_DATA_TRANSFER_SIZE : tx_used ) ; // length of data to be transmitted (respect max packet size)
2020-11-27 16:44:17 +01:00
usb_length = ( usb_length > ( LENGTH ( tx_buffer ) - tx_i ) ? LENGTH ( tx_buffer ) - tx_i : usb_length ) ; // since here we use the source array not as ring buffer, only go up to the end
2018-04-30 18:08:20 +02:00
usb_length = usbd_ep_write_packet ( usb_device , 0x82 , ( void * ) ( & tx_buffer [ tx_i ] ) , usb_length ) ; // transmit data (put into USB FIFO)
2020-11-27 17:06:21 +01:00
tx_i = ( tx_i + usb_length ) % LENGTH ( tx_buffer ) ; // update location on buffer
2016-08-14 18:37:30 +02:00
tx_used - = usb_length ; // update used size
2018-04-30 18:08:20 +02:00
tx_lock = false ; // release lock
2016-08-14 18:37:30 +02:00
} else {
2018-04-08 15:45:34 +02:00
usbd_ep_write_packet ( usb_device , usb_cdcacm_data_endpoints [ 1 ] . bEndpointAddress , NULL , 0 ) ; // trigger empty tx for a later callback
2016-01-28 21:21:50 +01:00
}
}
2018-04-30 18:08:20 +02:00
/** USB CDC ACM communication callback
2019-03-26 19:27:40 +01:00
* @ note if transmission happens before the control setting is complete with a response form the communication endpoint , Linux echoes back the data
2018-04-30 18:08:20 +02:00
* @ param [ in ] usbd_dev USB device descriptor
* @ param [ in ] ep endpoint where data came in
*/
static void usb_cdcacm_communication_cb ( usbd_device * usbd_dev , uint8_t ep )
{
( void ) ep ; // not used
( void ) usbd_dev ; // not used
first_connection | = usb_cdcacm_connecting ; // check if port has been opened
2020-11-27 16:44:17 +01:00
if ( tx_used > 0 & & first_connection ) { // if buffer is not empty
2018-04-30 18:08:20 +02:00
usb_cdcacm_data_tx_cb ( NULL , 0 ) ; // send available data
}
}
2016-08-14 18:37:30 +02:00
/** set USB CDC ACM configuration
* @ param [ in ] usbd_dev USB device descriptor
* @ param [ in ] wValue not used
*/
2017-04-15 13:57:45 +02:00
static void usb_cdcacm_set_config ( usbd_device * usbd_dev , uint16_t wValue )
2016-01-18 16:15:23 +01:00
{
2018-04-03 16:59:02 +02:00
( void ) wValue ; // not used
2016-01-18 16:15:23 +01:00
2018-04-03 16:59:02 +02:00
usbd_ep_setup ( usbd_dev , usb_cdcacm_communication_endpoints [ 0 ] . bEndpointAddress , usb_cdcacm_communication_endpoints [ 0 ] . bmAttributes , usb_cdcacm_communication_endpoints [ 0 ] . wMaxPacketSize , usb_cdcacm_communication_cb ) ; // set communication endpoint
usbd_ep_setup ( usbd_dev , usb_cdcacm_data_endpoints [ 0 ] . bEndpointAddress , usb_cdcacm_data_endpoints [ 0 ] . bmAttributes , usb_cdcacm_data_endpoints [ 0 ] . wMaxPacketSize , usb_cdcacm_data_rx_cb ) ; // set outgoing (from host) data endpoint
usbd_ep_setup ( usbd_dev , usb_cdcacm_data_endpoints [ 1 ] . bEndpointAddress , usb_cdcacm_data_endpoints [ 1 ] . bmAttributes , usb_cdcacm_data_endpoints [ 1 ] . wMaxPacketSize , usb_cdcacm_data_tx_cb ) ; // set incoming (to host) data endpoint
2016-01-18 16:15:23 +01:00
2018-04-03 16:59:02 +02:00
usbd_register_control_callback ( usbd_dev , USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE , USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT , usb_cdcacm_control_request ) ;
2016-01-18 16:15:23 +01:00
}
2018-04-30 18:08:20 +02:00
2017-04-15 13:57:45 +02:00
void usb_cdcacm_setup ( void )
2016-01-18 16:15:23 +01:00
{
2020-11-27 16:43:57 +01:00
// set serial according to STM32 USB-FS_Device developer kit
// see https://community.st.com/s/question/0D50X0000ADCaTJSQ1/dfu-serial-number
snprintf ( usb_serial , LENGTH ( usb_serial ) , " %08x%04x " , DESIG_UNIQUE_ID0 + DESIG_UNIQUE_ID2 , DESIG_UNIQUE_ID1 > > 16 ) ; // set actual device ID as serial
2019-12-06 17:10:24 +01:00
2017-04-15 13:57:45 +02:00
// initialize USB
rcc_periph_clock_enable ( RCC_GPIOA ) ; // enable clock for GPIO used for USB
2020-11-27 16:43:57 +01:00
gpio_mode_setup ( GPIOA , GPIO_MODE_AF , GPIO_PUPD_NONE , GPIO11 | GPIO12 ) ; // set pin to alternate function
gpio_set_af ( GPIOA , GPIO_AF10 , GPIO11 | GPIO12 ) ; // set alternate function to USB
usb_device = usbd_init ( & otgfs_usb_driver , & usb_cdcacm_device_descriptor , & usb_cdcacm_configuration_descriptor , usb_strings , LENGTH ( usb_strings ) , usbd_control_buffer , sizeof ( usbd_control_buffer ) ) ;
2017-04-15 13:57:45 +02:00
usbd_register_set_config_callback ( usb_device , usb_cdcacm_set_config ) ;
2020-11-27 16:43:57 +01:00
nvic_enable_irq ( NVIC_OTG_FS_IRQ ) ; // enable interrupts (to not have to poll all the time)
2019-03-26 19:27:40 +01:00
// reset buffer states
2016-08-14 18:37:30 +02:00
tx_i = 0 ;
tx_used = 0 ;
2018-04-03 16:59:02 +02:00
tx_lock = false ; // release lock
usb_cdcacm_connecting = false ; // clear flag
first_connection = false ; // reset first connection detection
2016-01-18 16:15:23 +01:00
}
2017-04-15 13:57:45 +02:00
void usb_cdcacm_putchar ( char c )
2016-01-18 16:15:23 +01:00
{
2018-04-03 16:59:02 +02:00
if ( ! usb_device ) {
2016-08-14 18:37:30 +02:00
return ;
}
2018-04-30 18:08:20 +02:00
while ( tx_lock ) ; // wait for lock to be released
2018-04-03 16:59:02 +02:00
tx_lock = true ; // put lock on transmit buffer
2020-11-27 17:06:21 +01:00
if ( tx_used < LENGTH ( tx_buffer ) ) { // buffer not full
tx_buffer [ ( tx_i + tx_used ) % LENGTH ( tx_buffer ) ] = c ; // put character in buffer
2016-01-28 21:21:50 +01:00
tx_used + + ; // update used buffer
2016-08-14 18:37:30 +02:00
} else { // buffer full (might be that no terminal is connected to this serial)
2020-11-27 17:06:21 +01:00
tx_i = ( tx_i + 1 ) % LENGTH ( tx_buffer ) ; // shift start
tx_buffer [ ( tx_i + tx_used ) % LENGTH ( tx_buffer ) ] = c ; // overwrite old data
2016-08-14 18:37:30 +02:00
}
2018-04-03 16:59:02 +02:00
tx_lock = false ; // release lock on transmit buffer
2018-04-30 18:08:20 +02:00
usb_cdcacm_data_tx_cb ( NULL , 0 ) ; // send data over USB when possible
2016-01-18 16:15:23 +01:00
}
2020-06-14 18:57:05 +02:00
void usb_cdcacm_flush ( void )
{
while ( tx_used ) ;
}
2016-08-14 18:37:30 +02:00
/** USB interrupt service routine called when data is received */
2020-11-27 16:43:57 +01:00
void otg_fs_isr ( void )
2020-06-14 18:57:05 +02:00
{
2016-08-14 18:37:30 +02:00
usbd_poll ( usb_device ) ;
2016-01-18 16:15:23 +01:00
}