Skip to content

Commit 4dc64d8

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 86e3f38 commit 4dc64d8

File tree

5 files changed

+144
-7
lines changed

5 files changed

+144
-7
lines changed

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -379,8 +379,7 @@ static USBD_StatusTypeDef USBD_Pybricks_EP0_RxReady(USBD_HandleTypeDef *pdev) {
379379
* @param fops: CD Interface callback
380380
* @retval status
381381
*/
382-
USBD_StatusTypeDef USBD_Pybricks_RegisterInterface(USBD_HandleTypeDef *pdev,
383-
USBD_Pybricks_ItfTypeDef *fops) {
382+
USBD_StatusTypeDef USBD_Pybricks_RegisterInterface(USBD_HandleTypeDef *pdev, USBD_Pybricks_ItfTypeDef *fops) {
384383
if (fops == NULL) {
385384
return USBD_FAIL;
386385
}
@@ -416,7 +415,6 @@ USBD_StatusTypeDef USBD_Pybricks_SetRxBuffer(USBD_HandleTypeDef *pdev, uint8_t *
416415
*/
417416
USBD_StatusTypeDef USBD_Pybricks_TransmitPacket(USBD_HandleTypeDef *pdev, uint8_t *pbuf, uint32_t length) {
418417
USBD_Pybricks_HandleTypeDef *hPybricks = pdev->pClassData;
419-
USBD_StatusTypeDef ret = USBD_BUSY;
420418

421419
if (hPybricks == NULL) {
422420
return USBD_FAIL;

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,32 @@ extern "C" {
4949
#define USBD_PYBRICKS_OUT_EP 0x01U /* EP1 for data OUT */
5050

5151
#define USBD_PYBRICKS_MAX_PACKET_SIZE 64U
52-
5352
/**
5453
* @}
5554
*/
5655

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

5879
/** @defgroup USBD_Pybricks_Exported_TypesDefinitions
5980
* @{
@@ -96,6 +117,7 @@ typedef struct
96117
extern USBD_ClassTypeDef USBD_Pybricks_ClassDriver;
97118

98119
extern const uint8_t USBD_OSDescSet[USBD_SIZ_MS_OS_DSCRPTR_SET];
120+
99121
/**
100122
* @}
101123
*/

lib/pbio/drv/usb/usb_stm32.c

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,33 @@
88
#if PBDRV_CONFIG_USB_STM32F4
99

1010
#include <stdbool.h>
11+
#include <string.h>
1112

1213
#include <contiki.h>
1314
#include <stm32f4xx_hal.h>
1415
#include <stm32f4xx_hal_pcd_ex.h>
1516
#include <usbd_core.h>
17+
#include <usbd_desc.h>
18+
#include <usbd_pybricks.h>
1619

1720
#include <pbdrv/usb.h>
1821
#include <pbio/util.h>
22+
#include <pbsys/command.h>
23+
#include <pbsys/status.h>
1924

2025
#include "../charger/charger.h"
2126
#include "./usb_stm32.h"
2227

2328
PROCESS(pbdrv_usb_process, "USB");
2429

30+
// These buffers need to be 32-bit aligned because the USB driver moves data
31+
// to/from FIFOs in 32-bit chunks.
32+
static uint8_t usb_in_buf[USBD_PYBRICKS_MAX_PACKET_SIZE] __aligned(4);
33+
static volatile uint32_t usb_in_sz;
34+
2535
static USBD_HandleTypeDef husbd;
2636
static PCD_HandleTypeDef hpcd;
37+
2738
static volatile bool vbus_active;
2839
static pbdrv_usb_bcd_t pbdrv_usb_bcd;
2940

@@ -121,14 +132,81 @@ void pbdrv_usb_stm32_handle_vbus_irq(bool active) {
121132
process_poll(&pbdrv_usb_process);
122133
}
123134

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

126199
void pbdrv_usb_init(void) {
127200
// Link the driver data structures
128201
husbd.pData = &hpcd;
129202
hpcd.pData = &husbd;
130203

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

134212
// VBUS may already be active
@@ -145,6 +223,7 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) {
145223
static struct pt bcd_pt;
146224
static PBIO_ONESHOT(no_vbus_oneshot);
147225
static PBIO_ONESHOT(bcd_oneshot);
226+
static PBIO_ONESHOT(pwrdn_oneshot);
148227
static bool bcd_busy;
149228

150229
PROCESS_POLLHANDLER({
@@ -169,6 +248,34 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) {
169248

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

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)