959 lines
34 KiB
C
959 lines
34 KiB
C
/**
|
|
******************************************************************************
|
|
* @file usbd_cdc_core.c
|
|
* @author MCD Application Team
|
|
* @version V1.1.0
|
|
* @date 19-March-2012
|
|
* @brief This file provides the high layer firmware functions to manage the
|
|
* following functionalities of the USB CDC Class:
|
|
* - Initialization and Configuration of high and low layer
|
|
* - Enumeration as CDC Device (and enumeration for each implemented memory interface)
|
|
* - OUT/IN data transfer
|
|
* - Command IN transfer (class requests management)
|
|
* - Error management
|
|
*
|
|
* @verbatim
|
|
*
|
|
* ===================================================================
|
|
* CDC Class Driver Description
|
|
* ===================================================================
|
|
* This driver manages the "Universal Serial Bus Class Definitions for Communications Devices
|
|
* Revision 1.2 November 16, 2007" and the sub-protocol specification of "Universal Serial Bus
|
|
* Communications Class Subclass Specification for PSTN Devices Revision 1.2 February 9, 2007"
|
|
* This driver implements the following aspects of the specification:
|
|
* - Device descriptor management
|
|
* - Configuration descriptor management
|
|
* - Enumeration as CDC device with 2 data endpoints (IN and OUT) and 1 command endpoint (IN)
|
|
* - Requests management (as described in section 6.2 in specification)
|
|
* - Abstract Control Model compliant
|
|
* - Union Functional collection (using 1 IN endpoint for control)
|
|
* - Data interface class
|
|
|
|
* @note
|
|
* For the Abstract Control Model, this core allows only transmitting the requests to
|
|
* lower layer dispatcher (ie. usbd_cdc_vcp.c/.h) which should manage each request and
|
|
* perform relative actions.
|
|
*
|
|
* These aspects may be enriched or modified for a specific user application.
|
|
*
|
|
* This driver doesn't implement the following aspects of the specification
|
|
* (but it is possible to manage these features with some modifications on this driver):
|
|
* - Any class-specific aspect relative to communication classes should be managed by user application.
|
|
* - All communication classes other than PSTN are not managed
|
|
*
|
|
* @endverbatim
|
|
*
|
|
******************************************************************************
|
|
* @attention
|
|
*
|
|
* <h2><center>© COPYRIGHT 2012 STMicroelectronics</center></h2>
|
|
*
|
|
* Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
|
|
* You may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at:
|
|
*
|
|
* http://www.st.com/software_license_agreement_liberty_v2
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
******************************************************************************
|
|
*/
|
|
|
|
/* Includes ------------------------------------------------------------------*/
|
|
#include <stdio.h>
|
|
|
|
#include "usbd_cdc_core.h"
|
|
#include "usbd_desc.h"
|
|
#include "usbd_req.h"
|
|
#include "usbd_conf.h"
|
|
#include "usbd_msc_bot.h"
|
|
#include "usbd_msc_mem.h"
|
|
|
|
// CDC VCP is the first interface
|
|
// the option below is supposed to select between MSC (1) and HID (0) for the
|
|
// second USB interface, but it does not work (only VCP+MSC works)
|
|
// to use HID on its own (ie no VCP), the functions in usbd_pyb_core2.c can be
|
|
// used, and selected in the call to pyb_usb_dev_init().
|
|
#define USB_PYB_USE_MSC (1)
|
|
|
|
#if USB_PYB_USE_MSC
|
|
//#define USB_PYB_CONFIG_DESC_SIZ (67) // for only CDC VCP interfaces
|
|
#define USB_PYB_CONFIG_DESC_SIZ (98) // for both CDC VCP and MSC interfaces
|
|
#else // USE_HID
|
|
#define USB_PYB_CONFIG_DESC_SIZ (100) // for both CDC VCP and HID interfaces
|
|
#endif
|
|
|
|
#define MSC_EPIN_SIZE MSC_MAX_PACKET
|
|
#define MSC_EPOUT_SIZE MSC_MAX_PACKET
|
|
|
|
#define HID_MOUSE_REPORT_DESC_SIZE (74)
|
|
|
|
#define HID_DESCRIPTOR_TYPE 0x21
|
|
#define HID_REPORT_DESC 0x22
|
|
|
|
// HID parameters
|
|
#define HID_IN_EP (0x83)
|
|
#define HID_IN_PACKET (4) /* maximum, and actual, packet size */
|
|
|
|
/*********************************************
|
|
PYB Device library callbacks
|
|
*********************************************/
|
|
static uint8_t usbd_pyb_Init (void *pdev, uint8_t cfgidx);
|
|
static uint8_t usbd_pyb_DeInit (void *pdev, uint8_t cfgidx);
|
|
static uint8_t usbd_pyb_Setup (void *pdev, USB_SETUP_REQ *req);
|
|
static uint8_t usbd_pyb_EP0_RxReady (void *pdev);
|
|
static uint8_t usbd_pyb_DataIn (void *pdev, uint8_t epnum);
|
|
static uint8_t usbd_pyb_DataOut (void *pdev, uint8_t epnum);
|
|
static uint8_t usbd_pyb_SOF (void *pdev);
|
|
|
|
/*********************************************
|
|
PYB specific management functions
|
|
*********************************************/
|
|
static void Handle_USBAsynchXfer (void *pdev);
|
|
static uint8_t *usbd_pyb_GetCfgDesc (uint8_t speed, uint16_t *length);
|
|
|
|
/** @defgroup usbd_cdc_Private_Variables
|
|
* @{
|
|
*/
|
|
extern CDC_IF_Prop_TypeDef VCP_fops;
|
|
extern uint8_t USBD_DeviceDesc [USB_SIZ_DEVICE_DESC];
|
|
|
|
__ALIGN_BEGIN static uint8_t usbd_cdc_AltSet __ALIGN_END = 0;
|
|
__ALIGN_BEGIN static uint8_t USB_Rx_Buffer[CDC_DATA_MAX_PACKET_SIZE] __ALIGN_END;
|
|
__ALIGN_BEGIN uint8_t APP_Rx_Buffer[APP_RX_DATA_SIZE] __ALIGN_END;
|
|
|
|
__ALIGN_BEGIN static uint8_t CmdBuff[CDC_CMD_PACKET_SZE] __ALIGN_END;
|
|
|
|
#if USB_PYB_USE_MSC
|
|
__ALIGN_BEGIN static uint8_t USBD_MSC_MaxLun __ALIGN_END = 0;
|
|
__ALIGN_BEGIN static uint8_t USBD_MSC_AltSet __ALIGN_END = 0;
|
|
#else
|
|
__ALIGN_BEGIN static uint8_t USBD_HID_AltSet __ALIGN_END = 0;
|
|
__ALIGN_BEGIN static uint8_t USBD_HID_Protocol __ALIGN_END = 0;
|
|
__ALIGN_BEGIN static uint8_t USBD_HID_IdleState __ALIGN_END = 0;
|
|
#endif
|
|
|
|
uint32_t APP_Rx_ptr_in = 0;
|
|
uint32_t APP_Rx_ptr_out = 0;
|
|
uint32_t APP_Rx_length = 0;
|
|
|
|
uint8_t USB_Tx_State = 0;
|
|
|
|
static uint32_t cdcCmd = 0xFF;
|
|
static uint32_t cdcLen = 0;
|
|
|
|
/* PYB interface class callbacks structure */
|
|
USBD_Class_cb_TypeDef USBD_PYB_cb =
|
|
{
|
|
usbd_pyb_Init,
|
|
usbd_pyb_DeInit,
|
|
usbd_pyb_Setup,
|
|
NULL, // EP0_TxSent
|
|
usbd_pyb_EP0_RxReady,
|
|
usbd_pyb_DataIn,
|
|
usbd_pyb_DataOut,
|
|
usbd_pyb_SOF,
|
|
NULL, // IsoINIncomplete
|
|
NULL, // IsoOUTIncomplete
|
|
usbd_pyb_GetCfgDesc,
|
|
// for OTG_HS support need to add other cfg desc here
|
|
};
|
|
|
|
#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
|
|
#if defined ( __ICCARM__ ) /*!< IAR Compiler */
|
|
#pragma data_alignment=4
|
|
#endif
|
|
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
|
|
/* USB PYB device Configuration Descriptor */
|
|
__ALIGN_BEGIN static uint8_t usbd_pyb_CfgDesc[USB_PYB_CONFIG_DESC_SIZ] __ALIGN_END =
|
|
{
|
|
//--------------------------------------------------------------------------
|
|
// Configuration Descriptor
|
|
0x09, // bLength: Configuration Descriptor size
|
|
USB_CONFIGURATION_DESCRIPTOR_TYPE, // bDescriptorType: Configuration
|
|
LOBYTE(USB_PYB_CONFIG_DESC_SIZ), // wTotalLength: no of returned bytes
|
|
HIBYTE(USB_PYB_CONFIG_DESC_SIZ),
|
|
0x03, // bNumInterfaces: 3 interfaces
|
|
0x01, // bConfigurationValue: Configuration value
|
|
0x04, // iConfiguration: Index of string descriptor describing the configuration
|
|
0x80, // bmAttributes: bus powered; 0xc0 for self powered
|
|
0xfa, // bMaxPower: in units of 2mA
|
|
|
|
//==========================================================================
|
|
// Interface Association for CDC VCP
|
|
0x08, // bLength: 8 bytes
|
|
USB_INTERFACE_ASSOCIATION_TYPE, // bDescriptorType: IAD
|
|
0x00, // bFirstInterface: first interface for this association
|
|
0x02, // bInterfaceCount: nummber of interfaces for this association
|
|
0x00, // bFunctionClass: ?
|
|
0x00, // bFunctionSubClass: ?
|
|
0x00, // bFunctionProtocol: ?
|
|
0x04, // iFunction: index of string for this function
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Interface Descriptor
|
|
0x09, // bLength: Interface Descriptor size
|
|
USB_INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType: Interface
|
|
0x00, // bInterfaceNumber: Number of Interface
|
|
0x00, // bAlternateSetting: Alternate setting
|
|
0x01, // bNumEndpoints: One endpoints used
|
|
0x02, // bInterfaceClass: Communication Interface Class
|
|
0x02, // bInterfaceSubClass: Abstract Control Model
|
|
0x01, // bInterfaceProtocol: Common AT commands
|
|
0x00, // iInterface:
|
|
|
|
// Header Functional Descriptor
|
|
0x05, // bLength: Endpoint Descriptor size
|
|
0x24, // bDescriptorType: CS_INTERFACE
|
|
0x00, // bDescriptorSubtype: Header Func Desc
|
|
0x10, // bcdCDC: spec release number
|
|
0x01, // ?
|
|
|
|
// Call Management Functional Descriptor
|
|
0x05, // bFunctionLength
|
|
0x24, // bDescriptorType: CS_INTERFACE
|
|
0x01, // bDescriptorSubtype: Call Management Func Desc
|
|
0x00, // bmCapabilities: D0+D1
|
|
0x01, // bDataInterface: 1
|
|
|
|
// ACM Functional Descriptor
|
|
0x04, // bFunctionLength
|
|
0x24, // bDescriptorType: CS_INTERFACE
|
|
0x02, // bDescriptorSubtype: Abstract Control Management desc
|
|
0x02, // bmCapabilities
|
|
|
|
// Union Functional Descriptor
|
|
0x05, // bFunctionLength
|
|
0x24, // bDescriptorType: CS_INTERFACE
|
|
0x06, // bDescriptorSubtype: Union func desc
|
|
0x00, // bMasterInterface: Communication class interface
|
|
0x01, // bSlaveInterface0: Data Class Interface
|
|
|
|
// Endpoint 2 Descriptor
|
|
0x07, // bLength: Endpoint Descriptor size
|
|
USB_ENDPOINT_DESCRIPTOR_TYPE, // bDescriptorType: Endpoint
|
|
CDC_CMD_EP, // bEndpointAddress
|
|
0x03, // bmAttributes: Interrupt
|
|
LOBYTE(CDC_CMD_PACKET_SZE), // wMaxPacketSize:
|
|
HIBYTE(CDC_CMD_PACKET_SZE),
|
|
0x80, // bInterval: polling interval in frames of 1ms
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Data class interface descriptor
|
|
0x09, // bLength: Endpoint Descriptor size
|
|
USB_INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType: interface
|
|
0x01, // bInterfaceNumber: Number of Interface
|
|
0x00, // bAlternateSetting: Alternate setting
|
|
0x02, // bNumEndpoints: Two endpoints used
|
|
0x0A, // bInterfaceClass: CDC
|
|
0x00, // bInterfaceSubClass: ?
|
|
0x00, // bInterfaceProtocol: ?
|
|
0x00, // iInterface:
|
|
|
|
// Endpoint OUT Descriptor
|
|
0x07, // bLength: Endpoint Descriptor size
|
|
USB_ENDPOINT_DESCRIPTOR_TYPE, // bDescriptorType: Endpoint
|
|
CDC_OUT_EP, // bEndpointAddress
|
|
0x02, // bmAttributes: Bulk
|
|
LOBYTE(CDC_DATA_MAX_PACKET_SIZE), // wMaxPacketSize:
|
|
HIBYTE(CDC_DATA_MAX_PACKET_SIZE),
|
|
0x00, // bInterval: ignore for Bulk transfer
|
|
|
|
// Endpoint IN Descriptor
|
|
0x07, // bLength: Endpoint Descriptor size
|
|
USB_ENDPOINT_DESCRIPTOR_TYPE, // bDescriptorType: Endpoint
|
|
CDC_IN_EP, // bEndpointAddress
|
|
0x02, // bmAttributes: Bulk
|
|
LOBYTE(CDC_DATA_MAX_PACKET_SIZE), // wMaxPacketSize:
|
|
HIBYTE(CDC_DATA_MAX_PACKET_SIZE),
|
|
0x00, // bInterval: ignore for Bulk transfer
|
|
|
|
#if USB_PYB_USE_MSC
|
|
//==========================================================================
|
|
// MSC only has 1 interface so doesn't need an IAD
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Interface Descriptor
|
|
0x09, // bLength: Interface Descriptor size
|
|
USB_INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType: interface descriptor
|
|
0x02, // bInterfaceNumber: Number of Interface
|
|
0x00, // bAlternateSetting: Alternate setting
|
|
0x02, // bNumEndpoints
|
|
0x08, // bInterfaceClass: MSC Class
|
|
0x06, // bInterfaceSubClass : SCSI transparent
|
|
0x50, // nInterfaceProtocol
|
|
0x00, // iInterface:
|
|
|
|
// Endpoint IN descriptor
|
|
0x07, // bLength: Endpoint descriptor length
|
|
USB_ENDPOINT_DESCRIPTOR_TYPE, // bDescriptorType: Endpoint descriptor type
|
|
MSC_IN_EP, // bEndpointAddress: IN, address 3
|
|
0x02, // bmAttributes: Bulk endpoint type
|
|
LOBYTE(MSC_MAX_PACKET), // wMaxPacketSize
|
|
HIBYTE(MSC_MAX_PACKET),
|
|
0x00, // bInterval: ignore for Bulk transfer
|
|
|
|
// Endpoint OUT descriptor
|
|
0x07, // bLength: Endpoint descriptor length
|
|
USB_ENDPOINT_DESCRIPTOR_TYPE, // bDescriptorType: Endpoint descriptor type
|
|
MSC_OUT_EP, // bEndpointAddress: OUT, address 3
|
|
0x02, // bmAttributes: Bulk endpoint type
|
|
LOBYTE(MSC_MAX_PACKET), // wMaxPacketSize
|
|
HIBYTE(MSC_MAX_PACKET),
|
|
0x00, // bInterval: ignore for Bulk transfer
|
|
|
|
#else
|
|
//==========================================================================
|
|
// HID only has 1 interface so doesn't need an IAD
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Interface Descriptor
|
|
0x09, // bLength: Interface Descriptor size
|
|
USB_INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType: interface descriptor
|
|
0x02, // bInterfaceNumber: Number of Interface
|
|
0x00, // bAlternateSetting: Alternate setting
|
|
0x01, // bNumEndpoints
|
|
0x03, // bInterfaceClass: HID Class
|
|
0x01, // bInterfaceSubClass: 0=no boot, 1=BOOT
|
|
0x02, // nInterfaceProtocol: 0=none, 1=keyboard, 2=mouse
|
|
0x00, // iInterface:
|
|
|
|
// Descriptor of Joystick Mouse HID
|
|
0x09, // bLength: HID Descriptor size
|
|
HID_DESCRIPTOR_TYPE, // bDescriptorType: HID
|
|
0x11, // bcdHID: HID Class Spec release number, low byte
|
|
0x01, // bcdHID: high byte
|
|
0x00, // bCountryCode: Hardware target country (0=unsupported)
|
|
0x01, // bNumDescriptors: Number of HID class descriptors to follow
|
|
HID_REPORT_DESC, // bDescriptorType: report
|
|
HID_MOUSE_REPORT_DESC_SIZE, // wItemLength: Total length of Report descriptor
|
|
0x00,
|
|
|
|
// Endpoint IN descriptor
|
|
0x07, // bLength: Endpoint descriptor length
|
|
USB_ENDPOINT_DESCRIPTOR_TYPE, // bDescriptorType: Endpoint descriptor type
|
|
HID_IN_EP, // bEndpointAddress: IN, address of HID
|
|
0x03, // bmAttributes: Interrupt endpoint type
|
|
LOBYTE(HID_IN_PACKET), // wMaxPacketSize
|
|
HIBYTE(HID_IN_PACKET),
|
|
0x0a, // bInterval: polling interval, units of 1ms
|
|
|
|
#endif
|
|
};
|
|
|
|
#if !USB_PYB_USE_MSC
|
|
__ALIGN_BEGIN static uint8_t HID_MOUSE_ReportDesc[HID_MOUSE_REPORT_DESC_SIZE] __ALIGN_END =
|
|
{
|
|
0x05, 0x01,
|
|
0x09, 0x02,
|
|
0xA1, 0x01,
|
|
0x09, 0x01,
|
|
|
|
0xA1, 0x00,
|
|
0x05, 0x09,
|
|
0x19, 0x01,
|
|
0x29, 0x03,
|
|
|
|
0x15, 0x00,
|
|
0x25, 0x01,
|
|
0x95, 0x03,
|
|
0x75, 0x01,
|
|
|
|
0x81, 0x02,
|
|
0x95, 0x01,
|
|
0x75, 0x05,
|
|
0x81, 0x01,
|
|
|
|
0x05, 0x01,
|
|
0x09, 0x30,
|
|
0x09, 0x31,
|
|
0x09, 0x38,
|
|
|
|
0x15, 0x81,
|
|
0x25, 0x7F,
|
|
0x75, 0x08,
|
|
0x95, 0x03,
|
|
|
|
0x81, 0x06,
|
|
0xC0, 0x09,
|
|
0x3c, 0x05,
|
|
0xff, 0x09,
|
|
|
|
0x01, 0x15,
|
|
0x00, 0x25,
|
|
0x01, 0x75,
|
|
0x01, 0x95,
|
|
|
|
0x02, 0xb1,
|
|
0x22, 0x75,
|
|
0x06, 0x95,
|
|
0x01, 0xb1,
|
|
|
|
0x01, 0xc0
|
|
};
|
|
#endif
|
|
|
|
/** @defgroup usbd_pyb_Private_Functions
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* @brief usbd_pyb_Init
|
|
* Initilaize the PYB interface
|
|
* @param pdev: device instance
|
|
* @param cfgidx: Configuration index
|
|
* @retval status
|
|
*/
|
|
static uint8_t usbd_pyb_Init(void *pdev, uint8_t cfgidx) {
|
|
// deinit first to reset
|
|
usbd_pyb_DeInit(pdev, cfgidx);
|
|
|
|
//----------------------------------
|
|
// CDC VCP component
|
|
|
|
// Open EP IN
|
|
DCD_EP_Open(pdev,
|
|
CDC_IN_EP,
|
|
CDC_DATA_IN_PACKET_SIZE,
|
|
USB_OTG_EP_BULK);
|
|
|
|
// Open EP OUT
|
|
DCD_EP_Open(pdev,
|
|
CDC_OUT_EP,
|
|
CDC_DATA_OUT_PACKET_SIZE,
|
|
USB_OTG_EP_BULK);
|
|
|
|
// Open Command IN EP
|
|
DCD_EP_Open(pdev,
|
|
CDC_CMD_EP,
|
|
CDC_CMD_PACKET_SZE,
|
|
USB_OTG_EP_INT);
|
|
|
|
/*
|
|
// can use this to dynamically set the device class
|
|
uint8_t *pbuf = USBD_DeviceDesc;
|
|
pbuf[4] = DEVICE_CLASS_CDC;
|
|
pbuf[5] = DEVICE_SUBCLASS_CDC;
|
|
*/
|
|
|
|
// Initialize the Interface physical components
|
|
VCP_fops.pIf_Init();
|
|
|
|
// Prepare Out endpoint to receive next packet */
|
|
DCD_EP_PrepareRx(pdev,
|
|
CDC_OUT_EP,
|
|
(uint8_t*)(USB_Rx_Buffer),
|
|
CDC_DATA_OUT_PACKET_SIZE);
|
|
|
|
#if USB_PYB_USE_MSC
|
|
//----------------------------------
|
|
// MSC component
|
|
|
|
// Open EP IN
|
|
DCD_EP_Open(pdev,
|
|
MSC_IN_EP,
|
|
MSC_EPIN_SIZE,
|
|
USB_OTG_EP_BULK);
|
|
|
|
// Open EP OUT
|
|
DCD_EP_Open(pdev,
|
|
MSC_OUT_EP,
|
|
MSC_EPOUT_SIZE,
|
|
USB_OTG_EP_BULK);
|
|
|
|
// Init the BOT layer
|
|
MSC_BOT_Init(pdev);
|
|
|
|
#else
|
|
//----------------------------------
|
|
// HID component
|
|
|
|
// Open EP IN
|
|
DCD_EP_Open(pdev,
|
|
HID_IN_EP,
|
|
HID_IN_PACKET,
|
|
USB_OTG_EP_INT);
|
|
#endif
|
|
|
|
return USBD_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief usbd_pyb_Init
|
|
* DeInitialize the CDC layer
|
|
* @param pdev: device instance
|
|
* @param cfgidx: Configuration index
|
|
* @retval status
|
|
*/
|
|
static uint8_t usbd_pyb_DeInit(void *pdev, uint8_t cfgidx) {
|
|
//----------------------------------
|
|
// CDC VCP component
|
|
// close CDC EPs
|
|
DCD_EP_Close(pdev, CDC_IN_EP);
|
|
DCD_EP_Close(pdev, CDC_OUT_EP);
|
|
DCD_EP_Close(pdev, CDC_CMD_EP);
|
|
|
|
// Restore default state of the Interface physical components
|
|
VCP_fops.pIf_DeInit();
|
|
|
|
#if USB_PYB_USE_MSC
|
|
//----------------------------------
|
|
// MSC component
|
|
|
|
// Close MSC EPs
|
|
DCD_EP_Close(pdev, MSC_IN_EP);
|
|
DCD_EP_Close(pdev, MSC_OUT_EP);
|
|
|
|
// Un Init the BOT layer
|
|
MSC_BOT_DeInit(pdev);
|
|
|
|
#else
|
|
//----------------------------------
|
|
// HID component
|
|
|
|
// Close HID EP
|
|
DCD_EP_Close(pdev, HID_IN_EP);
|
|
#endif
|
|
|
|
return USBD_OK;
|
|
}
|
|
|
|
#define BOT_GET_MAX_LUN 0xFE
|
|
#define BOT_RESET 0xFF
|
|
|
|
#define HID_REQ_SET_PROTOCOL (0x0B)
|
|
#define HID_REQ_GET_PROTOCOL (0x03)
|
|
#define HID_REQ_SET_IDLE (0x0A)
|
|
#define HID_REQ_GET_IDLE (0x02)
|
|
#define HID_REQ_SET_REPORT (0x09) // used?
|
|
#define HID_REQ_GET_REPORT (0x01) // used?
|
|
|
|
/**
|
|
* @brief usbd_pyb_Setup
|
|
* Handle the CDC specific requests
|
|
* @param pdev: instance
|
|
* @param req: usb requests
|
|
* @retval status
|
|
*/
|
|
static uint8_t usbd_pyb_Setup(void *pdev, USB_SETUP_REQ *req) {
|
|
switch (req->bmRequest & (USB_REQ_TYPE_MASK | USB_REQ_RECIPIENT_MASK)) {
|
|
|
|
// Standard Device Request ---------------------------------------------
|
|
case (USB_REQ_TYPE_STANDARD | USB_REQ_RECIPIENT_DEVICE):
|
|
/*
|
|
switch (req->bRequest) {
|
|
case USB_REQ_GET_DESCRIPTOR:
|
|
{
|
|
uint16_t len = USB_CDC_DESC_SIZ; // XXX WRONG!
|
|
uint8_t *pbuf = usbd_pyb_CfgDesc + 9;
|
|
if ((req->wValue >> 8) == CDC_DESCRIPTOR_TYPE) {
|
|
pbuf = usbd_pyb_CfgDesc + 9 + (9 * USBD_ITF_MAX_NUM); // TODO
|
|
len = MIN(USB_CDC_DESC_SIZ, req->wLength); // TODO
|
|
}
|
|
return USBD_CtlSendData(pdev, pbuf, len);
|
|
// TODO stuff here for HID, using HID_MOUSE_ReportDesc
|
|
}
|
|
}
|
|
*/
|
|
break;
|
|
|
|
// Standard Interface Request ------------------------------------------
|
|
case (USB_REQ_TYPE_STANDARD | USB_REQ_RECIPIENT_INTERFACE):
|
|
switch (req->bRequest) {
|
|
case USB_REQ_GET_DESCRIPTOR: // needed for HID; SU 0x81 0x06 0x2200 0x00 request
|
|
// wIndex & 0xff is the interface
|
|
if ((req->wIndex & 0xff) <= 1) {
|
|
// CDC VCP
|
|
} else {
|
|
#if USB_PYB_USE_MSC
|
|
#else
|
|
uint16_t len = 0;
|
|
uint8_t *pbuf = NULL;
|
|
if (req->wValue >> 8 == HID_REPORT_DESC) {
|
|
len = MIN(HID_MOUSE_REPORT_DESC_SIZE , req->wLength);
|
|
pbuf = HID_MOUSE_ReportDesc;
|
|
return USBD_CtlSendData(pdev, pbuf, len);
|
|
} else if( req->wValue >> 8 == HID_DESCRIPTOR_TYPE) {
|
|
pbuf = usbd_pyb_CfgDesc + /* skip VCP */ 0x09 + 0x08 + 0x09 + 0x05 + 0x05 + 0x04 + 0x05 + 0x07 + 0x09 + 0x07 + 0x07 + /* skip iface descr */ 0x09;
|
|
len = MIN(0x09, req->wLength);
|
|
return USBD_CtlSendData(pdev, pbuf, len);
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case USB_REQ_GET_INTERFACE:
|
|
// wIndex & 0xff is the interface
|
|
if ((req->wIndex & 0xff) <= 1) {
|
|
return USBD_CtlSendData(pdev, &usbd_cdc_AltSet, 1);
|
|
} else {
|
|
#if USB_PYB_USE_MSC
|
|
return USBD_CtlSendData(pdev, &USBD_MSC_AltSet, 1);
|
|
#else
|
|
return USBD_CtlSendData(pdev, &USBD_HID_AltSet, 1);
|
|
#endif
|
|
}
|
|
|
|
case USB_REQ_SET_INTERFACE:
|
|
if ((uint8_t)(req->wValue) < USBD_ITF_MAX_NUM) { // TODO
|
|
if ((req->wIndex & 0xff) <= 1) {
|
|
usbd_cdc_AltSet = req->wValue;
|
|
} else {
|
|
#if USB_PYB_USE_MSC
|
|
USBD_MSC_AltSet = req->wValue;
|
|
#else
|
|
USBD_HID_AltSet = req->wValue;
|
|
#endif
|
|
}
|
|
return USBD_OK;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// Standard Endpoint Request -------------------------------------------
|
|
case (USB_REQ_TYPE_STANDARD | USB_REQ_RECIPIENT_ENDPOINT):
|
|
// req->wIndex is the endpoint number, including direction
|
|
#if USB_PYB_USE_MSC
|
|
if (req->wIndex == MSC_IN_EP || req->wIndex == MSC_OUT_EP) {
|
|
// MSC component
|
|
switch (req->bRequest) {
|
|
case USB_REQ_CLEAR_FEATURE:
|
|
|
|
// Flush the FIFO and Clear the stall status
|
|
DCD_EP_Flush(pdev, (uint8_t)req->wIndex);
|
|
|
|
// Re-activate the EP
|
|
DCD_EP_Close(pdev, (uint8_t)req->wIndex);
|
|
|
|
if ((((uint8_t)req->wIndex) & 0x80) == 0x80) {
|
|
DCD_EP_Open(pdev, ((uint8_t)req->wIndex), MSC_EPIN_SIZE, USB_OTG_EP_BULK);
|
|
} else {
|
|
DCD_EP_Open(pdev, ((uint8_t)req->wIndex), MSC_EPOUT_SIZE, USB_OTG_EP_BULK);
|
|
}
|
|
|
|
// Handle BOT error
|
|
MSC_BOT_CplClrFeature(pdev, (uint8_t)req->wIndex);
|
|
return USBD_OK;
|
|
}
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
// CDC Class Requests ------------------------------
|
|
case (USB_REQ_TYPE_CLASS | USB_REQ_RECIPIENT_INTERFACE):
|
|
// req->wIndex is the recipient interface number
|
|
if (req->wIndex == 0) {
|
|
// CDC component, communications interface (TODO do we need to handle if#1?)
|
|
|
|
// Check if the request is a data setup packet
|
|
if (req->wLength) {
|
|
if (req->bmRequest & 0x80) {
|
|
// Device-to-Host request
|
|
|
|
// Get the data to be sent to Host from interface layer
|
|
VCP_fops.pIf_Ctrl(req->bRequest, CmdBuff, req->wLength);
|
|
|
|
// Send the data to the host
|
|
return USBD_CtlSendData(pdev, CmdBuff, req->wLength);
|
|
|
|
} else {
|
|
// Host-to-Device requeset
|
|
|
|
// Set the value of the current command to be processed */
|
|
cdcCmd = req->bRequest;
|
|
cdcLen = req->wLength;
|
|
|
|
// Prepare the reception of the buffer over EP0
|
|
// Next step: the received data will be managed in usbd_cdc_EP0_TxSent() function.
|
|
return USBD_CtlPrepareRx(pdev, CmdBuff, req->wLength);
|
|
}
|
|
} else {
|
|
// Not a Data request
|
|
|
|
// Transfer the command to the interface layer */
|
|
return VCP_fops.pIf_Ctrl(req->bRequest, NULL, req->wValue);
|
|
}
|
|
|
|
} else if (req->wIndex == 2) {
|
|
#if USB_PYB_USE_MSC
|
|
// MSC component
|
|
switch (req->bRequest) {
|
|
case BOT_GET_MAX_LUN:
|
|
if ((req->wValue == 0) && (req->wLength == 1) && ((req->bmRequest & 0x80) == 0x80)) {
|
|
USBD_MSC_MaxLun = USBD_STORAGE_fops->GetMaxLun();
|
|
if (USBD_MSC_MaxLun > 0) {
|
|
return USBD_CtlSendData(pdev, &USBD_MSC_MaxLun, 1);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case BOT_RESET:
|
|
if ((req->wValue == 0) && (req->wLength == 0) && ((req->bmRequest & 0x80) != 0x80)) {
|
|
MSC_BOT_Reset(pdev);
|
|
return USBD_OK;
|
|
}
|
|
break;
|
|
}
|
|
#else
|
|
// HID component
|
|
switch (req->bRequest) {
|
|
case HID_REQ_SET_PROTOCOL:
|
|
USBD_HID_Protocol = req->wValue;
|
|
return USBD_OK;
|
|
|
|
case HID_REQ_GET_PROTOCOL:
|
|
return USBD_CtlSendData(pdev, &USBD_HID_Protocol, 1);
|
|
|
|
case HID_REQ_SET_IDLE:
|
|
USBD_HID_IdleState = (req->wValue >> 8);
|
|
return USBD_OK;
|
|
|
|
case HID_REQ_GET_IDLE:
|
|
return USBD_CtlSendData(pdev, &USBD_HID_IdleState, 1);
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
|
|
printf("SU %x %x %x %x\n", req->bmRequest, req->bRequest, req->wValue, req->wIndex);
|
|
|
|
// invalid command
|
|
USBD_CtlError(pdev, req);
|
|
return USBD_FAIL;
|
|
}
|
|
|
|
/**
|
|
* @brief usbd_pyb_EP0_RxReady
|
|
* Data received on control endpoint
|
|
* @param pdev: device device instance
|
|
* @retval status
|
|
*/
|
|
static uint8_t usbd_pyb_EP0_RxReady(void *pdev) {
|
|
if (cdcCmd != NO_CMD) {
|
|
// Process the data
|
|
VCP_fops.pIf_Ctrl(cdcCmd, CmdBuff, cdcLen);
|
|
|
|
// Reset the command variable to default value
|
|
cdcCmd = NO_CMD;
|
|
}
|
|
|
|
return USBD_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief usbd_pyb_DataIn
|
|
* Data sent on non-control IN endpoint
|
|
* @param pdev: device instance
|
|
* @param epnum: endpoint number
|
|
* @retval status
|
|
*/
|
|
static uint8_t usbd_pyb_DataIn(void *pdev, uint8_t epnum) {
|
|
uint16_t USB_Tx_ptr;
|
|
uint16_t USB_Tx_length;
|
|
|
|
switch (epnum) {
|
|
case (CDC_IN_EP & 0x7f): // TODO is this correct?
|
|
case (CDC_CMD_EP & 0x7f): // TODO is this correct?
|
|
if (USB_Tx_State == 1) {
|
|
if (APP_Rx_length == 0) {
|
|
USB_Tx_State = 0;
|
|
} else {
|
|
if (APP_Rx_length > CDC_DATA_IN_PACKET_SIZE) {
|
|
USB_Tx_ptr = APP_Rx_ptr_out;
|
|
USB_Tx_length = CDC_DATA_IN_PACKET_SIZE;
|
|
|
|
APP_Rx_ptr_out += CDC_DATA_IN_PACKET_SIZE;
|
|
APP_Rx_length -= CDC_DATA_IN_PACKET_SIZE;
|
|
} else {
|
|
USB_Tx_ptr = APP_Rx_ptr_out;
|
|
USB_Tx_length = APP_Rx_length;
|
|
|
|
APP_Rx_ptr_out += APP_Rx_length;
|
|
APP_Rx_length = 0;
|
|
}
|
|
|
|
// Prepare the available data buffer to be sent on IN endpoint
|
|
DCD_EP_Tx(pdev,
|
|
CDC_IN_EP,
|
|
(uint8_t*)&APP_Rx_Buffer[USB_Tx_ptr],
|
|
USB_Tx_length);
|
|
}
|
|
}
|
|
return USBD_OK;
|
|
|
|
#if USB_PYB_USE_MSC
|
|
case (MSC_IN_EP & 0x7f): // TODO?
|
|
MSC_BOT_DataIn(pdev, epnum);
|
|
return USBD_OK;
|
|
|
|
#else
|
|
case (HID_IN_EP & 0x7f):
|
|
/* Ensure that the FIFO is empty before a new transfer, this condition could
|
|
be caused by a new transfer before the end of the previous transfer */
|
|
DCD_EP_Flush(pdev, HID_IN_EP);
|
|
return USBD_OK;
|
|
#endif
|
|
}
|
|
|
|
printf("DI %x\n", epnum);
|
|
|
|
return USBD_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief usbd_pyb_DataOut
|
|
* Data received on non-control Out endpoint
|
|
* @param pdev: device instance
|
|
* @param epnum: endpoint number
|
|
* @retval status
|
|
*/
|
|
static uint8_t usbd_pyb_DataOut(void *pdev, uint8_t epnum) {
|
|
uint16_t USB_Rx_Cnt;
|
|
|
|
switch (epnum) {
|
|
case (CDC_OUT_EP & 0x7f): // TODO is this correct?
|
|
// Get the received data buffer and update the counter */
|
|
USB_Rx_Cnt = ((USB_OTG_CORE_HANDLE*)pdev)->dev.out_ep[epnum].xfer_count;
|
|
|
|
/* USB data will be immediately processed, this allow next USB traffic being
|
|
NAKed till the end of the application Xfer */
|
|
VCP_fops.pIf_DataRx(USB_Rx_Buffer, USB_Rx_Cnt);
|
|
|
|
// Prepare Out endpoint to receive next packet */
|
|
DCD_EP_PrepareRx(pdev,
|
|
CDC_OUT_EP,
|
|
(uint8_t*)(USB_Rx_Buffer),
|
|
CDC_DATA_OUT_PACKET_SIZE);
|
|
return USBD_OK;
|
|
|
|
#if USB_PYB_USE_MSC
|
|
case (MSC_OUT_EP & 0x7f): // TODO is this correct?
|
|
MSC_BOT_DataOut(pdev, epnum);
|
|
return USBD_OK;
|
|
#endif
|
|
}
|
|
|
|
printf("DO %x\n", epnum);
|
|
|
|
return USBD_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief usbd_pyb_SOF
|
|
* Start Of Frame event management
|
|
* @param pdev: instance
|
|
* @retval status
|
|
*/
|
|
static uint8_t usbd_pyb_SOF(void *pdev) {
|
|
static uint32_t FrameCount = 0;
|
|
|
|
// TODO do we need to check that this is for CDC/VCP? can we even do that?
|
|
|
|
if (FrameCount++ == CDC_IN_FRAME_INTERVAL) {
|
|
// Reset the frame counter */
|
|
FrameCount = 0;
|
|
|
|
// Check the data to be sent through IN pipe */
|
|
Handle_USBAsynchXfer(pdev);
|
|
}
|
|
|
|
return USBD_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief Handle_USBAsynchXfer
|
|
* Send data to USB
|
|
* @param pdev: instance
|
|
* @retval None
|
|
*/
|
|
static void Handle_USBAsynchXfer (void *pdev)
|
|
{
|
|
uint16_t USB_Tx_ptr;
|
|
uint16_t USB_Tx_length;
|
|
|
|
if(USB_Tx_State != 1)
|
|
{
|
|
if (APP_Rx_ptr_out == APP_RX_DATA_SIZE)
|
|
{
|
|
APP_Rx_ptr_out = 0;
|
|
}
|
|
|
|
if(APP_Rx_ptr_out == APP_Rx_ptr_in)
|
|
{
|
|
USB_Tx_State = 0;
|
|
return;
|
|
}
|
|
|
|
if(APP_Rx_ptr_out > APP_Rx_ptr_in) // rollback */
|
|
{
|
|
APP_Rx_length = APP_RX_DATA_SIZE - APP_Rx_ptr_out;
|
|
|
|
}
|
|
else
|
|
{
|
|
APP_Rx_length = APP_Rx_ptr_in - APP_Rx_ptr_out;
|
|
|
|
}
|
|
#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
|
|
APP_Rx_length &= ~0x03;
|
|
#endif // USB_OTG_HS_INTERNAL_DMA_ENABLED */
|
|
|
|
if (APP_Rx_length > CDC_DATA_IN_PACKET_SIZE)
|
|
{
|
|
USB_Tx_ptr = APP_Rx_ptr_out;
|
|
USB_Tx_length = CDC_DATA_IN_PACKET_SIZE;
|
|
|
|
APP_Rx_ptr_out += CDC_DATA_IN_PACKET_SIZE;
|
|
APP_Rx_length -= CDC_DATA_IN_PACKET_SIZE;
|
|
}
|
|
else
|
|
{
|
|
USB_Tx_ptr = APP_Rx_ptr_out;
|
|
USB_Tx_length = APP_Rx_length;
|
|
|
|
APP_Rx_ptr_out += APP_Rx_length;
|
|
APP_Rx_length = 0;
|
|
}
|
|
USB_Tx_State = 1;
|
|
|
|
DCD_EP_Tx (pdev,
|
|
CDC_IN_EP,
|
|
(uint8_t*)&APP_Rx_Buffer[USB_Tx_ptr],
|
|
USB_Tx_length);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* @brief usbd_pyb_GetCfgDesc
|
|
* Return configuration descriptor
|
|
* @param speed : current device speed
|
|
* @param length : pointer data length
|
|
* @retval pointer to descriptor buffer
|
|
*/
|
|
static uint8_t *usbd_pyb_GetCfgDesc(uint8_t speed, uint16_t *length) {
|
|
*length = sizeof(usbd_pyb_CfgDesc);
|
|
return usbd_pyb_CfgDesc;
|
|
}
|
|
|
|
/**
|
|
* @brief USBD_HID_SendReport
|
|
* Send HID Report
|
|
* @param pdev: device instance
|
|
* @param buff: pointer to report (4 bytes: ?, x, y, ?)
|
|
* @retval status
|
|
*/
|
|
/*
|
|
uint8_t USBD_HID_SendReport(USB_OTG_CORE_HANDLE *pdev, uint8_t *report, uint16_t len) {
|
|
if (pdev->dev.device_status == USB_OTG_CONFIGURED) {
|
|
DCD_EP_Tx(pdev, HID_IN_EP, report, len);
|
|
}
|
|
return USBD_OK;
|
|
}
|
|
*/
|