aboutsummaryrefslogtreecommitdiff
path: root/ports/stm32/usbd_cdc_interface.c
diff options
context:
space:
mode:
Diffstat (limited to 'ports/stm32/usbd_cdc_interface.c')
-rw-r--r--ports/stm32/usbd_cdc_interface.c157
1 files changed, 80 insertions, 77 deletions
diff --git a/ports/stm32/usbd_cdc_interface.c b/ports/stm32/usbd_cdc_interface.c
index f1ec8d8b1..149971bfb 100644
--- a/ports/stm32/usbd_cdc_interface.c
+++ b/ports/stm32/usbd_cdc_interface.c
@@ -58,6 +58,9 @@
#define CDC_SET_CONTROL_LINE_STATE 0x22
#define CDC_SEND_BREAK 0x23
+// Used to control the connect_state variable when USB host opens the serial port
+static uint8_t usbd_cdc_connect_tx_timer;
+
uint8_t *usbd_cdc_init(usbd_cdc_state_t *cdc_in) {
usbd_cdc_itf_t *cdc = (usbd_cdc_itf_t*)cdc_in;
@@ -68,9 +71,8 @@ uint8_t *usbd_cdc_init(usbd_cdc_state_t *cdc_in) {
cdc->rx_buf_get = 0;
cdc->tx_buf_ptr_out = 0;
cdc->tx_buf_ptr_out_shadow = 0;
- cdc->tx_buf_ptr_wait_count = 0;
cdc->tx_need_empty_packet = 0;
- cdc->dev_is_connected = 0;
+ cdc->connect_state = USBD_CDC_CONNECT_STATE_DISCONNECTED;
#if MICROPY_HW_USB_ENABLE_CDC2
cdc->attached_to_repl = &cdc->base == cdc->base.usbd->cdc;
#else
@@ -83,7 +85,7 @@ uint8_t *usbd_cdc_init(usbd_cdc_state_t *cdc_in) {
void usbd_cdc_deinit(usbd_cdc_state_t *cdc_in) {
usbd_cdc_itf_t *cdc = (usbd_cdc_itf_t*)cdc_in;
- cdc->dev_is_connected = 0;
+ cdc->connect_state = USBD_CDC_CONNECT_STATE_DISCONNECTED;
}
// Manage the CDC class requests
@@ -137,9 +139,21 @@ int8_t usbd_cdc_control(usbd_cdc_state_t *cdc_in, uint8_t cmd, uint8_t* pbuf, ui
pbuf[6] = 8; // number of bits (8)
break;
- case CDC_SET_CONTROL_LINE_STATE:
- cdc->dev_is_connected = length & 1; // wValue is passed in Len (bit of a hack)
+ case CDC_SET_CONTROL_LINE_STATE: {
+ // wValue, indicating the state, is passed in length (bit of a hack)
+ if (length & 1) {
+ // The actual connection state is delayed to give the host a chance to
+ // configure its serial port (in most cases to disable local echo)
+ PCD_HandleTypeDef *hpcd = cdc->base.usbd->pdev->pData;
+ USB_OTG_GlobalTypeDef *USBx = hpcd->Instance;
+ cdc->connect_state = USBD_CDC_CONNECT_STATE_CONNECTING;
+ usbd_cdc_connect_tx_timer = 8; // wait for 8 SOF IRQs
+ USBx->GINTMSK |= USB_OTG_GINTMSK_SOFM;
+ } else {
+ cdc->connect_state = USBD_CDC_CONNECT_STATE_DISCONNECTED;
+ }
break;
+ }
case CDC_SEND_BREAK:
/* Add your code here */
@@ -152,73 +166,74 @@ int8_t usbd_cdc_control(usbd_cdc_state_t *cdc_in, uint8_t cmd, uint8_t* pbuf, ui
return USBD_OK;
}
-// This function is called to process outgoing data. We hook directly into the
-// SOF (start of frame) callback so that it is called exactly at the time it is
-// needed (reducing latency), and often enough (increasing bandwidth).
-static void usbd_cdc_sof(PCD_HandleTypeDef *hpcd, usbd_cdc_itf_t *cdc) {
- if (cdc == NULL || !cdc->dev_is_connected) {
- // CDC device is not connected to a host, so we are unable to send any data
- return;
- }
+// Called when the USB IN endpoint is ready to receive more data
+// (cdc.base.tx_in_progress must be 0)
+void usbd_cdc_tx_ready(usbd_cdc_state_t *cdc_in) {
+
+ usbd_cdc_itf_t *cdc = (usbd_cdc_itf_t*)cdc_in;
+ cdc->tx_buf_ptr_out = cdc->tx_buf_ptr_out_shadow;
if (cdc->tx_buf_ptr_out == cdc->tx_buf_ptr_in && !cdc->tx_need_empty_packet) {
// No outstanding data to send
return;
}
- if (cdc->tx_buf_ptr_out != cdc->tx_buf_ptr_out_shadow) {
- // We have sent data and are waiting for the low-level USB driver to
- // finish sending it over the USB in-endpoint.
- // SOF occurs every 1ms, so we have a 500 * 1ms = 500ms timeout
- // We have a relatively large timeout because the USB host may be busy
- // doing other things and we must give it a chance to read our data.
- if (cdc->tx_buf_ptr_wait_count < 500) {
- USB_OTG_GlobalTypeDef *USBx = hpcd->Instance;
- if (USBx_INEP(cdc->base.in_ep & 0x7f)->DIEPTSIZ & USB_OTG_DIEPTSIZ_XFRSIZ) {
- // USB in-endpoint is still reading the data
- cdc->tx_buf_ptr_wait_count++;
- return;
- }
- }
- cdc->tx_buf_ptr_out = cdc->tx_buf_ptr_out_shadow;
+ uint32_t len;
+ if (cdc->tx_buf_ptr_out > cdc->tx_buf_ptr_in) { // rollback
+ len = USBD_CDC_TX_DATA_SIZE - cdc->tx_buf_ptr_out;
+ } else {
+ len = cdc->tx_buf_ptr_in - cdc->tx_buf_ptr_out;
}
- if (cdc->tx_buf_ptr_out_shadow != cdc->tx_buf_ptr_in || cdc->tx_need_empty_packet) {
- uint32_t buffptr;
- uint32_t buffsize;
+ // Should always succeed because cdc.base.tx_in_progress==0
+ USBD_CDC_TransmitPacket(&cdc->base, len, &cdc->tx_buf[cdc->tx_buf_ptr_out]);
- if (cdc->tx_buf_ptr_out_shadow > cdc->tx_buf_ptr_in) { // rollback
- buffsize = USBD_CDC_TX_DATA_SIZE - cdc->tx_buf_ptr_out_shadow;
- } else {
- buffsize = cdc->tx_buf_ptr_in - cdc->tx_buf_ptr_out_shadow;
- }
+ cdc->tx_buf_ptr_out_shadow += len;
+ if (cdc->tx_buf_ptr_out_shadow == USBD_CDC_TX_DATA_SIZE) {
+ cdc->tx_buf_ptr_out_shadow = 0;
+ }
- buffptr = cdc->tx_buf_ptr_out_shadow;
+ // According to the USB specification, a packet size of 64 bytes (CDC_DATA_FS_MAX_PACKET_SIZE)
+ // gets held at the USB host until the next packet is sent. This is because a
+ // packet of maximum size is considered to be part of a longer chunk of data, and
+ // the host waits for all data to arrive (ie, waits for a packet < max packet size).
+ // To flush a packet of exactly max packet size, we need to send a zero-size packet.
+ // See eg http://www.cypress.com/?id=4&rID=92719
+ cdc->tx_need_empty_packet = (len > 0 && len % usbd_cdc_max_packet(cdc->base.usbd->pdev) == 0 && cdc->tx_buf_ptr_out_shadow == cdc->tx_buf_ptr_in);
+}
- if (USBD_CDC_TransmitPacket(&cdc->base, buffsize, &cdc->tx_buf[buffptr]) == USBD_OK) {
- cdc->tx_buf_ptr_out_shadow += buffsize;
- if (cdc->tx_buf_ptr_out_shadow == USBD_CDC_TX_DATA_SIZE) {
- cdc->tx_buf_ptr_out_shadow = 0;
- }
- cdc->tx_buf_ptr_wait_count = 0;
-
- // According to the USB specification, a packet size of 64 bytes (CDC_DATA_FS_MAX_PACKET_SIZE)
- // gets held at the USB host until the next packet is sent. This is because a
- // packet of maximum size is considered to be part of a longer chunk of data, and
- // the host waits for all data to arrive (ie, waits for a packet < max packet size).
- // To flush a packet of exactly max packet size, we need to send a zero-size packet.
- // See eg http://www.cypress.com/?id=4&rID=92719
- cdc->tx_need_empty_packet = (buffsize > 0 && buffsize % usbd_cdc_max_packet(cdc->base.usbd->pdev) == 0 && cdc->tx_buf_ptr_out_shadow == cdc->tx_buf_ptr_in);
- }
+// Attempt to queue data on the USB IN endpoint
+static void usbd_cdc_try_tx(usbd_cdc_itf_t *cdc) {
+ uint32_t basepri = raise_irq_pri(IRQ_PRI_OTG_FS);
+ if (cdc == NULL || cdc->connect_state == USBD_CDC_CONNECT_STATE_DISCONNECTED) {
+ // CDC device is not connected to a host, so we are unable to send any data
+ } else if (cdc->base.tx_in_progress) {
+ // USB driver will call callback when ready
+ } else {
+ usbd_cdc_tx_ready(&cdc->base);
}
+ restore_irq_pri(basepri);
}
void HAL_PCD_SOFCallback(PCD_HandleTypeDef *hpcd) {
- usbd_cdc_msc_hid_state_t *usbd = ((USBD_HandleTypeDef*)hpcd->pData)->pClassData;
- usbd_cdc_sof(hpcd, (usbd_cdc_itf_t*)usbd->cdc);
- #if MICROPY_HW_USB_ENABLE_CDC2
- usbd_cdc_sof(hpcd, (usbd_cdc_itf_t*)usbd->cdc2);
- #endif
+ if (usbd_cdc_connect_tx_timer > 0) {
+ --usbd_cdc_connect_tx_timer;
+ } else {
+ usbd_cdc_msc_hid_state_t *usbd = ((USBD_HandleTypeDef*)hpcd->pData)->pClassData;
+ hpcd->Instance->GINTMSK &= ~USB_OTG_GINTMSK_SOFM;
+ usbd_cdc_itf_t *cdc = (usbd_cdc_itf_t*)usbd->cdc;
+ if (cdc->connect_state == USBD_CDC_CONNECT_STATE_CONNECTING) {
+ cdc->connect_state = USBD_CDC_CONNECT_STATE_CONNECTED;
+ usbd_cdc_try_tx(cdc);
+ }
+ #if MICROPY_HW_USB_ENABLE_CDC2
+ cdc = (usbd_cdc_itf_t*)usbd->cdc2;
+ if (cdc->connect_state == USBD_CDC_CONNECT_STATE_CONNECTING) {
+ cdc->connect_state = USBD_CDC_CONNECT_STATE_CONNECTED;
+ usbd_cdc_try_tx(cdc);
+ }
+ #endif
+ }
}
// Data received over USB OUT endpoint is processed here.
@@ -262,7 +277,9 @@ int usbd_cdc_tx(usbd_cdc_itf_t *cdc, const uint8_t *buf, uint32_t len, uint32_t
for (uint32_t i = 0; i < len; i++) {
// Wait until the device is connected and the buffer has space, with a given timeout
uint32_t start = HAL_GetTick();
- while (!cdc->dev_is_connected || ((cdc->tx_buf_ptr_in + 1) & (USBD_CDC_TX_DATA_SIZE - 1)) == cdc->tx_buf_ptr_out) {
+ while (cdc->connect_state == USBD_CDC_CONNECT_STATE_DISCONNECTED
+ || ((cdc->tx_buf_ptr_in + 1) & (USBD_CDC_TX_DATA_SIZE - 1)) == cdc->tx_buf_ptr_out) {
+ usbd_cdc_try_tx(cdc);
// Wraparound of tick is taken care of by 2's complement arithmetic.
if (HAL_GetTick() - start >= timeout) {
// timeout
@@ -280,6 +297,8 @@ int usbd_cdc_tx(usbd_cdc_itf_t *cdc, const uint8_t *buf, uint32_t len, uint32_t
cdc->tx_buf_ptr_in = (cdc->tx_buf_ptr_in + 1) & (USBD_CDC_TX_DATA_SIZE - 1);
}
+ usbd_cdc_try_tx(cdc);
+
// Success, return number of bytes read
return len;
}
@@ -294,40 +313,24 @@ void usbd_cdc_tx_always(usbd_cdc_itf_t *cdc, const uint8_t *buf, uint32_t len) {
// and hope that it doesn't overflow by the time the device connects.
// If the device is not connected then we should go ahead and fill the buffer straight away,
// ignoring overflow. Otherwise, we should make sure that we have enough room in the buffer.
- if (cdc->dev_is_connected) {
+ if (cdc->connect_state != USBD_CDC_CONNECT_STATE_DISCONNECTED) {
// If the buffer is full, wait until it gets drained, with a timeout of 500ms
// (wraparound of tick is taken care of by 2's complement arithmetic).
uint32_t start = HAL_GetTick();
while (((cdc->tx_buf_ptr_in + 1) & (USBD_CDC_TX_DATA_SIZE - 1)) == cdc->tx_buf_ptr_out && HAL_GetTick() - start <= 500) {
+ usbd_cdc_try_tx(cdc);
if (query_irq() == IRQ_STATE_DISABLED) {
// IRQs disabled so buffer will never be drained; exit loop
break;
}
__WFI(); // enter sleep mode, waiting for interrupt
}
-
- // Some unused code that makes sure the low-level USB buffer is drained.
- // Waiting for low-level is handled in HAL_PCD_SOFCallback.
- /*
- start = HAL_GetTick();
- PCD_HandleTypeDef *hpcd = hUSBDDevice.pData;
- if (hpcd->IN_ep[0x83 & 0x7f].is_in) {
- //volatile uint32_t *xfer_count = &hpcd->IN_ep[0x83 & 0x7f].xfer_count;
- //volatile uint32_t *xfer_len = &hpcd->IN_ep[0x83 & 0x7f].xfer_len;
- USB_OTG_GlobalTypeDef *USBx = hpcd->Instance;
- while (
- // *xfer_count < *xfer_len // using this works
- // (USBx_INEP(3)->DIEPTSIZ & USB_OTG_DIEPTSIZ_XFRSIZ) // using this works
- && HAL_GetTick() - start <= 2000) {
- __WFI(); // enter sleep mode, waiting for interrupt
- }
- }
- */
}
cdc->tx_buf[cdc->tx_buf_ptr_in] = buf[i];
cdc->tx_buf_ptr_in = (cdc->tx_buf_ptr_in + 1) & (USBD_CDC_TX_DATA_SIZE - 1);
}
+ usbd_cdc_try_tx(cdc);
}
// Returns number of bytes in the rx buffer.