|
|
|
@ -12,8 +12,11 @@ |
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
* |
|
|
|
|
*/ |
|
|
|
|
/* Copyright (c) 2016 King Kévin <kingkevin@cuvoodoo.info> */ |
|
|
|
|
/* this library handles the USB CDC ACM */ |
|
|
|
|
/** @brief library for USB CDC ACM communication (code)
|
|
|
|
|
* @file usb_cdcacm.c |
|
|
|
|
* @author King Kévin <kingkevin@cuvoodoo.info> |
|
|
|
|
* @date 2016 |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
/* standard libraries */ |
|
|
|
|
#include <stdint.h> // standard integer types |
|
|
|
@ -31,8 +34,8 @@ |
|
|
|
|
|
|
|
|
|
#include "usb_cdcacm.h" // USB CDC ACM header and definitions |
|
|
|
|
|
|
|
|
|
/* USB devices descriptor
|
|
|
|
|
* as defined in USB CDC specification section 5 |
|
|
|
|
/** @brief USB CDC ACM device descriptor
|
|
|
|
|
* @note as defined in USB CDC specification section 5 |
|
|
|
|
*/ |
|
|
|
|
static const struct usb_device_descriptor device_descriptor = { |
|
|
|
|
.bLength = USB_DT_DEVICE_SIZE, // the size of this header in bytes, 18
|
|
|
|
@ -51,6 +54,9 @@ static const struct usb_device_descriptor device_descriptor = { |
|
|
|
|
.bNumConfigurations = 1, // the number of possible configurations this device has
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/** @brief USB CDC ACM data endpoints
|
|
|
|
|
* @note as defined in USB CDC specification section 5 |
|
|
|
|
*/ |
|
|
|
|
static const struct usb_endpoint_descriptor 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
|
|
|
|
@ -67,9 +73,8 @@ static const struct usb_endpoint_descriptor data_endpoints[] = {{ |
|
|
|
|
.bInterval = 1, // the frequency, in number of frames, that we're going to be sending data
|
|
|
|
|
}}; |
|
|
|
|
|
|
|
|
|
/* 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. |
|
|
|
|
/** @brief USB CDC ACM communication endpoints
|
|
|
|
|
* @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 |
|
|
|
|
*/ |
|
|
|
|
static const struct usb_endpoint_descriptor communication_endpoints[] = {{ |
|
|
|
|
.bLength = USB_DT_ENDPOINT_SIZE, // the size of the endpoint descriptor in bytes
|
|
|
|
@ -80,8 +85,8 @@ static const struct usb_endpoint_descriptor communication_endpoints[] = {{ |
|
|
|
|
.bInterval = 255, // the frequency, in number of frames, that we're going to be sending data
|
|
|
|
|
}}; |
|
|
|
|
|
|
|
|
|
/* functional descriptor
|
|
|
|
|
* as defined in USB CDC specification section 5.2.3 |
|
|
|
|
/** @brief USB CDC ACM functional descriptor
|
|
|
|
|
* @note as defined in USB CDC specification section 5.2.3 |
|
|
|
|
*/ |
|
|
|
|
static const struct { |
|
|
|
|
struct usb_cdc_header_descriptor header; |
|
|
|
@ -117,8 +122,8 @@ static const struct { |
|
|
|
|
}, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/* communication class interface descriptor
|
|
|
|
|
* as defined in USB CDC specification section 5.1.3 |
|
|
|
|
/** @brief USB CDC interface descriptor
|
|
|
|
|
* @note as defined in USB CDC specification section 5.1.3 |
|
|
|
|
*/ |
|
|
|
|
static const struct usb_interface_descriptor communication_interface[] = {{ |
|
|
|
|
.bLength = USB_DT_INTERFACE_SIZE, |
|
|
|
@ -137,8 +142,8 @@ static const struct usb_interface_descriptor communication_interface[] = {{ |
|
|
|
|
.extralen = sizeof(cdcacm_functional_descriptors), |
|
|
|
|
}}; |
|
|
|
|
|
|
|
|
|
/* data class interface descriptor
|
|
|
|
|
* as defined in USB CDC specification section 5.1.3 |
|
|
|
|
/** @brief USB CDC ACM data class interface descriptor
|
|
|
|
|
* @note as defined in USB CDC specification section 5.1.3 |
|
|
|
|
*/ |
|
|
|
|
static const struct usb_interface_descriptor data_interface[] = {{ |
|
|
|
|
.bLength = USB_DT_INTERFACE_SIZE, |
|
|
|
@ -154,6 +159,7 @@ static const struct usb_interface_descriptor data_interface[] = {{ |
|
|
|
|
.endpoint = data_endpoints, |
|
|
|
|
}}; |
|
|
|
|
|
|
|
|
|
/** @brief USB CDC ACM interface descriptor */ |
|
|
|
|
static const struct usb_interface interfaces[] = {{ |
|
|
|
|
.num_altsetting = 1, |
|
|
|
|
.altsetting = communication_interface, |
|
|
|
@ -162,6 +168,7 @@ static const struct usb_interface interfaces[] = {{ |
|
|
|
|
.altsetting = data_interface, |
|
|
|
|
}}; |
|
|
|
|
|
|
|
|
|
/** @brief USB CDC ACM configuration descriptor */ |
|
|
|
|
static const struct usb_config_descriptor config = { |
|
|
|
|
.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
|
|
|
|
@ -175,17 +182,19 @@ static const struct usb_config_descriptor config = { |
|
|
|
|
.interface = interfaces, // pointer to an array of interfaces
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/* string table (starting with index 1) */ |
|
|
|
|
const char *usb_strings[] = { |
|
|
|
|
/** @brief USB string table
|
|
|
|
|
* @note starting with index 1 |
|
|
|
|
*/ |
|
|
|
|
static const char *usb_strings[] = { |
|
|
|
|
"CuVoodoo", |
|
|
|
|
"CDC-ACM", |
|
|
|
|
"STM32F1", |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/* buffer to be used for control requests */ |
|
|
|
|
/** buffer to be used for control requests */ |
|
|
|
|
static uint8_t usbd_control_buffer[128]; |
|
|
|
|
|
|
|
|
|
/* structure holding all the info related to the USB device */ |
|
|
|
|
/** structure holding all the info related to the USB device */ |
|
|
|
|
static usbd_device *usb_device; |
|
|
|
|
|
|
|
|
|
/* input and output ring buffer, indexes, and available memory */ |
|
|
|
@ -195,10 +204,9 @@ static volatile uint8_t rx_used = 0; |
|
|
|
|
static uint8_t tx_buffer[CDCACM_BUFFER] = {0}; |
|
|
|
|
static volatile uint8_t tx_i = 0; |
|
|
|
|
static volatile uint8_t tx_used = 0; |
|
|
|
|
/* show the user how much data received over USB is ready */ |
|
|
|
|
volatile uint8_t cdcacm_received = 0; // same as rx_used, but since the user can write this variable we don't rely on it
|
|
|
|
|
|
|
|
|
|
/* disconnect USB by pulling down D+ to for re-enumerate */ |
|
|
|
|
/** @brief disconnect USB by pulling down D+ to for re-enumerate */ |
|
|
|
|
static void usb_disconnect(void) |
|
|
|
|
{ |
|
|
|
|
/* short USB disconnect to force re-enumerate */ |
|
|
|
@ -222,6 +230,9 @@ static void usb_disconnect(void) |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** @brief incoming USB CDC ACM control request
|
|
|
|
|
* @note resets device when configured with 5 bits |
|
|
|
|
*/ |
|
|
|
|
static int 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)) |
|
|
|
|
{ |
|
|
|
|
(void)complete; |
|
|
|
@ -338,7 +349,6 @@ void cdcacm_setup(void) |
|
|
|
|
usbd_ep_write_packet(usb_device, 0x82, NULL, 0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* get character from USB CDC ACM (blocking) */ |
|
|
|
|
char cdcacm_getchar(void) |
|
|
|
|
{ |
|
|
|
|
while (!rx_used) { // idle until data is available
|
|
|
|
@ -351,7 +361,6 @@ char cdcacm_getchar(void) |
|
|
|
|
return to_return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* put character on USB CDC ACM (blocking) */ |
|
|
|
|
void cdcacm_putchar(char c) |
|
|
|
|
{ |
|
|
|
|
if (tx_used<sizeof(tx_buffer)) { // buffer not full
|
|
|
|
@ -364,10 +373,12 @@ void cdcacm_putchar(char c) |
|
|
|
|
usbd_ep_write_packet(usb_device, 0x82, NULL, 0); // trigger tx (not sure why cdcacm_data_tx_cb doesn't work else)
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** @brief USB interrupt service routine called on wake-up event */ |
|
|
|
|
void usb_wakeup_isr(void) { |
|
|
|
|
usbd_poll(usb_device); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** @brief USB interrupt service routine called when data is received */ |
|
|
|
|
void usb_lp_can_rx0_isr(void) { |
|
|
|
|
usbd_poll(usb_device); |
|
|
|
|
} |
|
|
|
|