USB: fix packet loss

This commit is contained in:
King Kévin 2018-04-30 18:08:20 +02:00
parent 76994571b5
commit fa3293a1f9
1 changed files with 28 additions and 28 deletions

View File

@ -43,7 +43,6 @@ 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 */
@ -345,21 +344,6 @@ static enum usbd_request_return_codes usb_cdcacm_control_request(usbd_device *us
return USBD_REQ_HANDLED;
}
/** USB CDC ACM communication callback
* @note if transmission happens before the control setting is complete with a response form the communication endpoint, Linux echoes back the data
* @param[in] usbd_dev USB device descriptor
* @param[in] ep endpoint where data came in
*/
static void usb_cdcacm_communication_cb(usbd_device *usbd_dev, uint8_t ep)
{
(void)ep; // not used
first_connection |= usb_cdcacm_connecting; // check if port has been opened
if (usbd_dev && tx_used>0 && !usb_tx_ongoing && first_connection) { // if buffer is not empty
usbd_ep_write_packet(usbd_dev, usb_cdcacm_data_endpoints[1].bEndpointAddress, NULL, 0); // trigger tx callback
}
}
/** USB CDC ACM data received callback
* @note called when data has been received
* @param[in] usbd_dev USB device descriptor
@ -389,28 +373,46 @@ static void usb_cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep)
static void usb_cdcacm_data_tx_cb(usbd_device *usbd_dev, uint8_t ep)
{
(void)ep; // not used
static bool usb_tx_ongoing = false; // if USB transmission is already ongoing
if (!usbd_dev || 0==tx_used || !first_connection) { // verify if we can send and there is something to send
if (!usbd_dev && usb_tx_ongoing) { // putchar is trying to send data but a transmission is already ongoing
return;
}
if (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) {
if (!tx_lock) { // ensure no data is in being put in the buffer
tx_lock = true; // get the lock (no dead lock should occur since putchar should not be used in interrupts)
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
uint8_t usb_data[USB_DATA_TRANSFER_SIZE]; // buffer to transmit data
for (uint16_t i=0; i<usb_length && i<USB_DATA_TRANSFER_SIZE; i++) { // copy data to be transferred so it can not be tempered with
usb_data[i] = tx_buffer[tx_i+i];
}
while (usb_length != usbd_ep_write_packet(usb_device, usb_cdcacm_data_endpoints[1].bEndpointAddress, (void*)(usb_data), usb_length)); // ensure data is written into transmit buffer
usb_length = usbd_ep_write_packet(usb_device, 0x82, (void*)(&tx_buffer[tx_i]), usb_length); // transmit data (put into USB FIFO)
tx_i = (tx_i+usb_length)%LENGTH(tx_buffer); // update location on buffer
tx_used -= usb_length; // update used size
tx_lock = false; // release lock
} else {
usbd_ep_write_packet(usb_device, usb_cdcacm_data_endpoints[1].bEndpointAddress, NULL, 0); // trigger empty tx for a later callback
}
usbd_poll(usb_device); // ensure the data gets sent
}
/** USB CDC ACM communication callback
* @note if transmission happens before the control setting is complete with a response form the communication endpoint, Linux echoes back the data
* @param[in] usbd_dev USB device descriptor
* @param[in] ep endpoint where data came in
*/
static void usb_cdcacm_communication_cb(usbd_device *usbd_dev, uint8_t ep)
{
(void)ep; // not used
(void)usbd_dev; // not used
first_connection |= usb_cdcacm_connecting; // check if port has been opened
if (tx_used>0 && first_connection) { // if buffer is not empty
usb_cdcacm_data_tx_cb(NULL, 0); // send available data
}
}
/** set USB CDC ACM configuration
* @param[in] usbd_dev USB device descriptor
* @param[in] wValue not used
@ -442,7 +444,6 @@ 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
}
@ -451,6 +452,7 @@ void usb_cdcacm_putchar(char c)
if (!usb_device) {
return;
}
while (tx_lock); // wait for lock to be released
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
@ -460,9 +462,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 && usb_device && !usb_tx_ongoing) { // if buffer is not empty anymore
usbd_ep_write_packet(usb_device, usb_cdcacm_data_endpoints[1].bEndpointAddress, NULL, 0); // trigger tx callback
}
usb_cdcacm_data_tx_cb(NULL, 0); // send data over USB when possible
}
/** USB interrupt service routine called when data is received */