@@ -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,13 +858,10 @@ 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
@@ -906,124 +901,142 @@ 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+ static bool usb_send_response ;
910905
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;
906+ /**
907+ * Non-blocking poll handler to process pending incoming messages.
908+ */
909+ static void pbdrv_usb_handle_data_in (void ) {
910+ if (!usb_rx_is_ready ) {
911+ return ;
912+ }
913+ // This barrier prevents *subsequent* memory reads from being
914+ // speculatively moved *earlier*, outside the if statement
915+ // (which is technically allowed by the as-if rule).
916+ pbdrv_compiler_memory_barrier ();
917917
918- PBIO_OS_ASYNC_BEGIN (state );
918+ uint32_t usb_rx_sz = PBDRV_UNCACHED (cppi_descriptors [CPPI_DESC_RX ].word0 ).pktLength ;
919+ pbio_pybricks_error_t result ;
919920
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;
925- }
921+ // Skip empty commands
922+ if (usb_rx_sz ) {
923+ pbdrv_cache_prepare_after_dma (ep1_rx_buf , sizeof (ep1_rx_buf ));
926924
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 ;
925+ switch (ep1_rx_buf [0 ]) {
926+ case PBIO_PYBRICKS_OUT_EP_MSG_SUBSCRIBE :
927+ pbdrv_usb_is_events_subscribed = ep1_rx_buf [1 ];
928+ ep1_tx_response_buf [0 ] = PBIO_PYBRICKS_IN_EP_MSG_RESPONSE ;
929+ pbio_set_uint32_le (& ep1_tx_response_buf [1 ], PBIO_PYBRICKS_ERROR_OK );
930+ usb_send_response = true;
931+ // Resend status flags when host subscribes
932+ pbdrv_usb_status_data_pending = true;
933+ break ;
934+ case PBIO_PYBRICKS_OUT_EP_MSG_COMMAND :
935+ if (!pbdrv_usb_receive_handler ) {
936+ break ;
957937 }
958- }
938+ result = pbdrv_usb_receive_handler (ep1_rx_buf + 1 , usb_rx_sz - 1 );
939+ ep1_tx_response_buf [0 ] = PBIO_PYBRICKS_IN_EP_MSG_RESPONSE ;
940+ pbio_set_uint32_le (& ep1_tx_response_buf [1 ], result );
941+ usb_send_response = true;
942+ break ;
943+ }
944+ }
959945
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- }
946+ // Re-queue RX buffer after processing is complete
947+ usb_rx_is_ready = false;
948+ usb_setup_rx_dma_desc ();
949+ }
950+
951+ static pbio_error_t pbdrv_usb_handle_data_out (void ) {
952+
953+ static pbio_os_timer_t transmit_timer ;
965954
966- // Re-queue RX buffer after processing is complete
967- usb_rx_is_ready = false;
968- usb_setup_rx_dma_desc ();
955+ if (transmitting ) {
956+ if (!pbio_os_timer_is_expired (& transmit_timer )) {
957+ // Still transmitting, can't do anything for now.
958+ return PBIO_SUCCESS ;
969959 }
970960
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 ))) {
974- ep1_tx_status_buf [ 0 ] = PBIO_PYBRICKS_IN_EP_MSG_EVENT ;
975- uint32_t usb_status_sz = PBIO_PYBRICKS_USB_MESSAGE_SIZE ( pbsys_status_get_status_report ( & ep1_tx_status_buf [ 1 ]));
961+ // Transmission has taken too long, so reset the state to allow
962+ // new transmissions. This can happen if the host stops reading
963+ // data for some reason.
964+ return PBIO_ERROR_TIMEDOUT ;
965+ }
976966
977- usb_tx_status_is_not_ready = true;
978- 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 );
967+ // Transmit. Give priority to response, then status updates, then stdout.
968+ if (usb_send_response ) {
969+ usb_send_response = false;
970+ transmitting = true;
971+ pbdrv_cache_prepare_before_dma (ep1_tx_response_buf , sizeof (ep1_tx_response_buf ));
972+ usb_setup_tx_dma_desc (CPPI_DESC_TX_RESPONSE , ep1_tx_response_buf , PBIO_PYBRICKS_USB_MESSAGE_SIZE (sizeof (uint32_t )));
973+ } else if (pbdrv_usb_is_events_subscribed && pbdrv_usb_status_data_pending ) {
974+ pbdrv_usb_status_data_pending = false;
975+ ep1_tx_status_buf [0 ] = PBIO_PYBRICKS_IN_EP_MSG_EVENT ;
976+ uint32_t usb_status_sz = PBIO_PYBRICKS_USB_MESSAGE_SIZE (pbsys_status_get_status_report (& ep1_tx_status_buf [1 ]));
977+
978+ transmitting = true;
979+ pbdrv_cache_prepare_before_dma (ep1_tx_status_buf , sizeof (ep1_tx_status_buf ));
980+ usb_setup_tx_dma_desc (CPPI_DESC_TX_PYBRICKS_EVENT , ep1_tx_status_buf , usb_status_sz );
981+ } else if (ep1_tx_stdout_sz ) {
982+ transmitting = true;
983+ pbdrv_cache_prepare_before_dma (ep1_tx_stdout_buf , sizeof (ep1_tx_stdout_buf ));
984+ usb_setup_tx_dma_desc (CPPI_DESC_TX_PYBRICKS_EVENT , ep1_tx_stdout_buf , ep1_tx_stdout_sz );
985+ ep1_tx_stdout_sz = 0 ;
986+ }
980987
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;
988+ if (transmitting ) {
989+ // If the FIFO isn't emptied quickly, then there probably isn't an
990+ // app anymore. This timer is used to detect such a condition.
991+ pbio_os_timer_set (& transmit_timer , 50 );
992+ }
993+ return PBIO_SUCCESS ;
994+ }
995+
996+ static pbio_os_process_t pbdrv_usb_ev3_process ;
997+
998+ static pbio_error_t pbdrv_usb_ev3_process_thread (pbio_os_state_t * state , void * context ) {
999+
1000+ static pbio_os_timer_t timer ;
1001+
1002+ pbdrv_usb_handle_data_in ();
1003+
1004+ if (pbdrv_usb_config == 0 ) {
1005+ pbdrv_usb_is_events_subscribed = false;
1006+ }
1007+
1008+ PBIO_OS_ASYNC_BEGIN (state );
1009+
1010+ // REVISIT: Exit on pbdrv_usb_deinit
1011+ for (;;) {
1012+
1013+ PBIO_OS_ASYNC_SET_CHECKPOINT (state );
1014+ pbio_error_t err = pbdrv_usb_handle_data_out ();
1015+ if (err == PBIO_SUCCESS ) {
1016+ return PBIO_ERROR_AGAIN ;
9901017 }
9911018
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 );
1019+ // Flush _all_ TX packets
1020+ while (HWREGB (USB0_BASE + USB_O_TXCSRL1 ) & USB_TXCSRL1_TXRDY ) {
1021+ HWREGB (USB0_BASE + USB_O_TXCSRL1 ) = USB_TXCSRL1_FLUSH ;
1022+ // We need to wait a bit until the DMA refills the FIFO.
1023+ // There doesn't seem to be a good way to figure out if
1024+ // there are packets in flight *within* the DMA engine itself
1025+ // (i.e. no longer in the queue but in the transfer engine).
1026+ PBIO_OS_AWAIT_MS (state , & timer , 1 );
10141027 }
1015- was_transmitting = is_transmitting ;
1028+ transmitting = false;
1029+ pbdrv_usb_is_events_subscribed = false;
10161030
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 ;
10221031 }
10231032
10241033 PBIO_OS_ASYNC_END (PBIO_SUCCESS );
10251034}
10261035
1036+ void pbdrv_usb_deinit (void ) {
1037+ // todo
1038+ }
1039+
10271040void pbdrv_usb_init (void ) {
10281041 // If we came straight from a firmware update, we need to send a disconnect
10291042 // to the host, then reset the USB controller.
@@ -1138,7 +1151,7 @@ pbio_error_t pbdrv_usb_stdout_tx(const uint8_t *data, uint32_t *size) {
11381151 uint8_t * ptr = ep1_tx_stdout_buf ;
11391152 uint32_t ptr_len = pbdrv_usb_is_usb_hs ? PYBRICKS_EP_PKT_SZ_HS : PYBRICKS_EP_PKT_SZ_FS ;
11401153
1141- if (usb_tx_stdout_is_not_ready ) {
1154+ if (transmitting ) {
11421155 * size = 0 ;
11431156 return PBIO_ERROR_AGAIN ;
11441157 }
@@ -1153,10 +1166,8 @@ pbio_error_t pbdrv_usb_stdout_tx(const uint8_t *data, uint32_t *size) {
11531166 * size = ptr_len ;
11541167 }
11551168 memcpy (ptr , data , * size );
1156-
1157- usb_tx_stdout_is_not_ready = true;
1158- pbdrv_cache_prepare_before_dma (ep1_tx_stdout_buf , sizeof (ep1_tx_stdout_buf ));
1159- usb_setup_tx_dma_desc (CPPI_DESC_TX_STDOUT , ep1_tx_stdout_buf , 2 + * size );
1169+ ep1_tx_stdout_sz = 2 + * size ;
1170+ pbio_os_request_poll ();
11601171
11611172 return PBIO_SUCCESS ;
11621173}
@@ -1171,7 +1182,7 @@ uint32_t pbdrv_usb_stdout_tx_available(void) {
11711182 return UINT32_MAX ;
11721183 }
11731184
1174- if (usb_tx_stdout_is_not_ready ) {
1185+ if (transmitting ) {
11751186 return 0 ;
11761187 }
11771188
@@ -1183,7 +1194,7 @@ uint32_t pbdrv_usb_stdout_tx_available(void) {
11831194}
11841195
11851196bool pbdrv_usb_stdout_tx_is_idle (void ) {
1186- return !usb_tx_stdout_is_not_ready ;
1197+ return !transmitting ;
11871198}
11881199
11891200#endif // PBDRV_CONFIG_USB_EV3
0 commit comments