Skip to content

Commit aa884db

Browse files
nkarstensdlech
authored andcommitted
pbdrv/usb: Configure USB device and process pybricks commands
Add initial implementation of the Pybricks USB interface. This can process commands but doesn't send a response yet. Signed-off-by: Nate Karstens <[email protected]>
1 parent 51d0831 commit aa884db

File tree

4 files changed

+141
-3
lines changed

4 files changed

+141
-3
lines changed

lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,28 @@ extern "C" {
5454
* @}
5555
*/
5656

57+
// NOTE: These enums values are sent over the wire, so cannot be changed. Also,
58+
// 0 is skipped to avoid a zeroed buffer from being misinterpreted as a message.
59+
60+
/** Hub to host messages via the Pybricks interface IN endpoint. */
61+
enum {
62+
/**
63+
* Analog of BLE status response. Emitted in response to every OUT message
64+
* received.
65+
*/
66+
USBD_PYBRICKS_IN_EP_MSG_RESPONSE = 1,
67+
/**Analog to BLE notification. Only emitted if subscribed. */
68+
USBD_PYBRICKS_IN_EP_MSG_EVENT = 2,
69+
};
70+
71+
/** Host to hub messages via the Pybricks USB interface OUT endpoint. */
72+
enum {
73+
/** Analog of BLE Client Characteristic Configuration Descriptor (CCCD). */
74+
USBD_PYBRICKS_OUT_EP_MSG_SUBSCRIBE = 1,
75+
/** Analog of BLE Client Characteristic Write with response. */
76+
USBD_PYBRICKS_OUT_EP_MSG_COMMAND = 2,
77+
};
78+
5779

5880
/** @defgroup USBD_Pybricks_Exported_TypesDefinitions
5981
* @{

lib/pbio/drv/usb/usb_stm32.c

Lines changed: 107 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,27 @@
1313
#include <stm32f4xx_hal.h>
1414
#include <stm32f4xx_hal_pcd_ex.h>
1515
#include <usbd_core.h>
16+
#include <usbd_desc.h>
17+
#include <usbd_pybricks.h>
1618

1719
#include <pbdrv/usb.h>
1820
#include <pbio/util.h>
21+
#include <pbsys/command.h>
22+
#include <pbsys/status.h>
1923

2024
#include "../charger/charger.h"
2125
#include "./usb_stm32.h"
2226

2327
PROCESS(pbdrv_usb_process, "USB");
2428

29+
// These buffers need to be 32-bit aligned because the USB driver moves data
30+
// to/from FIFOs in 32-bit chunks.
31+
static uint8_t usb_in_buf[USBD_PYBRICKS_MAX_PACKET_SIZE] __aligned(4);
32+
static volatile uint32_t usb_in_sz;
33+
2534
static USBD_HandleTypeDef husbd;
2635
static PCD_HandleTypeDef hpcd;
36+
2737
static volatile bool vbus_active;
2838
static pbdrv_usb_bcd_t pbdrv_usb_bcd;
2939

@@ -121,14 +131,81 @@ void pbdrv_usb_stm32_handle_vbus_irq(bool active) {
121131
process_poll(&pbdrv_usb_process);
122132
}
123133

134+
/**
135+
* @brief Pybricks_Itf_Init
136+
* Initializes the Pybricks media low layer
137+
* @param None
138+
* @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
139+
*/
140+
static USBD_StatusTypeDef Pybricks_Itf_Init(void) {
141+
USBD_Pybricks_SetRxBuffer(&husbd, usb_in_buf);
142+
usb_in_sz = 0;
143+
144+
return USBD_OK;
145+
}
146+
147+
/**
148+
* @brief Pybricks_Itf_DeInit
149+
* DeInitializes the Pybricks media low layer
150+
* @param None
151+
* @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
152+
*/
153+
static USBD_StatusTypeDef Pybricks_Itf_DeInit(void) {
154+
return USBD_OK;
155+
}
156+
157+
/**
158+
* @brief Pybricks_Itf_DataRx
159+
* Data received over USB OUT endpoint are sent over Pybricks interface
160+
* through this function.
161+
* @param Buf: Buffer of data to be transmitted
162+
* @param Len: Number of data received (in bytes)
163+
* @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
164+
*/
165+
static USBD_StatusTypeDef Pybricks_Itf_Receive(uint8_t *Buf, uint32_t Len) {
166+
167+
usb_in_sz = Len;
168+
process_poll(&pbdrv_usb_process);
169+
return USBD_OK;
170+
}
171+
172+
/**
173+
* @brief Pybricks_Itf_TransmitCplt
174+
* Data transmitted callback
175+
*
176+
* @note
177+
* This function is IN transfer complete callback used to inform user that
178+
* the submitted Data is successfully sent over USB.
179+
*
180+
* @param Buf: Buffer of data that was transmitted
181+
* @param Len: Number of data transmitted (in bytes)
182+
* @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
183+
*/
184+
static USBD_StatusTypeDef Pybricks_Itf_TransmitCplt(uint8_t *Buf, uint32_t Len, uint8_t epnum) {
185+
process_poll(&pbdrv_usb_process);
186+
return USBD_OK;
187+
}
188+
189+
static USBD_Pybricks_ItfTypeDef USBD_Pybricks_fops = {
190+
.Init = Pybricks_Itf_Init,
191+
.DeInit = Pybricks_Itf_DeInit,
192+
.Receive = Pybricks_Itf_Receive,
193+
.TransmitCplt = Pybricks_Itf_TransmitCplt,
194+
};
195+
124196
// Common USB driver implementation.
125197

126198
void pbdrv_usb_init(void) {
127199
// Link the driver data structures
128200
husbd.pData = &hpcd;
129201
hpcd.pData = &husbd;
130202

131-
USBD_Init(&husbd, NULL, 0);
203+
USBD_Pybricks_Desc_Init();
204+
USBD_Init(&husbd, &USBD_Pybricks_Desc, 0);
205+
USBD_RegisterClass(&husbd, &USBD_Pybricks_ClassDriver);
206+
USBD_Pybricks_RegisterInterface(&husbd, &USBD_Pybricks_fops);
207+
USBD_Start(&husbd);
208+
132209
process_start(&pbdrv_usb_process);
133210

134211
// VBUS may already be active
@@ -145,6 +222,7 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) {
145222
static struct pt bcd_pt;
146223
static PBIO_ONESHOT(no_vbus_oneshot);
147224
static PBIO_ONESHOT(bcd_oneshot);
225+
static PBIO_ONESHOT(pwrdn_oneshot);
148226
static bool bcd_busy;
149227

150228
PROCESS_POLLHANDLER({
@@ -169,6 +247,34 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) {
169247

170248
if (bcd_busy) {
171249
bcd_busy = PT_SCHEDULE(pbdrv_usb_stm32_bcd_detect(&bcd_pt));
250+
251+
if (bcd_busy) {
252+
// All other USB functions are halted if BCD is busy
253+
continue;
254+
}
255+
}
256+
257+
if (pbsys_status_test(PBIO_PYBRICKS_STATUS_SHUTDOWN)) {
258+
if (pbio_oneshot(true, &pwrdn_oneshot)) {
259+
USBD_Stop(&husbd);
260+
USBD_DeInit(&husbd);
261+
}
262+
263+
// USB communication is stopped after a shutdown, but
264+
// the process is still needed to track the BCD state
265+
continue;
266+
}
267+
268+
if (usb_in_sz) {
269+
switch (usb_in_buf[0]) {
270+
case USBD_PYBRICKS_OUT_EP_MSG_COMMAND:
271+
pbsys_command(usb_in_buf + 1, usb_in_sz - 1);
272+
break;
273+
}
274+
275+
// Prepare to receive the next packet
276+
usb_in_sz = 0;
277+
USBD_Pybricks_ReceivePacket(&husbd);
172278
}
173279
}
174280

lib/pbio/platform/essential_hub/platform.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -670,8 +670,13 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd) {
670670
}
671671

672672
void HAL_PCD_MspDeInit(PCD_HandleTypeDef *hpcd) {
673-
HAL_NVIC_DisableIRQ(EXTI9_5_IRQn);
674673
HAL_NVIC_DisableIRQ(OTG_FS_IRQn);
674+
675+
// The VBUS IRQ remains enabled so that it can still
676+
// be triggered if the device is shut down but left
677+
// connected to charge. When the charging cable is
678+
// disconnected, the IRQ will trigger and lead to the
679+
// device fully powering down.
675680
}
676681

677682
void OTG_FS_IRQHandler(void) {

lib/pbio/platform/prime_hub/platform.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -970,8 +970,13 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd) {
970970
}
971971

972972
void HAL_PCD_MspDeInit(PCD_HandleTypeDef *hpcd) {
973-
HAL_NVIC_DisableIRQ(EXTI9_5_IRQn);
974973
HAL_NVIC_DisableIRQ(OTG_FS_IRQn);
974+
975+
// The VBUS IRQ remains enabled so that it can still
976+
// be triggered if the device is shut down but left
977+
// connected to charge. When the charging cable is
978+
// disconnected, the IRQ will trigger and lead to the
979+
// device fully powering down.
975980
}
976981

977982
void OTG_FS_IRQHandler(void) {

0 commit comments

Comments
 (0)