try to fix lost USB TX packet failed, but code improved
This commit is contained in:
parent
ec57456537
commit
8d22030107
|
@ -16,6 +16,7 @@
|
||||||
* @file usb_cdcacm.c
|
* @file usb_cdcacm.c
|
||||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||||
* @date 2016
|
* @date 2016
|
||||||
|
* @bug sometimes usbd_ep_write_packet packets get lost, even if the return value is right. I have no idea why
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* standard libraries */
|
/* standard libraries */
|
||||||
|
@ -31,6 +32,7 @@
|
||||||
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
|
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
|
||||||
#include <libopencm3/usb/usbd.h> // USB library
|
#include <libopencm3/usb/usbd.h> // USB library
|
||||||
#include <libopencm3/usb/cdc.h> // USB CDC library
|
#include <libopencm3/usb/cdc.h> // USB CDC library
|
||||||
|
#include <libopencm3/cm3/sync.h> // synchronisation utilities
|
||||||
|
|
||||||
#include "usb_cdcacm.h" // USB CDC ACM header and definitions
|
#include "usb_cdcacm.h" // USB CDC ACM header and definitions
|
||||||
|
|
||||||
|
@ -192,19 +194,19 @@ static const char *usb_strings[] = {
|
||||||
"STM32F1",
|
"STM32F1",
|
||||||
};
|
};
|
||||||
|
|
||||||
/** buffer to be used for control requests */
|
static uint8_t usbd_control_buffer[128] = {0}; /**< buffer to be used for control requests */
|
||||||
static uint8_t usbd_control_buffer[128];
|
static usbd_device *usb_device = NULL; /**< structure holding all the info related to the USB device */
|
||||||
|
static bool connected = false; /**< is the USB device is connected to a host */
|
||||||
/** structure holding all the info related to the USB device */
|
|
||||||
static usbd_device *usb_device;
|
|
||||||
|
|
||||||
/* input and output ring buffer, indexes, and available memory */
|
/* input and output ring buffer, indexes, and available memory */
|
||||||
static uint8_t rx_buffer[CDCACM_BUFFER] = {0}; /**< ring buffer for received data */
|
static uint8_t rx_buffer[CDCACM_BUFFER] = {0}; /**< ring buffer for received data */
|
||||||
static volatile uint8_t rx_i = 0; /**< current position of read received data */
|
static volatile uint8_t rx_i = 0; /**< current position of read received data */
|
||||||
static volatile uint8_t rx_used = 0; /**< how much data has been received and not red */
|
static volatile uint8_t rx_used = 0; /**< how much data has been received and not red */
|
||||||
|
mutex_t rx_lock = MUTEX_UNLOCKED; /**< lock to update rx_i or rx_used */
|
||||||
static uint8_t tx_buffer[CDCACM_BUFFER] = {0}; /**< ring buffer for data to transmit */
|
static uint8_t tx_buffer[CDCACM_BUFFER] = {0}; /**< ring buffer for data to transmit */
|
||||||
static volatile uint8_t tx_i = 0; /**< current position if transmitted data */
|
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 */
|
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 */
|
||||||
volatile uint8_t cdcacm_received = 0; // same as rx_used, but since the user can write this variable we don't rely on it
|
volatile uint8_t cdcacm_received = 0; // same as rx_used, but since the user can write this variable we don't rely on it
|
||||||
|
|
||||||
/** @brief disconnect USB by pulling down D+ to for re-enumerate */
|
/** @brief disconnect USB by pulling down D+ to for re-enumerate */
|
||||||
|
@ -247,18 +249,26 @@ static int cdcacm_control_request(usbd_device *usbd_dev, struct usb_setup_data *
|
||||||
(void)usbd_dev;
|
(void)usbd_dev;
|
||||||
|
|
||||||
switch (req->bRequest) {
|
switch (req->bRequest) {
|
||||||
case USB_CDC_REQ_SET_CONTROL_LINE_STATE: {
|
case USB_CDC_REQ_SET_CONTROL_LINE_STATE:
|
||||||
bool dtr = (req->wValue & (1 << 0)) ? true : false;
|
connected = req->wValue ? true : false; // check if terminal is open
|
||||||
bool rts = (req->wValue & (1 << 1)) ? true : false;
|
//bool dtr = (req->wValue & (1 << 0)) ? true : false;
|
||||||
if (dtr || rts) { // host opened serial port
|
//bool rts = (req->wValue & (1 << 1)) ? true : false;
|
||||||
usbd_ep_write_packet(usb_device, 0x82, NULL, 0); // start transmitting
|
|
||||||
}
|
|
||||||
/* this Linux cdc_acm driver requires this to be implemented
|
/* this Linux cdc_acm driver requires this to be implemented
|
||||||
* even though it's optional in the CDC spec, and we don't
|
* even though it's optional in the CDC spec, and we don't
|
||||||
* advertise it in the ACM functional descriptor.
|
* advertise it in the ACM functional descriptor.
|
||||||
*/
|
*/
|
||||||
return 1;
|
char local_buf[10];
|
||||||
}
|
struct usb_cdc_notification *notif = (void *)local_buf;
|
||||||
|
/* we echo signals back to host as notification. */
|
||||||
|
notif->bmRequestType = 0xA1;
|
||||||
|
notif->bNotification = USB_CDC_NOTIFY_SERIAL_STATE;
|
||||||
|
notif->wValue = 0;
|
||||||
|
notif->wIndex = 0;
|
||||||
|
notif->wLength = 2;
|
||||||
|
local_buf[8] = req->wValue & 3;
|
||||||
|
local_buf[9] = 0;
|
||||||
|
usbd_ep_write_packet(usbd_dev, 0x83, local_buf, 10);
|
||||||
|
break;
|
||||||
case USB_CDC_REQ_SET_LINE_CODING:
|
case USB_CDC_REQ_SET_LINE_CODING:
|
||||||
// ignore if length is wrong
|
// ignore if length is wrong
|
||||||
if (*len < sizeof(struct usb_cdc_line_coding)) {
|
if (*len < sizeof(struct usb_cdc_line_coding)) {
|
||||||
|
@ -275,11 +285,11 @@ static int cdcacm_control_request(usbd_device *usbd_dev, struct usb_setup_data *
|
||||||
scb_reset_system(); // reset device
|
scb_reset_system(); // reset device
|
||||||
while (true); // wait for the reset to happen
|
while (true); // wait for the reset to happen
|
||||||
}
|
}
|
||||||
return 1;
|
break;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @brief USB CDC ACM data received callback
|
/** @brief USB CDC ACM data received callback
|
||||||
|
@ -289,7 +299,6 @@ static int cdcacm_control_request(usbd_device *usbd_dev, struct usb_setup_data *
|
||||||
static void cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep)
|
static void cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep)
|
||||||
{
|
{
|
||||||
(void)ep;
|
(void)ep;
|
||||||
(void)usbd_dev;
|
|
||||||
|
|
||||||
char usb_data[64] = {0}; // buffer to read data
|
char usb_data[64] = {0}; // buffer to read data
|
||||||
uint16_t usb_length = 0; // length of incoming data
|
uint16_t usb_length = 0; // length of incoming data
|
||||||
|
@ -314,21 +323,20 @@ static void cdcacm_data_tx_cb(usbd_device *usbd_dev, uint8_t ep)
|
||||||
(void)ep;
|
(void)ep;
|
||||||
(void)usbd_dev;
|
(void)usbd_dev;
|
||||||
|
|
||||||
char usb_data[64] = {0}; // buffer to send data
|
if (!usbd_dev || !connected || !tx_used) { // verify if we can send and there is something to send
|
||||||
uint16_t usb_length = 0; // length of transmitted data
|
return;
|
||||||
|
|
||||||
/* transmit data */
|
|
||||||
if (tx_used) { // copy received data
|
|
||||||
for (usb_length=0; usb_length<sizeof(usb_data) && usb_length<tx_used; usb_length++) { // only until buffer is full
|
|
||||||
usb_data[usb_length] = tx_buffer[(tx_i+usb_length)%sizeof(tx_buffer)]; // put data in transmit data
|
|
||||||
}
|
|
||||||
// this could lead to a lock down
|
|
||||||
// while(usbd_ep_write_packet(usb_device, 0x82, usb_data, usb_length)==0);
|
|
||||||
// this is less critical
|
|
||||||
uint8_t transmitted = usbd_ep_write_packet(usb_device, 0x82, usb_data, usb_length); // try to transmit data
|
|
||||||
tx_i = (tx_i+transmitted)%sizeof(tx_buffer); // update location on buffer
|
|
||||||
tx_used -= transmitted; // update used size
|
|
||||||
}
|
}
|
||||||
|
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)
|
||||||
|
usb_length = (usb_length > (sizeof(tx_buffer)-tx_i) ? sizeof(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)%sizeof(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
|
||||||
|
}
|
||||||
|
usbd_poll(usb_device); // ensure the data gets sent
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @brief set USB CDC ACM configuration
|
/** @brief set USB CDC ACM configuration
|
||||||
|
@ -338,7 +346,6 @@ static void cdcacm_data_tx_cb(usbd_device *usbd_dev, uint8_t ep)
|
||||||
static void cdcacm_set_config(usbd_device *usbd_dev, uint16_t wValue)
|
static void cdcacm_set_config(usbd_device *usbd_dev, uint16_t wValue)
|
||||||
{
|
{
|
||||||
(void)wValue;
|
(void)wValue;
|
||||||
(void)usbd_dev;
|
|
||||||
|
|
||||||
usbd_ep_setup(usbd_dev, 0x01, USB_ENDPOINT_ATTR_BULK, 64, cdcacm_data_rx_cb);
|
usbd_ep_setup(usbd_dev, 0x01, USB_ENDPOINT_ATTR_BULK, 64, cdcacm_data_rx_cb);
|
||||||
usbd_ep_setup(usbd_dev, 0x82, USB_ENDPOINT_ATTR_BULK, 64, cdcacm_data_tx_cb);
|
usbd_ep_setup(usbd_dev, 0x82, USB_ENDPOINT_ATTR_BULK, 64, cdcacm_data_tx_cb);
|
||||||
|
@ -349,6 +356,7 @@ static void cdcacm_set_config(usbd_device *usbd_dev, uint16_t wValue)
|
||||||
|
|
||||||
void cdcacm_setup(void)
|
void cdcacm_setup(void)
|
||||||
{
|
{
|
||||||
|
connected = false; // start with USB not connected
|
||||||
usb_disconnect(); // force re-enumerate (useful after a restart or if there is a bootloader using another USB profile)
|
usb_disconnect(); // force re-enumerate (useful after a restart or if there is a bootloader using another USB profile)
|
||||||
|
|
||||||
/* initialize USB */
|
/* initialize USB */
|
||||||
|
@ -356,16 +364,16 @@ void cdcacm_setup(void)
|
||||||
usbd_register_set_config_callback(usb_device, cdcacm_set_config);
|
usbd_register_set_config_callback(usb_device, cdcacm_set_config);
|
||||||
|
|
||||||
/* enable interrupts (to not have to poll all the time) */
|
/* enable interrupts (to not have to poll all the time) */
|
||||||
nvic_enable_irq(NVIC_USB_WAKEUP_IRQ);
|
nvic_enable_irq(NVIC_USB_LP_CAN_RX0_IRQ); // without this USB isn't detected by the host
|
||||||
nvic_enable_irq(NVIC_USB_LP_CAN_RX0_IRQ);
|
|
||||||
|
|
||||||
/* reset buffer states */
|
/* reset buffer states */
|
||||||
rx_i = 0;
|
rx_i = 0;
|
||||||
rx_used = 0;
|
rx_used = 0;
|
||||||
|
mutex_unlock(&rx_lock);
|
||||||
cdcacm_received = 0;
|
cdcacm_received = 0;
|
||||||
|
tx_i = 0;
|
||||||
/* start sending */
|
tx_used = 0;
|
||||||
usbd_ep_write_packet(usb_device, 0x82, NULL, 0);
|
mutex_unlock(&tx_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
char cdcacm_getchar(void)
|
char cdcacm_getchar(void)
|
||||||
|
@ -382,22 +390,22 @@ char cdcacm_getchar(void)
|
||||||
|
|
||||||
void cdcacm_putchar(char c)
|
void cdcacm_putchar(char c)
|
||||||
{
|
{
|
||||||
|
if (!usb_device || !connected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mutex_lock(&tx_lock); // get lock to prevent race condition
|
||||||
if (tx_used<sizeof(tx_buffer)) { // buffer not full
|
if (tx_used<sizeof(tx_buffer)) { // buffer not full
|
||||||
tx_buffer[(tx_i+tx_used)%sizeof(tx_buffer)] = c; // put character in buffer
|
tx_buffer[(tx_i+tx_used)%sizeof(tx_buffer)] = c; // put character in buffer
|
||||||
tx_used++; // update used buffer
|
tx_used++; // update used buffer
|
||||||
} else { // buffer full
|
} else { // buffer full (might be that no terminal is connected to this serial)
|
||||||
tx_i = (tx_i+1)%sizeof(tx_buffer); // shift start
|
tx_i = (tx_i+1)%sizeof(tx_buffer); // shift start
|
||||||
tx_buffer[(tx_i+tx_used)%sizeof(tx_buffer)] = c; // overwrite old data
|
tx_buffer[(tx_i+tx_used)%sizeof(tx_buffer)] = c; // overwrite old data
|
||||||
}
|
}
|
||||||
usbd_ep_write_packet(usb_device, 0x82, NULL, 0); // trigger tx (not sure why cdcacm_data_tx_cb doesn't work else)
|
mutex_unlock(&tx_lock); // release lock
|
||||||
}
|
usbd_ep_write_packet(usb_device, 0x82, NULL, 0); // trigger tx callback
|
||||||
|
|
||||||
/** @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 */
|
/** @brief USB interrupt service routine called when data is received */
|
||||||
void usb_lp_can_rx0_isr(void) {
|
void usb_lp_can_rx0_isr(void) {
|
||||||
usbd_poll(usb_device);
|
usbd_poll(usb_device);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue