@@ -40,6 +40,7 @@ static volatile uint32_t usb_response_sz;
4040static volatile uint32_t usb_status_sz ;
4141static volatile uint32_t usb_stdout_sz ;
4242static volatile bool transmitting ;
43+ static volatile bool pbdrv_usb_stm32_is_events_subscribed ;
4344
4445static USBD_HandleTypeDef husbd ;
4546static PCD_HandleTypeDef hpcd ;
@@ -237,12 +238,14 @@ pbio_error_t pbdrv_usb_stdout_tx(const uint8_t *data, uint32_t *size) {
237238 return PBIO_ERROR_NOT_IMPLEMENTED ;
238239 #endif
239240
241+ if (!pbdrv_usb_stm32_is_events_subscribed ) {
242+ // If the app hasn't subscribed to events, we can't send stdout.
243+ return PBIO_ERROR_INVALID_OP ;
244+ }
245+
240246 uint8_t * ptr = usb_stdout_buf ;
241247 uint32_t ptr_len = sizeof (usb_stdout_buf );
242248
243- // TODO: return PBIO_ERROR_INVALID_OP if app flag is not set. Also need a
244- // timeout in case the app crashes and doesn't clear the flag on exit.
245-
246249 if (usb_stdout_sz ) {
247250 return PBIO_ERROR_AGAIN ;
248251 }
@@ -271,6 +274,14 @@ bool pbdrv_usb_stdout_tx_is_idle(void) {
271274 return usb_stdout_sz == 0 ;
272275}
273276
277+ static void pbdrv_usb_stm32_reset_tx_state (void ) {
278+ usb_response_sz = 0 ;
279+ usb_status_sz = 0 ;
280+ usb_stdout_sz = 0 ;
281+ transmitting = false;
282+ pbdrv_usb_stm32_is_events_subscribed = false;
283+ }
284+
274285/**
275286 * @brief Pybricks_Itf_Init
276287 * Initializes the Pybricks media low layer
@@ -280,10 +291,7 @@ bool pbdrv_usb_stdout_tx_is_idle(void) {
280291static USBD_StatusTypeDef Pybricks_Itf_Init (void ) {
281292 USBD_Pybricks_SetRxBuffer (& husbd , usb_in_buf );
282293 usb_in_sz = 0 ;
283- usb_response_sz = 0 ;
284- usb_status_sz = 0 ;
285- usb_stdout_sz = 0 ;
286- transmitting = false;
294+ pbdrv_usb_stm32_reset_tx_state ();
287295
288296 return USBD_OK ;
289297}
@@ -295,6 +303,7 @@ static USBD_StatusTypeDef Pybricks_Itf_Init(void) {
295303 * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
296304 */
297305static USBD_StatusTypeDef Pybricks_Itf_DeInit (void ) {
306+ pbdrv_usb_stm32_reset_tx_state ();
298307 return USBD_OK ;
299308}
300309
@@ -389,7 +398,8 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) {
389398 static PBIO_ONESHOT (pwrdn_oneshot );
390399 static bool bcd_busy ;
391400 static pbio_pybricks_error_t result ;
392- static struct etimer timer ;
401+ static struct etimer status_timer ;
402+ static struct etimer transmit_timer ;
393403 static uint32_t prev_status_flags = ~0 ;
394404 static uint32_t new_status_flags ;
395405
@@ -410,7 +420,7 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) {
410420
411421 PROCESS_BEGIN ();
412422
413- etimer_set (& timer , 500 );
423+ etimer_set (& status_timer , 500 );
414424
415425 for (;;) {
416426 PROCESS_WAIT_EVENT ();
@@ -442,6 +452,12 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) {
442452
443453 if (usb_in_sz ) {
444454 switch (usb_in_buf [0 ]) {
455+ case USBD_PYBRICKS_OUT_EP_MSG_SUBSCRIBE :
456+ pbdrv_usb_stm32_is_events_subscribed = usb_in_buf [1 ];
457+ usb_response_buf [0 ] = USBD_PYBRICKS_IN_EP_MSG_RESPONSE ;
458+ pbio_set_uint32_le (& usb_response_buf [1 ], PBIO_PYBRICKS_ERROR_OK );
459+ usb_response_sz = sizeof (usb_response_buf );
460+ break ;
445461 case USBD_PYBRICKS_OUT_EP_MSG_COMMAND :
446462 if (usb_response_sz == 0 ) {
447463 result = pbsys_command (usb_in_buf + 1 , usb_in_sz - 1 , PBSYS_COMMAND_TRANSPORT_USB );
@@ -458,6 +474,13 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) {
458474 }
459475
460476 if (transmitting ) {
477+ if (etimer_expired (& transmit_timer )) {
478+ // Transmission has taken too long, so reset the state to allow
479+ // new transmissions. This can happen if the host stops reading
480+ // data for some reason.
481+ pbdrv_usb_stm32_reset_tx_state ();
482+ }
483+
461484 continue ;
462485 }
463486
@@ -467,20 +490,28 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) {
467490 if (usb_response_sz ) {
468491 transmitting = true;
469492 USBD_Pybricks_TransmitPacket (& husbd , usb_response_buf , usb_response_sz );
470- } else if ((new_status_flags != prev_status_flags ) || etimer_expired (& timer )) {
471- usb_status_buf [0 ] = USBD_PYBRICKS_IN_EP_MSG_EVENT ;
472- _Static_assert (sizeof (usb_status_buf ) + 1 >= PBIO_PYBRICKS_EVENT_STATUS_REPORT_SIZE ,
473- "size of status report does not match size of event" );
474- usb_status_sz = 1 + pbsys_status_get_status_report (& usb_status_buf [1 ]);
475-
476- etimer_restart (& timer );
477- prev_status_flags = new_status_flags ;
493+ } else if (pbdrv_usb_stm32_is_events_subscribed ) {
494+ if ((new_status_flags != prev_status_flags ) || etimer_expired (& status_timer )) {
495+ usb_status_buf [0 ] = USBD_PYBRICKS_IN_EP_MSG_EVENT ;
496+ _Static_assert (sizeof (usb_status_buf ) + 1 >= PBIO_PYBRICKS_EVENT_STATUS_REPORT_SIZE ,
497+ "size of status report does not match size of event" );
498+ usb_status_sz = 1 + pbsys_status_get_status_report (& usb_status_buf [1 ]);
499+
500+ etimer_restart (& status_timer );
501+ prev_status_flags = new_status_flags ;
502+
503+ transmitting = true;
504+ USBD_Pybricks_TransmitPacket (& husbd , usb_status_buf , usb_status_sz );
505+ } else if (usb_stdout_sz ) {
506+ transmitting = true;
507+ USBD_Pybricks_TransmitPacket (& husbd , usb_stdout_buf , usb_stdout_sz );
508+ }
509+ }
478510
479- transmitting = true;
480- USBD_Pybricks_TransmitPacket (& husbd , usb_status_buf , usb_status_sz );
481- } else if (usb_stdout_sz ) {
482- transmitting = true;
483- USBD_Pybricks_TransmitPacket (& husbd , usb_stdout_buf , usb_stdout_sz );
511+ if (transmitting ) {
512+ // If the FIFO isn't emptied quickly, then there probably isn't an
513+ // app anymore. This timer is used to detect such a condition.
514+ etimer_set (& transmit_timer , 5 );
484515 }
485516 }
486517
0 commit comments