Skip to content

Commit 2fb2f0b

Browse files
committed
pbio/drv/usb_stm32: Don't stall when no app connected.
1 parent 490506e commit 2fb2f0b

File tree

1 file changed

+54
-8
lines changed

1 file changed

+54
-8
lines changed

lib/pbio/drv/usb/usb_stm32.c

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <usbd_desc.h>
1818
#include <usbd_pybricks.h>
1919

20+
#include <pbdrv/clock.h>
2021
#include <pbdrv/usb.h>
2122
#include <pbio/protocol.h>
2223
#include <pbio/util.h>
@@ -39,13 +40,58 @@ static volatile uint32_t usb_response_sz;
3940
static volatile uint32_t usb_status_sz;
4041
static volatile uint32_t usb_stdout_sz;
4142
static volatile bool transmitting;
43+
static volatile uint32_t transmission_start_time;
4244

4345
static USBD_HandleTypeDef husbd;
4446
static PCD_HandleTypeDef hpcd;
4547

4648
static volatile bool vbus_active;
4749
static pbdrv_usb_bcd_t pbdrv_usb_bcd;
4850

51+
/**
52+
* Set the USB transmit state.
53+
*
54+
* @param [in] active True indicates that USB is currently transmitting data,
55+
* otherwise false.
56+
*/
57+
static void pbdrv_usb_stm32_tx_active_set(bool active) {
58+
if (active) {
59+
transmitting = true;
60+
transmission_start_time = pbdrv_clock_get_ms();
61+
} else {
62+
transmitting = false;
63+
}
64+
}
65+
66+
/**
67+
* Checks if the USB interface can be used for stdout.
68+
*
69+
* Essentially tests for being connected and not being stalled. Can be used by
70+
* applications to test if it should wait or skip waiting on transmission.
71+
*
72+
* This relies on the status message that is periodically sent to the host but
73+
* stalls if not read by the host application.
74+
*
75+
* REVISIT: It would be better to update the protocol to expect periodic status
76+
* messages from the host rather than making it implicit.
77+
*
78+
* @return true if the USB interface is ready to send stdout data, otherwise false.
79+
*/
80+
static bool pbdrv_usb_stdout_is_awaitable(void) {
81+
// Not physically connected to a host.
82+
if (pbdrv_usb_get_bcd() == PBDRV_USB_BCD_NONE) {
83+
return false;
84+
}
85+
86+
if (!transmitting) {
87+
// Ready to send, so can send stdout data in principle.
88+
return true;
89+
}
90+
91+
// Currently transmitting data. Don't await if stalled.
92+
return pbdrv_clock_get_ms() - transmission_start_time < 600;
93+
}
94+
4995
/**
5096
* Battery charger detection task.
5197
*
@@ -155,9 +201,9 @@ pbio_error_t pbdrv_usb_stdout_tx(const uint8_t *data, uint32_t *size) {
155201
uint8_t *ptr = usb_stdout_buf;
156202
uint32_t ptr_len = sizeof(usb_stdout_buf);
157203

158-
// TODO: return PBIO_ERROR_INVALID_OP if app flag is not set. Also need a
159-
// timeout in case the app crashes and doesn't clear the flag on exit.
160-
204+
if (!pbdrv_usb_stdout_is_awaitable()) {
205+
return PBIO_ERROR_INVALID_OP;
206+
}
161207
if (usb_stdout_sz) {
162208
return PBIO_ERROR_AGAIN;
163209
}
@@ -198,7 +244,7 @@ static USBD_StatusTypeDef Pybricks_Itf_Init(void) {
198244
usb_response_sz = 0;
199245
usb_status_sz = 0;
200246
usb_stdout_sz = 0;
201-
transmitting = false;
247+
pbdrv_usb_stm32_tx_active_set(false);
202248

203249
return USBD_OK;
204250
}
@@ -253,7 +299,7 @@ static USBD_StatusTypeDef Pybricks_Itf_TransmitCplt(uint8_t *Buf, uint32_t Len,
253299
ret = USBD_FAIL;
254300
}
255301

256-
transmitting = false;
302+
pbdrv_usb_stm32_tx_active_set(false);
257303
process_poll(&pbdrv_usb_process);
258304
return ret;
259305
}
@@ -368,7 +414,7 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) {
368414

369415
// Transmit. Give priority to response, then status updates, then stdout.
370416
if (usb_response_sz) {
371-
transmitting = true;
417+
pbdrv_usb_stm32_tx_active_set(true);
372418
USBD_Pybricks_TransmitPacket(&husbd, usb_response_buf, usb_response_sz);
373419
} else if ((new_status_flags != prev_status_flags) || etimer_expired(&timer)) {
374420
usb_status_buf[0] = USBD_PYBRICKS_IN_EP_MSG_EVENT;
@@ -379,10 +425,10 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) {
379425
etimer_restart(&timer);
380426
prev_status_flags = new_status_flags;
381427

382-
transmitting = true;
428+
pbdrv_usb_stm32_tx_active_set(true);
383429
USBD_Pybricks_TransmitPacket(&husbd, usb_status_buf, usb_status_sz);
384430
} else if (usb_stdout_sz) {
385-
transmitting = true;
431+
pbdrv_usb_stm32_tx_active_set(true);
386432
USBD_Pybricks_TransmitPacket(&husbd, usb_stdout_buf, usb_stdout_sz);
387433
}
388434
}

0 commit comments

Comments
 (0)