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
2327PROCESS (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+
2534static USBD_HandleTypeDef husbd ;
2635static PCD_HandleTypeDef hpcd ;
36+
2737static volatile bool vbus_active ;
2838static 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
126198void 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
0 commit comments