USB: improve transmission and wait until port is opened before starting transmission

This commit is contained in:
King Kévin 2018-02-26 12:13:12 +01:00
parent fdff675c67
commit 0f712e5c0a
1 changed files with 39 additions and 23 deletions

View File

@ -36,13 +36,17 @@
#include "global.h" // global utilities
#include "usb_cdcacm.h" // USB CDC ACM header and definitions
#include "print.h"
/** maximum packet size for USB data transfer */
#define USB_DATA_TRANSFER_SIZE 64
#define USB_DATA_TRANSFER_SIZE 64 // 64 is the maximum for full speed devices
volatile bool usb_cdcacm_connecting = false;
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 */
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 */
/* output ring buffer, index, and available memory */
static uint8_t tx_buffer[512] = {0}; /**< ring buffer for data to transmit */
@ -102,7 +106,6 @@ static const struct usb_endpoint_descriptor usb_cdcacm_communication_endpoints[]
}};
/** USB CDC ACM functional descriptor
* @return
* @note as defined in USB CDC specification section 5.2.3
*/
static const struct {
@ -272,6 +275,7 @@ static void usb_reset(usbd_device *usbd_dev, struct usb_setup_data *req)
{
(void)usbd_dev; // variable not used
(void)req; // variable not used
usb_disconnect(); // USB detach (disconnect to force re-enumeration)
scb_reset_system(); // reset device
while (true); // wait for the reset to happen
@ -291,6 +295,19 @@ static void usb_dfu_detach(usbd_device *usbd_dev, struct usb_setup_data *req)
while (true); // wait for the reset to happen
}
/** USB CDC ACM control callback
* @note if transmission happens before the control setting is complete, linux echoes back the data
* @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
}
}
/** incoming USB CDC ACM control request
* @param[in] usbd_dev USB device descriptor
* @param[in] req control request information
@ -302,10 +319,6 @@ static void usb_dfu_detach(usbd_device *usbd_dev, struct usb_setup_data *req)
*/
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))
{
(void)complete;
(void)buf;
(void)usbd_dev;
if (usb_dfu_interface.bInterfaceNumber==req->wIndex) { // check if request is for DFU
switch (req->bRequest) {
case DFU_DETACH: // USB detach requested
@ -326,7 +339,7 @@ static int usb_cdcacm_control_request(usbd_device *usbd_dev, struct usb_setup_da
} else {
switch (req->bRequest) {
case USB_CDC_REQ_SET_CONTROL_LINE_STATE:
usb_cdcacm_connecting = req->wValue ? true : false; // check if terminal is open
usb_cdcacm_connecting = (0!=req->wValue); // check if terminal is open
//bool dtr = (req->wValue & (1 << 0)) ? true : false;
//bool rts = (req->wValue & (1 << 1)) ? true : false;
/* this Linux cdc_acm driver requires this to be implemented
@ -343,7 +356,8 @@ static int usb_cdcacm_control_request(usbd_device *usbd_dev, struct usb_setup_da
notif->wLength = 2;
reply[8] = req->wValue & 3;
reply[9] = 0;
usbd_ep_write_packet(usbd_dev, 0x81, reply, LENGTH(reply));
usbd_ep_write_packet(usbd_dev, 0x81, reply, LENGTH(reply)); // send the reply
*complete = usb_cdcacm_control_cb; // check state once reply is transmitted
break;
case USB_CDC_REQ_SET_LINE_CODING:
// ignore if length is wrong
@ -362,25 +376,23 @@ static int usb_cdcacm_control_request(usbd_device *usbd_dev, struct usb_setup_da
default:
return 0;
}
if (tx_used>0) { // if buffer is not empty
usbd_ep_write_packet(usb_device, 0x82, NULL, 0); // trigger tx callback
}
}
return 1;
}
/** USB CDC ACM data received callback
* @note called when data has been received
* @param[in] usbd_dev USB device descriptor
* @param[in] ep endpoint where data came in
*/
static void usb_cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep)
{
(void)ep;
(void)ep; // not used
char usb_data[USB_DATA_TRANSFER_SIZE] = {0}; // buffer to read data
uint16_t usb_length = 0; // length of incoming data
/* receive data */
// receive data
usb_length = usbd_ep_read_packet(usbd_dev, 0x02, usb_data, sizeof(usb_data));
if (usb_length) { // copy received data
for (uint16_t i=0; i<usb_length && i<LENGTH(usb_data); i++) { // only until buffer is full
@ -390,19 +402,21 @@ static void usb_cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep)
}
/** USB CDC ACM data transmitted callback
* @note called once transmission is completed
* @param[in] usbd_dev USB device descriptor
* @param[in] ep endpoint where data came in
*/
static void usb_cdcacm_data_tx_cb(usbd_device *usbd_dev, uint8_t ep)
{
(void)ep;
(void)usbd_dev;
(void)ep; // not used
if (!usbd_dev || 0==tx_used) { // verify if we can send and there is something to send
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
return;
}
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_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)
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
@ -419,13 +433,13 @@ static void usb_cdcacm_data_tx_cb(usbd_device *usbd_dev, uint8_t ep)
*/
static void usb_cdcacm_set_config(usbd_device *usbd_dev, uint16_t wValue)
{
(void)wValue;
(void)wValue; // not used
usbd_ep_setup(usbd_dev, 0x81, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL);
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_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
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);
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)
@ -444,6 +458,8 @@ void usb_cdcacm_setup(void)
tx_used = 0;
tx_lock = false; // release lock
usb_cdcacm_connecting = false; // clear flag
usb_tx_ongoing = false; // clear transmission lock
first_connection = false; // reset first connection detection
}
void usb_cdcacm_putchar(char c)
@ -460,7 +476,7 @@ void usb_cdcacm_putchar(char c)
tx_buffer[(tx_i+tx_used)%LENGTH(tx_buffer)] = c; // overwrite old data
}
tx_lock = false; // release lock on transmit buffer
if (tx_used>0) { // if buffer is not empty anymore
if (tx_used>0 && usb_device && !usb_tx_ongoing) { // if buffer is not empty anymore
usbd_ep_write_packet(usb_device, 0x82, NULL, 0); // trigger tx callback
}
}