1010#include <stdbool.h>
1111
1212#include <contiki.h>
13+ #include <contiki-lib.h>
1314#include <stm32f4xx_hal.h>
1415#include <stm32f4xx_hal_pcd_ex.h>
16+ #include <usbd_cdc.h>
1517#include <usbd_core.h>
1618
1719#include <pbdrv/usb.h>
2224
2325PROCESS (pbdrv_usb_process , "USB" );
2426
27+ static uint8_t usb_in_buf [CDC_DATA_FS_MAX_PACKET_SIZE ];
28+ static uint8_t usb_out_buf [CDC_DATA_FS_MAX_PACKET_SIZE - 1 ];
29+ static volatile bool usb_in_busy ;
30+ static volatile bool usb_out_busy ;
31+
32+ static volatile bool usb_connected ;
33+
34+ // size must be power of 2 for ringbuf! also can't be > 255!
35+ static uint8_t stdout_data [128 ];
36+ static uint8_t stdin_data [128 ];
37+ static struct ringbuf stdout_buf ;
38+ static struct ringbuf stdin_buf ;
39+
40+ static USBD_CDC_LineCodingTypeDef LineCoding = {
41+ .bitrate = 115200 ,
42+ .format = CDC_STOP_BITS_1 ,
43+ .paritytype = CDC_PARITY_NONE ,
44+ .datatype = 8 ,
45+ };
46+
2547static USBD_HandleTypeDef husbd ;
2648static PCD_HandleTypeDef hpcd ;
49+ extern USBD_DescriptorsTypeDef VCP_Desc ;
50+
2751static volatile bool vbus_active ;
2852static pbdrv_usb_bcd_t pbdrv_usb_bcd ;
2953
@@ -121,14 +145,146 @@ void pbdrv_usb_stm32_handle_vbus_irq(bool active) {
121145 process_poll (& pbdrv_usb_process );
122146}
123147
148+ /**
149+ * @brief CDC_Itf_Init
150+ * Initializes the CDC 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 int8_t CDC_Itf_Init (void ) {
155+ ringbuf_init (& stdin_buf , stdin_data , (uint8_t )PBIO_ARRAY_SIZE (stdin_data ));
156+ ringbuf_init (& stdout_buf , stdout_data , (uint8_t )PBIO_ARRAY_SIZE (stdout_data ));
157+ USBD_CDC_SetTxBuffer (& husbd , usb_out_buf , 0 );
158+ USBD_CDC_SetRxBuffer (& husbd , usb_in_buf );
159+ usb_in_busy = false;
160+ usb_out_busy = false;
161+ usb_connected = false;
162+
163+ return USBD_OK ;
164+ }
165+
166+ /**
167+ * @brief CDC_Itf_DeInit
168+ * DeInitializes the CDC media low layer
169+ * @param None
170+ * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
171+ */
172+ static int8_t CDC_Itf_DeInit (void ) {
173+ usb_connected = false;
174+ return USBD_OK ;
175+ }
176+
177+ /**
178+ * @brief CDC_Itf_Control
179+ * Manage the CDC class requests
180+ * @param Cmd: Command code
181+ * @param Buf: Buffer containing command data (request parameters)
182+ * @param Len: Number of data to be sent (in bytes)
183+ * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
184+ */
185+ static int8_t CDC_Itf_Control (uint8_t cmd , uint8_t * pbuf , uint16_t length ) {
186+ switch (cmd ) {
187+ case CDC_SEND_ENCAPSULATED_COMMAND :
188+ break ;
189+
190+ case CDC_GET_ENCAPSULATED_RESPONSE :
191+ break ;
192+
193+ case CDC_SET_COMM_FEATURE :
194+ break ;
195+
196+ case CDC_GET_COMM_FEATURE :
197+ break ;
198+
199+ case CDC_CLEAR_COMM_FEATURE :
200+ break ;
201+
202+ case CDC_SET_LINE_CODING :
203+ LineCoding .bitrate = pbuf [0 ] | (pbuf [1 ] << 8 ) | (pbuf [2 ] << 16 ) | (pbuf [3 ] << 24 );
204+ LineCoding .format = pbuf [4 ];
205+ LineCoding .paritytype = pbuf [5 ];
206+ LineCoding .datatype = pbuf [6 ];
207+ break ;
208+
209+ case CDC_GET_LINE_CODING :
210+ pbuf [0 ] = (uint8_t )(LineCoding .bitrate );
211+ pbuf [1 ] = (uint8_t )(LineCoding .bitrate >> 8 );
212+ pbuf [2 ] = (uint8_t )(LineCoding .bitrate >> 16 );
213+ pbuf [3 ] = (uint8_t )(LineCoding .bitrate >> 24 );
214+ pbuf [4 ] = LineCoding .format ;
215+ pbuf [5 ] = LineCoding .paritytype ;
216+ pbuf [6 ] = LineCoding .datatype ;
217+ break ;
218+
219+ case CDC_SET_CONTROL_LINE_STATE : {
220+ USBD_SetupReqTypedef * req = (void * )pbuf ;
221+ // REVISIT: MicroPython defers the connection state here to allow
222+ // some time to disable local echo on the remote terminal
223+ usb_connected = !!(req -> wValue & CDC_CONTROL_LINE_DTR );
224+ break ;
225+ }
226+ case CDC_SEND_BREAK :
227+ break ;
228+ }
229+
230+ return USBD_OK ;
231+ }
232+
233+ /**
234+ * @brief CDC_Itf_DataRx
235+ * Data received over USB OUT endpoint are sent over CDC interface
236+ * through this function.
237+ * @param Buf: Buffer of data to be transmitted
238+ * @param Len: Number of data received (in bytes)
239+ * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
240+ */
241+ static int8_t CDC_Itf_Receive (uint8_t * Buf , uint32_t * Len ) {
242+ for (uint32_t i = 0 ; i < * Len ; i ++ ) {
243+ ringbuf_put (& stdin_buf , Buf [i ]);
244+ }
245+ usb_in_busy = false;
246+ // process_poll(&pbdrv_usb_process);
247+ return USBD_OK ;
248+ }
249+
250+ /**
251+ * @brief CDC_Itf_TransmitCplt
252+ * Data transmitted callback
253+ *
254+ * @note
255+ * This function is IN transfer complete callback used to inform user that
256+ * the submitted Data is successfully sent over USB.
257+ *
258+ * @param Buf: Buffer of data to be received
259+ * @param Len: Number of data received (in bytes)
260+ * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
261+ */
262+ static int8_t CDC_Itf_TransmitCplt (uint8_t * Buf , uint32_t * Len , uint8_t epnum ) {
263+ usb_out_busy = false;
264+ // process_poll(&pbdrv_usb_process);
265+ return USBD_OK ;
266+ }
267+
268+ static USBD_CDC_ItfTypeDef USBD_CDC_fops = {
269+ .Init = CDC_Itf_Init ,
270+ .DeInit = CDC_Itf_DeInit ,
271+ .Control = CDC_Itf_Control ,
272+ .Receive = CDC_Itf_Receive ,
273+ .TransmitCplt = CDC_Itf_TransmitCplt ,
274+ };
275+
124276// Common USB driver implementation.
125277
126278void pbdrv_usb_init (void ) {
127279 // Link the driver data structures
128280 husbd .pData = & hpcd ;
129281 hpcd .pData = & husbd ;
130282
131- USBD_Init (& husbd , NULL , 0 );
283+ USBD_Init (& husbd , & VCP_Desc , 0 );
284+ USBD_RegisterClass (& husbd , USBD_CDC_CLASS );
285+ USBD_CDC_RegisterInterface (& husbd , & USBD_CDC_fops );
286+ USBD_Start (& husbd );
287+
132288 process_start (& pbdrv_usb_process );
133289
134290 // VBUS may already be active
@@ -139,13 +295,75 @@ pbdrv_usb_bcd_t pbdrv_usb_get_bcd(void) {
139295 return pbdrv_usb_bcd ;
140296}
141297
298+ static void pbdrv_usb_serial_transmit (void ) {
299+ static uint32_t tx_size = 0 ;
300+
301+ if (usb_out_busy ) {
302+ return ;
303+ }
304+
305+ // If tx_size > 0 it means we have a pending retry, otherwise we get as
306+ // much as we can from the stdout buffer.
307+ if (tx_size == 0 ) {
308+ tx_size = ringbuf_elements (& stdout_buf );
309+ if (tx_size > PBIO_ARRAY_SIZE (usb_out_buf )) {
310+ tx_size = PBIO_ARRAY_SIZE (usb_out_buf );
311+ }
312+ if (tx_size > 0 ) {
313+ for (uint32_t i = 0 ; i < tx_size ; i ++ ) {
314+ usb_out_buf [i ] = ringbuf_get (& stdout_buf );
315+ }
316+ }
317+ }
318+
319+ if (tx_size > 0 ) {
320+ USBD_CDC_SetTxBuffer (& husbd , usb_out_buf , tx_size );
321+ if (USBD_CDC_TransmitPacket (& husbd ) == USBD_OK ) {
322+ usb_out_busy = true;
323+ tx_size = 0 ;
324+ }
325+ }
326+ }
327+
328+ static void pbdrv_usb_serial_receive (void ) {
329+ if (usb_in_busy ) {
330+ return ;
331+ }
332+
333+ if (USBD_CDC_ReceivePacket (& husbd ) == USBD_OK ) {
334+ usb_in_busy = true;
335+ }
336+ }
337+
338+ // TODO: need to multiplex stdin/stdout with Bluetooth or remove USB serial
339+
340+ pbio_error_t pbsys_usb_put_char (uint8_t c ) {
341+ if (!usb_connected ) {
342+ // don't lock up print() when USB not connected - data is discarded
343+ return PBIO_SUCCESS ;
344+ }
345+ if (ringbuf_put (& stdout_buf , c ) == 0 ) {
346+ return PBIO_SUCCESS ;
347+ }
348+ return PBIO_SUCCESS ;
349+ }
350+
351+ pbio_error_t pbsys_usb_get_char (uint8_t * c ) {
352+ if (ringbuf_elements (& stdin_buf ) == 0 ) {
353+ return PBIO_ERROR_AGAIN ;
354+ }
355+ * c = ringbuf_get (& stdin_buf );
356+ return PBIO_SUCCESS ;
357+ }
358+
142359// Event loop
143360
144361PROCESS_THREAD (pbdrv_usb_process , ev , data ) {
145362 static struct pt bcd_pt ;
146363 static PBIO_ONESHOT (no_vbus_oneshot );
147364 static PBIO_ONESHOT (bcd_oneshot );
148365 static bool bcd_busy ;
366+ static struct etimer timer ;
149367
150368 PROCESS_POLLHANDLER ({
151369 if (!bcd_busy && pbio_oneshot (!vbus_active , & no_vbus_oneshot )) {
@@ -164,12 +382,20 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) {
164382
165383 PROCESS_BEGIN ();
166384
385+ etimer_set (& timer , 5 );
386+
167387 for (;;) {
168- PROCESS_WAIT_EVENT ( );
388+ PROCESS_WAIT_EVENT_UNTIL ( ev == PROCESS_EVENT_POLL || ( ev == PROCESS_EVENT_TIMER && etimer_expired ( & timer )) );
169389
170390 if (bcd_busy ) {
171391 bcd_busy = PT_SCHEDULE (pbdrv_usb_stm32_bcd_detect (& bcd_pt ));
172392 }
393+
394+ if (etimer_expired (& timer )) {
395+ etimer_reset (& timer );
396+ pbdrv_usb_serial_transmit ();
397+ pbdrv_usb_serial_receive ();
398+ }
173399 }
174400
175401 PROCESS_END ();
0 commit comments