2 ******************************************************************************
3 * @addtogroup PIOS PIOS Core hardware abstraction layer
5 * @addtogroup PIOS_USBHOOK USB glue code
6 * @brief Glue between PiOS and STM32 libs
10 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
11 * @brief Glue between PiOS and STM32 libs
12 * @see The GNU Public License (GPL) Version 3
14 *****************************************************************************/
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 3 of the License, or
19 * (at your option) any later version.
21 * This program is distributed in the hope that it will be useful, but
22 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
23 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
26 * You should have received a copy of the GNU General Public License along
27 * with this program; if not, write to the Free Software Foundation, Inc.,
28 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33 #ifdef PIOS_INCLUDE_USB
35 #include "pios_usb.h" /* PIOS_USB_* */
36 #include "pios_usbhook.h"
37 #include "pios_usb_defs.h" /* struct usb_* */
38 #include "pios_usb_cdc_priv.h" /* PIOS_USB_CDC_* */
39 #include "pios_usb_board_data.h" /* PIOS_USB_BOARD_* */
42 /* STM32 USB Library Definitions */
43 #include "usb_core.h" /* USBD_Class_cb_TypeDef */
44 #include "usbd_core.h" /* USBD_Init USBD_OK*/
45 #include "usbd_ioreq.h" /* USBD_CtlPrepareRx, USBD_CtlSendData */
46 #include "usbd_req.h" /* USBD_CtlError */
47 #include "usb_dcd_int.h" /* USBD_OTG_ISR_Handler */
49 static void reconnect(void);
54 static struct pios_usbhook_descriptor Device_Descriptor
;
56 void PIOS_USBHOOK_RegisterDevice(const uint8_t *desc
, uint16_t length
)
58 Device_Descriptor
.descriptor
= desc
;
59 Device_Descriptor
.length
= length
;
62 static struct pios_usbhook_descriptor String_Descriptor
[4];
64 void PIOS_USBHOOK_RegisterString(enum usb_string_desc string_id
, const uint8_t *desc
, uint16_t desc_size
)
66 if (string_id
< NELEMENTS(String_Descriptor
)) {
67 String_Descriptor
[string_id
].descriptor
= desc
;
68 String_Descriptor
[string_id
].length
= desc_size
;
72 static struct pios_usbhook_descriptor Config_Descriptor
;
74 void PIOS_USBHOOK_RegisterConfig(__attribute__((unused
)) uint8_t config_id
, const uint8_t *desc
, uint16_t desc_size
)
76 Config_Descriptor
.descriptor
= desc
;
77 Config_Descriptor
.length
= desc_size
;
80 static USB_OTG_CORE_HANDLE pios_usb_otg_core_handle
;
81 static USBD_Class_cb_TypeDef class_callbacks
;
82 static USBD_DEVICE device_callbacks
;
83 static USBD_Usr_cb_TypeDef user_callbacks
;
85 void PIOS_USBHOOK_Activate(void)
87 PIOS_USB_RegisterDisconnectionCallback(&reconnect
);
89 USBD_Init(&pios_usb_otg_core_handle
,
96 void PIOS_USBHOOK_Deactivate(void)
98 DCD_DevDisconnect(&pios_usb_otg_core_handle
);
99 USBD_DeInit(&pios_usb_otg_core_handle
);
100 USB_OTG_StopDevice(&pios_usb_otg_core_handle
);
103 void OTG_FS_IRQHandler(void)
105 if (!USBD_OTG_ISR_Handler(&pios_usb_otg_core_handle
)) {
106 /* spurious interrupt, disable IRQ */
110 struct usb_if_entry
{
111 struct pios_usb_ifops
*ifops
;
114 static struct usb_if_entry usb_if_table
[3];
115 void PIOS_USBHOOK_RegisterIfOps(uint8_t ifnum
, struct pios_usb_ifops
*ifops
, uint32_t context
)
117 PIOS_Assert(ifnum
< NELEMENTS(usb_if_table
));
120 usb_if_table
[ifnum
].ifops
= ifops
;
121 usb_if_table
[ifnum
].context
= context
;
124 struct usb_ep_entry
{
125 pios_usbhook_epcb cb
;
129 static struct usb_ep_entry usb_epin_table
[6];
130 void PIOS_USBHOOK_RegisterEpInCallback(uint8_t epnum
, uint16_t max_len
, pios_usbhook_epcb cb
, uint32_t context
)
132 PIOS_Assert(epnum
< NELEMENTS(usb_epin_table
));
135 usb_epin_table
[epnum
].cb
= cb
;
136 usb_epin_table
[epnum
].context
= context
;
137 usb_epin_table
[epnum
].max_len
= max_len
;
139 DCD_EP_Open(&pios_usb_otg_core_handle
,
144 * FIXME do not hardcode endpoint type
148 extern void PIOS_USBHOOK_DeRegisterEpInCallback(uint8_t epnum
)
150 PIOS_Assert(epnum
< NELEMENTS(usb_epin_table
));
152 usb_epin_table
[epnum
].cb
= NULL
;
154 DCD_EP_Close(&pios_usb_otg_core_handle
, epnum
| 0x80);
157 static struct usb_ep_entry usb_epout_table
[6];
158 void PIOS_USBHOOK_RegisterEpOutCallback(uint8_t epnum
, uint16_t max_len
, pios_usbhook_epcb cb
, uint32_t context
)
160 PIOS_Assert(epnum
< NELEMENTS(usb_epout_table
));
163 usb_epout_table
[epnum
].cb
= cb
;
164 usb_epout_table
[epnum
].context
= context
;
165 usb_epout_table
[epnum
].max_len
= max_len
;
167 DCD_EP_Open(&pios_usb_otg_core_handle
,
172 * FIXME do not hardcode endpoint type
176 * Make sure we refuse OUT transactions until we explicitly
177 * connect a receive buffer with PIOS_USBHOOK_EndpointRx().
179 * Without this, the ST USB code will receive on this endpoint
180 * and blindly write the data to a NULL pointer which will
181 * have the side effect of placing the internal flash into an
182 * errored state. Address 0x0000_0000 is aliased into internal
183 * flash via the "Section 2.4 Boot configuration" BOOT0/1 pins.
185 DCD_SetEPStatus(&pios_usb_otg_core_handle
,
190 extern void PIOS_USBHOOK_DeRegisterEpOutCallback(uint8_t epnum
)
192 PIOS_Assert(epnum
< NELEMENTS(usb_epout_table
));
194 usb_epout_table
[epnum
].cb
= NULL
;
196 DCD_EP_Close(&pios_usb_otg_core_handle
, epnum
);
199 void PIOS_USBHOOK_CtrlTx(const uint8_t *buf
, uint16_t len
)
201 USBD_CtlSendData(&pios_usb_otg_core_handle
, buf
, len
);
204 void PIOS_USBHOOK_CtrlRx(uint8_t *buf
, uint16_t len
)
206 USBD_CtlPrepareRx(&pios_usb_otg_core_handle
, buf
, len
);
209 void PIOS_USBHOOK_EndpointTx(uint8_t epnum
, const uint8_t *buf
, uint16_t len
)
211 if (pios_usb_otg_core_handle
.dev
.device_status
== USB_OTG_CONFIGURED
) {
212 DCD_EP_Tx(&pios_usb_otg_core_handle
, epnum
, buf
, len
);
216 void PIOS_USBHOOK_EndpointRx(uint8_t epnum
, uint8_t *buf
, uint16_t len
)
218 DCD_EP_PrepareRx(&pios_usb_otg_core_handle
, epnum
, buf
, len
);
221 uint32_t PIOS_USBHOOK_EndpointGetStatus(uint8_t epnum
)
223 return DCD_GetEPStatus(&pios_usb_otg_core_handle
, epnum
);
227 * Device level hooks into STM USB library
230 static const uint8_t *PIOS_USBHOOK_DEV_GetDeviceDescriptor(__attribute__((unused
)) uint8_t speed
, uint16_t *length
)
232 *length
= Device_Descriptor
.length
;
233 return Device_Descriptor
.descriptor
;
236 static const uint8_t *PIOS_USBHOOK_DEV_GetLangIDStrDescriptor(__attribute__((unused
)) uint8_t speed
, uint16_t *length
)
238 *length
= String_Descriptor
[USB_STRING_DESC_LANG
].length
;
239 return String_Descriptor
[USB_STRING_DESC_LANG
].descriptor
;
242 static const uint8_t *PIOS_USBHOOK_DEV_GetManufacturerStrDescriptor(__attribute__((unused
)) uint8_t speed
, uint16_t *length
)
244 *length
= String_Descriptor
[USB_STRING_DESC_VENDOR
].length
;
245 return String_Descriptor
[USB_STRING_DESC_VENDOR
].descriptor
;
248 static const uint8_t *PIOS_USBHOOK_DEV_GetProductStrDescriptor(__attribute__((unused
)) uint8_t speed
, uint16_t *length
)
250 *length
= String_Descriptor
[USB_STRING_DESC_PRODUCT
].length
;
251 return String_Descriptor
[USB_STRING_DESC_PRODUCT
].descriptor
;
254 static const uint8_t *PIOS_USBHOOK_DEV_GetSerialStrDescriptor(__attribute__((unused
)) uint8_t speed
, uint16_t *length
)
256 *length
= String_Descriptor
[USB_STRING_DESC_SERIAL
].length
;
257 return String_Descriptor
[USB_STRING_DESC_SERIAL
].descriptor
;
260 static const uint8_t *PIOS_USBHOOK_DEV_GetConfigurationStrDescriptor(__attribute__((unused
)) uint8_t speed
, __attribute__((unused
)) uint16_t *length
)
265 static const uint8_t *PIOS_USBHOOK_DEV_GetInterfaceStrDescriptor(__attribute__((unused
)) uint8_t speed
, __attribute__((unused
)) uint16_t *length
)
270 static USBD_DEVICE device_callbacks
= {
271 .GetDeviceDescriptor
= PIOS_USBHOOK_DEV_GetDeviceDescriptor
,
272 .GetLangIDStrDescriptor
= PIOS_USBHOOK_DEV_GetLangIDStrDescriptor
,
273 .GetManufacturerStrDescriptor
= PIOS_USBHOOK_DEV_GetManufacturerStrDescriptor
,
274 .GetProductStrDescriptor
= PIOS_USBHOOK_DEV_GetProductStrDescriptor
,
275 .GetSerialStrDescriptor
= PIOS_USBHOOK_DEV_GetSerialStrDescriptor
,
276 .GetConfigurationStrDescriptor
= PIOS_USBHOOK_DEV_GetConfigurationStrDescriptor
,
277 .GetInterfaceStrDescriptor
= PIOS_USBHOOK_DEV_GetInterfaceStrDescriptor
,
280 static void PIOS_USBHOOK_USR_Init(void)
282 PIOS_USB_ChangeConnectionState(false);
283 // reconnect dev logically on init (previously a call to reconnect())
284 DCD_DevDisconnect(&pios_usb_otg_core_handle
);
285 DCD_DevConnect(&pios_usb_otg_core_handle
);
288 static void PIOS_USBHOOK_USR_DeviceReset(__attribute__((unused
)) uint8_t speed
)
290 PIOS_USB_ChangeConnectionState(false);
293 static void PIOS_USBHOOK_USR_DeviceConfigured(void)
295 PIOS_USB_ChangeConnectionState(true);
298 static void PIOS_USBHOOK_USR_DeviceSuspended(void)
303 static void PIOS_USBHOOK_USR_DeviceResumed(void)
308 static void PIOS_USBHOOK_USR_DeviceConnected(void)
313 static void PIOS_USBHOOK_USR_DeviceDisconnected(void)
315 PIOS_USB_ChangeConnectionState(false);
318 static USBD_Usr_cb_TypeDef user_callbacks
= {
319 .Init
= PIOS_USBHOOK_USR_Init
,
320 .DeviceReset
= PIOS_USBHOOK_USR_DeviceReset
,
321 .DeviceConfigured
= PIOS_USBHOOK_USR_DeviceConfigured
,
322 .DeviceSuspended
= PIOS_USBHOOK_USR_DeviceSuspended
,
323 .DeviceResumed
= PIOS_USBHOOK_USR_DeviceResumed
,
324 .DeviceConnected
= PIOS_USBHOOK_USR_DeviceConnected
,
325 .DeviceDisconnected
= PIOS_USBHOOK_USR_DeviceDisconnected
,
328 static uint8_t PIOS_USBHOOK_CLASS_Init(__attribute__((unused
)) void *pdev
, __attribute__((unused
)) uint8_t cfgidx
)
330 /* Call all of the registered init callbacks */
331 for (uint8_t i
= 0; i
< NELEMENTS(usb_if_table
); i
++) {
332 struct usb_if_entry
*usb_if
= &(usb_if_table
[i
]);
333 if (usb_if
->ifops
&& usb_if
->ifops
->init
) {
334 usb_if
->ifops
->init(usb_if
->context
);
340 static uint8_t PIOS_USBHOOK_CLASS_DeInit(__attribute__((unused
)) void *pdev
, __attribute__((unused
)) uint8_t cfgidx
)
342 /* Call all of the registered deinit callbacks */
343 for (uint8_t i
= 0; i
< NELEMENTS(usb_if_table
); i
++) {
344 struct usb_if_entry
*usb_if
= &(usb_if_table
[i
]);
345 if (usb_if
->ifops
&& usb_if
->ifops
->deinit
) {
346 usb_if
->ifops
->deinit(usb_if
->context
);
352 static struct usb_setup_request usb_ep0_active_req
;
353 static uint8_t PIOS_USBHOOK_CLASS_Setup(__attribute__((unused
)) void *pdev
, USB_SETUP_REQ
*req
)
355 switch (req
->bmRequest
& (USB_REQ_TYPE_MASK
| USB_REQ_RECIPIENT_MASK
)) {
356 case (USB_REQ_TYPE_STANDARD
| USB_REQ_RECIPIENT_INTERFACE
):
357 case (USB_REQ_TYPE_CLASS
| USB_REQ_RECIPIENT_INTERFACE
):
359 uint8_t ifnum
= LOBYTE(req
->wIndex
);
360 if ((ifnum
< NELEMENTS(usb_if_table
)) &&
361 (usb_if_table
[ifnum
].ifops
&& usb_if_table
[ifnum
].ifops
->setup
)) {
362 usb_if_table
[ifnum
].ifops
->setup(usb_if_table
[ifnum
].context
,
363 (struct usb_setup_request
*)req
);
364 if (!(req
->bmRequest
& 0x80) && req
->wLength
> 0) {
365 /* Request is a host-to-device data setup packet, keep track of the request details for the EP0_RxReady call */
366 usb_ep0_active_req
.bmRequestType
= req
->bmRequest
;
367 usb_ep0_active_req
.bRequest
= req
->bRequest
;
368 usb_ep0_active_req
.wValue
= req
->wValue
;
369 usb_ep0_active_req
.wIndex
= req
->wIndex
;
370 usb_ep0_active_req
.wLength
= req
->wLength
;
373 /* No Setup handler or Setup handler failed */
374 USBD_CtlError(&pios_usb_otg_core_handle
, req
);
379 /* Unhandled Setup */
380 USBD_CtlError(&pios_usb_otg_core_handle
, req
);
387 static uint8_t PIOS_USBHOOK_CLASS_EP0_TxSent(__attribute__((unused
)) void *pdev
)
392 static uint8_t PIOS_USBHOOK_CLASS_EP0_RxReady(__attribute__((unused
)) void *pdev
)
394 uint8_t ifnum
= LOBYTE(usb_ep0_active_req
.wIndex
);
396 if ((ifnum
< NELEMENTS(usb_if_table
)) &&
397 (usb_if_table
[ifnum
].ifops
&& usb_if_table
[ifnum
].ifops
->ctrl_data_out
)) {
398 usb_if_table
[ifnum
].ifops
->ctrl_data_out(usb_if_table
[ifnum
].context
,
399 &usb_ep0_active_req
);
405 static uint8_t PIOS_USBHOOK_CLASS_DataIn(void *pdev
, uint8_t epnum
)
407 /* Remove the direction bit so we can use this as an index */
408 uint8_t epnum_idx
= epnum
& 0x7F;
410 if ((epnum_idx
< NELEMENTS(usb_epin_table
)) && usb_epin_table
[epnum_idx
].cb
) {
411 struct usb_ep_entry
*ep
= &(usb_epin_table
[epnum_idx
]);
412 if (!ep
->cb(ep
->context
, epnum_idx
, ep
->max_len
)) {
413 /* NOTE: use real endpoint number including direction bit */
414 DCD_SetEPStatus(pdev
, epnum
, USB_OTG_EP_TX_NAK
);
421 static uint8_t PIOS_USBHOOK_CLASS_DataOut(void *pdev
, uint8_t epnum
)
423 /* Remove the direction bit so we can use this as an index */
424 uint8_t epnum_idx
= epnum
& 0x7F;
426 if ((epnum_idx
< NELEMENTS(usb_epout_table
)) && usb_epout_table
[epnum_idx
].cb
) {
427 struct usb_ep_entry
*ep
= &(usb_epout_table
[epnum_idx
]);
428 uint16_t len
= USBD_GetRxCount(pdev
, epnum
);
429 PIOS_Assert(ep
->max_len
>= len
);
430 if (!ep
->cb(ep
->context
, epnum_idx
, len
)) {
431 /* NOTE: use real endpoint number including direction bit */
432 DCD_SetEPStatus(pdev
, epnum
, USB_OTG_EP_RX_NAK
);
439 static uint8_t PIOS_USBHOOK_CLASS_SOF(__attribute__((unused
)) void *pdev
)
444 static uint8_t PIOS_USBHOOK_CLASS_IsoINIncomplete(__attribute__((unused
)) void *pdev
)
449 static uint8_t PIOS_USBHOOK_CLASS_IsoOUTIncomplete(__attribute__((unused
)) void *pdev
)
454 static const uint8_t *PIOS_USBHOOK_CLASS_GetConfigDescriptor(__attribute__((unused
)) uint8_t speed
, uint16_t *length
)
456 *length
= Config_Descriptor
.length
;
457 return Config_Descriptor
.descriptor
;
460 #ifdef USB_OTG_HS_CORE
461 static const uint8_t *PIOS_USBHOOK_CLASS_GetOtherConfigDescriptor(uint8_t speed
, uint16_t *length
)
463 return PIOS_USBHOOK_CLASS_GetConfigDescriptor(speed
, length
);
465 #endif /* USB_OTG_HS_CORE */
467 #ifdef USB_SUPPORT_USER_STRING_DESC
468 static const uint8_t *PIOS_USBHOOK_CLASS_GetUsrStrDescriptor(uint8_t speed
, uint8_t index
, uint16_t *length
)
472 #endif /* USB_SUPPORT_USER_STRING_DESC */
474 static USBD_Class_cb_TypeDef class_callbacks
= {
475 .Init
= PIOS_USBHOOK_CLASS_Init
,
476 .DeInit
= PIOS_USBHOOK_CLASS_DeInit
,
477 .Setup
= PIOS_USBHOOK_CLASS_Setup
,
478 .EP0_TxSent
= PIOS_USBHOOK_CLASS_EP0_TxSent
,
479 .EP0_RxReady
= PIOS_USBHOOK_CLASS_EP0_RxReady
,
480 .DataIn
= PIOS_USBHOOK_CLASS_DataIn
,
481 .DataOut
= PIOS_USBHOOK_CLASS_DataOut
,
482 .SOF
= PIOS_USBHOOK_CLASS_SOF
,
483 .IsoINIncomplete
= PIOS_USBHOOK_CLASS_IsoINIncomplete
,
484 .IsoOUTIncomplete
= PIOS_USBHOOK_CLASS_IsoOUTIncomplete
,
485 .GetConfigDescriptor
= PIOS_USBHOOK_CLASS_GetConfigDescriptor
,
486 #ifdef USB_OTG_HS_CORE
487 .GetOtherConfigDescriptor
= PIOS_USBHOOK_CLASS_GetOtherConfigDescriptor
,
488 #endif /* USB_OTG_HS_CORE */
489 #ifdef USB_SUPPORT_USER_STRING_DESC
490 .GetUsrStrDescriptor
= PIOS_USBHOOK_CLASS_GetUsrStrDescriptor
,
491 #endif /* USB_SUPPORT_USER_STRING_DESC */
494 static void reconnect(void)
496 static volatile bool in_reconnect
= false;
498 /* Force a complete device reset. This can trigger a call to reconnect() so prevent recursion */
500 in_reconnect
= true; // save since volatile and STM32F4 is single core
501 // disable USB device
502 DCD_DevDisconnect(&pios_usb_otg_core_handle
);
503 USBD_DeInit(&pios_usb_otg_core_handle
);
504 USB_OTG_StopDevice(&pios_usb_otg_core_handle
);
506 USBD_Init(&pios_usb_otg_core_handle
,
512 in_reconnect
= false;
516 #endif /* PIOS_INCLUDE_USB */