2016-01-18 16:15:23 +01:00
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*
*/
2016-08-14 18:37:30 +02:00
/** library for USB CDC ACM communication (code)
* @ file usb_cdcacm . c
* @ author King Kévin < kingkevin @ cuvoodoo . info >
2018-02-23 11:34:21 +01:00
* @ date 2016 - 2018
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-02-23 11:34:21 +01: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-02-23 12:38:59 +01: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
2018-02-23 12:38:59 +01:00
/** maximum packet size for USB data transfer */
2018-02-26 12:13:12 +01:00
# define USB_DATA_TRANSFER_SIZE 64 // 64 is the maximum for full speed devices
2018-02-23 12:38:59 +01:00
2018-02-23 15:59:28 +01:00
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-02-26 12:13:12 +01:00
static volatile bool usb_tx_ongoing = false ; /**< if USB transmission is already ongoing */
static volatile bool first_connection = false ; /**< used to detect when the first connection occurred */
2017-04-15 13:57:45 +02:00
2018-02-23 12:38:59 +01:00
/* output ring buffer, index, and available memory */
static uint8_t tx_buffer [ 512 ] = { 0 } ; /**< ring buffer for data to transmit */
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 */
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-02-23 11:34:21 +01:00
. idVendor = 0x1209 , /**< pid.codes vendor ID */
. idProduct = 0x4256 , /**< BusVoodo product ID within the Vendor ID space */
. 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 */
2018-02-23 11:34:21 +01:00
. iSerialNumber = 0 , /**< 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-02-23 12:38:59 +01: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-02-23 12:38:59 +01: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-02-23 12:38:59 +01: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
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 */
. bDataInterface = 1 , /**< data interface */
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 */
. bControlInterface = 0 , /**< control interface */
. bSubordinateInterface0 = 1 , /**< subordinate interface */
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 = {
2016-01-18 16:15:23 +01:00
. bLength = USB_DT_INTERFACE_SIZE ,
. bDescriptorType = USB_DT_INTERFACE ,
. bInterfaceNumber = 0 ,
. bAlternateSetting = 0 ,
. bNumEndpoints = 1 ,
. bInterfaceClass = USB_CLASS_CDC ,
. bInterfaceSubClass = USB_CDC_SUBCLASS_ACM ,
. bInterfaceProtocol = USB_CDC_PROTOCOL_NONE ,
. iInterface = 0 ,
2017-04-15 13:57:45 +02:00
. endpoint = usb_cdcacm_communication_endpoints ,
2016-01-18 16:15:23 +01:00
2017-04-15 13:57:45 +02:00
. extra = & usb_cdcacm_functional_descriptors ,
. extralen = sizeof ( usb_cdcacm_functional_descriptors ) ,
} ;
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-02-23 11:34:21 +01: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 */
. bNumEndpoints = 2 , /**< only the control pipe at endpoint 0 is used */
. bInterfaceClass = USB_CLASS_DATA , /**< USB CDC interface class */
. 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 */
2018-02-23 12:38:59 +01:00
2018-02-23 11:34:21 +01:00
. endpoint = usb_cdcacm_data_endpoints , /**< point 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) */
2018-02-23 11:34:21 +01:00
. iInterface = 3 , /**< the index of the string in the string table that represents interface description */
2018-02-23 15:59:28 +01: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-02-23 11:34:21 +01: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
} ;
2016-08-14 18:37:30 +02:00
/** USB string table
* @ note starting with index 1
*/
static const char * usb_strings [ ] = {
2018-02-23 11:34:21 +01:00
" CuVoodoo " , /**< manufacturer string */
" BusVoodoo multi-protocol debugging adapter " , /**< product string */
" DFU bootloader (runtime mode) " , /**< DFU interface string */
2016-01-18 16:15:23 +01:00
} ;
2016-08-14 18:37:30 +02:00
/** disconnect USB by pulling down D+ to for re-enumerate */
2016-02-18 10:39:08 +01:00
static void usb_disconnect ( void )
{
2017-04-15 13:57:45 +02:00
# if defined(MAPLE_MINI)
2016-02-18 10:39:08 +01:00
// disconnect USB D+ using dedicated DISC line/circuit on PB9
rcc_periph_clock_enable ( RCC_GPIOB ) ;
gpio_set_mode ( GPIOB , GPIO_MODE_OUTPUT_2_MHZ , GPIO_CNF_OUTPUT_PUSHPULL , GPIO9 ) ;
gpio_set ( GPIOB , GPIO9 ) ;
2018-03-01 17:45:29 +01:00
for ( uint32_t i = 0 ; i < 0x2000 ; i + + ) { // wait for at least 10 ms
__asm__ ( " nop " ) ;
}
2017-04-15 13:57:45 +02:00
# else
// pull USB D+ low for a short while
rcc_periph_clock_enable ( RCC_GPIOA ) ;
gpio_set_mode ( GPIOA , GPIO_MODE_OUTPUT_2_MHZ , GPIO_CNF_OUTPUT_PUSHPULL , GPIO12 ) ;
gpio_clear ( GPIOA , GPIO12 ) ;
2018-03-01 17:45:29 +01:00
for ( uint32_t i = 0 ; i < 0x2000 ; i + + ) { // wait for at least 10 ms
__asm__ ( " nop " ) ;
}
2016-02-18 10:39:08 +01:00
# endif
}
2017-04-15 13:57:45 +02:00
/** disconnect USB and perform system reset
* @ param [ in ] usbd_dev USB device ( unused )
* @ param [ in ] req USB request ( unused )
*/
static void usb_reset ( usbd_device * usbd_dev , struct usb_setup_data * req )
{
( void ) usbd_dev ; // variable not used
( void ) req ; // variable not used
2018-02-26 12:13:12 +01:00
2017-04-15 13:57:45 +02:00
usb_disconnect ( ) ; // USB detach (disconnect to force re-enumeration)
scb_reset_system ( ) ; // reset device
while ( true ) ; // wait for the reset to happen
}
/** 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
RCC_CSR | = RCC_CSR_RMVF ; // clear reset flag for the bootloader to detect the core reset
usb_disconnect ( ) ; // USB detach (disconnect to force re-enumeration)
scb_reset_core ( ) ; // reset device (only the core, to the peripheral stay configured)
while ( true ) ; // wait for the reset to happen
}
2018-02-26 12:13:12 +01:00
/** USB CDC ACM control callback
2018-02-26 12:41:24 +01:00
* @ note if transmission happens before the control setting is complete , Linux echoes back the data
2018-02-26 12:13:12 +01:00
* @ param [ in ] usbd_dev USB device descriptor
* @ param [ in ] req control request information
*/
static void usb_cdcacm_control_cb ( usbd_device * usbd_dev , struct usb_setup_data * req )
{
first_connection | = ( 0 ! = req - > wValue ) ; // check if port has been opened (windows set the control line state before a terminal opens the port, but with value 0)
if ( usbd_dev & & tx_used > 0 & & ! usb_tx_ongoing & & first_connection ) { // if buffer is not empty
usbd_ep_write_packet ( usbd_dev , 0x82 , NULL , 0 ) ; // trigger tx callback
}
}
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
2016-08-14 18:37:30 +02:00
* @ return 0 if succeeded , error else
* @ note resets device when configured with 5 bits
*/
2017-04-15 13:57:45 +02:00
static int 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
{
2017-04-15 13:57:45 +02:00
if ( usb_dfu_interface . bInterfaceNumber = = req - > wIndex ) { // check if request is for DFU
switch ( req - > bRequest ) {
case DFU_DETACH : // USB detach requested
* complete = usb_dfu_detach ; // detach after reply
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
return 0 ;
}
} else {
switch ( req - > bRequest ) {
case USB_CDC_REQ_SET_CONTROL_LINE_STATE :
2018-02-26 12:13:12 +01:00
usb_cdcacm_connecting = ( 0 ! = req - > wValue ) ; // check if terminal is open
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-02-26 12:41:24 +01: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-02-26 12:13:12 +01:00
usbd_ep_write_packet ( usbd_dev , 0x81 , reply , LENGTH ( reply ) ) ; // send the reply
* complete = usb_cdcacm_control_cb ; // check state once reply is transmitted
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 ) ) {
return 0 ;
}
// get the line coding
struct usb_cdc_line_coding * coding = ( struct usb_cdc_line_coding * ) * buf ;
/* reset device is the data bits is set to 5
* to reset the device from the host you can use stty - - file / dev / ttyACM0 raw cs5
*/
if ( coding - > bDataBits = = 5 ) {
* complete = usb_reset ; // perform reset after reply
}
break ;
default :
2016-01-28 21:21:50 +01:00
return 0 ;
2017-04-15 13:57:45 +02:00
}
2016-01-18 16:15:23 +01:00
}
2016-08-14 18:37:30 +02:00
return 1 ;
2016-01-18 16:15:23 +01:00
}
2016-08-14 18:37:30 +02:00
/** USB CDC ACM data received callback
2018-02-26 12:13:12 +01: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-02-26 12:13:12 +01:00
( void ) ep ; // not used
2016-01-18 16:15:23 +01:00
2018-02-23 12:38:59 +01: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
2018-02-26 12:13:12 +01:00
// receive data
2017-04-15 13:57:45 +02:00
usb_length = usbd_ep_read_packet ( usbd_dev , 0x02 , usb_data , sizeof ( usb_data ) ) ;
2016-01-18 16:15:23 +01:00
if ( usb_length ) { // copy received data
2018-02-23 15:59:28 +01:00
for ( uint16_t i = 0 ; i < usb_length & & i < LENGTH ( usb_data ) ; i + + ) { // only until buffer is full
2018-01-24 22:18:58 +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-02-26 12:13:12 +01: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-02-26 12:13:12 +01:00
( void ) ep ; // not used
2016-01-28 21:21:50 +01:00
2018-02-26 12:13:12 +01:00
if ( ! usbd_dev | | 0 = = tx_used | | ! first_connection ) { // verify if we can send and there is something to send
usb_tx_ongoing = false ; // transmission ended
2016-08-14 18:37:30 +02:00
return ;
}
2018-02-23 12:38:59 +01:00
if ( ! tx_lock ) {
2018-02-26 12:13:12 +01:00
usb_tx_ongoing = true ; // remember we started transmission
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)
2016-08-14 18:37:30 +02: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
while ( usb_length ! = usbd_ep_write_packet ( usb_device , 0x82 , ( void * ) ( & tx_buffer [ tx_i ] ) , usb_length ) ) ; // ensure data is written into transmit buffer
tx_i = ( tx_i + usb_length ) % LENGTH ( tx_buffer ) ; // update location on buffer
tx_used - = usb_length ; // update used size
} else {
usbd_ep_write_packet ( usb_device , 0x82 , NULL , 0 ) ; // trigger empty tx for a later callback
2016-01-28 21:21:50 +01:00
}
2016-08-14 18:37:30 +02:00
usbd_poll ( usb_device ) ; // ensure the data gets sent
2016-01-28 21:21:50 +01:00
}
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-02-26 12:13:12 +01:00
( void ) wValue ; // not used
2016-01-18 16:15:23 +01:00
2018-02-26 12:13:12 +01:00
usbd_ep_setup ( usbd_dev , usb_cdcacm_communication_endpoints [ 0 ] . bEndpointAddress , usb_cdcacm_communication_endpoints [ 0 ] . bmAttributes , usb_cdcacm_communication_endpoints [ 0 ] . wMaxPacketSize , NULL ) ; // 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-02-26 12:13:12 +01: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-02-23 12:38:59 +01:00
2017-04-15 13:57:45 +02:00
void usb_cdcacm_setup ( void )
2016-01-18 16:15:23 +01:00
{
2017-04-15 13:57:45 +02:00
// initialize USB
rcc_periph_reset_pulse ( RST_USB ) ; // reset USB peripheral
usb_disconnect ( ) ; // disconnect to force re-enumeration
rcc_periph_clock_enable ( RCC_GPIOA ) ; // enable clock for GPIO used for USB
rcc_periph_clock_enable ( RCC_USB ) ; // enable clock for USB domain
usb_device = usbd_init ( & st_usbfs_v1_usb_driver , & usb_cdcacm_device_descriptor , & usb_cdcacm_configuration_descriptor , usb_strings , LENGTH ( usb_strings ) , usbd_control_buffer , sizeof ( usbd_control_buffer ) ) ;
usbd_register_set_config_callback ( usb_device , usb_cdcacm_set_config ) ;
nvic_enable_irq ( NVIC_USB_LP_CAN_RX0_IRQ ) ; // enable interrupts (to not have to poll all the time)
2016-01-18 16:15:23 +01:00
2017-04-15 13:57:45 +02:00
// reset buffer states
2016-08-14 18:37:30 +02:00
tx_i = 0 ;
tx_used = 0 ;
2018-02-23 12:38:59 +01:00
tx_lock = false ; // release lock
2018-02-23 15:59:28 +01:00
usb_cdcacm_connecting = false ; // clear flag
2018-02-26 12:13:12 +01:00
usb_tx_ongoing = false ; // clear transmission lock
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-02-23 15:59:28 +01:00
if ( ! usb_device ) {
2016-08-14 18:37:30 +02:00
return ;
}
2018-02-23 12:38:59 +01:00
tx_lock = true ; // put lock on transmit buffer
2016-08-14 18:37:30 +02: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)
tx_i = ( tx_i + 1 ) % LENGTH ( tx_buffer ) ; // shift start
tx_buffer [ ( tx_i + tx_used ) % LENGTH ( tx_buffer ) ] = c ; // overwrite old data
}
2018-02-23 12:38:59 +01:00
tx_lock = false ; // release lock on transmit buffer
2018-02-26 12:13:12 +01:00
if ( tx_used > 0 & & usb_device & & ! usb_tx_ongoing ) { // if buffer is not empty anymore
2016-08-14 18:37:30 +02:00
usbd_ep_write_packet ( usb_device , 0x82 , NULL , 0 ) ; // trigger tx callback
2016-01-28 21:21:50 +01:00
}
2016-01-18 16:15:23 +01:00
}
2016-08-14 18:37:30 +02:00
/** USB interrupt service routine called when data is received */
2016-01-18 16:15:23 +01:00
void usb_lp_can_rx0_isr ( void ) {
2016-08-14 18:37:30 +02:00
usbd_poll ( usb_device ) ;
2016-01-18 16:15:23 +01:00
}