/** ****************************************************************************** * @addtogroup PIOS PIOS Core hardware abstraction layer * @{ * @addtogroup PIOS_USB USB Setup Functions * @brief PIOS USB device implementation * @{ * * @file pios_usb.c * @author The LibrePilot Project, http://www.librepilot.org (C) 2017. * Tau Labs, http://taulabs.org, Copyright (C) 2012-2013 * The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. * @brief USB device functions (STM32 dependent code) * @see The GNU Public License (GPL) Version 3 * *****************************************************************************/ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Project Includes */ #include "pios.h" #if defined(PIOS_INCLUDE_USB) #include "usb_lib.h" #include "pios_usb_board_data.h" #include "pios_usb.h" #include "pios_usb_priv.h" /* Rx/Tx status */ static bool transfer_possible = false; #ifdef PIOS_INCLUDE_FREERTOS struct { void (*callback)(bool connected, uint32_t context); uint32_t context; } connectionState_cb_list[3]; #endif /* USB activity detection */ static volatile bool sof_seen_since_reset = false; enum pios_usb_dev_magic { PIOS_USB_DEV_MAGIC = 0x17365904, }; struct pios_usb_dev { enum pios_usb_dev_magic magic; const struct pios_usb_cfg *cfg; }; #ifdef PIOS_INCLUDE_FREERTOS static void raiseConnectionStateCallback(bool connected); #endif /** * @brief Validate the usb device structure * @returns 0 if valid device or -1 otherwise */ static int32_t PIOS_USB_validate(struct pios_usb_dev *usb_dev) { if (usb_dev == NULL) { return -1; } if (usb_dev->magic != PIOS_USB_DEV_MAGIC) { return -1; } return 0; } #ifdef PIOS_INCLUDE_FREERTOS static struct pios_usb_dev *PIOS_USB_alloc(void) { struct pios_usb_dev *usb_dev; usb_dev = (struct pios_usb_dev *)pios_malloc(sizeof(*usb_dev)); if (!usb_dev) { return NULL; } usb_dev->magic = PIOS_USB_DEV_MAGIC; return usb_dev; } #else static struct pios_usb_dev pios_usb_devs[PIOS_USB_MAX_DEVS]; static uint8_t pios_usb_num_devs; static struct pios_usb_dev *PIOS_USB_alloc(void) { struct pios_usb_dev *usb_dev; if (pios_usb_num_devs >= PIOS_USB_MAX_DEVS) { return NULL; } usb_dev = &pios_usb_devs[pios_usb_num_devs++]; usb_dev->magic = PIOS_USB_DEV_MAGIC; return usb_dev; } #endif /* ifdef PIOS_INCLUDE_FREERTOS */ /** * Initialises USB COM layer * \return < 0 if initialisation failed * \note Applications shouldn't call this function directly, instead please use \ref PIOS_COM layer functions */ static uint32_t pios_usb_com_id; int32_t PIOS_USB_Init(uint32_t *usb_id, const struct pios_usb_cfg *cfg) { PIOS_Assert(usb_id); PIOS_Assert(cfg); struct pios_usb_dev *usb_dev; usb_dev = (struct pios_usb_dev *)PIOS_USB_alloc(); if (!usb_dev) { goto out_fail; } /* Bind the configuration to the device instance */ usb_dev->cfg = cfg; PIOS_USB_Reenumerate(); /* * This is a horrible hack to make this available to * the interrupt callbacks. This should go away ASAP. */ pios_usb_com_id = (uintptr_t)usb_dev; /* Enable the USB Interrupts */ NVIC_Init((NVIC_InitTypeDef *)&usb_dev->cfg->irq.init); /* Configure USB D-/D+ (DM/DP) pins */ GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_PinAFConfig(GPIOA, GPIO_PinSource11, GPIO_AF_14); GPIO_PinAFConfig(GPIOA, GPIO_PinSource12, GPIO_AF_14); /* Configure VBUS sense pin */ if (usb_dev->cfg->vsense.gpio) { GPIO_Init(usb_dev->cfg->vsense.gpio, (GPIO_InitTypeDef *)&usb_dev->cfg->vsense.init); } /* Select USBCLK source */ RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5); /* Enable the USB clock */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE); USB_Init(); USB_SIL_Init(); *usb_id = (uintptr_t)usb_dev; return 0; /* No error */ out_fail: return -1; } /** * This function is called by the USB driver on cable connection/disconnection * \param[in] connected connection status (1 if connected) * \return < 0 on errors * \note Applications shouldn't call this function directly, instead please use \ref PIOS_COM layer functions */ int32_t PIOS_USB_ChangeConnectionState(bool Connected) { // In all cases: re-initialise USB HID driver if (Connected) { transfer_possible = true; // TODO: Check SetEPRxValid(ENDP1); #if defined(USB_LED_ON) USB_LED_ON; // turn the USB led on #endif } else { // Cable disconnected: disable transfers transfer_possible = false; #if defined(USB_LED_OFF) USB_LED_OFF; // turn the USB led off #endif } #ifdef PIOS_INCLUDE_FREERTOS raiseConnectionStateCallback(Connected); #endif return 0; } int32_t PIOS_USB_Reenumerate() { /* Force USB reset and power-down (this will also release the USB pins for direct GPIO control) */ _SetCNTR(CNTR_FRES | CNTR_PDWN); /* Using a "dirty" method to force a re-enumeration: */ /* Force DPM (Pin PA12) low for ca. 10 mS before USB Tranceiver will be enabled */ /* This overrules the external Pull-Up at PA12, and at least Windows & MacOS will enumerate again */ GPIO_InitTypeDef GPIO_InitStructure; GPIO_StructInit(&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); PIOS_DELAY_WaitmS(50); /* Release power-down, still hold reset */ _SetCNTR(CNTR_PDWN); PIOS_DELAY_WaituS(5); /* CNTR_FRES = 0 */ _SetCNTR(0); /* Clear pending interrupts */ _SetISTR(0); /* set back to alternate function */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_Init(GPIOA, &GPIO_InitStructure); /* Configure USB clock */ /* USBCLK = PLLCLK / 1.5 */ RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5); /* Enable USB clock */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE); return 0; } bool PIOS_USB_CableConnected(__attribute__((unused)) uint8_t id) { struct pios_usb_dev *usb_dev = (struct pios_usb_dev *)pios_usb_com_id; if (PIOS_USB_validate(usb_dev) != 0) { return false; } // If board is configured to have a VSENSE pin, use that if (usb_dev->cfg->vsense.gpio != NULL) { return GPIO_ReadInputDataBit(usb_dev->cfg->vsense.gpio, usb_dev->cfg->vsense.init.GPIO_Pin) == Bit_SET; } return sof_seen_since_reset; } /** * This function returns the connection status of the USB HID interface * \return 1: interface available * \return 0: interface not available * \note Applications shouldn't call this function directly, instead please use \ref PIOS_COM layer functions */ bool PIOS_USB_CheckAvailable(uint32_t id) { struct pios_usb_dev *usb_dev = (struct pios_usb_dev *)pios_usb_com_id; if (PIOS_USB_validate(usb_dev) != 0) { return false; } return PIOS_USB_CableConnected(id) && transfer_possible; } void SOF_Callback(void) { sof_seen_since_reset = true; } void SUSP_Callback(void) { sof_seen_since_reset = false; } #ifdef PIOS_INCLUDE_FREERTOS void PIOS_USB_RegisterConnectionStateCallback(void (*connectionStateCallback)(bool connected, uint32_t context), uint32_t context) { PIOS_Assert(connectionStateCallback); for (uint32_t i = 0; i < NELEMENTS(connectionState_cb_list); i++) { if (connectionState_cb_list[i].callback == NULL) { connectionState_cb_list[i].callback = connectionStateCallback; connectionState_cb_list[i].context = context; return; } } PIOS_Assert(0); } static void raiseConnectionStateCallback(bool connected) { uint32_t i = 0; while (i < NELEMENTS(connectionState_cb_list) && connectionState_cb_list[i].callback != NULL) { connectionState_cb_list[i].callback(connected, connectionState_cb_list[i].context); i++; } } #else /* PIOS_INCLUDE_FREERTOS */ void PIOS_USB_RegisterConnectionStateCallback(__attribute__((unused)) void (*connectionStateCallback)(bool connected, uint32_t context), __attribute__((unused)) uint32_t context) {} #endif /* PIOS_INCLUDE_FREERTOS */ #endif /* if defined(PIOS_INCLUDE_USB) */ /** * @} * @} */