2 ******************************************************************************
3 * @addtogroup PIOS PIOS Core hardware abstraction layer
5 * @addtogroup PIOS_USB USB Setup Functions
6 * @brief PIOS USB device implementation
10 * @author The LibrePilot Project, http://www.librepilot.org (C) 2017.
11 * Tau Labs, http://taulabs.org, Copyright (C) 2012-2013
12 * The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
13 * @brief USB device functions (STM32 dependent code)
14 * @see The GNU Public License (GPL) Version 3
16 *****************************************************************************/
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 3 of the License, or
21 * (at your option) any later version.
23 * This program is distributed in the hope that it will be useful, but
24 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
25 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
28 * You should have received a copy of the GNU General Public License along
29 * with this program; if not, write to the Free Software Foundation, Inc.,
30 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33 /* Project Includes */
36 #if defined(PIOS_INCLUDE_USB)
39 #include "pios_usb_board_data.h"
42 #include "pios_usb_priv.h"
46 static bool transfer_possible
= false;
48 #ifdef PIOS_INCLUDE_FREERTOS
50 void (*callback
)(bool connected
, uint32_t context
);
52 } connectionState_cb_list
[3];
55 /* USB activity detection */
56 static volatile bool sof_seen_since_reset
= false;
58 enum pios_usb_dev_magic
{
59 PIOS_USB_DEV_MAGIC
= 0x17365904,
63 enum pios_usb_dev_magic magic
;
64 const struct pios_usb_cfg
*cfg
;
66 #ifdef PIOS_INCLUDE_FREERTOS
67 static void raiseConnectionStateCallback(bool connected
);
71 * @brief Validate the usb device structure
72 * @returns 0 if valid device or -1 otherwise
74 static int32_t PIOS_USB_validate(struct pios_usb_dev
*usb_dev
)
76 if (usb_dev
== NULL
) {
80 if (usb_dev
->magic
!= PIOS_USB_DEV_MAGIC
) {
87 #ifdef PIOS_INCLUDE_FREERTOS
88 static struct pios_usb_dev
*PIOS_USB_alloc(void)
90 struct pios_usb_dev
*usb_dev
;
92 usb_dev
= (struct pios_usb_dev
*)pios_malloc(sizeof(*usb_dev
));
97 usb_dev
->magic
= PIOS_USB_DEV_MAGIC
;
101 static struct pios_usb_dev pios_usb_devs
[PIOS_USB_MAX_DEVS
];
102 static uint8_t pios_usb_num_devs
;
103 static struct pios_usb_dev
*PIOS_USB_alloc(void)
105 struct pios_usb_dev
*usb_dev
;
107 if (pios_usb_num_devs
>= PIOS_USB_MAX_DEVS
) {
111 usb_dev
= &pios_usb_devs
[pios_usb_num_devs
++];
112 usb_dev
->magic
= PIOS_USB_DEV_MAGIC
;
116 #endif /* ifdef PIOS_INCLUDE_FREERTOS */
119 * Initialises USB COM layer
120 * \return < 0 if initialisation failed
121 * \note Applications shouldn't call this function directly, instead please use \ref PIOS_COM layer functions
123 static uint32_t pios_usb_com_id
;
124 int32_t PIOS_USB_Init(uint32_t *usb_id
, const struct pios_usb_cfg
*cfg
)
129 struct pios_usb_dev
*usb_dev
;
131 usb_dev
= (struct pios_usb_dev
*)PIOS_USB_alloc();
136 /* Bind the configuration to the device instance */
139 PIOS_USB_Reenumerate();
142 * This is a horrible hack to make this available to
143 * the interrupt callbacks. This should go away ASAP.
145 pios_usb_com_id
= (uintptr_t)usb_dev
;
147 /* Enable the USB Interrupts */
148 NVIC_Init((NVIC_InitTypeDef
*)&usb_dev
->cfg
->irq
.init
);
150 /* Configure USB D-/D+ (DM/DP) pins */
151 GPIO_InitTypeDef GPIO_InitStructure
;
152 GPIO_InitStructure
.GPIO_Pin
= GPIO_Pin_11
| GPIO_Pin_12
;
153 GPIO_InitStructure
.GPIO_Speed
= GPIO_Speed_50MHz
;
154 GPIO_InitStructure
.GPIO_Mode
= GPIO_Mode_AF
;
155 GPIO_InitStructure
.GPIO_OType
= GPIO_OType_PP
;
156 GPIO_InitStructure
.GPIO_PuPd
= GPIO_PuPd_NOPULL
;
157 GPIO_Init(GPIOA
, &GPIO_InitStructure
);
159 GPIO_PinAFConfig(GPIOA
, GPIO_PinSource11
, GPIO_AF_14
);
160 GPIO_PinAFConfig(GPIOA
, GPIO_PinSource12
, GPIO_AF_14
);
162 /* Configure VBUS sense pin */
163 if (usb_dev
->cfg
->vsense
.gpio
) {
164 GPIO_Init(usb_dev
->cfg
->vsense
.gpio
, (GPIO_InitTypeDef
*)&usb_dev
->cfg
->vsense
.init
);
167 /* Select USBCLK source */
168 RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5
);
169 /* Enable the USB clock */
170 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB
, ENABLE
);
175 *usb_id
= (uintptr_t)usb_dev
;
177 return 0; /* No error */
184 * This function is called by the USB driver on cable connection/disconnection
185 * \param[in] connected connection status (1 if connected)
186 * \return < 0 on errors
187 * \note Applications shouldn't call this function directly, instead please use \ref PIOS_COM layer functions
189 int32_t PIOS_USB_ChangeConnectionState(bool Connected
)
191 // In all cases: re-initialise USB HID driver
193 transfer_possible
= true;
195 // TODO: Check SetEPRxValid(ENDP1);
197 #if defined(USB_LED_ON)
198 USB_LED_ON
; // turn the USB led on
201 // Cable disconnected: disable transfers
202 transfer_possible
= false;
204 #if defined(USB_LED_OFF)
205 USB_LED_OFF
; // turn the USB led off
209 #ifdef PIOS_INCLUDE_FREERTOS
210 raiseConnectionStateCallback(Connected
);
216 int32_t PIOS_USB_Reenumerate()
218 /* Force USB reset and power-down (this will also release the USB pins for direct GPIO control) */
219 _SetCNTR(CNTR_FRES
| CNTR_PDWN
);
221 /* Using a "dirty" method to force a re-enumeration: */
222 /* Force DPM (Pin PA12) low for ca. 10 mS before USB Tranceiver will be enabled */
223 /* This overrules the external Pull-Up at PA12, and at least Windows & MacOS will enumerate again */
224 GPIO_InitTypeDef GPIO_InitStructure
;
225 GPIO_StructInit(&GPIO_InitStructure
);
226 GPIO_InitStructure
.GPIO_Pin
= GPIO_Pin_12
;
227 GPIO_InitStructure
.GPIO_Mode
= GPIO_Mode_OUT
;
228 GPIO_InitStructure
.GPIO_OType
= GPIO_OType_PP
;
229 GPIO_InitStructure
.GPIO_Speed
= GPIO_Speed_50MHz
;
230 GPIO_Init(GPIOA
, &GPIO_InitStructure
);
232 PIOS_DELAY_WaitmS(50);
234 /* Release power-down, still hold reset */
236 PIOS_DELAY_WaituS(5);
241 /* Clear pending interrupts */
244 /* set back to alternate function */
245 GPIO_InitStructure
.GPIO_Mode
= GPIO_Mode_AF
;
246 GPIO_Init(GPIOA
, &GPIO_InitStructure
);
248 /* Configure USB clock */
249 /* USBCLK = PLLCLK / 1.5 */
250 RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5
);
251 /* Enable USB clock */
252 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB
, ENABLE
);
257 bool PIOS_USB_CableConnected(__attribute__((unused
)) uint8_t id
)
259 struct pios_usb_dev
*usb_dev
= (struct pios_usb_dev
*)pios_usb_com_id
;
261 if (PIOS_USB_validate(usb_dev
) != 0) {
265 // If board is configured to have a VSENSE pin, use that
266 if (usb_dev
->cfg
->vsense
.gpio
!= NULL
) {
267 return GPIO_ReadInputDataBit(usb_dev
->cfg
->vsense
.gpio
, usb_dev
->cfg
->vsense
.init
.GPIO_Pin
) == Bit_SET
;
270 return sof_seen_since_reset
;
274 * This function returns the connection status of the USB HID interface
275 * \return 1: interface available
276 * \return 0: interface not available
277 * \note Applications shouldn't call this function directly, instead please use \ref PIOS_COM layer functions
279 bool PIOS_USB_CheckAvailable(uint32_t id
)
281 struct pios_usb_dev
*usb_dev
= (struct pios_usb_dev
*)pios_usb_com_id
;
283 if (PIOS_USB_validate(usb_dev
) != 0) {
287 return PIOS_USB_CableConnected(id
) && transfer_possible
;
290 void SOF_Callback(void)
292 sof_seen_since_reset
= true;
295 void SUSP_Callback(void)
297 sof_seen_since_reset
= false;
300 #ifdef PIOS_INCLUDE_FREERTOS
301 void PIOS_USB_RegisterConnectionStateCallback(void (*connectionStateCallback
)(bool connected
, uint32_t context
), uint32_t context
)
303 PIOS_Assert(connectionStateCallback
);
305 for (uint32_t i
= 0; i
< NELEMENTS(connectionState_cb_list
); i
++) {
306 if (connectionState_cb_list
[i
].callback
== NULL
) {
307 connectionState_cb_list
[i
].callback
= connectionStateCallback
;
308 connectionState_cb_list
[i
].context
= context
;
316 static void raiseConnectionStateCallback(bool connected
)
320 while (i
< NELEMENTS(connectionState_cb_list
) && connectionState_cb_list
[i
].callback
!= NULL
) {
321 connectionState_cb_list
[i
].callback(connected
, connectionState_cb_list
[i
].context
);
325 #else /* PIOS_INCLUDE_FREERTOS */
326 void PIOS_USB_RegisterConnectionStateCallback(__attribute__((unused
)) void (*connectionStateCallback
)(bool connected
, uint32_t context
), __attribute__((unused
)) uint32_t context
)
328 #endif /* PIOS_INCLUDE_FREERTOS */
330 #endif /* if defined(PIOS_INCLUDE_USB) */