|
7 | 7 |
|
8 | 8 | #include <lwrb/lwrb.h> |
9 | 9 |
|
| 10 | +#include <pbdrv/bluetooth.h> |
10 | 11 | #include <pbdrv/usb.h> |
11 | 12 | #include <pbsys/host.h> |
12 | 13 |
|
@@ -101,53 +102,57 @@ pbio_error_t pbsys_host_stdin_read(uint8_t *data, uint32_t *size) { |
101 | 102 | } |
102 | 103 |
|
103 | 104 | /** |
104 | | - * Transmits data over Bluetooth and USB. |
| 105 | + * Transmits data over any connected transport that is subscribed to Pybricks |
| 106 | + * protocol events. |
105 | 107 | * |
106 | | - * Should be called in a loop with the same arguments until it no longer |
107 | | - * returns ::PBIO_ERROR_AGAIN. |
| 108 | + * This may perform partial writes. Callers should check the number of bytes |
| 109 | + * actually written and call again with the remaining data until all data is |
| 110 | + * written. |
108 | 111 | * |
109 | | - * @param data [in] The data to transmit. |
110 | | - * @param size [in] The size of the data to transmit. |
111 | | - * @return ::PBIO_ERROR_AGAIN if the data is still being transmitted |
112 | | - * ::PBIO_SUCCESS if complete or failed. |
| 112 | + * @param data [in] The data to transmit. |
| 113 | + * @param size [inout] The size of the data to transmit. Upon success, this |
| 114 | + * contains the number of bytes actually processed. |
| 115 | + * @return ::PBIO_ERROR_INVALID_OP if there is no active transport, |
| 116 | + * ::PBIO_ERROR_AGAIN if no @p data could be queued, |
| 117 | + * ::PBIO_SUCCESS if at least some data was queued. |
113 | 118 | */ |
114 | | -pbio_error_t pbsys_host_tx(const uint8_t *data, uint32_t size) { |
115 | | - |
116 | | - static bool transmitting = false; |
117 | | - static uint32_t tx_done_ble; |
118 | | - static uint32_t tx_done_usb; |
119 | | - |
120 | | - if (!transmitting) { |
121 | | - tx_done_ble = 0; |
122 | | - tx_done_usb = 0; |
123 | | - transmitting = true; |
| 119 | +pbio_error_t pbsys_host_stdout_write(const uint8_t *data, uint32_t *size) { |
| 120 | + #if PBSYS_CONFIG_BLUETOOTH && (!PBDRV_CONFIG_USB || PBDRV_CONFIG_USB_CHARGE_ONLY) |
| 121 | + return pbsys_bluetooth_tx(data, size); |
| 122 | + #elif !PBSYS_CONFIG_BLUETOOTH && PBDRV_CONFIG_USB && !PBDRV_CONFIG_USB_CHARGE_ONLY |
| 123 | + return pbdrv_usb_stdout_tx(data, size); |
| 124 | + #elif PBSYS_CONFIG_BLUETOOTH && PBDRV_CONFIG_USB && !PBDRV_CONFIG_USB_CHARGE_ONLY |
| 125 | + |
| 126 | + uint32_t bt_avail = pbsys_bluetooth_tx_available(); |
| 127 | + uint32_t usb_avail = pbdrv_usb_stdout_tx_available(); |
| 128 | + uint32_t available = MIN(UINT32_MAX, MIN(bt_avail, usb_avail)); |
| 129 | + |
| 130 | + // If all tx_available() calls returned UINT32_MAX, then there is one listening. |
| 131 | + if (available == UINT32_MAX) { |
| 132 | + return PBIO_ERROR_INVALID_OP; |
124 | 133 | } |
125 | | - |
126 | | - pbio_error_t err_ble = PBIO_SUCCESS; |
127 | | - pbio_error_t err_usb = PBIO_SUCCESS; |
128 | | - uint32_t size_now; |
129 | | - |
130 | | - if (tx_done_ble < size) { |
131 | | - size_now = size - tx_done_ble; |
132 | | - err_ble = pbsys_bluetooth_tx(data + tx_done_ble, &size_now); |
133 | | - tx_done_ble += size_now; |
| 134 | + // If one or more tx_available() calls returned 0, then we need to wait. |
| 135 | + if (available == 0) { |
| 136 | + return PBIO_ERROR_AGAIN; |
134 | 137 | } |
135 | 138 |
|
136 | | - if (tx_done_usb < size) { |
137 | | - size_now = size - tx_done_usb; |
138 | | - err_usb = pbdrv_usb_stdout_tx(data + tx_done_usb, &size_now); |
139 | | - tx_done_usb += size_now; |
140 | | - } |
| 139 | + // Limit size to smallest available space from all transports so that we |
| 140 | + // don't do partial writes to one transport and not the other. |
| 141 | + *size = MIN(*size, available); |
141 | 142 |
|
142 | | - // Keep waiting as long as at least has not completed or errored. |
143 | | - if (err_ble == PBIO_ERROR_AGAIN || err_usb == PBIO_ERROR_AGAIN) { |
144 | | - return PBIO_ERROR_AGAIN; |
145 | | - } |
| 143 | + // Unless something became disconnected in an interrupt handler, these |
| 144 | + // functions should always succeed since we already checked tx_available(). |
| 145 | + // And if both somehow got disconnected at the same time, it is not a big deal |
| 146 | + // if we return PBIO_SUCCESS without actually sending anything. |
| 147 | + (void)pbsys_bluetooth_tx(data, size); |
| 148 | + (void)pbdrv_usb_stdout_tx(data, size); |
| 149 | + |
| 150 | + return PBIO_SUCCESS; |
146 | 151 |
|
147 | | - // Both of them are either complete or failed. The caller of this function |
148 | | - // does not currently raise errors, so we just return success. |
149 | | - transmitting = false; |
| 152 | + #else |
| 153 | + // stdout goes to /dev/null |
150 | 154 | return PBIO_SUCCESS; |
| 155 | + #endif |
151 | 156 | } |
152 | 157 |
|
153 | 158 | bool pbsys_host_tx_is_idle(void) { |
|
0 commit comments