Skip to content

Commit a637c53

Browse files
committed
Add support for transmitting stdout over USB
Adds support for transmitting stdout messages over USB. Signed-off-by: Nate Karstens <[email protected]>
1 parent 559b78d commit a637c53

File tree

3 files changed

+125
-6
lines changed

3 files changed

+125
-6
lines changed

bricks/_common_stm32/mphalport.c

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <contiki.h>
1010

1111
#include <pbdrv/config.h>
12+
#include <pbdrv/usb.h>
1213
#include <pbio/main.h>
1314
#include <pbsys/bluetooth.h>
1415

@@ -128,9 +129,40 @@ int mp_hal_stdin_rx_chr(void) {
128129

129130
// Send string of given length
130131
void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
132+
uint32_t size;
133+
pbio_error_t err;
134+
135+
#if PBDRV_CONFIG_USB
136+
137+
const char *usb_ptr = str;
138+
mp_uint_t usb_len = len;
139+
140+
while (usb_len) {
141+
size = usb_len;
142+
err = pbdrv_usb_stdout_tx((const uint8_t *)usb_ptr, &size);
143+
144+
if (err == PBIO_SUCCESS) {
145+
usb_ptr += size;
146+
usb_len -= size;
147+
continue;
148+
}
149+
150+
if (err != PBIO_ERROR_AGAIN) {
151+
// Ignoring error for now. This means
152+
// stdout lost if USB is disconnected.
153+
break;
154+
}
155+
156+
if (usb_len) {
157+
MICROPY_EVENT_POLL_HOOK
158+
}
159+
}
160+
161+
#endif // PBDRV_CONFIG_USB
162+
131163
while (len) {
132-
uint32_t size = len;
133-
pbio_error_t err = pbsys_bluetooth_tx((const uint8_t *)str, &size);
164+
size = len;
165+
err = pbsys_bluetooth_tx((const uint8_t *)str, &size);
134166

135167
if (err == PBIO_SUCCESS) {
136168
str += size;
@@ -144,12 +176,14 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
144176
return;
145177
}
146178

147-
MICROPY_EVENT_POLL_HOOK
179+
if (len) {
180+
MICROPY_EVENT_POLL_HOOK
181+
}
148182
}
149183
}
150184

151185
void mp_hal_stdout_tx_flush(void) {
152-
while (!pbsys_bluetooth_tx_is_idle()) {
186+
while (!pbsys_bluetooth_tx_is_idle() && !pbdrv_usb_stdout_tx_is_idle()) {
153187
MICROPY_EVENT_POLL_HOOK
154188
}
155189
}

lib/pbio/drv/usb/usb_stm32.c

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <usbd_desc.h>
1818
#include <usbd_pybricks.h>
1919

20+
#include <pbdrv/clock.h>
2021
#include <pbdrv/usb.h>
2122
#include <pbio/protocol.h>
2223
#include <pbio/util.h>
@@ -31,9 +32,13 @@ PROCESS(pbdrv_usb_process, "USB");
3132
static uint8_t usb_in_buf[USBD_PYBRICKS_MAX_PACKET_SIZE];
3233
static uint8_t usb_response_buf[1 + sizeof(uint32_t)];
3334
static uint8_t usb_status_buf[1 + 1 + sizeof(uint32_t)];
35+
static uint8_t usb_stdout_buf[USBD_PYBRICKS_MAX_PACKET_SIZE];
3436
static volatile uint32_t usb_in_sz;
3537
static volatile uint32_t usb_response_sz;
3638
static volatile uint32_t usb_status_sz;
39+
static volatile uint32_t usb_stdout_sz;
40+
static volatile clock_time_t usb_response_tx_time;
41+
static volatile clock_time_t usb_stdout_tx_time;
3742
static volatile bool transmitting;
3843

3944
static USBD_HandleTypeDef husbd;
@@ -136,6 +141,53 @@ void pbdrv_usb_stm32_handle_vbus_irq(bool active) {
136141
process_poll(&pbdrv_usb_process);
137142
}
138143

144+
/**
145+
* Queues data to be transmitted via USB.
146+
* @param data [in] The data to be sent.
147+
* @param size [in, out] The size of @p data in bytes. After return, @p size
148+
* contains the number of bytes actually written.
149+
* @return ::PBIO_SUCCESS if @p data was queued, ::PBIO_ERROR_AGAIN
150+
* if @p data could not be queued at this time (e.g. buffer
151+
* is full), ::PBIO_ERROR_INVALID_OP if there is not an
152+
* active USB connection or ::PBIO_ERROR_NOT_SUPPORTED
153+
* if this platform does not support USB.
154+
*/
155+
pbio_error_t pbdrv_usb_stdout_tx(const uint8_t *data, uint32_t *size) {
156+
uint8_t *ptr = usb_stdout_buf;
157+
uint32_t ptr_len = sizeof(usb_stdout_buf);
158+
159+
// TODO: return PBIO_ERROR_INVALID_OP if not connected
160+
161+
if (usb_stdout_sz) {
162+
return PBIO_ERROR_AGAIN;
163+
}
164+
165+
*ptr = USBD_PYBRICKS_MSG_EVENT;
166+
ptr++;
167+
ptr_len--;
168+
169+
*ptr = PBIO_PYBRICKS_EVENT_WRITE_STDOUT;
170+
ptr++;
171+
ptr_len--;
172+
173+
*size = MIN(*size, ptr_len);
174+
memcpy(ptr, data, *size);
175+
176+
usb_stdout_sz = 1 + 1 + *size;
177+
178+
process_poll(&pbdrv_usb_process);
179+
180+
return PBIO_SUCCESS;
181+
}
182+
183+
/**
184+
* Indicates if there is stdout data waiting to be transmitted over USB.
185+
* @retval false if stdout data is currently being transmitted.
186+
*/
187+
bool pbdrv_usb_stdout_tx_is_idle(void) {
188+
return usb_stdout_sz == 0;
189+
}
190+
139191
/**
140192
* @brief Pybricks_Itf_Init
141193
* Initializes the Pybricks media low layer
@@ -147,6 +199,7 @@ static USBD_StatusTypeDef Pybricks_Itf_Init(void) {
147199
usb_in_sz = 0;
148200
usb_response_sz = 0;
149201
usb_status_sz = 0;
202+
usb_stdout_sz = 0;
150203
transmitting = false;
151204

152205
return USBD_OK;
@@ -200,6 +253,8 @@ static USBD_StatusTypeDef Pybricks_Itf_TransmitCplt(uint8_t *Buf, uint32_t *Len,
200253
usb_response_sz = 0;
201254
} else if (Buf == usb_status_buf) {
202255
usb_status_sz = 0;
256+
} else if (Buf == usb_stdout_buf) {
257+
usb_stdout_sz = 0;
203258
} else {
204259
ret = USBD_FAIL;
205260
}
@@ -322,7 +377,8 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) {
322377

323378
new_status_flags = pbsys_status_get_flags();
324379

325-
// Transmit. Give priority to status updates.
380+
// Transmit. Give priority to status updates, then
381+
// cycle between response and stdout buffers.
326382
if ((new_status_flags != prev_status_flags) || etimer_expired(&timer)) {
327383

328384
usb_status_buf[0] = USBD_PYBRICKS_MSG_EVENT;
@@ -334,10 +390,18 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) {
334390
transmitting = true;
335391
USBD_Pybricks_TransmitPacket(&husbd, usb_status_buf, usb_status_sz);
336392

337-
} else if (usb_response_sz) {
393+
} else if (usb_response_sz &&
394+
(!usb_stdout_sz || (usb_response_tx_time < usb_stdout_tx_time))) {
338395

339396
transmitting = true;
397+
usb_response_tx_time = clock_time();
340398
USBD_Pybricks_TransmitPacket(&husbd, usb_response_buf, usb_response_sz);
399+
400+
} else if (usb_stdout_sz) {
401+
402+
transmitting = true;
403+
usb_stdout_tx_time = clock_time();
404+
USBD_Pybricks_TransmitPacket(&husbd, usb_stdout_buf, usb_stdout_sz);
341405
}
342406
}
343407

lib/pbio/include/pbdrv/usb.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <stdbool.h>
1313

1414
#include <pbdrv/config.h>
15+
#include <pbio/error.h>
1516

1617
/**
1718
* Indicates battery charging capabilites that were detected on a USB port.
@@ -43,6 +44,18 @@ pbdrv_usb_bcd_t pbdrv_usb_get_bcd(void);
4344
*/
4445
bool pbdrv_usb_get_vbus_status(void);
4546

47+
/**
48+
* Transmits the given buffer over the USB stdout stream.
49+
* @return The result of the operation.
50+
*/
51+
pbio_error_t pbdrv_usb_stdout_tx(const uint8_t *data, uint32_t *size);
52+
53+
/**
54+
* Indicates if the USB stdout stream is idle.
55+
* @return true if the USB stdout stream is idle.
56+
*/
57+
bool pbdrv_usb_stdout_tx_is_idle(void);
58+
4659
#else // PBDRV_CONFIG_USB
4760

4861
static inline pbdrv_usb_bcd_t pbdrv_usb_get_bcd(void) {
@@ -53,6 +66,14 @@ static inline bool pbdrv_usb_get_vbus_status(void) {
5366
return false;
5467
}
5568

69+
static inline pbio_error_t pbdrv_usb_stdout_tx(const uint8_t *data, uint32_t *size) {
70+
return PBIO_SUCCESS;
71+
}
72+
73+
static inline bool pbdrv_usb_stdout_tx_is_idle(void) {
74+
return true;
75+
}
76+
5677
#endif // PBDRV_CONFIG_USB
5778

5879
#endif // _PBDRV_USB_H_

0 commit comments

Comments
 (0)