1
0
mirror of https://bitbucket.org/librepilot/librepilot.git synced 2024-12-02 10:24:11 +01:00
LibrePilot/flight/PiOS/STM32F10x/pios_usb.c
stac 6b2760da1a USB HID: basic COM functionality over USB HID
This fixes the COM interface over USB HID to the point where
it can establish and maintain solid communications with the UAVObject
Browser in the GCS.

Tested only on Linux.  The USB HID interface is still disabled for now
until it is tested successfully by a wider group.

Edit telemetry.c and set ALLOW_HID_TELEMETRY to 1 to enable telemetry
over the USB HID interface and report your results in the forum.

git-svn-id: svn://svn.openpilot.org/OpenPilot/trunk@656 ebee16cc-31ac-478f-84a7-5cbb03baadba
2010-05-24 16:33:40 +00:00

580 lines
18 KiB
C

/**
******************************************************************************
*
* @file pios_usb.c
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
* Parts by Thorsten Klose (tk@midibox.org)
* @brief USB functions
* @see The GNU Public License (GPL) Version 3
* @defgroup PIOS_USB USB Functions
* @{
*
*****************************************************************************/
/*
* 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)
/* Local definitions */
#define DSCR_DEVICE 1 /* Descriptor type: Device */
#define DSCR_CONFIG 2 /* Descriptor type: Configuration */
#define DSCR_STRING 3 /* Descriptor type: String */
#define DSCR_INTRFC 4 /* Descriptor type: Interface */
#define DSCR_ENDPNT 5 /* Descriptor type: Endpoint */
#define CS_INTERFACE 0x24 /* Class-specific type: Interface */
#define CS_ENDPOINT 0x25 /* Class-specific type: Endpoint */
/* ISTR events */
/* mask defining which events has to be handled by the device application software */
#define IMR_MSK (CNTR_RESETM | CNTR_SOFM | CNTR_CTRM)
/* Local types */
typedef enum _DEVICE_STATE {
UNCONNECTED,
ATTACHED,
POWERED,
SUSPENDED,
ADDRESSED,
CONFIGURED
} DEVICE_STATE;
/* Global Variables used by STM32 USB Driver */
/* (unfortunately no unique names are used...) */
/* Points to the DEVICE_INFO/DEVICE_PROP_USER_STANDARD_REQUESTS structure of current device */
/* The purpose of this register is to speed up the execution */
DEVICE_INFO *pInformation;
DEVICE Device_Table;
DEVICE_PROP *pProperty;
USER_STANDARD_REQUESTS *pUser_Standard_Requests;
/* Stored in RAM, vectors can be changed on-the-fly */
void (*pEpInt_IN[7])(void) = {
NOP_Process,
NOP_Process,
NOP_Process,
NOP_Process,
NOP_Process,
NOP_Process,
NOP_Process
};
void (*pEpInt_OUT[7])(void) = {
NOP_Process,
NOP_Process,
NOP_Process,
NOP_Process,
NOP_Process,
NOP_Process,
NOP_Process
};
#define USB_ENDPOINT_DESCRIPTOR_TYPE 0x05
#define PIOS_USB_HID_NUM_INTERFACES 1
#define PIOS_USB_HID_SIZ_CLASS_DESC 18
#define PIOS_USB_HID_SIZ_CONFIG_DESC 32
#define PIOS_USB_NUM_INTERFACES (PIOS_USB_HID_NUM_INTERFACES)
#define PIOS_USB_SIZ_CONFIG_DESC (9 + PIOS_USB_HID_SIZ_CONFIG_DESC)
/* USB Standard Device Descriptor */
#define PIOS_USB_SIZ_DEVICE_DESC 18
static const uint8_t PIOS_USB_DeviceDescriptor[PIOS_USB_SIZ_DEVICE_DESC] = {
(uint8_t)(PIOS_USB_SIZ_DEVICE_DESC & 0xff), /* Device Descriptor length */
DSCR_DEVICE, /* Descriptor type */
(uint8_t)(0x0200 & 0xff), /* Specification Version (BCD, LSB) */
(uint8_t)(0x0200 >> 8), /* Specification Version (BCD, MSB) */
0x00, /* Device class "Communication" */
0x00, /* Device sub-class */
0x00, /* Device sub-sub-class */
0x40, /* Maximum packet size */
(uint8_t)((PIOS_USB_VENDOR_ID) & 0xff), /* Vendor ID (LSB) */
(uint8_t)((PIOS_USB_VENDOR_ID) >> 8), /* Vendor ID (MSB) */
(uint8_t)((PIOS_USB_PRODUCT_ID) & 0xff), /* Product ID (LSB) */
(uint8_t)((PIOS_USB_PRODUCT_ID) >> 8), /* Product ID (MSB) */
(uint8_t)((PIOS_USB_VERSION_ID) & 0xff), /* Product version ID (LSB) */
(uint8_t)((PIOS_USB_VERSION_ID) >> 8), /* Product version ID (MSB) */
0x01, /* Manufacturer string index */
0x02, /* Product string index */
0x03, /* Serial number string index */
0x01 /* Number of configurations */
};
/* USB Configuration Descriptor */
static const uint8_t PIOS_USB_ConfigDescriptor[PIOS_USB_SIZ_CONFIG_DESC] = {
/* Configuration Descriptor */
9, /* Descriptor length */
DSCR_CONFIG, // Descriptor type */
(PIOS_USB_SIZ_CONFIG_DESC) & 0xff, /* Config + End Points length (LSB) */
(PIOS_USB_SIZ_CONFIG_DESC) >> 8, /* Config + End Points length (LSB) */
PIOS_USB_NUM_INTERFACES, /* Number of interfaces */
0x01, /* Configuration Value */
0x00, /* Configuration string */
0x80, /* Attributes (b7 - buspwr, b6 - selfpwr, b5 - rwu) */
0x32, /* Power requirement (div 2 ma) */
/* HID */
/************** Descriptor of Custom HID interface ****************/
/* 09 */
0x09, /* bLength: Interface Descriptor size */
0x04,/* bDescriptorType: Interface descriptor type */
0x00, /* bInterfaceNumber: Number of Interface */
0x00, /* bAlternateSetting: Alternate setting */
0x02, /* bNumEndpoints */
0x03, /* bInterfaceClass: HID */
0x00, /* bInterfaceSubClass : 1=BOOT, 0=no boot */
0x00, /* nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse */
0, /* iInterface: Index of string descriptor */
/******************** Descriptor of Custom HID HID ********************/
/* 18 */
0x09, /* bLength: HID Descriptor size */
0x21, /* bDescriptorType: HID */
0x10, 0x01, /* bcdHID: HID Class Spec release number */
0x00, /* bCountryCode: Hardware target country */
0x01, /* bNumDescriptors: Number of HID class descriptors to follow */
0x22, /* bDescriptorType */
PIOS_USB_HID_SIZ_REPORT_DESC, 0x00, /* wItemLength: Total length of Report descriptor */
/******************** Descriptor of Custom HID endpoints ******************/
/* 27 */
0x07, /* bLength: Endpoint Descriptor size */
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */
0x81, /* bEndpointAddress: Endpoint Address (IN) */
0x03, /* bmAttributes: Interrupt endpoint */
(PIOS_USB_HID_DATA_LENGTH), 0x00, /* wMaxPacketSize */
0x02, /* bInterval: Polling Interval (2 ms) */
/* 34 */
0x07, /* bLength: Endpoint Descriptor size */
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */
/* Endpoint descriptor type */
0x01, /* bEndpointAddress: */
/* Endpoint Address (OUT) */
0x03, /* bmAttributes: Interrupt endpoint */
(PIOS_USB_HID_DATA_LENGTH), /* wMaxPacketSize */
0x00, 8,//0x20, /* bInterval: Polling Interval (8 ms) */
/* 41 */
};
/* Local prototypes */
static void PIOS_USB_CB_Reset(void);
static void PIOS_USB_CB_SetConfiguration(void);
static void PIOS_USB_CB_SetDeviceAddress(void);
static void PIOS_USB_CB_Status_In(void);
static void PIOS_USB_CB_Status_Out(void);
static RESULT PIOS_USB_CB_Data_Setup(uint8_t RequestNo);
static RESULT PIOS_USB_CB_NoData_Setup(uint8_t RequestNo);
static uint8_t *PIOS_USB_CB_GetDeviceDescriptor(uint16_t Length);
static uint8_t *PIOS_USB_CB_GetConfigDescriptor(uint16_t Length);
static uint8_t *PIOS_USB_CB_GetStringDescriptor(uint16_t Length);
static RESULT PIOS_USB_CB_Get_Interface_Setting(uint8_t Interface, uint8_t AlternateSetting);
/* USB callback vectors */
static const DEVICE My_Device_Table = {PIOS_USB_EP_NUM, 1};
static const DEVICE_PROP My_Device_Property = {
0, /* PIOS_USB_CB_Init, */
PIOS_USB_CB_Reset,
PIOS_USB_CB_Status_In,
PIOS_USB_CB_Status_Out,
PIOS_USB_CB_Data_Setup,
PIOS_USB_CB_NoData_Setup,
PIOS_USB_CB_Get_Interface_Setting,
PIOS_USB_CB_GetDeviceDescriptor,
PIOS_USB_CB_GetConfigDescriptor,
PIOS_USB_CB_GetStringDescriptor,
0,
0x40 /*MAX PACKET SIZE*/
};
static const USER_STANDARD_REQUESTS My_User_Standard_Requests = {
NOP_Process, /* PIOS_USB_CB_GetConfiguration, */
PIOS_USB_CB_SetConfiguration,
NOP_Process, /* PIOS_USB_CB_GetInterface, */
NOP_Process, /* PIOS_USB_CB_SetInterface, */
NOP_Process, /* PIOS_USB_CB_GetStatus, */
NOP_Process, /* PIOS_USB_CB_ClearFeature, */
NOP_Process, /* PIOS_USB_CB_SetEndPointFeature, */
NOP_Process, /* PIOS_USB_CB_SetDeviceFeature, */
PIOS_USB_CB_SetDeviceAddress};
/* Local Variables */
/* USB Device informations */
static DEVICE_INFO My_Device_Info;
/* USB device status */
static volatile uint32_t bDeviceState = UNCONNECTED;
__IO uint8_t bIntPackSOF = 0;
/**
* Initialises USB interface
* \param[in] mode
* <UL>
* <LI>if 0, USB peripheral won't be initialised if this has already been done before
* <LI>if 1, USB peripheral re-initialisation will be forced
* <LI>if 2, USB peripheral re-initialisation will be forced, STM32 driver hooks won't be overwritten.<BR>
* This mode can be used for a local USB driver which installs it's own hooks during runtime.<BR>
* The application can switch back to PIOS drivers by calling PIOS_USB_Init(1)
* </UL>
* \return < 0 if initialisation failed
* \note Applications shouldn't call this function directly, instead please use \ref PIOS_COM layer functions
*/
int32_t PIOS_USB_Init(uint32_t mode)
{
/* Currently only mode 0..2 supported */
if(mode >= 3) {
/* Unsupported mode */
return -1;
}
/* Clear all USB interrupt requests */
PIOS_IRQ_Disable();
/* Interrupt Mask */
_SetCNTR(0);
PIOS_IRQ_Enable();
/* If mode != 2: install PIOS hooks */
/* A local driver can install it's own hooks and call PIOS_USB_Init(2) to force re-enumeration */
if(mode != 2) {
/* Note: usually no need to duplicate this for external drivers */
pInformation = &My_Device_Info;
pInformation->Ctrl_Info.Usb_wLength = (PIOS_USB_HID_DATA_LENGTH); /* TODO: Is this required? */
/* Following hooks/pointers should be replaced by external drivers */
memcpy(&Device_Table, (DEVICE *) &My_Device_Table, sizeof(Device_Table));
pProperty = (DEVICE_PROP *) &My_Device_Property;
pUser_Standard_Requests = (USER_STANDARD_REQUESTS *) &My_User_Standard_Requests;
pEpInt_OUT[0] = PIOS_USB_HID_EP1_OUT_Callback;
}
PIOS_USB_HID_ChangeConnectionState(0);
pInformation->ControlState = 2;
pInformation->Current_Configuration = 0;
/* If mode == 0: don't initialise USB if not required (important for BSL) */
if(mode == 0 && PIOS_USB_IsInitialized()) {
pInformation->Current_Feature = PIOS_USB_ConfigDescriptor[7];
pInformation->Current_Configuration = 1;
pUser_Standard_Requests->User_SetConfiguration();
} else {
/* 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_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);
/* Configure USB clock */
/* USBCLK = PLLCLK / 1.5 */
RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5);
/* Enable USB clock */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE);
}
/* Don't set interrupt mask on custom driver installation */
if(mode != 2) {
/* Clear pending interrupts (again) */
_SetISTR(0);
/* Set interrupts mask */
_SetCNTR(IMR_MSK);
}
bDeviceState = UNCONNECTED;
/* Enable USB interrupts */
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_USB_PRIORITY;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* No error */
return 0;
}
/**
* Interrupt handler for USB
* \note Applications shouldn't call this function directly, instead please use \ref PIOS_COM layer functions
*/
void USB_LP_CAN1_RX0_IRQHandler(void)
{
uint16_t wIstr = _GetISTR();
if(wIstr & ISTR_RESET) {
_SetISTR((uint16_t)CLR_RESET);
pProperty->Reset();
}
if(wIstr & ISTR_SOF) {
_SetISTR((uint16_t)CLR_SOF);
}
if(wIstr & ISTR_CTR) {
/* Servicing of the endpoint correct transfer interrupt */
/* Clear of the CTR flag into the sub */
CTR_LP();
}
}
/**
* Allows to query, if the USB interface has already been initialised.<BR>
* This function is used by the bootloader to avoid a reconnection, it isn't
* relevant for typical applications!
* \return 1 if USB already initialised, 0 if not initialised
*/
int32_t PIOS_USB_IsInitialized(void)
{
/* We assume that initialisation has been done when endpoint 0 contains a value */
return GetEPType(ENDP0) ? 1 : 0;
}
/**
* Reads the USB detect pin to determine if a USB cable is connected
* \return 0 if cable not connected
* \return 1 if cable is connected
*/
int32_t PIOS_USB_CableConnected(void)
{
return GPIO_ReadInputDataBit(PIOS_USB_DETECT_GPIO_PORT, PIOS_USB_DETECT_GPIO_PIN);
}
/*
* Hooks of STM32 USB library
* \note Applications shouldn't call this function directly, instead please use \ref PIOS_COM layer functions
*/
/**
* Reset Routine
*/
static void PIOS_USB_CB_Reset(void)
{
/* Set PIOS Device as not configured */
pInformation->Current_Configuration = 0;
/* Current Feature initialisation */
pInformation->Current_Feature = PIOS_USB_ConfigDescriptor[7];
/* Set PIOS Device with the default Interface */
pInformation->Current_Interface = 0;
SetBTABLE(PIOS_USB_BTABLE_ADDRESS);
/* Initialise Endpoint 0 */
SetEPType(ENDP0, EP_CONTROL);
SetEPTxStatus(ENDP0, EP_TX_STALL);
SetEPRxAddr(ENDP0, PIOS_USB_ENDP0_RXADDR);
SetEPTxAddr(ENDP0, PIOS_USB_ENDP0_TXADDR);
Clear_Status_Out(ENDP0);
SetEPRxCount(ENDP0, pProperty->MaxPacketSize);
SetEPRxValid(ENDP0);
/* Initialise Endpoint 1 */
SetEPType(ENDP1, EP_INTERRUPT);
SetEPTxAddr(ENDP1, PIOS_USB_ENDP1_TXADDR);
SetEPRxAddr(ENDP1, PIOS_USB_ENDP1_RXADDR);
SetEPTxCount(ENDP1, (PIOS_USB_HID_DATA_LENGTH));
SetEPRxCount(ENDP1, (PIOS_USB_HID_DATA_LENGTH));
SetEPTxStatus(ENDP1, EP_TX_NAK);
SetEPRxStatus(ENDP1, EP_RX_VALID);
/* Propagate connection state to USB HID driver */
PIOS_USB_HID_ChangeConnectionState(0);
/* Set this device to response on default address */
SetDeviceAddress(0);
bDeviceState = ATTACHED;
}
/**
* Update the device state to configured
*/
static void PIOS_USB_CB_SetConfiguration(void)
{
if(pInformation->Current_Configuration != 0) {
/* Propagate connection state to USB HID driver */
PIOS_USB_HID_ChangeConnectionState(1); /* Connected */
bDeviceState = CONFIGURED;
}
}
/**
* Update the device state to addressed
*/
static void PIOS_USB_CB_SetDeviceAddress(void)
{
bDeviceState = ADDRESSED;
}
/**
* Status IN routine
*/
static void PIOS_USB_CB_Status_In(void)
{
}
/**
* Status OUT routine
*/
static void PIOS_USB_CB_Status_Out(void)
{
}
/**
* Data setup routine
*/
static RESULT PIOS_USB_CB_Data_Setup(uint8_t RequestNo)
{
RESULT Result;
if((Result = PIOS_USB_HID_CB_Data_Setup(RequestNo)) != USB_UNSUPPORT) {
return Result;
}
return USB_UNSUPPORT;
}
/**
* Handles the non data class specific requests
*/
static RESULT PIOS_USB_CB_NoData_Setup(uint8_t RequestNo)
{
RESULT res;
if((res = PIOS_USB_HID_CB_NoData_Setup(RequestNo)) != USB_UNSUPPORT) {
return res;
}
return USB_UNSUPPORT;
}
/**
* Gets the device descriptor
*/
static uint8_t *PIOS_USB_CB_GetDeviceDescriptor(uint16_t Length)
{
ONE_DESCRIPTOR desc = {(uint8_t *) PIOS_USB_DeviceDescriptor, PIOS_USB_SIZ_DEVICE_DESC};
return Standard_GetDescriptorData(Length, &desc);
}
/**
* Gets the configuration descriptor
*/
static uint8_t *PIOS_USB_CB_GetConfigDescriptor(uint16_t Length)
{
ONE_DESCRIPTOR desc = {(uint8_t *) PIOS_USB_ConfigDescriptor, PIOS_USB_SIZ_CONFIG_DESC};
return Standard_GetDescriptorData(Length, &desc);
}
/**
* Gets the string descriptors according to the needed index
*/
static uint8_t *PIOS_USB_CB_GetStringDescriptor(uint16_t Length)
{
const uint8_t vendor_str[] = PIOS_USB_VENDOR_STR;
const uint8_t product_str[] = PIOS_USB_PRODUCT_STR;
uint8_t buffer[200];
uint16_t len;
int i;
switch(pInformation->USBwValue0) {
case 0: /* Language */
/* buffer[0] and [1] initialised below */
buffer[2] = 0x09; // CharSet
buffer[3] = 0x04; // U.S.
len = 4;
break;
case 1: /* Vendor */
/* buffer[0] and [1] initialised below */
for(i = 0, len = 2; vendor_str[i] != '\0' && len < 200; ++i) {
buffer[len++] = vendor_str[i];
buffer[len++] = 0;
}
break;
case 2: /* Product */
/* buffer[0] and [1] initialised below */
for(i = 0, len = 2; product_str[i] != '\0' && len < 200; ++i) {
buffer[len++] = product_str[i];
buffer[len++] = 0;
}
break;
case 3: { /* Serial Number */
uint8_t serial_number_str[40];
if(PIOS_SYS_SerialNumberGet((char *) serial_number_str) >= 0) {
for(i = 0, len = 2; serial_number_str[i] != '\0' && len < 200; ++i) {
buffer[len++] = serial_number_str[i];
buffer[len++] = 0;
}
} else
return NULL;
}
break;
default: /* string ID not supported */
return NULL;
}
buffer[0] = len; /* Descriptor Length */
buffer[1] = DSCR_STRING; /* Descriptor Type */
ONE_DESCRIPTOR desc = {(uint8_t *) buffer, len};
return Standard_GetDescriptorData(Length, &desc);
}
/**
* Test the interface and the alternate setting according to the supported one.
*/
static RESULT PIOS_USB_CB_Get_Interface_Setting(uint8_t Interface, uint8_t AlternateSetting)
{
if(AlternateSetting > 0) {
return USB_UNSUPPORT;
} else if(Interface >= PIOS_USB_NUM_INTERFACES) {
return USB_UNSUPPORT;
}
return USB_SUCCESS;
}
#endif