@@ -225,12 +225,11 @@ static uint8_t ep1_rx_buf[PYBRICKS_EP_PKT_SZ_HS] PBDRV_DMA_BUF;
225225static uint8_t ep1_tx_response_buf [PYBRICKS_EP_PKT_SZ_HS ];
226226static uint8_t ep1_tx_status_buf [PYBRICKS_EP_PKT_SZ_HS ];
227227static uint8_t ep1_tx_stdout_buf [PYBRICKS_EP_PKT_SZ_HS ];
228+ static uint32_t ep1_tx_stdout_sz ;
228229
229230// Buffer status flags
230231static volatile bool usb_rx_is_ready ;
231- static volatile bool usb_tx_response_is_not_ready ;
232- static volatile bool usb_tx_status_is_not_ready ;
233- static volatile bool usb_tx_stdout_is_not_ready ;
232+ static volatile bool transmitting ;
234233
235234// CPPI DMA support code
236235
@@ -261,8 +260,7 @@ _Static_assert(sizeof(usb_cppi_hpd_t) <= CPPI_DESCRIPTOR_ALIGN);
261260enum {
262261 CPPI_DESC_RX ,
263262 CPPI_DESC_TX_RESPONSE ,
264- CPPI_DESC_TX_STATUS ,
265- CPPI_DESC_TX_STDOUT ,
263+ CPPI_DESC_TX_PYBRICKS_EVENT ,
266264 // the minimum number of descriptors we can allocate is 32,
267265 // even though we do not use nearly all of them
268266 CPPI_DESC_COUNT = 32 ,
@@ -860,18 +858,15 @@ static void usb_device_intr(void) {
860858 uint32_t qctrld = HWREG (USB_0_OTGBASE + CPDMA_QUEUE_REGISTER_D + TX_COMPQ1 * 16 ) & ~CPDMA_QUEUE_REGISTER_DESC_SIZE_MASK ;
861859
862860 if (qctrld == (uint32_t )(& cppi_descriptors [CPPI_DESC_TX_RESPONSE ])) {
863- usb_tx_response_is_not_ready = false;
861+ transmitting = false;
864862 pbio_os_request_poll ();
865- } else if (qctrld == (uint32_t )(& cppi_descriptors [CPPI_DESC_TX_STATUS ])) {
866- usb_tx_status_is_not_ready = false;
867- pbio_os_request_poll ();
868- } else if (qctrld == (uint32_t )(& cppi_descriptors [CPPI_DESC_TX_STDOUT ])) {
869- usb_tx_stdout_is_not_ready = false;
863+ } else if (qctrld == (uint32_t )(& cppi_descriptors [CPPI_DESC_TX_PYBRICKS_EVENT ])) {
864+ transmitting = false;
870865 pbio_os_request_poll ();
871866 }
872867
873868 // Multiple TX completions can happen at once
874- // (since we have up to three descriptors in flight)
869+ // (since we have multiple descriptors in flight)
875870 dma_q_pend_0 = HWREG (USB_0_OTGBASE + CPDMA_PEND_0_REGISTER );
876871 }
877872
@@ -906,122 +901,148 @@ void pbdrv_usb_schedule_status_update(const uint8_t *status_msg) {
906901 pbio_os_request_poll ();
907902}
908903
909- static pbio_os_process_t pbdrv_usb_ev3_process ;
904+ // True if we are expected to send a response to a recent incoming message.
905+ static bool usb_send_response ;
910906
911- static pbio_error_t pbdrv_usb_ev3_process_thread (pbio_os_state_t * state , void * context ) {
912- static pbio_os_timer_t delay_timer ;
913- static pbio_os_timer_t tx_timeout_timer ;
914- static pbio_os_timer_t keepalive_timer ;
915- static bool was_transmitting = false;
916- static bool is_transmitting = false;
907+ /**
908+ * Non-blocking poll handler to process pending incoming messages.
909+ */
910+ static void pbdrv_usb_handle_data_in (void ) {
911+ if (!usb_rx_is_ready ) {
912+ return ;
913+ }
917914
918- PBIO_OS_ASYNC_BEGIN (state );
915+ // This barrier prevents *subsequent* memory reads from being
916+ // speculatively moved *earlier*, outside the if statement
917+ // (which is technically allowed by the as-if rule).
918+ pbdrv_compiler_memory_barrier ();
919919
920- for (;;) {
921- if (pbdrv_usb_config == 0 ) {
922- pbdrv_usb_is_events_subscribed = false;
923- // Resend status flags when host subscribes
924- pbdrv_usb_status_data_pending = true;
920+ uint32_t usb_rx_sz = PBDRV_UNCACHED (cppi_descriptors [CPPI_DESC_RX ].word0 ).pktLength ;
921+ pbio_pybricks_error_t result ;
922+
923+ // Skip empty commands.
924+ if (usb_rx_sz ) {
925+ pbdrv_cache_prepare_after_dma (ep1_rx_buf , sizeof (ep1_rx_buf ));
926+
927+ switch (ep1_rx_buf [0 ]) {
928+ case PBIO_PYBRICKS_OUT_EP_MSG_SUBSCRIBE :
929+ pbdrv_usb_is_events_subscribed = ep1_rx_buf [1 ];
930+ ep1_tx_response_buf [0 ] = PBIO_PYBRICKS_IN_EP_MSG_RESPONSE ;
931+ pbio_set_uint32_le (& ep1_tx_response_buf [1 ], PBIO_PYBRICKS_ERROR_OK );
932+ usb_send_response = true;
933+ // Request to resend status flags now that the host has subscribed.
934+ // Our response will take priority, status follows right after.
935+ pbdrv_usb_status_data_pending = true;
936+ break ;
937+ case PBIO_PYBRICKS_OUT_EP_MSG_COMMAND :
938+ if (!pbdrv_usb_receive_handler ) {
939+ break ;
940+ }
941+ result = pbdrv_usb_receive_handler (ep1_rx_buf + 1 , usb_rx_sz - 1 );
942+ ep1_tx_response_buf [0 ] = PBIO_PYBRICKS_IN_EP_MSG_RESPONSE ;
943+ pbio_set_uint32_le (& ep1_tx_response_buf [1 ], result );
944+ usb_send_response = true;
945+ break ;
925946 }
947+ }
926948
927- if (usb_rx_is_ready ) {
928- // This barrier prevents *subsequent* memory reads from being
929- // speculatively moved *earlier*, outside the if statement
930- // (which is technically allowed by the as-if rule).
931- pbdrv_compiler_memory_barrier ();
932-
933- uint32_t usb_rx_sz = PBDRV_UNCACHED (cppi_descriptors [CPPI_DESC_RX ].word0 ).pktLength ;
934- pbio_pybricks_error_t result ;
935- bool usb_send_response = false;
936- // Skip empty commands, or commands sent when previous response is still busy
937- if (usb_rx_sz && !usb_tx_response_is_not_ready ) {
938- pbdrv_cache_prepare_after_dma (ep1_rx_buf , sizeof (ep1_rx_buf ));
939-
940- switch (ep1_rx_buf [0 ]) {
941- case PBIO_PYBRICKS_OUT_EP_MSG_SUBSCRIBE :
942- pbio_os_timer_set (& keepalive_timer , 1000 );
943- pbdrv_usb_is_events_subscribed = ep1_rx_buf [1 ];
944- ep1_tx_response_buf [0 ] = PBIO_PYBRICKS_IN_EP_MSG_RESPONSE ;
945- pbio_set_uint32_le (& ep1_tx_response_buf [1 ], PBIO_PYBRICKS_ERROR_OK );
946- usb_send_response = true;
947- break ;
948- case PBIO_PYBRICKS_OUT_EP_MSG_COMMAND :
949- if (!pbdrv_usb_receive_handler ) {
950- break ;
951- }
952- result = pbdrv_usb_receive_handler (ep1_rx_buf + 1 , usb_rx_sz - 1 );
953- ep1_tx_response_buf [0 ] = PBIO_PYBRICKS_IN_EP_MSG_RESPONSE ;
954- pbio_set_uint32_le (& ep1_tx_response_buf [1 ], result );
955- usb_send_response = true;
956- break ;
957- }
958- }
949+ // Re-queue RX buffer after processing is complete
950+ usb_rx_is_ready = false;
951+ usb_setup_rx_dma_desc ();
952+ }
959953
960- if (usb_send_response ) {
961- usb_tx_response_is_not_ready = true;
962- pbdrv_cache_prepare_before_dma (ep1_tx_response_buf , sizeof (ep1_tx_response_buf ));
963- usb_setup_tx_dma_desc (CPPI_DESC_TX_RESPONSE , ep1_tx_response_buf , PBIO_PYBRICKS_USB_MESSAGE_SIZE (sizeof (uint32_t )));
964- }
954+ static pbio_error_t pbdrv_usb_handle_data_out (void ) {
955+
956+ static pbio_os_timer_t transmit_timer ;
965957
966- // Re-queue RX buffer after processing is complete
967- usb_rx_is_ready = false;
968- usb_setup_rx_dma_desc ();
958+ if (transmitting ) {
959+ if (!pbio_os_timer_is_expired (& transmit_timer )) {
960+ // Still transmitting, can't do anything for now.
961+ return PBIO_SUCCESS ;
969962 }
970963
971- // Send status flags if they've changed (and we can)
972- if (pbdrv_usb_is_events_subscribed && !usb_tx_status_is_not_ready &&
973- (pbdrv_usb_status_data_pending || pbio_os_timer_is_expired (& keepalive_timer ))) {
964+ // Transmission has taken too long, so reset the state to allow
965+ // new transmissions. This can happen if the host stops reading
966+ // data for some reason. This need some time to complete, so delegate
967+ // the reset back to the process.
968+ return PBIO_ERROR_TIMEDOUT ;
969+ }
970+
971+ // Transmit. Give priority to response, then status updates, then stdout.
972+ if (usb_send_response ) {
973+ usb_send_response = false;
974+ transmitting = true;
975+ pbdrv_cache_prepare_before_dma (ep1_tx_response_buf , sizeof (ep1_tx_response_buf ));
976+ usb_setup_tx_dma_desc (CPPI_DESC_TX_RESPONSE , ep1_tx_response_buf , PBIO_PYBRICKS_USB_MESSAGE_SIZE (sizeof (uint32_t )));
977+ } else if (pbdrv_usb_is_events_subscribed ) {
978+ if (pbdrv_usb_status_data_pending ) {
979+ pbdrv_usb_status_data_pending = false;
974980 ep1_tx_status_buf [0 ] = PBIO_PYBRICKS_IN_EP_MSG_EVENT ;
975981 uint32_t usb_status_sz = PBIO_PYBRICKS_USB_MESSAGE_SIZE (pbsys_status_get_status_report (& ep1_tx_status_buf [1 ]));
976982
977- usb_tx_status_is_not_ready = true;
983+ transmitting = true;
978984 pbdrv_cache_prepare_before_dma (ep1_tx_status_buf , sizeof (ep1_tx_status_buf ));
979- usb_setup_tx_dma_desc (CPPI_DESC_TX_STATUS , ep1_tx_status_buf , usb_status_sz );
980-
981- if (pbdrv_usb_status_data_pending ) {
982- // If we are sending a status because the flags have changed,
983- // we can bump out the keepalive timer.
984- pbio_os_timer_set (& keepalive_timer , 1000 );
985- } else {
986- // Otherwise, we want to send keepalives at a particular rate.
987- pbio_os_timer_extend (& keepalive_timer );
988- }
989- pbdrv_usb_status_data_pending = false;
985+ usb_setup_tx_dma_desc (CPPI_DESC_TX_PYBRICKS_EVENT , ep1_tx_status_buf , usb_status_sz );
986+ } else if (ep1_tx_stdout_sz ) {
987+ transmitting = true;
988+ pbdrv_cache_prepare_before_dma (ep1_tx_stdout_buf , sizeof (ep1_tx_stdout_buf ));
989+ usb_setup_tx_dma_desc (CPPI_DESC_TX_PYBRICKS_EVENT , ep1_tx_stdout_buf , ep1_tx_stdout_sz );
990+ ep1_tx_stdout_sz = 0 ;
990991 }
992+ }
991993
992- // Handle timeouts
993- is_transmitting = usb_tx_response_is_not_ready || usb_tx_status_is_not_ready || usb_tx_stdout_is_not_ready ;
994- if (was_transmitting && is_transmitting ) {
995- if (pbio_os_timer_is_expired (& tx_timeout_timer )) {
996- // Flush _all_ TX packets
997- while (HWREGB (USB0_BASE + USB_O_TXCSRL1 ) & USB_TXCSRL1_TXRDY ) {
998- HWREGB (USB0_BASE + USB_O_TXCSRL1 ) = USB_TXCSRL1_FLUSH ;
999- // We need to wait a bit until the DMA refills the FIFO.
1000- // There doesn't seem to be a good way to figure out if
1001- // there are packets in flight *within* the DMA engine itself
1002- // (i.e. no longer in the queue but in the transfer engine).
1003- PBIO_OS_AWAIT_MS (state , & delay_timer , 1 );
1004- }
1005- usb_tx_response_is_not_ready = false;
1006- usb_tx_status_is_not_ready = false;
1007- usb_tx_stdout_is_not_ready = false;
1008- pbdrv_usb_is_events_subscribed = false;
1009- // Resend status flags when host subscribes
1010- pbdrv_usb_status_data_pending = true;
1011- }
1012- } else if (!was_transmitting && is_transmitting ) {
1013- pbio_os_timer_set (& tx_timeout_timer , 50 );
994+ if (transmitting ) {
995+ // If the FIFO isn't emptied quickly, then there probably isn't an
996+ // app anymore. This timer is used to detect such a condition.
997+ pbio_os_timer_set (& transmit_timer , 50 );
998+ }
999+
1000+ return PBIO_SUCCESS ;
1001+ }
1002+
1003+ static pbio_os_process_t pbdrv_usb_ev3_process ;
1004+
1005+ static pbio_error_t pbdrv_usb_ev3_process_thread (pbio_os_state_t * state , void * context ) {
1006+
1007+ static pbio_os_timer_t timer ;
1008+
1009+ pbdrv_usb_handle_data_in ();
1010+
1011+ if (pbdrv_usb_config == 0 ) {
1012+ pbdrv_usb_is_events_subscribed = false;
1013+ }
1014+
1015+ PBIO_OS_ASYNC_BEGIN (state );
1016+
1017+ // Process pending outgoing data until shutdown requested.
1018+ while (pbdrv_usb_ev3_process .request != PBIO_OS_PROCESS_REQUEST_TYPE_CANCEL ) {
1019+
1020+ PBIO_OS_ASYNC_SET_CHECKPOINT (state );
1021+ pbio_error_t err = pbdrv_usb_handle_data_out ();
1022+ if (err == PBIO_SUCCESS ) {
1023+ return PBIO_ERROR_AGAIN ;
10141024 }
1015- was_transmitting = is_transmitting ;
10161025
1017- // Make this process loop run from the beginning.
1018- // Because we check so many different conditions,
1019- // we don't use the normal await macros.
1020- PBIO_OS_ASYNC_RESET (state );
1021- return PBIO_ERROR_AGAIN ;
1026+ transmitting = false;
1027+ ep1_tx_stdout_sz = 0 ;
1028+ pbdrv_usb_is_events_subscribed = false;
1029+
1030+ // Flush _all_ TX packets
1031+ while (HWREGB (USB0_BASE + USB_O_TXCSRL1 ) & USB_TXCSRL1_TXRDY ) {
1032+ HWREGB (USB0_BASE + USB_O_TXCSRL1 ) = USB_TXCSRL1_FLUSH ;
1033+ // We need to wait a bit until the DMA refills the FIFO.
1034+ // There doesn't seem to be a good way to figure out if
1035+ // there are packets in flight *within* the DMA engine itself
1036+ // (i.e. no longer in the queue but in the transfer engine).
1037+ PBIO_OS_AWAIT_MS (state , & timer , 1 );
1038+ }
10221039 }
10231040
1024- PBIO_OS_ASYNC_END (PBIO_SUCCESS );
1041+ PBIO_OS_ASYNC_END (PBIO_ERROR_CANCELED );
1042+ }
1043+
1044+ void pbdrv_usb_deinit (void ) {
1045+ pbio_os_process_make_request (& pbdrv_usb_ev3_process , PBIO_OS_PROCESS_REQUEST_TYPE_CANCEL );
10251046}
10261047
10271048void pbdrv_usb_init (void ) {
@@ -1110,10 +1131,6 @@ void pbdrv_usb_init(void) {
11101131 pbio_os_process_start (& pbdrv_usb_ev3_process , pbdrv_usb_ev3_process_thread , NULL );
11111132}
11121133
1113- void pbdrv_usb_deinit (void ) {
1114- // todo
1115- }
1116-
11171134pbdrv_usb_bcd_t pbdrv_usb_get_bcd (void ) {
11181135 // This function is not used on EV3
11191136 return PBDRV_USB_BCD_NONE ;
@@ -1142,7 +1159,7 @@ pbio_error_t pbdrv_usb_stdout_tx(const uint8_t *data, uint32_t *size) {
11421159 uint8_t * ptr = ep1_tx_stdout_buf ;
11431160 uint32_t ptr_len = pbdrv_usb_is_usb_hs ? PYBRICKS_EP_PKT_SZ_HS : PYBRICKS_EP_PKT_SZ_FS ;
11441161
1145- if (usb_tx_stdout_is_not_ready ) {
1162+ if (transmitting ) {
11461163 * size = 0 ;
11471164 return PBIO_ERROR_AGAIN ;
11481165 }
@@ -1157,10 +1174,8 @@ pbio_error_t pbdrv_usb_stdout_tx(const uint8_t *data, uint32_t *size) {
11571174 * size = ptr_len ;
11581175 }
11591176 memcpy (ptr , data , * size );
1160-
1161- usb_tx_stdout_is_not_ready = true;
1162- pbdrv_cache_prepare_before_dma (ep1_tx_stdout_buf , sizeof (ep1_tx_stdout_buf ));
1163- usb_setup_tx_dma_desc (CPPI_DESC_TX_STDOUT , ep1_tx_stdout_buf , 2 + * size );
1177+ ep1_tx_stdout_sz = 2 + * size ;
1178+ pbio_os_request_poll ();
11641179
11651180 return PBIO_SUCCESS ;
11661181}
@@ -1175,7 +1190,7 @@ uint32_t pbdrv_usb_stdout_tx_available(void) {
11751190 return UINT32_MAX ;
11761191 }
11771192
1178- if (usb_tx_stdout_is_not_ready ) {
1193+ if (transmitting ) {
11791194 return 0 ;
11801195 }
11811196
@@ -1187,7 +1202,7 @@ uint32_t pbdrv_usb_stdout_tx_available(void) {
11871202}
11881203
11891204bool pbdrv_usb_stdout_tx_is_idle (void ) {
1190- return !usb_tx_stdout_is_not_ready ;
1205+ return !transmitting ;
11911206}
11921207
11931208#endif // PBDRV_CONFIG_USB_EV3
0 commit comments