fix(driver): Add docs and driver fix for the case where a full EP does not cause the host to pickup the data

This commit is contained in:
Jeroen Domburg 2023-11-24 17:37:22 +08:00
parent e334cda304
commit 3f08e5c91a
7 changed files with 50 additions and 7 deletions

View File

@ -94,7 +94,7 @@ static void usb_serial_jtag_isr_handler_default(void *arg) {
uint32_t sent_size = usb_serial_jtag_write_and_flush(queued_buff, queued_size);
if (sent_size < queued_size) {
// Not all bytes could be sent at once, stash the unwritten bytes in a tx buffer
// Not all bytes could be sent at once; stash the unwritten bytes in a tx buffer
// stash_size will not larger than USB_SER_JTAG_ENDP_SIZE because queued_size is got from xRingbufferReceiveUpToFromISR
size_t stash_size = queued_size - sent_size;
memcpy(p_usb_serial_jtag_obj->tx_data_buf, &queued_buff[sent_size], stash_size);
@ -109,6 +109,14 @@ static void usb_serial_jtag_isr_handler_default(void *arg) {
vRingbufferReturnItemFromISR(p_usb_serial_jtag_obj->tx_ring_buf, queued_buff, &xTaskWoken);
}
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
} else {
// The last transmit may have sent a full EP worth of data. The host will interpret
// this as a transaction that hasn't finished yet and keep the data in its internal
// buffers rather than releasing it to the program listening on the CDC serial port.
// We need to flush again in order to send a 0-byte packet that ends the transaction.
usb_serial_jtag_ll_txfifo_flush();
// Note that since this doesn't re-enable USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY, the
// flush will not by itself cause this ISR to be called again.
}
} else {
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);

View File

@ -161,8 +161,14 @@ static inline int usb_serial_jtag_ll_txfifo_writable(void)
* @brief Flushes the TX buffer, that is, make it available for the
* host to pick up.
*
* @note When fifo is full (with 64 byte), HW will flush the buffer automatically.
* It won't be executed if there is nothing in the fifo.
* @note When fifo is full (with 64 byte), HW will flush the buffer automatically,
* if this function is called directly after, this effectively turns into a
* no-op. Because a 64-byte packet will be interpreted as a not-complete USB
* transaction, you need to transfer either more data or a zero-length packet
* for the data to actually end up at the program listening to the CDC-ACM
* serial port. To send a zero-length packet, call
* usb_serial_jtag_ll_txfifo_flush() again when
* usb_serial_jtag_ll_txfifo_writable() returns true.
*
* @return na
*/

View File

@ -161,8 +161,14 @@ static inline int usb_serial_jtag_ll_txfifo_writable(void)
* @brief Flushes the TX buffer, that is, make it available for the
* host to pick up.
*
* @note When fifo is full (with 64 byte), HW will flush the buffer automatically.
* It won't be executed if there is nothing in the fifo.
* @note When fifo is full (with 64 byte), HW will flush the buffer automatically,
* if this function is called directly after, this effectively turns into a
* no-op. Because a 64-byte packet will be interpreted as a not-complete USB
* transaction, you need to transfer either more data or a zero-length packet
* for the data to actually end up at the program listening to the CDC-ACM
* serial port. To send a zero-length packet, call
* usb_serial_jtag_ll_txfifo_flush() again when
* usb_serial_jtag_ll_txfifo_writable() returns true.
*
* @return na
*/

View File

@ -161,8 +161,14 @@ static inline int usb_serial_jtag_ll_txfifo_writable(void)
* @brief Flushes the TX buffer, that is, make it available for the
* host to pick up.
*
* @note When fifo is full (with 64 byte), HW will flush the buffer automatically.
* It won't be executed if there is nothing in the fifo.
* @note When fifo is full (with 64 byte), HW will flush the buffer automatically,
* if this function is called directly after, this effectively turns into a
* no-op. Because a 64-byte packet will be interpreted as a not-complete USB
* transaction, you need to transfer either more data or a zero-length packet
* for the data to actually end up at the program listening to the CDC-ACM
* serial port. To send a zero-length packet, call
* usb_serial_jtag_ll_txfifo_flush() again when
* usb_serial_jtag_ll_txfifo_writable() returns true.
*
* @return na
*/

View File

@ -161,6 +161,15 @@ static inline int usb_serial_jtag_ll_txfifo_writable(void)
* @brief Flushes the TX buffer, that is, make it available for the
* host to pick up.
*
* @note When fifo is full (with 64 byte), HW will flush the buffer automatically,
* if this function is called directly after, this effectively turns into a
* no-op. Because a 64-byte packet will be interpreted as a not-complete USB
* transaction, you need to transfer either more data or a zero-length packet
* for the data to actually end up at the program listening to the CDC-ACM
* serial port. To send a zero-length packet, call
* usb_serial_jtag_ll_txfifo_flush() again when
* usb_serial_jtag_ll_txfifo_writable() returns true.
*
* @return na
*/
static inline void usb_serial_jtag_ll_txfifo_flush(void)

View File

@ -270,6 +270,10 @@ static int usb_serial_jtag_fsync(int fd)
while ((esp_timer_get_time() - s_ctx.last_tx_ts) < TX_FLUSH_TIMEOUT_US) {
if (usb_serial_jtag_ll_txfifo_writable()) {
s_ctx.last_tx_ts = esp_timer_get_time();
//The last transfer may have been a 64-byte one. Flush again in order to
//send a 0-byte packet to indicate the end of the USB transfer, otherwise
//those 64 bytes will get stuck in the hosts buffer.
usb_serial_jtag_ll_txfifo_flush();
break;
}
}

View File

@ -89,6 +89,10 @@ For data transmitted from {IDF_TARGET_NAME} to PC Terminal (e.g., stdout, logs),
For data transmitted from the PC Terminal to {IDF_TARGET_NAME} (e.g., console commands), many PC Terminals wait for the {IDF_TARGET_NAME} to ingest the bytes before allowing you to send more data. This is in contrast to using a USB-to-Serial (UART) bridge chip, which always ingests the bytes and sends them to a (possibly not listening) {IDF_TARGET_NAME}.
.. note::
In rare cases it's possible that data sent from the {IDF_TARGET_NAME} to the host gets 'stuck' in host memory. Sending more data will get it 'unstuck', but if the application does not send more data, depending on the driver, this data needs to be flushed to the host manually. The non-blocking (default) driver and the VFS implementation will flush automatically after a newline. The blocking (interrupt-based) driver will automatically flush when its transmit buffer becomes empty.
Sleep Mode Considerations
-------------------------