USB: improve transmission and wait until port is opened before starting transmission
This commit is contained in:
parent
fdff675c67
commit
0f712e5c0a
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue