Files
edgetx-gx12/radio/bootloader/usb/device/core/USBDDriver.c
T

754 lines
27 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ----------------------------------------------------------------------------
*/
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include "USBDDriver.h"
#include "USBDDriverCallbacks.h"
#include "USBD.h"
#include <board.h>
#include "debug.h"
#include <usb/common/core/USBGenericDescriptor.h>
#include <usb/common/core/USBDeviceDescriptor.h>
#include <usb/common/core/USBConfigurationDescriptor.h>
#include <usb/common/core/USBDeviceQualifierDescriptor.h>
#include <usb/common/core/USBEndpointDescriptor.h>
#include <usb/common/core/USBFeatureRequest.h>
#include <usb/common/core/USBSetAddressRequest.h>
#include <usb/common/core/USBGetDescriptorRequest.h>
#include <usb/common/core/USBSetConfigurationRequest.h>
#include <usb/common/core/USBInterfaceRequest.h>
#include <string.h>
//------------------------------------------------------------------------------
// Local functions
//------------------------------------------------------------------------------
#if defined(CHIP_USB_OTGHS)
static unsigned char otg_features_supported = 0;
#endif
//------------------------------------------------------------------------------
/// Send a NULL packet
//------------------------------------------------------------------------------
static void TerminateCtrlInWithNull(void *pArg,
unsigned char status,
unsigned int transferred,
unsigned int remaining)
{
USBD_Write(0, // Endpoint #0
0, // No data buffer
0, // No data buffer
(TransferCallback) 0,
(void *) 0);
}
//------------------------------------------------------------------------------
/// Configures the device by setting it into the Configured state and
/// initializing all endpoints.
/// \param pDriver Pointer to a USBDDriver instance.
/// \param cfgnum Configuration number to set.
//------------------------------------------------------------------------------
static void SetConfiguration(USBDDriver *pDriver, unsigned char cfgnum)
{
USBEndpointDescriptor *pEndpoints[CHIP_USB_NUMENDPOINTS+1];
const USBConfigurationDescriptor *pConfiguration;
// Use different descriptor depending on device speed
if (USBD_IsHighSpeed()) {
pConfiguration = pDriver->pDescriptors->pHsConfiguration;
}
else {
pConfiguration = pDriver->pDescriptors->pFsConfiguration;
}
// Set & save the desired configuration
USBD_SetConfiguration(cfgnum);
pDriver->cfgnum = cfgnum;
// If the configuration is not 0, configure endpoints
if (cfgnum != 0) {
// Parse configuration to get endpoint descriptors
USBConfigurationDescriptor_Parse(pConfiguration, 0, pEndpoints, 0);
// Configure endpoints
int i = 0;
while (pEndpoints[i] != 0) {
USBD_ConfigureEndpoint(pEndpoints[i]);
i++;
}
}
// Should be done before send the ZLP
USBDDriverCallbacks_ConfigurationChanged(cfgnum);
// Acknowledge the request
USBD_Write(0, // Endpoint #0
0, // No data buffer
0, // No data buffer
(TransferCallback) 0,
(void *) 0);
}
//------------------------------------------------------------------------------
/// Sends the current configuration number to the host.
/// \param pDriver Pointer to a USBDDriver instance.
//------------------------------------------------------------------------------
static void GetConfiguration(const USBDDriver *pDriver)
{
USBD_Write(0, &(pDriver->cfgnum), 1, 0, 0);
}
//------------------------------------------------------------------------------
/// Sends the current status of the device to the host.
/// \param pDriver Pointer to a USBDDriver instance.
//------------------------------------------------------------------------------
static void GetDeviceStatus(const USBDDriver *pDriver)
{
static unsigned short data;
const USBConfigurationDescriptor *pConfiguration;
data = 0;
// Use different configuration depending on device speed
if (USBD_IsHighSpeed()) {
pConfiguration = pDriver->pDescriptors->pHsConfiguration;
}
else {
pConfiguration = pDriver->pDescriptors->pFsConfiguration;
}
// Check current configuration for power mode (if device is configured)
if (pDriver->cfgnum != 0) {
if (USBConfigurationDescriptor_IsSelfPowered(pConfiguration)) {
data |= 1;
}
}
// Check if remote wake-up is enabled
if (pDriver->isRemoteWakeUpEnabled) {
data |= 2;
}
// Send the device status
USBD_Write(0, &data, 2, 0, 0);
}
//------------------------------------------------------------------------------
/// Sends the current status of an endpoints to the USB host.
/// \param bEndpoint Endpoint number.
//------------------------------------------------------------------------------
static void GetEndpointStatus(unsigned char bEndpoint)
{
static unsigned short data;
data = 0;
// Check if the endpoint exists
if (bEndpoint > CHIP_USB_NUMENDPOINTS) {
USBD_Stall(0);
}
else {
// Check if the endpoint if currently halted
if (USBD_IsHalted(bEndpoint)) {
data = 1;
}
// Send the endpoint status
USBD_Write(0, &data, 2, 0, 0);
}
}
//------------------------------------------------------------------------------
/// Sends the requested USB descriptor to the host if available, or STALLs the
/// request.
/// \param pDriver Pointer to a USBDDriver instance.
/// \param type Type of the requested descriptor
/// \param index Index of the requested descriptor.
/// \param length Maximum number of bytes to return.
//------------------------------------------------------------------------------
static void GetDescriptor(
const USBDDriver *pDriver,
unsigned char type,
unsigned char indexRDesc,
unsigned int length)
{
const USBDeviceDescriptor *pDevice;
const USBConfigurationDescriptor *pConfiguration;
const USBDeviceQualifierDescriptor *pQualifier;
const USBConfigurationDescriptor *pOtherSpeed;
const USBGenericDescriptor **pStrings =
(const USBGenericDescriptor **) pDriver->pDescriptors->pStrings;
const USBGenericDescriptor *pString;
unsigned char numStrings = pDriver->pDescriptors->numStrings;
unsigned char terminateWithNull = 0;
// Use different set of descriptors depending on device speed
if (USBD_IsHighSpeed()) {
TRACE_DEBUG("HS ");
pDevice = pDriver->pDescriptors->pHsDevice;
pConfiguration = pDriver->pDescriptors->pHsConfiguration;
pQualifier = pDriver->pDescriptors->pHsQualifier;
pOtherSpeed = pDriver->pDescriptors->pHsOtherSpeed;
}
else {
TRACE_DEBUG("FS ");
pDevice = pDriver->pDescriptors->pFsDevice;
pConfiguration = pDriver->pDescriptors->pFsConfiguration;
pQualifier = pDriver->pDescriptors->pFsQualifier;
pOtherSpeed = pDriver->pDescriptors->pFsOtherSpeed;
}
// Check the descriptor type
switch (type) {
case USBGenericDescriptor_DEVICE:
TRACE_INFO_WP("Dev ");
// Adjust length and send descriptor
if (length > USBGenericDescriptor_GetLength((USBGenericDescriptor *) pDevice)) {
length = USBGenericDescriptor_GetLength((USBGenericDescriptor *) pDevice);
}
USBD_Write(0, pDevice, length, 0, 0);
break;
case USBGenericDescriptor_CONFIGURATION:
TRACE_INFO_WP("Cfg ");
// Adjust length and send descriptor
if (length > USBConfigurationDescriptor_GetTotalLength(pConfiguration)) {
length = USBConfigurationDescriptor_GetTotalLength(pConfiguration);
terminateWithNull = ((length % pDevice->bMaxPacketSize0) == 0);
}
USBD_Write(0,
pConfiguration,
length,
terminateWithNull ? TerminateCtrlInWithNull : 0,
0);
break;
case USBGenericDescriptor_DEVICEQUALIFIER:
TRACE_INFO_WP("Qua ");
// Check if descriptor exists
if (!pQualifier) {
USBD_Stall(0);
}
else {
// Adjust length and send descriptor
if (length > USBGenericDescriptor_GetLength((USBGenericDescriptor *) pQualifier)) {
length = USBGenericDescriptor_GetLength((USBGenericDescriptor *) pQualifier);
}
USBD_Write(0, pQualifier, length, 0, 0);
}
break;
case USBGenericDescriptor_OTHERSPEEDCONFIGURATION:
TRACE_INFO_WP("OSC ");
// Check if descriptor exists
if (!pOtherSpeed) {
USBD_Stall(0);
}
else {
// Adjust length and send descriptor
if (length > USBConfigurationDescriptor_GetTotalLength(pOtherSpeed)) {
length = USBConfigurationDescriptor_GetTotalLength(pOtherSpeed);
terminateWithNull = ((length % pDevice->bMaxPacketSize0) == 0);
}
USBD_Write(0,
pOtherSpeed,
length,
terminateWithNull ? TerminateCtrlInWithNull : 0,
0);
}
break;
case USBGenericDescriptor_STRING:
TRACE_INFO_WP("Str%d ", indexRDesc);
// Check if descriptor exists
if (indexRDesc > numStrings) {
USBD_Stall(0);
}
else {
pString = pStrings[indexRDesc];
// Adjust length and send descriptor
if (length > USBGenericDescriptor_GetLength(pString)) {
length = USBGenericDescriptor_GetLength(pString);
terminateWithNull = ((length % pDevice->bMaxPacketSize0) == 0);
}
USBD_Write(0,
pString,
length,
terminateWithNull ? TerminateCtrlInWithNull : 0,
0);
}
break;
default:
TRACE_WARNING(
"USBDDriver_GetDescriptor: Unknown descriptor type (%d)\n\r",
type);
USBD_Stall(0);
}
}
//------------------------------------------------------------------------------
/// Sets the active setting of the given interface if the configuration supports
/// it; otherwise, the control pipe is STALLed. If the setting of an interface
/// changes.
/// \parma pDriver Pointer to a USBDDriver instance.
/// \parma infnum Interface number.
/// \parma setting New active setting for the interface.
//------------------------------------------------------------------------------
static void SetInterface(
USBDDriver *pDriver,
unsigned char infnum,
unsigned char setting)
{
// Make sure alternate settings are supported
if (!pDriver->pInterfaces) {
USBD_Stall(0);
}
else {
// Change the current setting of the interface and trigger the callback
// if necessary
if (pDriver->pInterfaces[infnum] != setting) {
pDriver->pInterfaces[infnum] = setting;
USBDDriverCallbacks_InterfaceSettingChanged(infnum, setting);
}
// Acknowledge the request
USBD_Write(0, 0, 0, 0, 0);
}
}
//------------------------------------------------------------------------------
/// Sends the currently active setting of the given interface to the USB
/// host. If alternate settings are not supported, this function STALLs the
/// control pipe.
/// \param pDriver Pointer to a USBDDriver instance.
/// \param infnum Interface number.
//------------------------------------------------------------------------------
static void GetInterface(
const USBDDriver *pDriver,
unsigned char infnum)
{
// Make sure alternate settings are supported, or STALL the control pipe
if (!pDriver->pInterfaces) {
USBD_Stall(0);
}
else {
// Sends the current interface setting to the host
USBD_Write(0, &(pDriver->pInterfaces[infnum]), 1, 0, 0);
}
}
#if defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS)
//------------------------------------------------------------------------------
// Performs the selected test on the USB device (high-speed only).
// \param test Test selector value.
//------------------------------------------------------------------------------
static void USBDDriver_Test(unsigned char test)
{
TRACE_DEBUG("UDPHS_Test\n\r");
// the lower byte of wIndex must be zero
// the most significant byte of wIndex is used to specify the specific test mode
switch (test) {
case USBFeatureRequest_TESTPACKET:
//Test mode Test_Packet:
//Upon command, a port must repetitively transmit the following test packet until
//the exit action is taken. This enables the testing of rise and fall times, eye
//patterns, jitter, and any other dynamic waveform specifications.
//The test packet is made up by concatenating the following strings.
//(Note: For J/K NRZI data, and for NRZ data, the bit on the left is the first one
//transmitted. “S” indicates that a bit stuff occurs, which inserts an “extra” NRZI data bit.
//“* N” is used to indicate N occurrences of a string of bits or symbols.)
//A port in Test_Packet mode must send this packet repetitively. The inter-packet timing
//must be no less than the minimum allowable inter-packet gap as defined in Section 7.1.18 and
//no greater than 125 us.
// Send ZLP
USBD_Test(USBFeatureRequest_TESTSENDZLP);
// Tst PACKET
USBD_Test(USBFeatureRequest_TESTPACKET);
while (1);
//break; not reached
case USBFeatureRequest_TESTJ:
//Test mode Test_J:
//Upon command, a port’s transceiver must enter the high-speed J state and remain in that
//state until the exit action is taken. This enables the testing of the high output drive
//level on the D+ line.
// Send ZLP
USBD_Test(USBFeatureRequest_TESTSENDZLP);
// Tst J
USBD_Test(USBFeatureRequest_TESTJ);
while (1);
//break; not reached
case USBFeatureRequest_TESTK:
//Test mode Test_K:
//Upon command, a port’s transceiver must enter the high-speed K state and remain in
//that state until the exit action is taken. This enables the testing of the high output drive
//level on the D- line.
// Send a ZLP
USBD_Test(USBFeatureRequest_TESTSENDZLP);
USBD_Test(USBFeatureRequest_TESTK);
while (1);
//break; not reached
case USBFeatureRequest_TESTSE0NAK:
//Test mode Test_SE0_NAK:
//Upon command, a port’s transceiver must enter the high-speed receive mode
//and remain in that mode until the exit action is taken. This enables the testing
//of output impedance, low level output voltage, and loading characteristics.
//In addition, while in this mode, upstream facing ports (and only upstream facing ports)
//must respond to any IN token packet with a NAK handshake (only if the packet CRC is
//determined to be correct) within the normal allowed device response time. This enables testing of
//the device squelch level circuitry and, additionally, provides a general purpose stimulus/response
//test for basic functional testing.
// Send a ZLP
USBD_Test(USBFeatureRequest_TESTSENDZLP);
// Test SE0_NAK
USBD_Test(USBFeatureRequest_TESTSE0NAK);
while (1);
//break; not reached
default:
USBD_Stall(0);
break;
}
// The exit action is to power cycle the device.
// The device must be disconnected from the host
}
#endif
//------------------------------------------------------------------------------
// Exported functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// Initializes a USBDDriver instance with a list of descriptors. If
/// interfaces can have multiple alternate settings, an array to store the
/// current setting for each interface must be provided.
/// \param pDriver Pointer to a USBDDriver instance.
/// \param pDescriptors Pointer to a USBDDriverDescriptors instance.
/// \param pInterfaces Pointer to an array for storing the current alternate
/// setting of each interface (optional).
//------------------------------------------------------------------------------
void USBDDriver_Initialize(
USBDDriver *pDriver,
const USBDDriverDescriptors *pDescriptors,
unsigned char *pInterfaces)
{
pDriver->cfgnum = 0;
#if (BOARD_USB_BMATTRIBUTES == USBConfigurationDescriptor_SELFPOWERED_RWAKEUP) \
|| (BOARD_USB_BMATTRIBUTES == USBConfigurationDescriptor_BUSPOWERED_RWAKEUP)
pDriver->isRemoteWakeUpEnabled = 1;
#else
pDriver->isRemoteWakeUpEnabled = 0;
#endif
pDriver->pDescriptors = pDescriptors;
pDriver->pInterfaces = pInterfaces;
// Initialize interfaces array if not null
if (pInterfaces != 0) {
memset(pInterfaces, sizeof(pInterfaces), 0);
}
}
//------------------------------------------------------------------------------
/// Handles the given request if it is standard, otherwise STALLs it.
/// \param pDriver Pointer to a USBDDriver instance.
/// \param pRequest Pointer to a USBGenericRequest instance.
//------------------------------------------------------------------------------
void USBDDriver_RequestHandler(
USBDDriver *pDriver,
const USBGenericRequest *pRequest)
{
unsigned char cfgnum;
unsigned char infnum;
unsigned char eptnum;
unsigned char setting;
unsigned char type;
unsigned char indexDesc;
unsigned int length;
unsigned int address;
TRACE_INFO_WP("Std ");
// Check request code
switch (USBGenericRequest_GetRequest(pRequest)) {
case USBGenericRequest_GETDESCRIPTOR:
TRACE_INFO_WP("gDesc ");
// Send the requested descriptor
type = USBGetDescriptorRequest_GetDescriptorType(pRequest);
indexDesc = USBGetDescriptorRequest_GetDescriptorIndex(pRequest);
length = USBGenericRequest_GetLength(pRequest);
GetDescriptor(pDriver, type, indexDesc, length);
break;
case USBGenericRequest_SETADDRESS:
TRACE_INFO_WP("sAddr ");
// Sends a zero-length packet and then set the device address
address = USBSetAddressRequest_GetAddress(pRequest);
USBD_Write(0, 0, 0, (TransferCallback) USBD_SetAddress, (void *) address);
break;
case USBGenericRequest_SETCONFIGURATION:
TRACE_INFO_WP("sCfg ");
// Set the requested configuration
cfgnum = USBSetConfigurationRequest_GetConfiguration(pRequest);
SetConfiguration(pDriver, cfgnum);
break;
case USBGenericRequest_GETCONFIGURATION:
TRACE_INFO_WP("gCfg ");
// Send the current configuration number
GetConfiguration(pDriver);
break;
case USBGenericRequest_GETSTATUS:
TRACE_INFO_WP("gSta ");
// Check who is the recipient
switch (USBGenericRequest_GetRecipient(pRequest)) {
case USBGenericRequest_DEVICE:
TRACE_INFO_WP("Dev ");
// Send the device status
GetDeviceStatus(pDriver);
break;
case USBGenericRequest_ENDPOINT:
TRACE_INFO_WP("Ept ");
// Send the endpoint status
eptnum = USBGenericRequest_GetEndpointNumber(pRequest);
GetEndpointStatus(eptnum);
break;
default:
TRACE_WARNING(
"USBDDriver_RequestHandler: Unknown recipient (%d)\n\r",
USBGenericRequest_GetRecipient(pRequest));
USBD_Stall(0);
}
break;
case USBGenericRequest_CLEARFEATURE:
TRACE_INFO_WP("cFeat ");
// Check which is the requested feature
switch (USBFeatureRequest_GetFeatureSelector(pRequest)) {
case USBFeatureRequest_ENDPOINTHALT:
TRACE_INFO_WP("Hlt ");
// Unhalt endpoint and send a zero-length packet
USBD_Unhalt(USBGenericRequest_GetEndpointNumber(pRequest));
USBD_Write(0, 0, 0, 0, 0);
break;
case USBFeatureRequest_DEVICEREMOTEWAKEUP:
TRACE_INFO_WP("RmWU ");
// Disable remote wake-up and send a zero-length packet
pDriver->isRemoteWakeUpEnabled = 0;
USBD_Write(0, 0, 0, 0, 0);
break;
default:
TRACE_WARNING(
"USBDDriver_RequestHandler: Unknown feature selector (%d)\n\r",
USBFeatureRequest_GetFeatureSelector(pRequest));
USBD_Stall(0);
}
break;
case USBGenericRequest_SETFEATURE:
TRACE_INFO_WP("sFeat ");
// Check which is the selected feature
switch (USBFeatureRequest_GetFeatureSelector(pRequest)) {
case USBFeatureRequest_DEVICEREMOTEWAKEUP:
TRACE_INFO_WP("RmWU ");
// Enable remote wake-up and send a ZLP
pDriver->isRemoteWakeUpEnabled = 1;
USBD_Write(0, 0, 0, 0, 0);
break;
case USBFeatureRequest_ENDPOINTHALT:
TRACE_INFO_WP("Halt ");
// Halt endpoint
USBD_Halt(USBGenericRequest_GetEndpointNumber(pRequest));
USBD_Write(0, 0, 0, 0, 0);
break;
#if defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS)
case USBFeatureRequest_TESTMODE:
// 7.1.20 Test Mode Support
if ((USBGenericRequest_GetType(pRequest) == USBGenericRequest_DEVICE)
&& ((USBGenericRequest_GetIndex(pRequest) & 0x000F) == 0)) {
// Handle test request
USBDDriver_Test(USBFeatureRequest_GetTestSelector(pRequest));
}
else {
USBD_Stall(0);
}
break;
#endif
#if defined(CHIP_USB_OTGHS)
case USBFeatureRequest_OTG_B_HNP_ENABLE:
TRACE_INFO_WP("OTG_B_HNP_ENABLE ");
otg_features_supported |= 1<<USBFeatureRequest_OTG_B_HNP_ENABLE;
USBD_Write(0, 0, 0, 0, 0);
break;
case USBFeatureRequest_OTG_A_HNP_SUPPORT:
TRACE_INFO_WP("OTG_A_HNP_SUPPORT ");
otg_features_supported |= 1<<USBFeatureRequest_OTG_A_HNP_SUPPORT;
USBD_Write(0, 0, 0, 0, 0);
break;
case USBFeatureRequest_OTG_A_ALT_HNP_SUPPORT:
TRACE_INFO_WP("OTG_A_ALT_HNP_SUPPORT ");
otg_features_supported |= 1<<USBFeatureRequest_OTG_A_ALT_HNP_SUPPORT;
USBD_Write(0, 0, 0, 0, 0);
break;
#endif
default:
TRACE_WARNING(
"USBDDriver_RequestHandler: Unknown feature selector (%d)\n\r",
USBFeatureRequest_GetFeatureSelector(pRequest));
USBD_Stall(0);
}
break;
case USBGenericRequest_SETINTERFACE:
TRACE_INFO_WP("sInterface ");
infnum = USBInterfaceRequest_GetInterface(pRequest);
setting = USBInterfaceRequest_GetAlternateSetting(pRequest);
SetInterface(pDriver, infnum, setting);
break;
case USBGenericRequest_GETINTERFACE:
TRACE_INFO_WP("gInterface ");
infnum = USBInterfaceRequest_GetInterface(pRequest);
GetInterface(pDriver, infnum);
break;
default:
TRACE_WARNING(
"USBDDriver_RequestHandler: Unknown request code (%d)\n\r",
USBGenericRequest_GetRequest(pRequest));
USBD_Stall(0);
}
}
//------------------------------------------------------------------------------
/// Test if RemoteWakeUP feature is enabled
/// \param pDriver Pointer to an USBDDriver instance.
/// \return 1 if remote wake up has been enabled by the host; otherwise, returns
/// 0
//------------------------------------------------------------------------------
unsigned char USBDDriver_IsRemoteWakeUpEnabled(const USBDDriver *pDriver)
{
return pDriver->isRemoteWakeUpEnabled;
}
#if defined(CHIP_USB_OTGHS)
//------------------------------------------------------------------------------
/// Return OTG features supported
/// \return the OTG features
//------------------------------------------------------------------------------
unsigned char USBDDriver_returnOTGFeatures(void)
{
return otg_features_supported;
}
//------------------------------------------------------------------------------
/// Clear OTG features supported
/// \return none
//------------------------------------------------------------------------------
void USBDDriver_clearOTGFeatures(void)
{
otg_features_supported = 0;
}
#endif