Skip to content

Commit f3e480e

Browse files
committed
pbio/drv/usb: Use common driver for EV3 and STM32.
This applies the newly added common driver code to EV3 and STM32, stripping away most of the code that is the same among them. Hooks have been added so that the NXT driver builds, but it is not functional yet.
1 parent 23b2630 commit f3e480e

File tree

7 files changed

+185
-453
lines changed

7 files changed

+185
-453
lines changed

lib/pbio/drv/usb/usb.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
#include <pbdrv/config.h>
55

6-
#if PBDRV_CONFIG_USB_SIMULATION
6+
#if PBDRV_CONFIG_USB
77

88
#include <errno.h>
99
#include <signal.h>
@@ -224,4 +224,4 @@ void pbdrv_usb_deinit(void) {
224224
pbio_os_process_make_request(&pbdrv_usb_process, PBIO_OS_PROCESS_REQUEST_TYPE_CANCEL);
225225
}
226226

227-
#endif // PBDRV_CONFIG_USB_SIMULATION
227+
#endif // PBDRV_CONFIG_USB

lib/pbio/drv/usb/usb_ev3.c

Lines changed: 67 additions & 209 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
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>
@@ -49,6 +51,13 @@
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
218227
static 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
224230
static 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
231235
static 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

11341002
pbdrv_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

Comments
 (0)