2525#include <pbsys/status.h>
2626#include <pbsys/storage.h>
2727
28+ #include "usb.h"
29+
2830#include <lego/usb.h>
2931
3032#include <tiam1808/armv5/am1808/interrupt.h>
4951#define PYBRICKS_EP_PKT_SZ_FS 64
5052#define PYBRICKS_EP_PKT_SZ_HS 512
5153
54+ // All buffers must allow the highest possible packet size. When writing
55+ // to them, pbdrv_usb_max_package_size() gets the actual limit based on
56+ // active speed mode.
57+ #if PBDRV_CONFIG_USB_MAX_PACKET_SIZE != PYBRICKS_EP_PKT_SZ_HS
58+ #error Inconsistent USB packet size
59+ #endif
60+
5261/**
5362 * Indices for string descriptors
5463 */
@@ -217,15 +226,10 @@ static uint32_t pbdrv_usb_setup_misc_tx_byte;
217226// Whether the device is using USB high-speed mode or not
218227static bool pbdrv_usb_is_usb_hs ;
219228
220- // Whether there is a host app listening to events
221- static bool pbdrv_usb_is_events_subscribed ;
222-
223229// Buffers, used for different logical flows on the data endpoint
224230static uint8_t ep1_rx_buf [PYBRICKS_EP_PKT_SZ_HS ] PBDRV_DMA_BUF ;
225- static uint8_t ep1_tx_response_buf [PYBRICKS_EP_PKT_SZ_HS ];
226- static uint8_t ep1_tx_status_buf [PYBRICKS_EP_PKT_SZ_HS ];
227- static uint8_t ep1_tx_stdout_buf [PYBRICKS_EP_PKT_SZ_HS ];
228- static uint32_t ep1_tx_stdout_sz ;
231+ static uint8_t ep1_tx_response_buf [PYBRICKS_EP_PKT_SZ_HS ] = { PBIO_PYBRICKS_IN_EP_MSG_RESPONSE };
232+ static uint8_t ep1_tx_event_buf [PYBRICKS_EP_PKT_SZ_HS ] = { PBIO_PYBRICKS_IN_EP_MSG_EVENT };
229233
230234// Buffer status flags
231235static volatile bool usb_rx_is_ready ;
@@ -874,42 +878,10 @@ static void usb_device_intr(void) {
874878 HWREG (USB_0_OTGBASE + USB_0_END_OF_INTR ) = 0 ;
875879}
876880
877- /**
878- * Pybricks system command handler.
879- */
880- static pbdrv_usb_receive_handler_t pbdrv_usb_receive_handler ;
881-
882- void pbdrv_usb_set_receive_handler (pbdrv_usb_receive_handler_t handler ) {
883- pbdrv_usb_receive_handler = handler ;
884- }
885-
886- /**
887- * Buffer for scheduled status message.
888- */
889- static uint8_t pbdrv_usb_status_data [PBIO_PYBRICKS_EVENT_STATUS_REPORT_SIZE ];
890- static bool pbdrv_usb_status_data_pending ;
891-
892- void pbdrv_usb_schedule_status_update (const uint8_t * status_msg ) {
893- // Ignore if message identical to last.
894- if (!memcmp (pbdrv_usb_status_data , status_msg , sizeof (pbdrv_usb_status_data ))) {
895- return ;
896- }
897-
898- // Schedule to send whenever the USB process gets round to it.
899- memcpy (pbdrv_usb_status_data , status_msg , sizeof (pbdrv_usb_status_data ));
900- pbdrv_usb_status_data_pending = true;
901- pbio_os_request_poll ();
902- }
903-
904- // True if we are expected to send a response to a recent incoming message.
905- static bool usb_send_response ;
881+ uint32_t pbdrv_usb_get_data_in (uint8_t * data ) {
906882
907- /**
908- * Non-blocking poll handler to process pending incoming messages.
909- */
910- static void pbdrv_usb_handle_data_in (void ) {
911883 if (!usb_rx_is_ready ) {
912- return ;
884+ return 0 ;
913885 }
914886
915887 // This barrier prevents *subsequent* memory reads from being
@@ -918,134 +890,24 @@ static void pbdrv_usb_handle_data_in(void) {
918890 pbdrv_compiler_memory_barrier ();
919891
920892 uint32_t usb_rx_sz = PBDRV_UNCACHED (cppi_descriptors [CPPI_DESC_RX ].word0 ).pktLength ;
921- pbio_pybricks_error_t result ;
922893
923894 // Skip empty commands.
924895 if (usb_rx_sz ) {
925896 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 ;
946- }
897+ memcpy (data , ep1_rx_buf , usb_rx_sz );
947898 }
948899
949900 // Re-queue RX buffer after processing is complete
950901 usb_rx_is_ready = false;
951902 usb_setup_rx_dma_desc ();
952- }
953-
954- static pbio_error_t pbdrv_usb_handle_data_out (void ) {
955-
956- static pbio_os_timer_t transmit_timer ;
957-
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 ;
962- }
963-
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;
980- ep1_tx_status_buf [0 ] = PBIO_PYBRICKS_IN_EP_MSG_EVENT ;
981- uint32_t usb_status_sz = PBIO_PYBRICKS_USB_MESSAGE_SIZE (pbsys_status_get_status_report (& ep1_tx_status_buf [1 ]));
982-
983- transmitting = true;
984- pbdrv_cache_prepare_before_dma (ep1_tx_status_buf , sizeof (ep1_tx_status_buf ));
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 ;
991- }
992- }
993-
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 ;
1004903
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 ;
1024- }
1025-
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- }
1039- }
1040-
1041- PBIO_OS_ASYNC_END (PBIO_ERROR_CANCELED );
904+ return usb_rx_sz ;
1042905}
1043906
1044- void pbdrv_usb_deinit (void ) {
1045- pbio_os_process_make_request (& pbdrv_usb_ev3_process , PBIO_OS_PROCESS_REQUEST_TYPE_CANCEL );
907+ void pbdrv_usb_deinit_device (void ) {
1046908}
1047909
1048- void pbdrv_usb_init (void ) {
910+ void pbdrv_usb_init_device (void ) {
1049911 // If we came straight from a firmware update, we need to send a disconnect
1050912 // to the host, then reset the USB controller.
1051913 USBDevDisconnect (USB0_BASE );
@@ -1126,83 +988,79 @@ void pbdrv_usb_init(void) {
1126988
1127989 // Finally signal a connection
1128990 USBDevConnect (USB0_BASE );
991+ }
1129992
1130- // We are basically done. USB is event-driven, and so we don't have to block boot.
1131- pbio_os_process_start (& pbdrv_usb_ev3_process , pbdrv_usb_ev3_process_thread , NULL );
993+ pbio_error_t pbdrv_usb_wait_for_charger (pbio_os_state_t * state ) {
994+ return PBIO_ERROR_NOT_SUPPORTED ;
995+ }
996+
997+ bool pbdrv_usb_is_ready (void ) {
998+ // REVISIT: Return whether physically plugged in.
999+ return true;
11321000}
11331001
11341002pbdrv_usb_bcd_t pbdrv_usb_get_bcd (void ) {
11351003 // This function is not used on EV3
11361004 return PBDRV_USB_BCD_NONE ;
11371005}
11381006
1139- bool pbdrv_usb_connection_is_active (void ) {
1140- return pbdrv_usb_is_events_subscribed ;
1007+ uint32_t pbdrv_usb_tx_get_buf (pbio_pybricks_usb_in_ep_msg_t message_type , uint8_t * * buf ) {
1008+ * buf = message_type == PBIO_PYBRICKS_IN_EP_MSG_RESPONSE ? ep1_tx_response_buf : ep1_tx_event_buf ;
1009+ return pbdrv_usb_is_usb_hs ? PYBRICKS_EP_PKT_SZ_HS : PYBRICKS_EP_PKT_SZ_FS ;
11411010}
11421011
1143- /**
1144- * Queues data to be transmitted via USB.
1145- * @param data [in] The data to be sent.
1146- * @param size [in, out] The size of @p data in bytes. After return, @p size
1147- * contains the number of bytes actually written.
1148- * @return ::PBIO_SUCCESS if some @p data was queued, ::PBIO_ERROR_AGAIN
1149- * if no @p data could not be queued at this time (e.g. buffer
1150- * is full), ::PBIO_ERROR_INVALID_OP if there is not an
1151- * active USB connection
1152- */
1153- pbio_error_t pbdrv_usb_stdout_tx (const uint8_t * data , uint32_t * size ) {
1154- if (!pbdrv_usb_is_events_subscribed ) {
1155- // If the app hasn't subscribed to events, we can't send stdout.
1156- return PBIO_ERROR_INVALID_OP ;
1157- }
1158-
1159- uint8_t * ptr = ep1_tx_stdout_buf ;
1160- uint32_t ptr_len = pbdrv_usb_is_usb_hs ? PYBRICKS_EP_PKT_SZ_HS : PYBRICKS_EP_PKT_SZ_FS ;
1012+ pbio_error_t pbdrv_usb_tx_reset (pbio_os_state_t * state ) {
11611013
1162- if (transmitting ) {
1163- * size = 0 ;
1164- return PBIO_ERROR_AGAIN ;
1165- }
1014+ static pbio_os_timer_t timer ;
11661015
1167- * ptr ++ = PBIO_PYBRICKS_IN_EP_MSG_EVENT ;
1168- ptr_len -- ;
1016+ PBIO_OS_ASYNC_BEGIN (state );
11691017
1170- * ptr ++ = PBIO_PYBRICKS_EVENT_WRITE_STDOUT ;
1171- ptr_len -- ;
1018+ transmitting = false;
11721019
1173- if (* size > ptr_len ) {
1174- * size = ptr_len ;
1020+ // Flush _all_ TX packets
1021+ while (HWREGB (USB0_BASE + USB_O_TXCSRL1 ) & USB_TXCSRL1_TXRDY ) {
1022+ HWREGB (USB0_BASE + USB_O_TXCSRL1 ) = USB_TXCSRL1_FLUSH ;
1023+ // We need to wait a bit until the DMA refills the FIFO.
1024+ // There doesn't seem to be a good way to figure out if
1025+ // there are packets in flight *within* the DMA engine itself
1026+ // (i.e. no longer in the queue but in the transfer engine).
1027+ PBIO_OS_AWAIT_MS (state , & timer , 1 );
11751028 }
1176- memcpy (ptr , data , * size );
1177- ep1_tx_stdout_sz = 2 + * size ;
1178- pbio_os_request_poll ();
11791029
1180- return PBIO_SUCCESS ;
1030+ PBIO_OS_ASYNC_END ( PBIO_SUCCESS ) ;
11811031}
11821032
1183- // REVISIT: These two functions do not keep track of how much data
1184- // is held in the DMA engine or the packet FIFO (i.e. the DMA engine
1185- // has returned the descriptor to us (and thus we can queue more),
1186- // but the hardware is still buffering data which we cannot see without
1187- // performing much more accurate accounting)
1188- uint32_t pbdrv_usb_stdout_tx_available (void ) {
1189- if (!pbdrv_usb_is_events_subscribed ) {
1190- return UINT32_MAX ;
1191- }
1033+ pbio_error_t pbdrv_usb_tx (pbio_os_state_t * state , const uint8_t * data , uint32_t size ) {
11921034
1193- if (transmitting ) {
1194- return 0 ;
1035+ static pbio_os_timer_t timer ;
1036+
1037+ PBIO_OS_ASYNC_BEGIN (state );
1038+
1039+ transmitting = true;
1040+ pbio_os_timer_set (& timer , PBDRV_USB_TRANSMIT_TIMEOUT );
1041+
1042+ if (data == ep1_tx_response_buf ) {
1043+ pbdrv_cache_prepare_before_dma (ep1_tx_response_buf , sizeof (ep1_tx_response_buf ));
1044+ usb_setup_tx_dma_desc (CPPI_DESC_TX_RESPONSE , ep1_tx_response_buf , size );
1045+ } else if (data == ep1_tx_event_buf ) {
1046+ pbdrv_cache_prepare_before_dma (ep1_tx_event_buf , sizeof (ep1_tx_event_buf ));
1047+ usb_setup_tx_dma_desc (CPPI_DESC_TX_PYBRICKS_EVENT , ep1_tx_event_buf , size );
1048+ } else {
1049+ transmitting = false;
1050+ return PBIO_ERROR_INVALID_ARG ;
11951051 }
11961052
1197- // Subtract 2 bytes for header
1198- if (pbdrv_usb_is_usb_hs ) {
1199- return PYBRICKS_EP_PKT_SZ_HS - 2 ;
1053+ PBIO_OS_AWAIT_UNTIL (state , !transmitting || pbio_os_timer_is_expired (& timer ));
1054+
1055+ if (pbio_os_timer_is_expired (& timer )) {
1056+ // Transmission has taken too long, so reset the state to allow
1057+ // new transmissions. This can happen if the host stops reading
1058+ // data for some reason. This need some time to complete, so delegate
1059+ // the reset back to the process.
1060+ return PBIO_ERROR_TIMEDOUT ;
12001061 }
1201- return PYBRICKS_EP_PKT_SZ_FS - 2 ;
1202- }
12031062
1204- bool pbdrv_usb_stdout_tx_is_idle (void ) {
1205- return !transmitting ;
1063+ PBIO_OS_ASYNC_END (PBIO_SUCCESS );
12061064}
12071065
12081066#endif // PBDRV_CONFIG_USB_EV3
0 commit comments