USB: increase transmit buffer and simplify locking mechanism

This commit is contained in:
King Kévin 2018-02-23 12:38:59 +01:00
parent 22932d0135
commit 721e181f15
1 changed files with 25 additions and 23 deletions

View File

@ -20,26 +20,35 @@
/* standard libraries */
#include <stdint.h> // standard integer types
#include <stdio.h> // standard I/O facilities
#include <stdlib.h> // general utilities
/* STM32 (including CM3) libraries */
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
#include <libopencm3/cm3/scb.h> // reset utilities
#include <libopencm3/cm3/nvic.h> // interrupt handler
#include <libopencm3/cm3/sync.h> // synchronisation utilities
#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
#include <libopencm3/usb/dfu.h> // DFU definitions
/* own libraries */
#include "global.h" // global utilities
#include "usb_cdcacm.h" // USB CDC ACM header and definitions
/** maximum packet size for USB data transfer */
#define USB_DATA_TRANSFER_SIZE 64
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 */
/* 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 */
static bool connected = false; /**< is the USB device is connected to a host */
/** USB CDC ACM device descriptor
* @note as defined in USB CDC specification section 5
*/
@ -68,19 +77,19 @@ static const struct usb_endpoint_descriptor usb_cdcacm_data_endpoints[] = {{
.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 */
.wMaxPacketSize = 64, /**< maximum packet size */
.wMaxPacketSize = USB_DATA_TRANSFER_SIZE, /**< maximum packet size */
.bInterval = 1, /**< the frequency, in number of frames, that we're going to be sending data */
},{
.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 */
.wMaxPacketSize = 64, /**< maximum packet size */
.wMaxPacketSize = USB_DATA_TRANSFER_SIZE, /**< maximum packet size */
.bInterval = 1, /**< the frequency, in number of frames, that we're going to be sending data */
}};
/** 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
* @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 usb_cdcacm_communication_endpoints[] = {{
.bLength = USB_DT_ENDPOINT_SIZE, /**< the size of the endpoint descriptor in bytes */
@ -162,6 +171,7 @@ static const struct usb_interface_descriptor usb_cdcacm_data_interface = {
.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 */
.endpoint = usb_cdcacm_data_endpoints, /**< point to endpoint descriptor */
};
@ -229,13 +239,6 @@ static const char *usb_strings[] = {
"DFU bootloader (runtime mode)", /**< DFU interface string */
};
/* output ring buffer, index, and available memory */
static uint8_t tx_buffer[64] = {0}; /**< ring buffer for data to transmit */
static volatile uint8_t tx_i = 0; /**< current position if transmitted data */
static volatile uint8_t tx_used = 0; /**< how much data needs to be transmitted */
mutex_t tx_lock = MUTEX_UNLOCKED; /**< lock to update tx_i or tx_used */
static bool connected = false; /**< is the USB device is connected to a host */
/** disconnect USB by pulling down D+ to for re-enumerate */
static void usb_disconnect(void)
{
@ -369,7 +372,7 @@ static void usb_cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep)
{
(void)ep;
char usb_data[64] = {0}; // buffer to read data
char usb_data[USB_DATA_TRANSFER_SIZE] = {0}; // buffer to read data
uint16_t usb_length = 0; // length of incoming data
/* receive data */
@ -393,13 +396,12 @@ static void usb_cdcacm_data_tx_cb(usbd_device *usbd_dev, uint8_t ep)
if (!usbd_dev || !connected || !tx_used) { // verify if we can send and there is something to send
return;
}
if (mutex_trylock(&tx_lock)) { // try to get lock
uint8_t usb_length = (tx_used > 64 ? 64 : tx_used); // length of data to be transmitted (respect max packet size)
if (!tx_lock) {
uint8_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)
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
mutex_unlock(&tx_lock); // release lock
} else {
usbd_ep_write_packet(usb_device, 0x82, NULL, 0); // trigger empty tx for a later callback
}
@ -415,12 +417,12 @@ static void usb_cdcacm_set_config(usbd_device *usbd_dev, uint16_t wValue)
(void)wValue;
usbd_ep_setup(usbd_dev, 0x81, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL);
usbd_ep_setup(usbd_dev, 0x02, USB_ENDPOINT_ATTR_BULK, 64, usb_cdcacm_data_rx_cb);
usbd_ep_setup(usbd_dev, 0x82, USB_ENDPOINT_ATTR_BULK, 64, usb_cdcacm_data_tx_cb);
usbd_ep_setup(usbd_dev, 0x02, USB_ENDPOINT_ATTR_BULK, USB_DATA_TRANSFER_SIZE, usb_cdcacm_data_rx_cb);
usbd_ep_setup(usbd_dev, 0x82, USB_ENDPOINT_ATTR_BULK, USB_DATA_TRANSFER_SIZE, usb_cdcacm_data_tx_cb);
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);
}
void usb_cdcacm_setup(void)
{
connected = false; // start with USB not connected
@ -437,7 +439,7 @@ void usb_cdcacm_setup(void)
// reset buffer states
tx_i = 0;
tx_used = 0;
mutex_unlock(&tx_lock);
tx_lock = false; // release lock
}
void usb_cdcacm_putchar(char c)
@ -445,7 +447,7 @@ void usb_cdcacm_putchar(char c)
if (!usb_device || !connected) {
return;
}
mutex_lock(&tx_lock); // get lock to prevent race condition
tx_lock = true; // put lock on transmit buffer
if (tx_used<LENGTH(tx_buffer)) { // buffer not full
tx_buffer[(tx_i+tx_used)%LENGTH(tx_buffer)] = c; // put character in buffer
tx_used++; // update used buffer
@ -453,8 +455,8 @@ void usb_cdcacm_putchar(char c)
tx_i = (tx_i+1)%LENGTH(tx_buffer); // shift start
tx_buffer[(tx_i+tx_used)%LENGTH(tx_buffer)] = c; // overwrite old data
}
mutex_unlock(&tx_lock); // release lock
if (tx_used==1) { // to buffer is not empty anymore
tx_lock = false; // release lock on transmit buffer
if (tx_used>0) { // to buffer is not empty anymore
usbd_ep_write_packet(usb_device, 0x82, NULL, 0); // trigger tx callback
}
}