Skip to content

Commit

Permalink
Add support for transmitting stdout over USB
Browse files Browse the repository at this point in the history
Adds support for transmitting stdout messages over USB.

Signed-off-by: Nate Karstens <[email protected]>
  • Loading branch information
nkarstens committed Feb 19, 2024
1 parent 559b78d commit a637c53
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 6 deletions.
42 changes: 38 additions & 4 deletions bricks/_common_stm32/mphalport.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <contiki.h>

#include <pbdrv/config.h>
#include <pbdrv/usb.h>
#include <pbio/main.h>
#include <pbsys/bluetooth.h>

Expand Down Expand Up @@ -128,9 +129,40 @@ int mp_hal_stdin_rx_chr(void) {

// Send string of given length
void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
uint32_t size;
pbio_error_t err;

#if PBDRV_CONFIG_USB

const char *usb_ptr = str;
mp_uint_t usb_len = len;

while (usb_len) {
size = usb_len;
err = pbdrv_usb_stdout_tx((const uint8_t *)usb_ptr, &size);

if (err == PBIO_SUCCESS) {
usb_ptr += size;
usb_len -= size;
continue;
}

if (err != PBIO_ERROR_AGAIN) {
// Ignoring error for now. This means
// stdout lost if USB is disconnected.
break;
}

if (usb_len) {
MICROPY_EVENT_POLL_HOOK
}
}

#endif // PBDRV_CONFIG_USB

while (len) {
uint32_t size = len;
pbio_error_t err = pbsys_bluetooth_tx((const uint8_t *)str, &size);
size = len;
err = pbsys_bluetooth_tx((const uint8_t *)str, &size);

if (err == PBIO_SUCCESS) {
str += size;
Expand All @@ -144,12 +176,14 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
return;
}

MICROPY_EVENT_POLL_HOOK
if (len) {
MICROPY_EVENT_POLL_HOOK
}
}
}

void mp_hal_stdout_tx_flush(void) {
while (!pbsys_bluetooth_tx_is_idle()) {
while (!pbsys_bluetooth_tx_is_idle() && !pbdrv_usb_stdout_tx_is_idle()) {
MICROPY_EVENT_POLL_HOOK
}
}
Expand Down
68 changes: 66 additions & 2 deletions lib/pbio/drv/usb/usb_stm32.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <usbd_desc.h>
#include <usbd_pybricks.h>

#include <pbdrv/clock.h>
#include <pbdrv/usb.h>
#include <pbio/protocol.h>
#include <pbio/util.h>
Expand All @@ -31,9 +32,13 @@ PROCESS(pbdrv_usb_process, "USB");
static uint8_t usb_in_buf[USBD_PYBRICKS_MAX_PACKET_SIZE];
static uint8_t usb_response_buf[1 + sizeof(uint32_t)];
static uint8_t usb_status_buf[1 + 1 + sizeof(uint32_t)];
static uint8_t usb_stdout_buf[USBD_PYBRICKS_MAX_PACKET_SIZE];
static volatile uint32_t usb_in_sz;
static volatile uint32_t usb_response_sz;
static volatile uint32_t usb_status_sz;
static volatile uint32_t usb_stdout_sz;
static volatile clock_time_t usb_response_tx_time;
static volatile clock_time_t usb_stdout_tx_time;
static volatile bool transmitting;

static USBD_HandleTypeDef husbd;
Expand Down Expand Up @@ -136,6 +141,53 @@ void pbdrv_usb_stm32_handle_vbus_irq(bool active) {
process_poll(&pbdrv_usb_process);
}

/**
* Queues data to be transmitted via USB.
* @param data [in] The data to be sent.
* @param size [in, out] The size of @p data in bytes. After return, @p size
* contains the number of bytes actually written.
* @return ::PBIO_SUCCESS if @p data was queued, ::PBIO_ERROR_AGAIN
* if @p data could not be queued at this time (e.g. buffer
* is full), ::PBIO_ERROR_INVALID_OP if there is not an
* active USB connection or ::PBIO_ERROR_NOT_SUPPORTED
* if this platform does not support USB.
*/
pbio_error_t pbdrv_usb_stdout_tx(const uint8_t *data, uint32_t *size) {
uint8_t *ptr = usb_stdout_buf;
uint32_t ptr_len = sizeof(usb_stdout_buf);

// TODO: return PBIO_ERROR_INVALID_OP if not connected

if (usb_stdout_sz) {
return PBIO_ERROR_AGAIN;
}

*ptr = USBD_PYBRICKS_MSG_EVENT;
ptr++;
ptr_len--;

*ptr = PBIO_PYBRICKS_EVENT_WRITE_STDOUT;
ptr++;
ptr_len--;

*size = MIN(*size, ptr_len);
memcpy(ptr, data, *size);

usb_stdout_sz = 1 + 1 + *size;

process_poll(&pbdrv_usb_process);

return PBIO_SUCCESS;
}

/**
* Indicates if there is stdout data waiting to be transmitted over USB.
* @retval false if stdout data is currently being transmitted.
*/
bool pbdrv_usb_stdout_tx_is_idle(void) {
return usb_stdout_sz == 0;
}

/**
* @brief Pybricks_Itf_Init
* Initializes the Pybricks media low layer
Expand All @@ -147,6 +199,7 @@ static USBD_StatusTypeDef Pybricks_Itf_Init(void) {
usb_in_sz = 0;
usb_response_sz = 0;
usb_status_sz = 0;
usb_stdout_sz = 0;
transmitting = false;

return USBD_OK;
Expand Down Expand Up @@ -200,6 +253,8 @@ static USBD_StatusTypeDef Pybricks_Itf_TransmitCplt(uint8_t *Buf, uint32_t *Len,
usb_response_sz = 0;
} else if (Buf == usb_status_buf) {
usb_status_sz = 0;
} else if (Buf == usb_stdout_buf) {
usb_stdout_sz = 0;
} else {
ret = USBD_FAIL;
}
Expand Down Expand Up @@ -322,7 +377,8 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) {

new_status_flags = pbsys_status_get_flags();

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

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

} else if (usb_response_sz) {
} else if (usb_response_sz &&
(!usb_stdout_sz || (usb_response_tx_time < usb_stdout_tx_time))) {

transmitting = true;
usb_response_tx_time = clock_time();
USBD_Pybricks_TransmitPacket(&husbd, usb_response_buf, usb_response_sz);

} else if (usb_stdout_sz) {

transmitting = true;
usb_stdout_tx_time = clock_time();
USBD_Pybricks_TransmitPacket(&husbd, usb_stdout_buf, usb_stdout_sz);
}
}

Expand Down
21 changes: 21 additions & 0 deletions lib/pbio/include/pbdrv/usb.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <stdbool.h>

#include <pbdrv/config.h>
#include <pbio/error.h>

/**
* Indicates battery charging capabilites that were detected on a USB port.
Expand Down Expand Up @@ -43,6 +44,18 @@ pbdrv_usb_bcd_t pbdrv_usb_get_bcd(void);
*/
bool pbdrv_usb_get_vbus_status(void);

/**
* Transmits the given buffer over the USB stdout stream.
* @return The result of the operation.
*/
pbio_error_t pbdrv_usb_stdout_tx(const uint8_t *data, uint32_t *size);

/**
* Indicates if the USB stdout stream is idle.
* @return true if the USB stdout stream is idle.
*/
bool pbdrv_usb_stdout_tx_is_idle(void);

#else // PBDRV_CONFIG_USB

static inline pbdrv_usb_bcd_t pbdrv_usb_get_bcd(void) {
Expand All @@ -53,6 +66,14 @@ static inline bool pbdrv_usb_get_vbus_status(void) {
return false;
}

static inline pbio_error_t pbdrv_usb_stdout_tx(const uint8_t *data, uint32_t *size) {
return PBIO_SUCCESS;
}

static inline bool pbdrv_usb_stdout_tx_is_idle(void) {
return true;
}

#endif // PBDRV_CONFIG_USB

#endif // _PBDRV_USB_H_
Expand Down

0 comments on commit a637c53

Please sign in to comment.