|
// DOM-IGNORE-BEGIN
|
|
/*******************************************************************************
|
|
Copyright 2015 Microchip Technology Inc. (www.microchip.com)
|
|
|
|
Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
|
|
|
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.
|
|
|
|
To request to license the code under the MLA license (www.microchip.com/mla_license),
|
|
please contact mla_licensing@microchip.com
|
|
*******************************************************************************/
|
|
//DOM-IGNORE-END
|
|
|
|
/*******************************************************************************
|
|
USB Device Layer
|
|
|
|
Company:
|
|
Microchip Technology Inc.
|
|
|
|
File Name:
|
|
usb_device.c
|
|
|
|
Summary:
|
|
Provides basic USB device functionality, including enumeration and USB
|
|
chapter 9 required behavior.
|
|
|
|
Description:
|
|
Provides basic USB device functionality, including enumeration and USB
|
|
chapter 9 required behavior.
|
|
*******************************************************************************/
|
|
|
|
// *****************************************************************************
|
|
// *****************************************************************************
|
|
// Section: Included Files
|
|
// *****************************************************************************
|
|
// *****************************************************************************
|
|
#include <xc.h>
|
|
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
|
|
#include "usb_config.h"
|
|
|
|
#include "usb.h"
|
|
#include "usb_ch9.h"
|
|
#include "usb_device.h"
|
|
#include "usb_device_local.h"
|
|
|
|
#ifndef uintptr_t
|
|
#if defined(__XC8__) || defined(__XC16__)
|
|
#define uintptr_t uint16_t
|
|
#elif defined (__XC32__)
|
|
#define uintptr_t uint32_t
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(USB_USE_MSD)
|
|
#include "usb_device_msd.h"
|
|
#endif
|
|
|
|
// *****************************************************************************
|
|
// *****************************************************************************
|
|
// Section: File Scope or Global Constants
|
|
// *****************************************************************************
|
|
// *****************************************************************************
|
|
#if !defined(USE_USB_BUS_SENSE_IO)
|
|
//Assume the +5V VBUS is always present (like it would be in a bus powered
|
|
//only application), unless USE_USB_BUS_SENSE_IO and USB_BUS_SENSE have
|
|
//been properly defined elsewhere in the project.
|
|
#undef USB_BUS_SENSE
|
|
#define USB_BUS_SENSE 1
|
|
#endif
|
|
|
|
#if defined(USB_DEVICE_DISABLE_DTS_CHECKING)
|
|
#define _DTS_CHECKING_ENABLED 0
|
|
#else
|
|
#define _DTS_CHECKING_ENABLED _DTSEN
|
|
#endif
|
|
|
|
#if !defined(self_power)
|
|
//Assume the application is always bus powered, unless self_power has been
|
|
//defined elsewhere in the project
|
|
#define self_power 0 //0 = bus powered
|
|
#endif
|
|
|
|
#if !defined(USB_MAX_NUM_CONFIG_DSC)
|
|
//Assume the application only implements one configuration descriptor,
|
|
//unless otherwise specified elsewhere in the project
|
|
#define USB_MAX_NUM_CONFIG_DSC 1
|
|
#endif
|
|
|
|
#if defined(__XC8)
|
|
//Suppress expected/harmless compiler warning message about unused RAM variables
|
|
//and certain function pointer usage.
|
|
//Certain variables and function pointers are not used if you don't use all
|
|
//of the USB stack APIs. However, these variables should not be
|
|
//removed (since they are still used/needed in some applications, and this
|
|
//is a common file shared by many projects, some of which rely on the "unused"
|
|
//variables/function pointers).
|
|
#pragma warning disable 1090
|
|
#if __XC8_VERSION > 1300
|
|
#pragma warning disable 1471
|
|
#endif
|
|
#endif
|
|
|
|
// *****************************************************************************
|
|
// *****************************************************************************
|
|
// Section: File Scope Data Types
|
|
// *****************************************************************************
|
|
// *****************************************************************************
|
|
typedef union
|
|
{
|
|
uint8_t Val;
|
|
struct __PACKED
|
|
{
|
|
unsigned b0:1;
|
|
unsigned b1:1;
|
|
unsigned b2:1;
|
|
unsigned b3:1;
|
|
unsigned b4:1;
|
|
unsigned b5:1;
|
|
unsigned b6:1;
|
|
unsigned b7:1;
|
|
} bits;
|
|
} uint8_t_VAL, uint8_t_BITS;
|
|
|
|
// *****************************************************************************
|
|
// *****************************************************************************
|
|
// Section: Variables
|
|
// *****************************************************************************
|
|
// *****************************************************************************
|
|
USB_VOLATILE USB_DEVICE_STATE USBDeviceState;
|
|
USB_VOLATILE uint8_t USBActiveConfiguration;
|
|
USB_VOLATILE uint8_t USBAlternateInterface[USB_MAX_NUM_INT];
|
|
volatile BDT_ENTRY *pBDTEntryEP0OutCurrent;
|
|
volatile BDT_ENTRY *pBDTEntryEP0OutNext;
|
|
volatile BDT_ENTRY *pBDTEntryOut[USB_MAX_EP_NUMBER+1];
|
|
volatile BDT_ENTRY *pBDTEntryIn[USB_MAX_EP_NUMBER+1];
|
|
USB_VOLATILE uint8_t shortPacketStatus;
|
|
USB_VOLATILE uint8_t controlTransferState;
|
|
USB_VOLATILE IN_PIPE inPipes[1];
|
|
USB_VOLATILE OUT_PIPE outPipes[1];
|
|
USB_VOLATILE uint8_t *pDst;
|
|
USB_VOLATILE bool RemoteWakeup;
|
|
USB_VOLATILE bool USBBusIsSuspended;
|
|
USB_VOLATILE USTAT_FIELDS USTATcopy;
|
|
USB_VOLATILE uint8_t endpoint_number;
|
|
USB_VOLATILE bool BothEP0OutUOWNsSet;
|
|
USB_VOLATILE EP_STATUS ep_data_in[USB_MAX_EP_NUMBER+1];
|
|
USB_VOLATILE EP_STATUS ep_data_out[USB_MAX_EP_NUMBER+1];
|
|
USB_VOLATILE uint8_t USBStatusStageTimeoutCounter;
|
|
volatile bool USBDeferStatusStagePacket;
|
|
volatile bool USBStatusStageEnabledFlag1;
|
|
volatile bool USBStatusStageEnabledFlag2;
|
|
volatile bool USBDeferINDataStagePackets;
|
|
volatile bool USBDeferOUTDataStagePackets;
|
|
USB_VOLATILE uint32_t USB1msTickCount;
|
|
USB_VOLATILE uint8_t USBTicksSinceSuspendEnd;
|
|
|
|
/** USB FIXED LOCATION VARIABLES ***********************************/
|
|
#if defined(COMPILER_MPLAB_C18)
|
|
#pragma udata USB_BDT=USB_BDT_ADDRESS
|
|
#endif
|
|
|
|
volatile BDT_ENTRY BDT[BDT_NUM_ENTRIES] BDT_BASE_ADDR_TAG;
|
|
|
|
/********************************************************************
|
|
* EP0 Buffer Space
|
|
*******************************************************************/
|
|
volatile CTRL_TRF_SETUP SetupPkt CTRL_TRF_SETUP_ADDR_TAG;
|
|
volatile uint8_t CtrlTrfData[USB_EP0_BUFF_SIZE] CTRL_TRF_DATA_ADDR_TAG;
|
|
|
|
/********************************************************************
|
|
* non-EP0 Buffer Space
|
|
*******************************************************************/
|
|
#if defined(USB_USE_MSD)
|
|
//Check if the MSD application specific USB endpoint buffer placement address
|
|
//macros have already been defined or not (ex: in a processor specific header)
|
|
//The msd_cbw and msd_csw buffers must be USB module accessible (and therefore
|
|
//must be at a certain address range on certain microcontrollers).
|
|
#if !defined(MSD_CBW_ADDR_TAG)
|
|
//Not previously defined. Assume in this case all microcontroller RAM is
|
|
//USB module accessible, and therefore, no specific address tag value is needed.
|
|
#define MSD_CBW_ADDR_TAG
|
|
#define MSD_CSW_ADDR_TAG
|
|
#endif
|
|
volatile USB_MSD_CBW msd_cbw MSD_CBW_ADDR_TAG; //Must be located in USB module accessible RAM
|
|
volatile USB_MSD_CSW msd_csw MSD_CSW_ADDR_TAG; //Must be located in USB module accessible RAM
|
|
|
|
#if defined(__18CXX) || defined(__XC8)
|
|
#if(__XC8_VERSION < 2000)
|
|
volatile char msd_buffer[512] @ MSD_BUFFER_ADDRESS;
|
|
#else
|
|
volatile char msd_buffer[512] __at(MSD_BUFFER_ADDRESS);
|
|
#endif
|
|
#else
|
|
volatile char msd_buffer[512];
|
|
#endif
|
|
#endif
|
|
|
|
//Depricated in v2.2 - will be removed in a future revision
|
|
#if !defined(USB_USER_DEVICE_DESCRIPTOR)
|
|
//Device descriptor
|
|
extern const USB_DEVICE_DESCRIPTOR device_dsc;
|
|
#else
|
|
USB_USER_DEVICE_DESCRIPTOR_INCLUDE;
|
|
#endif
|
|
|
|
#if !defined(USB_USER_CONFIG_DESCRIPTOR)
|
|
//Array of configuration descriptors
|
|
extern const uint8_t *const USB_CD_Ptr[];
|
|
#else
|
|
USB_USER_CONFIG_DESCRIPTOR_INCLUDE;
|
|
#endif
|
|
|
|
extern const uint8_t *const USB_SD_Ptr[];
|
|
|
|
|
|
// *****************************************************************************
|
|
// *****************************************************************************
|
|
// Section: Private and External Prototypes
|
|
// *****************************************************************************
|
|
// *****************************************************************************
|
|
extern bool USER_USB_CALLBACK_EVENT_HANDLER(USB_EVENT event, void *pdata, uint16_t size);
|
|
|
|
static void USBCtrlEPService(void);
|
|
static void USBCtrlTrfSetupHandler(void);
|
|
static void USBCtrlTrfInHandler(void);
|
|
static void USBCheckStdRequest(void);
|
|
static void USBStdGetDscHandler(void);
|
|
static void USBCtrlEPServiceComplete(void);
|
|
static void USBCtrlTrfTxService(void);
|
|
static void USBCtrlTrfRxService(void);
|
|
static void USBStdSetCfgHandler(void);
|
|
static void USBStdGetStatusHandler(void);
|
|
static void USBStdFeatureReqHandler(void);
|
|
static void USBCtrlTrfOutHandler(void);
|
|
static void USBConfigureEndpoint(uint8_t EPNum, uint8_t direction);
|
|
static void USBWakeFromSuspend(void);
|
|
static void USBSuspend(void);
|
|
static void USBStallHandler(void);
|
|
|
|
// *****************************************************************************
|
|
// *****************************************************************************
|
|
// Section: Macros or Functions
|
|
// *****************************************************************************
|
|
// *****************************************************************************
|
|
|
|
/**************************************************************************
|
|
Function:
|
|
void USBDeviceInit(void)
|
|
|
|
Description:
|
|
This function initializes the device stack it in the default state. The
|
|
USB module will be completely reset including all of the internal
|
|
variables, registers, and interrupt flags.
|
|
|
|
Precondition:
|
|
This function must be called before any of the other USB Device
|
|
functions can be called, including USBDeviceTasks().
|
|
|
|
Parameters:
|
|
None
|
|
|
|
Return Values:
|
|
None
|
|
|
|
Remarks:
|
|
None
|
|
|
|
***************************************************************************/
|
|
void USBDeviceInit(void)
|
|
{
|
|
uint8_t i;
|
|
|
|
USBDisableInterrupts();
|
|
|
|
//Make sure that if a GPIO output driver exists on VBUS, that it is
|
|
//tri-stated to avoid potential contention with the host
|
|
USB_HAL_VBUSTristate();
|
|
|
|
// Clear all USB error flags
|
|
USBClearInterruptRegister(U1EIR);
|
|
|
|
// Clears all USB interrupts
|
|
USBClearInterruptRegister(U1IR);
|
|
|
|
//Clear all of the endpoint control registers
|
|
U1EP0 = 0;
|
|
|
|
DisableNonZeroEndpoints(USB_MAX_EP_NUMBER);
|
|
|
|
SetConfigurationOptions();
|
|
|
|
//power up the module (if not already powered)
|
|
USBPowerModule();
|
|
|
|
//set the address of the BDT (if applicable)
|
|
USBSetBDTAddress(BDT);
|
|
|
|
//Clear all of the BDT entries
|
|
for(i = 0; i < (sizeof(BDT)/sizeof(BDT_ENTRY)); i++)
|
|
{
|
|
BDT[i].Val = 0x00;
|
|
}
|
|
|
|
// Assert reset request to all of the Ping Pong buffer pointers
|
|
USBPingPongBufferReset = 1;
|
|
|
|
// Reset to default address
|
|
U1ADDR = 0x00;
|
|
|
|
// Make sure packet processing is enabled
|
|
USBPacketDisable = 0;
|
|
|
|
//Stop trying to reset ping pong buffer pointers
|
|
USBPingPongBufferReset = 0;
|
|
|
|
// Flush any pending transactions
|
|
do
|
|
{
|
|
USBClearInterruptFlag(USBTransactionCompleteIFReg,USBTransactionCompleteIFBitNum);
|
|
//Initialize USB stack software state variables
|
|
inPipes[0].info.Val = 0;
|
|
outPipes[0].info.Val = 0;
|
|
outPipes[0].wCount.Val = 0;
|
|
}while(USBTransactionCompleteIF == 1);
|
|
|
|
//Set flags to true, so the USBCtrlEPAllowStatusStage() function knows not to
|
|
//try and arm a status stage, even before the first control transfer starts.
|
|
USBStatusStageEnabledFlag1 = true;
|
|
USBStatusStageEnabledFlag2 = true;
|
|
//Initialize other flags
|
|
USBDeferINDataStagePackets = false;
|
|
USBDeferOUTDataStagePackets = false;
|
|
USBBusIsSuspended = false;
|
|
|
|
//Initialize all pBDTEntryIn[] and pBDTEntryOut[]
|
|
//pointers to NULL, so they don't get used inadvertently.
|
|
for(i = 0; i < (uint8_t)(USB_MAX_EP_NUMBER+1u); i++)
|
|
{
|
|
pBDTEntryIn[i] = 0u;
|
|
pBDTEntryOut[i] = 0u;
|
|
ep_data_in[i].Val = 0u;
|
|
ep_data_out[i].Val = 0u;
|
|
}
|
|
|
|
//Get ready for the first packet
|
|
pBDTEntryIn[0] = (volatile BDT_ENTRY*)&BDT[EP0_IN_EVEN];
|
|
// Initialize EP0 as a Ctrl EP
|
|
U1EP0 = EP_CTRL|USB_HANDSHAKE_ENABLED;
|
|
//Prepare for the first SETUP on EP0 OUT
|
|
BDT[EP0_OUT_EVEN].ADR = ConvertToPhysicalAddress(&SetupPkt);
|
|
BDT[EP0_OUT_EVEN].CNT = USB_EP0_BUFF_SIZE;
|
|
BDT[EP0_OUT_EVEN].STAT.Val = _DAT0|_BSTALL;
|
|
BDT[EP0_OUT_EVEN].STAT.Val |= _USIE;
|
|
|
|
// Clear active configuration
|
|
USBActiveConfiguration = 0;
|
|
|
|
USB1msTickCount = 0; //Keeps track of total number of milliseconds since calling USBDeviceInit() when first initializing the USB module/stack code.
|
|
USBTicksSinceSuspendEnd = 0; //Keeps track of the number of milliseconds since a suspend condition has ended.
|
|
|
|
//Indicate that we are now in the detached state
|
|
USBDeviceState = DETACHED_STATE;
|
|
}
|
|
|
|
|
|
|
|
/**************************************************************************
|
|
Function:
|
|
void USBDeviceTasks(void)
|
|
|
|
Summary:
|
|
This function is the main state machine/transaction handler of the USB
|
|
device side stack. When the USB stack is operated in "USB_POLLING" mode
|
|
(usb_config.h user option) the USBDeviceTasks() function should be called
|
|
periodically to receive and transmit packets through the stack. This
|
|
function also takes care of control transfers associated with the USB
|
|
enumeration process, and detecting various USB events (such as suspend).
|
|
This function should be called at least once every 1.8ms during the USB
|
|
enumeration process. After the enumeration process is complete (which can
|
|
be determined when USBGetDeviceState() returns CONFIGURED_STATE), the
|
|
USBDeviceTasks() handler may be called the faster of: either once
|
|
every 9.8ms, or as often as needed to make sure that the hardware USTAT
|
|
FIFO never gets full. A good rule of thumb is to call USBDeviceTasks() at
|
|
a minimum rate of either the frequency that USBTransferOnePacket() gets
|
|
called, or, once/1.8ms, whichever is faster. See the inline code comments
|
|
near the top of usb_device.c for more details about minimum timing
|
|
requirements when calling USBDeviceTasks().
|
|
|
|
When the USB stack is operated in "USB_INTERRUPT" mode, it is not necessary
|
|
to call USBDeviceTasks() from the main loop context. In the USB_INTERRUPT
|
|
mode, the USBDeviceTasks() handler only needs to execute when a USB
|
|
interrupt occurs, and therefore only needs to be called from the interrupt
|
|
context.
|
|
|
|
Description:
|
|
This function is the main state machine/transaction handler of the USB
|
|
device side stack. When the USB stack is operated in "USB_POLLING" mode
|
|
(usb_config.h user option) the USBDeviceTasks() function should be called
|
|
periodically to receive and transmit packets through the stack. This
|
|
function also takes care of control transfers associated with the USB
|
|
enumeration process, and detecting various USB events (such as suspend).
|
|
This function should be called at least once every 1.8ms during the USB
|
|
enumeration process. After the enumeration process is complete (which can
|
|
be determined when USBGetDeviceState() returns CONFIGURED_STATE), the
|
|
USBDeviceTasks() handler may be called the faster of: either once
|
|
every 9.8ms, or as often as needed to make sure that the hardware USTAT
|
|
FIFO never gets full. A good rule of thumb is to call USBDeviceTasks() at
|
|
a minimum rate of either the frequency that USBTransferOnePacket() gets
|
|
called, or, once/1.8ms, whichever is faster. See the inline code comments
|
|
near the top of usb_device.c for more details about minimum timing
|
|
requirements when calling USBDeviceTasks().
|
|
|
|
When the USB stack is operated in "USB_INTERRUPT" mode, it is not necessary
|
|
to call USBDeviceTasks() from the main loop context. In the USB_INTERRUPT
|
|
mode, the USBDeviceTasks() handler only needs to execute when a USB
|
|
interrupt occurs, and therefore only needs to be called from the interrupt
|
|
context.
|
|
|
|
Typical usage:
|
|
<code>
|
|
void main(void)
|
|
{
|
|
USBDeviceInit();
|
|
while(1)
|
|
{
|
|
USBDeviceTasks(); //Takes care of enumeration and other USB events
|
|
if((USBGetDeviceState() \< CONFIGURED_STATE) ||
|
|
(USBIsDeviceSuspended() == true))
|
|
{
|
|
//Either the device is not configured or we are suspended,
|
|
// so we don't want to execute any USB related application code
|
|
continue; //go back to the top of the while loop
|
|
}
|
|
else
|
|
{
|
|
//Otherwise we are free to run USB and non-USB related user
|
|
//application code.
|
|
UserApplication();
|
|
}
|
|
}
|
|
}
|
|
</code>
|
|
|
|
Precondition:
|
|
Make sure the USBDeviceInit() function has been called prior to calling
|
|
USBDeviceTasks() for the first time.
|
|
Remarks:
|
|
USBDeviceTasks() does not need to be called while in the USB suspend mode,
|
|
if the user application firmware in the USBCBSuspend() callback function
|
|
enables the ACTVIF USB interrupt source and put the microcontroller into
|
|
sleep mode. If the application firmware decides not to sleep the
|
|
microcontroller core during USB suspend (ex: continues running at full
|
|
frequency, or clock switches to a lower frequency), then the USBDeviceTasks()
|
|
function must still be called periodically, at a rate frequent enough to
|
|
ensure the 10ms resume recovery interval USB specification is met. Assuming
|
|
a worst case primary oscillator and PLL start up time of less than 5ms, then
|
|
USBDeviceTasks() should be called once every 5ms in this scenario.
|
|
|
|
When the USB cable is detached, or the USB host is not actively powering
|
|
the VBUS line to +5V nominal, the application firmware does not always have
|
|
to call USBDeviceTasks() frequently, as no USB activity will be taking
|
|
place. However, if USBDeviceTasks() is not called regularly, some
|
|
alternative means of promptly detecting when VBUS is powered (indicating
|
|
host attachment), or not powered (host powered down or USB cable unplugged)
|
|
is still needed. For self or dual self/bus powered USB applications, see
|
|
the USBDeviceAttach() and USBDeviceDetach() API documentation for additional
|
|
considerations.
|
|
***************************************************************************/
|
|
void USBDeviceTasks(void)
|
|
{
|
|
uint8_t i;
|
|
|
|
#ifdef USB_SUPPORT_OTG
|
|
//SRP Time Out Check
|
|
if (USBOTGSRPIsReady())
|
|
{
|
|
if (USBT1MSECIF && USBT1MSECIE)
|
|
{
|
|
if (USBOTGGetSRPTimeOutFlag())
|
|
{
|
|
if (USBOTGIsSRPTimeOutExpired())
|
|
{
|
|
USB_OTGEventHandler(0,OTG_EVENT_SRP_FAILED,0,0);
|
|
}
|
|
}
|
|
|
|
//Clear Interrupt Flag
|
|
USBClearInterruptFlag(USBT1MSECIFReg,USBT1MSECIFBitNum);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(USB_POLLING)
|
|
//If the interrupt option is selected then the customer is required
|
|
// to notify the stack when the device is attached or removed from the
|
|
// bus by calling the USBDeviceAttach() and USBDeviceDetach() functions.
|
|
if (USB_BUS_SENSE != 1)
|
|
{
|
|
// Disable module & detach from bus
|
|
U1CON = 0;
|
|
|
|
// Mask all USB interrupts
|
|
U1IE = 0;
|
|
|
|
//Move to the detached state
|
|
USBDeviceState = DETACHED_STATE;
|
|
|
|
#ifdef USB_SUPPORT_OTG
|
|
//Disable D+ Pullup
|
|
U1OTGCONbits.DPPULUP = 0;
|
|
|
|
//Disable HNP
|
|
USBOTGDisableHnp();
|
|
|
|
//Deactivate HNP
|
|
USBOTGDeactivateHnp();
|
|
|
|
//If ID Pin Changed State
|
|
if (USBIDIF && USBIDIE)
|
|
{
|
|
//Re-detect & Initialize
|
|
USBOTGInitialize();
|
|
|
|
//Clear ID Interrupt Flag
|
|
USBClearInterruptFlag(USBIDIFReg,USBIDIFBitNum);
|
|
}
|
|
#endif
|
|
|
|
#if defined __C30__ || defined __XC16__
|
|
//USBClearInterruptFlag(U1OTGIR, 3);
|
|
#endif
|
|
//return so that we don't go through the rest of
|
|
//the state machine
|
|
USBClearUSBInterrupt();
|
|
return;
|
|
}
|
|
|
|
#ifdef USB_SUPPORT_OTG
|
|
//If Session Is Started Then
|
|
else
|
|
{
|
|
//If SRP Is Ready
|
|
if (USBOTGSRPIsReady())
|
|
{
|
|
//Clear SRPReady
|
|
USBOTGClearSRPReady();
|
|
|
|
//Clear SRP Timeout Flag
|
|
USBOTGClearSRPTimeOutFlag();
|
|
|
|
//Indicate Session Started
|
|
UART2PrintString( "\r\n***** USB OTG B Event - Session Started *****\r\n" );
|
|
}
|
|
}
|
|
#endif //#ifdef USB_SUPPORT_OTG
|
|
|
|
//if we are in the detached state
|
|
if(USBDeviceState == DETACHED_STATE)
|
|
{
|
|
//Initialize register to known value
|
|
U1CON = 0;
|
|
|
|
// Mask all USB interrupts
|
|
U1IE = 0;
|
|
|
|
//Enable/set things like: pull ups, full/low-speed mode,
|
|
//set the ping pong mode, and set internal transceiver
|
|
SetConfigurationOptions();
|
|
|
|
// Enable module & attach to bus
|
|
while(!U1CONbits.USBEN){U1CONbits.USBEN = 1;}
|
|
|
|
//moved to the attached state
|
|
USBDeviceState = ATTACHED_STATE;
|
|
|
|
#ifdef USB_SUPPORT_OTG
|
|
U1OTGCON |= USB_OTG_DPLUS_ENABLE | USB_OTG_ENABLE;
|
|
#endif
|
|
}
|
|
#endif //#if defined(USB_POLLING)
|
|
|
|
if(USBDeviceState == ATTACHED_STATE)
|
|
{
|
|
/*
|
|
* After enabling the USB module, it takes some time for the
|
|
* voltage on the D+ or D- line to rise high enough to get out
|
|
* of the SE0 condition. The USB Reset interrupt should not be
|
|
* unmasked until the SE0 condition is cleared. This helps
|
|
* prevent the firmware from misinterpreting this unique event
|
|
* as a USB bus reset from the USB host.
|
|
*/
|
|
|
|
if(!USBSE0Event)
|
|
{
|
|
//We recently attached, make sure we are in a clean state
|
|
#if defined(__dsPIC33E__) || defined(_PIC24E__) || defined(__PIC32MM__)
|
|
U1IR = 0xFFEF; //Preserve IDLEIF info, so we can detect suspend
|
|
//during attach de-bounce interval
|
|
#else
|
|
USBClearInterruptRegister(U1IR);
|
|
#endif
|
|
|
|
#if defined(USB_POLLING)
|
|
U1IE=0; // Mask all USB interrupts
|
|
#endif
|
|
USBResetIE = 1; // Unmask RESET interrupt
|
|
USBIdleIE = 1; // Unmask IDLE interrupt
|
|
USBDeviceState = POWERED_STATE;
|
|
}
|
|
}
|
|
|
|
#ifdef USB_SUPPORT_OTG
|
|
//If ID Pin Changed State
|
|
if (USBIDIF && USBIDIE)
|
|
{
|
|
//Re-detect & Initialize
|
|
USBOTGInitialize();
|
|
|
|
USBClearInterruptFlag(USBIDIFReg,USBIDIFBitNum);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Task A: Service USB Activity Interrupt
|
|
*/
|
|
if(USBActivityIF && USBActivityIE)
|
|
{
|
|
USBClearInterruptFlag(USBActivityIFReg,USBActivityIFBitNum);
|
|
#if defined(USB_SUPPORT_OTG)
|
|
U1OTGIR = 0x10;
|
|
#else
|
|
USBWakeFromSuspend();
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Pointless to continue servicing if the device is in suspend mode.
|
|
*/
|
|
if(USBSuspendControl==1)
|
|
{
|
|
USBClearUSBInterrupt();
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Task B: Service USB Bus Reset Interrupt.
|
|
* When bus reset is received during suspend, ACTVIF will be set first,
|
|
* once the UCONbits.SUSPND is clear, then the URSTIF bit will be asserted.
|
|
* This is why URSTIF is checked after ACTVIF.
|
|
*
|
|
* The USB reset flag is masked when the USB state is in
|
|
* DETACHED_STATE or ATTACHED_STATE, and therefore cannot
|
|
* cause a USB reset event during these two states.
|
|
*/
|
|
if(USBResetIF && USBResetIE)
|
|
{
|
|
USBDeviceInit();
|
|
|
|
//Re-enable the interrupts since the USBDeviceInit() function will
|
|
// disable them. This will do nothing in a polling setup
|
|
USBUnmaskInterrupts();
|
|
|
|
USBDeviceState = DEFAULT_STATE;
|
|
|
|
#ifdef USB_SUPPORT_OTG
|
|
//Disable HNP
|
|
USBOTGDisableHnp();
|
|
|
|
//Deactivate HNP
|
|
USBOTGDeactivateHnp();
|
|
#endif
|
|
|
|
USBClearInterruptFlag(USBResetIFReg,USBResetIFBitNum);
|
|
}
|
|
|
|
/*
|
|
* Task C: Service other USB interrupts
|
|
*/
|
|
if(USBIdleIF && USBIdleIE)
|
|
{
|
|
#ifdef USB_SUPPORT_OTG
|
|
//If Suspended, Try to switch to Host
|
|
USBOTGSelectRole(ROLE_HOST);
|
|
USBClearInterruptFlag(USBIdleIFReg,USBIdleIFBitNum);
|
|
#else
|
|
USBSuspend();
|
|
#endif
|
|
}
|
|
|
|
#if defined(__XC16__) || defined(__C30__) || defined(__XC32__)
|
|
//Check if a 1ms interval has elapsed.
|
|
if(USBT1MSECIF)
|
|
{
|
|
USBClearInterruptFlag(USBT1MSECIFReg, USBT1MSECIFBitNum);
|
|
USBIncrement1msInternalTimers();
|
|
}
|
|
#endif
|
|
|
|
//Start-of-Frame Interrupt
|
|
if(USBSOFIF)
|
|
{
|
|
//Call the user SOF event callback if enabled.
|
|
if(USBSOFIE)
|
|
{
|
|
USB_SOF_HANDLER(EVENT_SOF,0,1);
|
|
}
|
|
USBClearInterruptFlag(USBSOFIFReg,USBSOFIFBitNum);
|
|
|
|
#if defined(__XC8__) || defined(__C18__)
|
|
USBIncrement1msInternalTimers();
|
|
#endif
|
|
|
|
#if defined(USB_ENABLE_STATUS_STAGE_TIMEOUTS)
|
|
//Supporting this feature requires a 1ms time base for keeping track of the timeout interval.
|
|
#if(USB_SPEED_OPTION == USB_LOW_SPEED)
|
|
#warning "Double click this message. See inline code comments."
|
|
//The "USB_ENABLE_STATUS_STAGE_TIMEOUTS" feature is optional and is
|
|
//not strictly needed in all applications (ex: those that never call
|
|
//USBDeferStatusStage() and don't use host to device (OUT) control
|
|
//transfers with data stage).
|
|
//However, if this feature is enabled and used in a low speed application,
|
|
//it is required for the application code to periodically call the
|
|
//USBIncrement1msInternalTimers() function at a nominally 1ms rate.
|
|
#endif
|
|
|
|
//Decrement our status stage counter.
|
|
if(USBStatusStageTimeoutCounter != 0u)
|
|
{
|
|
USBStatusStageTimeoutCounter--;
|
|
}
|
|
//Check if too much time has elapsed since progress was made in
|
|
//processing the control transfer, without arming the status stage.
|
|
//If so, auto-arm the status stage to ensure that the control
|
|
//transfer can [eventually] complete, within the timing limits
|
|
//dictated by section 9.2.6 of the official USB 2.0 specifications.
|
|
if(USBStatusStageTimeoutCounter == 0)
|
|
{
|
|
USBCtrlEPAllowStatusStage(); //Does nothing if the status stage was already armed.
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if(USBStallIF && USBStallIE)
|
|
{
|
|
USBStallHandler();
|
|
}
|
|
|
|
if(USBErrorIF && USBErrorIE)
|
|
{
|
|
USB_ERROR_HANDLER(EVENT_BUS_ERROR,0,1);
|
|
USBClearInterruptRegister(U1EIR); // This clears UERRIF
|
|
|
|
//On PIC18, clearing the source of the error will automatically clear
|
|
// the interrupt flag. On other devices the interrupt flag must be
|
|
// manually cleared.
|
|
#if defined(__C32__) || defined(__C30__) || defined __XC16__
|
|
USBClearInterruptFlag( USBErrorIFReg, USBErrorIFBitNum );
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Pointless to continue servicing if the host has not sent a bus reset.
|
|
* Once bus reset is received, the device transitions into the DEFAULT
|
|
* state and is ready for communication.
|
|
*/
|
|
if(USBDeviceState < DEFAULT_STATE)
|
|
{
|
|
USBClearUSBInterrupt();
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Task D: Servicing USB Transaction Complete Interrupt
|
|
*/
|
|
if(USBTransactionCompleteIE)
|
|
{
|
|
for(i = 0; i < 4u; i++) //Drain or deplete the USAT FIFO entries. If the USB FIFO ever gets full, USB bandwidth
|
|
{ //utilization can be compromised, and the device won't be able to receive SETUP packets.
|
|
if(USBTransactionCompleteIF)
|
|
{
|
|
//Save and extract USTAT register info. Will use this info later.
|
|
USTATcopy.Val = U1STAT;
|
|
endpoint_number = USBHALGetLastEndpoint(USTATcopy);
|
|
|
|
USBClearInterruptFlag(USBTransactionCompleteIFReg,USBTransactionCompleteIFBitNum);
|
|
|
|
//Keep track of the hardware ping pong state for endpoints other
|
|
//than EP0, if ping pong buffering is enabled.
|
|
#if (USB_PING_PONG_MODE == USB_PING_PONG__ALL_BUT_EP0) || (USB_PING_PONG_MODE == USB_PING_PONG__FULL_PING_PONG)
|
|
if(USBHALGetLastDirection(USTATcopy) == OUT_FROM_HOST)
|
|
{
|
|
ep_data_out[endpoint_number].bits.ping_pong_state ^= 1;
|
|
}
|
|
else
|
|
{
|
|
ep_data_in[endpoint_number].bits.ping_pong_state ^= 1;
|
|
}
|
|
#endif
|
|
|
|
//USBCtrlEPService only services transactions over EP0.
|
|
//It ignores all other EP transactions.
|
|
if(endpoint_number == 0)
|
|
{
|
|
USBCtrlEPService();
|
|
}
|
|
else
|
|
{
|
|
USB_TRANSFER_COMPLETE_HANDLER(EVENT_TRANSFER, (uint8_t*)&USTATcopy.Val, 0);
|
|
}
|
|
}//end if(USBTransactionCompleteIF)
|
|
else
|
|
{
|
|
break; //USTAT FIFO must be empty.
|
|
}
|
|
}//end for()
|
|
}//end if(USBTransactionCompleteIE)
|
|
|
|
USBClearUSBInterrupt();
|
|
}//end of USBDeviceTasks()
|
|
|
|
/*******************************************************************************
|
|
Function:
|
|
void USBEnableEndpoint(uint8_t ep, uint8_t options)
|
|
|
|
Summary:
|
|
This function will enable the specified endpoint with the specified
|
|
options
|
|
Description:
|
|
This function will enable the specified endpoint with the specified
|
|
options.
|
|
|
|
Typical Usage:
|
|
<code>
|
|
void USBCBInitEP(void)
|
|
{
|
|
USBEnableEndpoint(MSD_DATA_IN_EP,USB_IN_ENABLED|USB_OUT_ENABLED|USB_HANDSHAKE_ENABLED|USB_DISALLOW_SETUP);
|
|
USBMSDInit();
|
|
}
|
|
</code>
|
|
|
|
In the above example endpoint number MSD_DATA_IN_EP is being configured
|
|
for both IN and OUT traffic with handshaking enabled. Also since
|
|
MSD_DATA_IN_EP is not endpoint 0 (MSD does not allow this), then we can
|
|
explicitly disable SETUP packets on this endpoint.
|
|
Conditions:
|
|
None
|
|
Input:
|
|
uint8_t ep - the endpoint to be configured
|
|
uint8_t options - optional settings for the endpoint. The options should
|
|
be ORed together to form a single options string. The
|
|
available optional settings for the endpoint. The
|
|
options should be ORed together to form a single options
|
|
string. The available options are the following\:
|
|
* USB_HANDSHAKE_ENABLED enables USB handshaking (ACK,
|
|
NAK)
|
|
* USB_HANDSHAKE_DISABLED disables USB handshaking (ACK,
|
|
NAK)
|
|
* USB_OUT_ENABLED enables the out direction
|
|
* USB_OUT_DISABLED disables the out direction
|
|
* USB_IN_ENABLED enables the in direction
|
|
* USB_IN_DISABLED disables the in direction
|
|
* USB_ALLOW_SETUP enables control transfers
|
|
* USB_DISALLOW_SETUP disables control transfers
|
|
* USB_STALL_ENDPOINT STALLs this endpoint
|
|
Return:
|
|
None
|
|
Remarks:
|
|
None
|
|
*****************************************************************************/
|
|
void USBEnableEndpoint(uint8_t ep, uint8_t options)
|
|
{
|
|
unsigned char* p;
|
|
|
|
//Use USBConfigureEndpoint() to set up the pBDTEntryIn/Out[ep] pointer and
|
|
//starting DTS state in the BDT entry.
|
|
if(options & USB_OUT_ENABLED)
|
|
{
|
|
USBConfigureEndpoint(ep, OUT_FROM_HOST);
|
|
}
|
|
if(options & USB_IN_ENABLED)
|
|
{
|
|
USBConfigureEndpoint(ep, IN_TO_HOST);
|
|
}
|
|
|
|
//Update the relevant UEPx register to actually enable the endpoint with
|
|
//the specified options (ex: handshaking enabled, control transfers allowed,
|
|
//etc.)
|
|
#if defined(__C32__)
|
|
p = (unsigned char*)(&U1EP0+(4*ep));
|
|
#else
|
|
p = (unsigned char*)(&U1EP0+ep);
|
|
#endif
|
|
*p = options;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Function:
|
|
USB_HANDLE USBTransferOnePacket(uint8_t ep, uint8_t dir, uint8_t* data, uint8_t len)
|
|
|
|
Summary:
|
|
Transfers a single packet (one transaction) of data on the USB bus.
|
|
|
|
Description:
|
|
The USBTransferOnePacket() function prepares a USB endpoint
|
|
so that it may send data to the host (an IN transaction), or
|
|
receive data from the host (an OUT transaction). The
|
|
USBTransferOnePacket() function can be used both to receive and
|
|
send data to the host. This function is the primary API function
|
|
provided by the USB stack firmware for sending or receiving application
|
|
data over the USB port.
|
|
|
|
The USBTransferOnePacket() is intended for use with all application
|
|
endpoints. It is not used for sending or receiving application data
|
|
through endpoint 0 by using control transfers. Separate API
|
|
functions, such as USBEP0Receive(), USBEP0SendRAMPtr(), and
|
|
USBEP0SendROMPtr() are provided for this purpose.
|
|
|
|
The USBTransferOnePacket() writes to the Buffer Descriptor Table (BDT)
|
|
entry associated with an endpoint buffer, and sets the UOWN bit, which
|
|
prepares the USB hardware to allow the transaction to complete. The
|
|
application firmware can use the USBHandleBusy() macro to check the
|
|
status of the transaction, to see if the data has been successfully
|
|
transmitted yet.
|
|
|
|
|
|
Typical Usage
|
|
<code>
|
|
//make sure that the we are in the configured state
|
|
if(USBGetDeviceState() == CONFIGURED_STATE)
|
|
{
|
|
//make sure that the last transaction isn't busy by checking the handle
|
|
if(!USBHandleBusy(USBInHandle))
|
|
{
|
|
//Write the new data that we wish to send to the host to the INPacket[] array
|
|
INPacket[0] = USEFUL_APPLICATION_VALUE1;
|
|
INPacket[1] = USEFUL_APPLICATION_VALUE2;
|
|
//INPacket[2] = ... (fill in the rest of the packet data)
|
|
|
|
//Send the data contained in the INPacket[] array through endpoint "EP_NUM"
|
|
USBInHandle = USBTransferOnePacket(EP_NUM,IN_TO_HOST,(uint8_t*)&INPacket[0],sizeof(INPacket));
|
|
}
|
|
}
|
|
</code>
|
|
|
|
Conditions:
|
|
Before calling USBTransferOnePacket(), the following should be true.
|
|
1. The USB stack has already been initialized (USBDeviceInit() was called).
|
|
2. A transaction is not already pending on the specified endpoint. This
|
|
is done by checking the previous request using the USBHandleBusy()
|
|
macro (see the typical usage example).
|
|
3. The host has already sent a set configuration request and the
|
|
enumeration process is complete.
|
|
This can be checked by verifying that the USBGetDeviceState()
|
|
macro returns "CONFIGURED_STATE", prior to calling
|
|
USBTransferOnePacket().
|
|
|
|
Input:
|
|
uint8_t ep - The endpoint number that the data will be transmitted or
|
|
received on
|
|
uint8_t dir - The direction of the transfer
|
|
This value is either OUT_FROM_HOST or IN_TO_HOST
|
|
uint8_t* data - For IN transactions: pointer to the RAM buffer containing
|
|
the data to be sent to the host. For OUT transactions: pointer
|
|
to the RAM buffer that the received data should get written to.
|
|
uint8_t len - Length of the data needing to be sent (for IN transactions).
|
|
For OUT transactions, the len parameter should normally be set
|
|
to the endpoint size specified in the endpoint descriptor.
|
|
|
|
Return Values:
|
|
USB_HANDLE - handle to the transfer. The handle is a pointer to
|
|
the BDT entry associated with this transaction. The
|
|
status of the transaction (ex: if it is complete or still
|
|
pending) can be checked using the USBHandleBusy() macro
|
|
and supplying the USB_HANDLE provided by
|
|
USBTransferOnePacket().
|
|
|
|
Remarks:
|
|
If calling the USBTransferOnePacket() function from within the USBCBInitEP()
|
|
callback function, the set configuration is still being processed and the
|
|
USBDeviceState may not be == CONFIGURED_STATE yet. In this special case,
|
|
the USBTransferOnePacket() may still be called, but make sure that the
|
|
endpoint has been enabled and initialized by the USBEnableEndpoint()
|
|
function first.
|
|
|
|
*************************************************************************/
|
|
USB_HANDLE USBTransferOnePacket(uint8_t ep,uint8_t dir,uint8_t* data,uint8_t len)
|
|
{
|
|
volatile BDT_ENTRY* handle;
|
|
|
|
//If the direction is IN
|
|
if(dir != 0)
|
|
{
|
|
//point to the IN BDT of the specified endpoint
|
|
handle = pBDTEntryIn[ep];
|
|
}
|
|
else
|
|
{
|
|
//else point to the OUT BDT of the specified endpoint
|
|
handle = pBDTEntryOut[ep];
|
|
}
|
|
|
|
//Error checking code. Make sure the handle (pBDTEntryIn[ep] or
|
|
//pBDTEntryOut[ep]) is initialized before using it.
|
|
if(handle == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
//Toggle the DTS bit if required
|
|
#if (USB_PING_PONG_MODE == USB_PING_PONG__NO_PING_PONG)
|
|
handle->STAT.Val ^= _DTSMASK;
|
|
#elif (USB_PING_PONG_MODE == USB_PING_PONG__EP0_OUT_ONLY)
|
|
if(ep != 0)
|
|
{
|
|
handle->STAT.Val ^= _DTSMASK;
|
|
}
|
|
#endif
|
|
|
|
//Set the data pointer, data length, and enable the endpoint
|
|
handle->ADR = ConvertToPhysicalAddress(data);
|
|
handle->CNT = len;
|
|
handle->STAT.Val &= _DTSMASK;
|
|
handle->STAT.Val |= (_DTSEN & _DTS_CHECKING_ENABLED);
|
|
handle->STAT.Val |= _USIE;
|
|
|
|
//Point to the next buffer for ping pong purposes.
|
|
if(dir != OUT_FROM_HOST)
|
|
{
|
|
//toggle over the to the next buffer for an IN endpoint
|
|
pBDTEntryIn[ep] = (BDT_ENTRY*)(((uintptr_t)pBDTEntryIn[ep]) ^ USB_NEXT_PING_PONG);
|
|
}
|
|
else
|
|
{
|
|
//toggle over the to the next buffer for an OUT endpoint
|
|
pBDTEntryOut[ep] = (BDT_ENTRY*)(((uintptr_t)pBDTEntryOut[ep]) ^ USB_NEXT_PING_PONG);
|
|
}
|
|
return (USB_HANDLE)handle;
|
|
}
|
|
|
|
|
|
/********************************************************************
|
|
Function:
|
|
void USBStallEndpoint(uint8_t ep, uint8_t dir)
|
|
|
|
Summary:
|
|
Configures the specified endpoint to send STALL to the host, the next
|
|
time the host tries to access the endpoint.
|
|
|
|
PreCondition:
|
|
None
|
|
|
|
Parameters:
|
|
uint8_t ep - The endpoint number that should be configured to send STALL.
|
|
uint8_t dir - The direction of the endpoint to STALL, either
|
|
IN_TO_HOST or OUT_FROM_HOST.
|
|
|
|
Return Values:
|
|
None
|
|
|
|
Remarks:
|
|
None
|
|
|
|
*******************************************************************/
|
|
void USBStallEndpoint(uint8_t ep, uint8_t dir)
|
|
{
|
|
BDT_ENTRY *p;
|
|
|
|
if(ep == 0)
|
|
{
|
|
//For control endpoints (ex: EP0), we need to STALL both IN and OUT
|
|
//endpoints. EP0 OUT must also be prepared to receive the next SETUP
|
|
//packet that will arrive.
|
|
pBDTEntryEP0OutNext->CNT = USB_EP0_BUFF_SIZE;
|
|
pBDTEntryEP0OutNext->ADR = ConvertToPhysicalAddress(&SetupPkt);
|
|
pBDTEntryEP0OutNext->STAT.Val = _DAT0|(_DTSEN & _DTS_CHECKING_ENABLED)|_BSTALL;
|
|
pBDTEntryEP0OutNext->STAT.Val |= _USIE;
|
|
pBDTEntryIn[0]->STAT.Val = _BSTALL;
|
|
pBDTEntryIn[0]->STAT.Val |= _USIE;
|
|
|
|
}
|
|
else
|
|
{
|
|
p = (BDT_ENTRY*)(&BDT[EP(ep,dir,0)]);
|
|
p->STAT.Val |= _BSTALL;
|
|
p->STAT.Val |= _USIE;
|
|
|
|
//If the device is in FULL or ALL_BUT_EP0 ping pong modes
|
|
//then stall that entry as well
|
|
#if (USB_PING_PONG_MODE == USB_PING_PONG__FULL_PING_PONG) || (USB_PING_PONG_MODE == USB_PING_PONG__ALL_BUT_EP0)
|
|
p = (BDT_ENTRY*)(&BDT[EP(ep,dir,1)]);
|
|
p->STAT.Val |= _BSTALL;
|
|
p->STAT.Val |= _USIE;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/**************************************************************************
|
|
Function:
|
|
void USBCancelIO(uint8_t endpoint)
|
|
|
|
Description:
|
|
This function cancels the transfers pending on the specified endpoint.
|
|
This function can only be used after a SETUP packet is received and
|
|
before that setup packet is handled. This is the time period in which
|
|
the EVENT_EP0_REQUEST is thrown, before the event handler function
|
|
returns to the stack.
|
|
|
|
Precondition:
|
|
|
|
Parameters:
|
|
uint8_t endpoint - the endpoint number you wish to cancel the transfers for
|
|
|
|
Return Values:
|
|
None
|
|
|
|
Remarks:
|
|
None
|
|
|
|
**************************************************************************/
|
|
void USBCancelIO(uint8_t endpoint)
|
|
{
|
|
if(USBPacketDisable == 1)
|
|
{
|
|
//The PKTDIS bit is currently set right now. It is therefore "safe"
|
|
//to mess with the BDT right now.
|
|
pBDTEntryIn[endpoint]->Val &= _DTSMASK; //Makes UOWN = 0 (_UCPU mode). Deactivates endpoint. Only sends NAKs.
|
|
pBDTEntryIn[endpoint]->Val ^= _DTSMASK; //Toggle the DTS bit. This packet didn't get sent yet, and the next call to USBTransferOnePacket() will re-toggle the DTS bit back to the original (correct) value.
|
|
|
|
//Need to do additional handling if ping-pong buffering is being used
|
|
#if ((USB_PING_PONG_MODE == USB_PING_PONG__FULL_PING_PONG) || (USB_PING_PONG_MODE == USB_PING_PONG__ALL_BUT_EP0))
|
|
//Point to the next buffer for ping pong purposes. UOWN getting cleared
|
|
//(either due to SIE clearing it after a transaction, or the firmware
|
|
//clearing it) makes hardware ping pong pointer advance.
|
|
pBDTEntryIn[endpoint] = (BDT_ENTRY*)(((uintptr_t)pBDTEntryIn[endpoint]) ^ USB_NEXT_PING_PONG);
|
|
|
|
pBDTEntryIn[endpoint]->STAT.Val &= _DTSMASK;
|
|
pBDTEntryIn[endpoint]->STAT.Val ^= _DTSMASK;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/**************************************************************************
|
|
Function:
|
|
void USBDeviceDetach(void)
|
|
|
|
Summary:
|
|
This function configures the USB module to "soft detach" itself from
|
|
the USB host.
|
|
|
|
Description:
|
|
This function configures the USB module to perform a "soft detach"
|
|
operation, by disabling the D+ (or D-) ~1.5k pull up resistor, which
|
|
lets the host know the device is present and attached. This will make
|
|
the host think that the device has been unplugged. This is potentially
|
|
useful, as it allows the USB device to force the host to re-enumerate
|
|
the device (on the firmware has re-enabled the USB module/pull up, by
|
|
calling USBDeviceAttach(), to "soft re-attach" to the host).
|
|
|
|
Precondition:
|
|
Should only be called when USB_INTERRUPT is defined. See remarks
|
|
section if USB_POLLING mode option is being used (usb_config.h option).
|
|
|
|
Additionally, this function should only be called from the main() loop
|
|
context. Do not call this function from within an interrupt handler, as
|
|
this function may modify global interrupt enable bits and settings.
|
|
|
|
Parameters:
|
|
None
|
|
|
|
Return Values:
|
|
None
|
|
|
|
Remarks:
|
|
If the application firmware calls USBDeviceDetach(), it is strongly
|
|
recommended that the firmware wait at least >= 80ms before calling
|
|
USBDeviceAttach(). If the firmware performs a soft detach, and then
|
|
re-attaches too soon (ex: after a few micro seconds for instance), some
|
|
hosts may interpret this as an unexpected "glitch" rather than as a
|
|
physical removal/re-attachment of the USB device. In this case the host
|
|
may simply ignore the event without re-enumerating the device. To
|
|
ensure that the host properly detects and processes the device soft
|
|
detach/re-attach, it is recommended to make sure the device remains
|
|
detached long enough to mimic a real human controlled USB
|
|
unplug/re-attach event (ex: after calling USBDeviceDetach(), do not
|
|
call USBDeviceAttach() for at least 80+ms, preferably longer.
|
|
|
|
Neither the USBDeviceDetach() or USBDeviceAttach() functions are blocking
|
|
or take long to execute. It is the application firmwares
|
|
responsibility for adding the 80+ms delay, when using these API
|
|
functions.
|
|
|
|
Note: The Windows plug and play event handler processing is fairly
|
|
slow, especially in certain versions of Windows, and for certain USB
|
|
device classes. It has been observed that some device classes need to
|
|
provide even more USB detach dwell interval (before calling
|
|
USBDeviceAttach()), in order to work correctly after re-enumeration.
|
|
If the USB device is a CDC class device, it is recommended to wait
|
|
at least 1.5 seconds or longer, before soft re-attaching to the host,
|
|
to provide the plug and play event handler enough time to finish
|
|
processing the removal event, before the re-attach occurs.
|
|
|
|
If the application is using the USB_POLLING mode option, then the
|
|
USBDeviceDetach() and USBDeviceAttach() functions are not available.
|
|
In this mode, the USB stack relies on the "#define USE_USB_BUS_SENSE_IO"
|
|
and "#define USB_BUS_SENSE" options in the
|
|
HardwareProfile – [platform name].h file.
|
|
|
|
When using the USB_POLLING mode option, and the
|
|
"#define USE_USB_BUS_SENSE_IO" definition has been commented out, then
|
|
the USB stack assumes that it should always enable the USB module at
|
|
pretty much all times. Basically, anytime the application firmware
|
|
calls USBDeviceTasks(), the firmware will automatically enable the USB
|
|
module. This mode would typically be selected if the application was
|
|
designed to be a purely bus powered device. In this case, the
|
|
application is powered from the +5V VBUS supply from the USB port, so
|
|
it is correct and sensible in this type of application to power up and
|
|
turn on the USB module, at anytime that the microcontroller is
|
|
powered (which implies the USB cable is attached and the host is also
|
|
powered).
|
|
|
|
In a self powered application, the USB stack is designed with the
|
|
intention that the user will enable the "#define USE_USB_BUS_SENSE_IO"
|
|
option in the HardwareProfile – [platform name].h file. When this
|
|
option is defined, then the USBDeviceTasks() function will automatically
|
|
check the I/O pin port value of the designated pin (based on the
|
|
#define USB_BUS_SENSE option in the HardwareProfile – [platform name].h
|
|
file), every time the application calls USBDeviceTasks(). If the
|
|
USBDeviceTasks() function is executed and finds that the pin defined by
|
|
the #define USB_BUS_SENSE is in a logic low state, then it will
|
|
automatically disable the USB module and tri-state the D+ and D- pins.
|
|
If however the USBDeviceTasks() function is executed and finds the pin
|
|
defined by the #define USB_BUS_SENSE is in a logic high state, then it
|
|
will automatically enable the USB module, if it has not already been
|
|
enabled.
|
|
|
|
**************************************************************************/
|
|
#if defined(USB_INTERRUPT)
|
|
void USBDeviceDetach(void)
|
|
{
|
|
//If the interrupt option is selected then the customer is required
|
|
// to notify the stack when the device is attached or removed from the
|
|
// bus by calling the USBDeviceAttach() and USBDeviceDetach() functions.
|
|
#ifdef USB_SUPPORT_OTG
|
|
if (USB_BUS_SENSE != 1)
|
|
#endif
|
|
{
|
|
// Disable module & detach from bus
|
|
U1CON = 0;
|
|
|
|
// Mask all USB interrupts
|
|
U1IE = 0;
|
|
|
|
//Move to the detached state
|
|
USBDeviceState = DETACHED_STATE;
|
|
|
|
#ifdef USB_SUPPORT_OTG
|
|
//Disable D+ Pull-up
|
|
U1OTGCONbits.DPPULUP = 0;
|
|
|
|
//Disable HNP
|
|
USBOTGDisableHnp();
|
|
|
|
//Deactivate HNP
|
|
USBOTGDeactivateHnp();
|
|
|
|
//If ID Pin Changed State
|
|
if (USBIDIF && USBIDIE)
|
|
{
|
|
//Re-detect & Initialize
|
|
USBOTGInitialize();
|
|
|
|
//Clear ID Interrupt Flag
|
|
USBClearInterruptFlag(USBIDIFReg,USBIDIFBitNum);
|
|
}
|
|
#endif
|
|
|
|
#if defined __C30__ || defined __XC16__
|
|
//USBClearInterruptFlag(U1OTGIR, 3);
|
|
#endif
|
|
//return so that we don't go through the rest of
|
|
//the state machine
|
|
return;
|
|
}
|
|
|
|
#ifdef USB_SUPPORT_OTG
|
|
//If Session Is Started Then
|
|
else
|
|
{
|
|
//If SRP Is Ready
|
|
if (USBOTGSRPIsReady())
|
|
{
|
|
//Clear SRPReady
|
|
USBOTGClearSRPReady();
|
|
|
|
//Clear SRP Timeout Flag
|
|
USBOTGClearSRPTimeOutFlag();
|
|
|
|
//Indicate Session Started
|
|
UART2PrintString( "\r\n***** USB OTG B Event - Session Started *****\r\n" );
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
#endif //#if defined(USB_INTERRUPT)
|
|
/**************************************************************************
|
|
Function:
|
|
void USBDeviceAttach(void)
|
|
|
|
Summary:
|
|
Checks if VBUS is present, and that the USB module is not already
|
|
initialized, and if so, enables the USB module so as to signal device
|
|
attachment to the USB host.
|
|
|
|
Description:
|
|
This function indicates to the USB host that the USB device has been
|
|
attached to the bus. This function needs to be called in order for the
|
|
device to start to enumerate on the bus.
|
|
|
|
Precondition:
|
|
Should only be called when USB_INTERRUPT is defined. Also, should only
|
|
be called from the main() loop context. Do not call USBDeviceAttach()
|
|
from within an interrupt handler, as the USBDeviceAttach() function
|
|
may modify global interrupt enable bits and settings.
|
|
|
|
For normal USB devices:
|
|
Make sure that if the module was previously on, that it has been turned off
|
|
for a long time (ex: 100ms+) before calling this function to re-enable the module.
|
|
If the device turns off the D+ (for full speed) or D- (for low speed) ~1.5k ohm
|
|
pull up resistor, and then turns it back on very quickly, common hosts will sometimes
|
|
reject this event, since no human could ever unplug and re-attach a USB device in a
|
|
microseconds (or nanoseconds) timescale. The host could simply treat this as some kind
|
|
of glitch and ignore the event altogether.
|
|
Parameters:
|
|
None
|
|
|
|
Return Values:
|
|
None
|
|
|
|
Remarks:
|
|
See also the USBDeviceDetach() API function documentation.
|
|
****************************************************************************/
|
|
#if defined(USB_INTERRUPT)
|
|
void USBDeviceAttach(void)
|
|
{
|
|
//if we are in the detached state
|
|
if(USBDeviceState == DETACHED_STATE)
|
|
{
|
|
if(USB_BUS_SENSE == 1)
|
|
{
|
|
//Initialize registers to known states.
|
|
U1CON = 0;
|
|
|
|
// Mask all USB interrupts
|
|
U1IE = 0;
|
|
|
|
//Configure things like: pull ups, full/low-speed mode,
|
|
//set the ping pong mode, and set internal transceiver
|
|
SetConfigurationOptions();
|
|
|
|
USBEnableInterrupts(); //Modifies global interrupt settings
|
|
|
|
// Enable module & attach to bus
|
|
while(!U1CONbits.USBEN){U1CONbits.USBEN = 1;}
|
|
|
|
//moved to the attached state
|
|
USBDeviceState = ATTACHED_STATE;
|
|
|
|
#ifdef USB_SUPPORT_OTG
|
|
U1OTGCON = USB_OTG_DPLUS_ENABLE | USB_OTG_ENABLE;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
#endif //#if defined(USB_INTERRUPT)
|
|
|
|
|
|
/*******************************************************************************
|
|
Function: void USBCtrlEPAllowStatusStage(void);
|
|
|
|
Summary: This function prepares the proper endpoint 0 IN or endpoint 0 OUT
|
|
(based on the controlTransferState) to allow the status stage packet
|
|
of a control transfer to complete. This function gets used
|
|
internally by the USB stack itself, but it may also be called from
|
|
the application firmware, IF the application firmware called
|
|
the USBDeferStatusStage() function during the initial processing
|
|
of the control transfer request. In this case, the application
|
|
must call the USBCtrlEPAllowStatusStage() once, after it has fully
|
|
completed processing and handling the data stage portion of the
|
|
request.
|
|
|
|
If the application firmware has no need for delaying control
|
|
transfers, and therefore never calls USBDeferStatusStage(), then the
|
|
application firmware should not call USBCtrlEPAllowStatusStage().
|
|
|
|
Description:
|
|
|
|
Conditions:
|
|
None
|
|
|
|
Input:
|
|
|
|
Return:
|
|
|
|
Remarks:
|
|
None
|
|
*****************************************************************************/
|
|
void USBCtrlEPAllowStatusStage(void)
|
|
{
|
|
//Check and set two flags, prior to actually modifying any BDT entries.
|
|
//This double checking is necessary to make certain that
|
|
//USBCtrlEPAllowStatusStage() can be called twice simultaneously (ex: once
|
|
//in main loop context, while simultaneously getting an interrupt which
|
|
//tries to call USBCtrlEPAllowStatusStage() again, at the same time).
|
|
if(USBStatusStageEnabledFlag1 == false)
|
|
{
|
|
USBStatusStageEnabledFlag1 = true;
|
|
if(USBStatusStageEnabledFlag2 == false)
|
|
{
|
|
USBStatusStageEnabledFlag2 = true;
|
|
|
|
//Determine which endpoints (EP0 IN or OUT needs arming for the status
|
|
//stage), based on the type of control transfer currently pending.
|
|
if(controlTransferState == CTRL_TRF_RX)
|
|
{
|
|
pBDTEntryIn[0]->CNT = 0;
|
|
pBDTEntryIn[0]->STAT.Val = _DAT1|(_DTSEN & _DTS_CHECKING_ENABLED);
|
|
pBDTEntryIn[0]->STAT.Val |= _USIE;
|
|
}
|
|
else if(controlTransferState == CTRL_TRF_TX)
|
|
{
|
|
BothEP0OutUOWNsSet = false; //Indicator flag used in USBCtrlTrfOutHandler()
|
|
|
|
//This buffer (when ping pong buffering is enabled on EP0 OUT) receives the
|
|
//next SETUP packet.
|
|
#if((USB_PING_PONG_MODE == USB_PING_PONG__EP0_OUT_ONLY) || (USB_PING_PONG_MODE == USB_PING_PONG__FULL_PING_PONG))
|
|
pBDTEntryEP0OutCurrent->CNT = USB_EP0_BUFF_SIZE;
|
|
pBDTEntryEP0OutCurrent->ADR = ConvertToPhysicalAddress(&SetupPkt);
|
|
pBDTEntryEP0OutCurrent->STAT.Val = _BSTALL; //Prepare endpoint to accept a SETUP transaction
|
|
pBDTEntryEP0OutCurrent->STAT.Val |= _USIE;
|
|
BothEP0OutUOWNsSet = true; //Indicator flag used in USBCtrlTrfOutHandler()
|
|
#endif
|
|
|
|
//This EP0 OUT buffer receives the 0-byte OUT status stage packet.
|
|
pBDTEntryEP0OutNext->CNT = USB_EP0_BUFF_SIZE;
|
|
pBDTEntryEP0OutNext->ADR = ConvertToPhysicalAddress(&SetupPkt);
|
|
pBDTEntryEP0OutNext->STAT.Val = _USIE; // Note: DTSEN is 0
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
Function: void USBCtrlEPAllowDataStage(void);
|
|
|
|
Summary: This function allows the data stage of either a host-to-device or
|
|
device-to-host control transfer (with data stage) to complete.
|
|
This function is meant to be used in conjunction with either the
|
|
USBDeferOUTDataStage() or USBDeferINDataStage(). If the firmware
|
|
does not call either USBDeferOUTDataStage() or USBDeferINDataStage(),
|
|
then the firmware does not need to manually call
|
|
USBCtrlEPAllowDataStage(), as the USB stack will call this function
|
|
instead.
|
|
|
|
Description:
|
|
|
|
Conditions: A control transfer (with data stage) should already be pending,
|
|
if the firmware calls this function. Additionally, the firmware
|
|
should have called either USBDeferOUTDataStage() or
|
|
USBDeferINDataStage() at the start of the control transfer, if
|
|
the firmware will be calling this function manually.
|
|
|
|
Input:
|
|
|
|
Return:
|
|
|
|
Remarks:
|
|
*****************************************************************************/
|
|
void USBCtrlEPAllowDataStage(void)
|
|
{
|
|
USBDeferINDataStagePackets = false;
|
|
USBDeferOUTDataStagePackets = false;
|
|
|
|
if(controlTransferState == CTRL_TRF_RX) //(<setup><out><out>...<out><in>)
|
|
{
|
|
//Prepare EP0 OUT to receive the first OUT data packet in the data stage sequence.
|
|
pBDTEntryEP0OutNext->CNT = USB_EP0_BUFF_SIZE;
|
|
pBDTEntryEP0OutNext->ADR = ConvertToPhysicalAddress(&CtrlTrfData);
|
|
pBDTEntryEP0OutNext->STAT.Val = _DAT1|(_DTSEN & _DTS_CHECKING_ENABLED);
|
|
pBDTEntryEP0OutNext->STAT.Val |= _USIE;
|
|
}
|
|
else //else must be controlTransferState == CTRL_TRF_TX (<setup><in><in>...<in><out>)
|
|
{
|
|
//Error check the data stage byte count. Make sure the user specified
|
|
//value was no greater than the number of bytes the host requested.
|
|
if(SetupPkt.wLength < inPipes[0].wCount.Val)
|
|
{
|
|
inPipes[0].wCount.Val = SetupPkt.wLength;
|
|
}
|
|
USBCtrlTrfTxService(); //Copies one IN data packet worth of data from application buffer
|
|
//to CtrlTrfData buffer. Also keeps track of how many bytes remaining.
|
|
|
|
//Cnt should have been initialized by responsible request owner (ex: by
|
|
//using the USBEP0SendRAMPtr() or USBEP0SendROMPtr() API function).
|
|
pBDTEntryIn[0]->ADR = ConvertToPhysicalAddress(&CtrlTrfData);
|
|
pBDTEntryIn[0]->STAT.Val = _DAT1|(_DTSEN & _DTS_CHECKING_ENABLED);
|
|
pBDTEntryIn[0]->STAT.Val |= _USIE;
|
|
}
|
|
}
|
|
|
|
|
|
/******************************************************************************/
|
|
/** Internal Functions *********************************************************/
|
|
/******************************************************************************/
|
|
|
|
/********************************************************************
|
|
* Function: void USBConfigureEndpoint(uint8_t EPNum, uint8_t direction)
|
|
*
|
|
* PreCondition: None
|
|
*
|
|
* Input: uint8_t EPNum - the endpoint to be configured
|
|
* uint8_t direction - the direction to be configured
|
|
* (either OUT_FROM_HOST or IN_TO_HOST)
|
|
*
|
|
* Output: None
|
|
*
|
|
* Side Effects: None
|
|
*
|
|
* Overview: This function will configure the specified
|
|
* endpoint
|
|
*
|
|
* Note: None
|
|
*******************************************************************/
|
|
static void USBConfigureEndpoint(uint8_t EPNum, uint8_t direction)
|
|
{
|
|
volatile BDT_ENTRY* handle;
|
|
|
|
//Compute a pointer to the even BDT entry corresponding to the
|
|
//EPNum and direction values passed to this function.
|
|
handle = (volatile BDT_ENTRY*)&BDT[EP0_OUT_EVEN]; //Get address of start of BDT
|
|
handle += EP(EPNum,direction,0u); //Add in offset to the BDT of interest
|
|
|
|
handle->STAT.UOWN = 0; //mostly redundant, since USBStdSetCfgHandler()
|
|
//already cleared the entire BDT table
|
|
|
|
//Make sure our pBDTEntryIn/Out[] pointer is initialized. Needed later
|
|
//for USBTransferOnePacket() API calls.
|
|
if(direction == OUT_FROM_HOST)
|
|
{
|
|
pBDTEntryOut[EPNum] = handle;
|
|
}
|
|
else
|
|
{
|
|
pBDTEntryIn[EPNum] = handle;
|
|
}
|
|
|
|
#if (USB_PING_PONG_MODE == USB_PING_PONG__FULL_PING_PONG)
|
|
handle->STAT.DTS = 0;
|
|
(handle+1)->STAT.DTS = 1;
|
|
#elif (USB_PING_PONG_MODE == USB_PING_PONG__NO_PING_PONG)
|
|
//Set DTS to one because the first thing we will do
|
|
//when transmitting is toggle the bit
|
|
handle->STAT.DTS = 1;
|
|
#elif (USB_PING_PONG_MODE == USB_PING_PONG__EP0_OUT_ONLY)
|
|
if(EPNum != 0)
|
|
{
|
|
handle->STAT.DTS = 1;
|
|
}
|
|
#elif (USB_PING_PONG_MODE == USB_PING_PONG__ALL_BUT_EP0)
|
|
if(EPNum != 0)
|
|
{
|
|
handle->STAT.DTS = 0;
|
|
(handle+1)->STAT.DTS = 1;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* Function: void USBCtrlEPServiceComplete(void)
|
|
*
|
|
* PreCondition: None
|
|
*
|
|
* Input: None
|
|
*
|
|
* Output: None
|
|
*
|
|
* Side Effects: None
|
|
*
|
|
* Overview: This routine wrap up the remaining tasks in servicing
|
|
* a Setup Request. Its main task is to set the endpoint
|
|
* controls appropriately for a given situation. See code
|
|
* below.
|
|
* There are three main scenarios:
|
|
* a) There was no handler for the Request, in this case
|
|
* a STALL should be sent out.
|
|
* b) The host has requested a read control transfer,
|
|
* endpoints are required to be setup in a specific way.
|
|
* c) The host has requested a write control transfer, or
|
|
* a control data stage is not required, endpoints are
|
|
* required to be setup in a specific way.
|
|
*
|
|
* Packet processing is resumed by clearing PKTDIS bit.
|
|
*
|
|
* Note: None
|
|
*****************************************************************************/
|
|
static void USBCtrlEPServiceComplete(void)
|
|
{
|
|
/*
|
|
* PKTDIS bit is set when a Setup Transaction is received.
|
|
* Clear to resume packet processing.
|
|
*/
|
|
USBPacketDisable = 0;
|
|
|
|
//Check the busy bits and the SetupPtk.DataDir variables to determine what type of
|
|
//control transfer is currently in progress. We need to know the type of control
|
|
//transfer that is currently pending, in order to know how to properly arm the
|
|
//EP0 IN and EP0 OUT endpoints.
|
|
if(inPipes[0].info.bits.busy == 0)
|
|
{
|
|
if(outPipes[0].info.bits.busy == 1)
|
|
{
|
|
controlTransferState = CTRL_TRF_RX;
|
|
/*
|
|
* Control Write:
|
|
* <SETUP[0]><OUT[1]><OUT[0]>...<IN[1]> | <SETUP[0]>
|
|
*/
|
|
|
|
//1. Prepare OUT EP to receive data, unless a USB class request handler
|
|
// function decided to defer the data stage (ex: because the intended
|
|
// RAM buffer wasn't available yet) by calling USBDeferDataStage().
|
|
// If it did so, it is then responsible for calling USBCtrlEPAllowDataStage(),
|
|
// once it is ready to begin receiving the data.
|
|
if(USBDeferOUTDataStagePackets == false)
|
|
{
|
|
USBCtrlEPAllowDataStage();
|
|
}
|
|
|
|
//2. IN endpoint 0 status stage will be armed by USBCtrlEPAllowStatusStage()
|
|
//after all of the OUT data has been received and consumed, or if a timeout occurs.
|
|
USBStatusStageEnabledFlag2 = false;
|
|
USBStatusStageEnabledFlag1 = false;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* If no one knows how to service this request then stall.
|
|
* Must also prepare EP0 to receive the next SETUP transaction.
|
|
*/
|
|
pBDTEntryEP0OutNext->CNT = USB_EP0_BUFF_SIZE;
|
|
pBDTEntryEP0OutNext->ADR = ConvertToPhysicalAddress(&SetupPkt);
|
|
pBDTEntryEP0OutNext->STAT.Val = _DAT0|(_DTSEN & _DTS_CHECKING_ENABLED)|_BSTALL;
|
|
pBDTEntryEP0OutNext->STAT.Val |= _USIE;
|
|
pBDTEntryIn[0]->STAT.Val = _BSTALL;
|
|
pBDTEntryIn[0]->STAT.Val |= _USIE;
|
|
}
|
|
}
|
|
else // A module has claimed ownership of the control transfer session.
|
|
{
|
|
if(SetupPkt.DataDir == USB_SETUP_DEVICE_TO_HOST_BITFIELD)
|
|
{
|
|
controlTransferState = CTRL_TRF_TX;
|
|
/*
|
|
* Control Read:
|
|
* <SETUP[0]><IN[1]><IN[0]>...<OUT[1]> | <SETUP[0]>
|
|
*
|
|
* 1. Prepare IN EP to transfer data to the host. If however the data
|
|
* wasn't ready yet (ex: because the firmware needs to go and read it from
|
|
* some slow/currently unavailable resource, such as an external I2C EEPconst),
|
|
* Then the class request handler responsible should call the USBDeferDataStage()
|
|
* macro. In this case, the firmware may wait up to 500ms, before it is required
|
|
* to transmit the first IN data packet. Once the data is ready, and the firmware
|
|
* is ready to begin sending the data, it should then call the
|
|
* USBCtrlEPAllowDataStage() function to start the data stage.
|
|
*/
|
|
if(USBDeferINDataStagePackets == false)
|
|
{
|
|
USBCtrlEPAllowDataStage();
|
|
}
|
|
|
|
// 2. (Optionally) allow the status stage now, to prepare for early termination.
|
|
// Note: If a class request handler decided to set USBDeferStatusStagePacket == true,
|
|
// then it is responsible for eventually calling USBCtrlEPAllowStatusStage() once it
|
|
// is ready. If the class request handler does this, it needs to be careful to
|
|
// be written so that it can handle the early termination scenario.
|
|
// Ex: It should call USBCtrlEPAllowStatusStage() when any of the following occurs:
|
|
// 1. The desired total number of bytes were sent to the host.
|
|
// 2. The number of bytes that the host originally requested (in the SETUP packet that
|
|
// started the control transfer) has been reached.
|
|
// 3. Or, if a timeout occurs (ex: <50ms since the last successful EP0 IN transaction), regardless
|
|
// of how many bytes have actually been sent. This is necessary to prevent a deadlock situation
|
|
// (where the control transfer can't complete, due to continuous NAK on status stage) if the
|
|
// host performs early termination. If enabled, the USB_ENABLE_STATUS_STAGE_TIMEOUTS usb_config.h
|
|
// option can take care of this for you.
|
|
// Note: For this type of control transfer, there is normally no harm in simply arming the
|
|
// status stage packet right now, even if the IN data is not ready yet. This allows for
|
|
// immediate early termination, without adding unnecessary delay. Therefore, it is generally not
|
|
// recommended for the USB class handler firmware to call USBDeferStatusStage(), for this
|
|
// type of control transfer. If the USB class handler firmware needs more time to fetch the IN
|
|
// data that needs to be sent to the host, it should instead use the USBDeferDataStage() function.
|
|
USBStatusStageEnabledFlag2 = false;
|
|
USBStatusStageEnabledFlag1 = false;
|
|
if(USBDeferStatusStagePacket == false)
|
|
{
|
|
USBCtrlEPAllowStatusStage();
|
|
}
|
|
}
|
|
else // (SetupPkt.DataDir == USB_SETUP_DIRECTION_HOST_TO_DEVICE)
|
|
{
|
|
//This situation occurs for special types of control transfers,
|
|
//such as that which occurs when the host sends a SET_ADDRESS
|
|
//control transfer. Ex:
|
|
//
|
|
//<SETUP[0]><IN[1]> | <SETUP[0]>
|
|
|
|
//Although the data direction is HOST_TO_DEVICE, there is no data stage
|
|
//(hence: outPipes[0].info.bits.busy == 0). There is however still
|
|
//an IN status stage.
|
|
|
|
controlTransferState = CTRL_TRF_RX; //Since this is a HOST_TO_DEVICE control transfer
|
|
|
|
//1. Prepare OUT EP to receive the next SETUP packet.
|
|
pBDTEntryEP0OutNext->CNT = USB_EP0_BUFF_SIZE;
|
|
pBDTEntryEP0OutNext->ADR = ConvertToPhysicalAddress(&SetupPkt);
|
|
pBDTEntryEP0OutNext->STAT.Val = _BSTALL;
|
|
pBDTEntryEP0OutNext->STAT.Val |= _USIE;
|
|
|
|
//2. Prepare for IN status stage of the control transfer
|
|
USBStatusStageEnabledFlag2 = false;
|
|
USBStatusStageEnabledFlag1 = false;
|
|
if(USBDeferStatusStagePacket == false)
|
|
{
|
|
USBCtrlEPAllowStatusStage();
|
|
}
|
|
}
|
|
|
|
}//end if(ctrl_trf_session_owner == MUID_NULL)
|
|
|
|
}//end USBCtrlEPServiceComplete
|
|
|
|
|
|
/******************************************************************************
|
|
* Function: void USBCtrlTrfTxService(void)
|
|
*
|
|
* PreCondition: pSrc, wCount, and usb_stat.ctrl_trf_mem are setup properly.
|
|
*
|
|
* Input: None
|
|
*
|
|
* Output: None
|
|
*
|
|
* Side Effects: None
|
|
*
|
|
* Overview: This routine is used for device to host control transfers
|
|
* (IN transactions). This function takes care of managing a
|
|
* transfer over multiple USB transactions.
|
|
* This routine should be called from only two places.
|
|
* One from USBCtrlEPServiceComplete() and one from
|
|
* USBCtrlTrfInHandler().
|
|
*
|
|
* Note:
|
|
*****************************************************************************/
|
|
static void USBCtrlTrfTxService(void)
|
|
{
|
|
uint8_t byteToSend;
|
|
|
|
//Figure out how many bytes of data to send in the next IN transaction.
|
|
//Assume a full size packet, unless otherwise determined below.
|
|
byteToSend = USB_EP0_BUFF_SIZE;
|
|
if(inPipes[0].wCount.Val < (uint8_t)USB_EP0_BUFF_SIZE)
|
|
{
|
|
byteToSend = inPipes[0].wCount.Val;
|
|
|
|
//Keep track of whether or not we have sent a "short packet" yet.
|
|
//This is useful so that later on, we can configure EP0 IN to STALL,
|
|
//after we have sent all of the intended data. This makes sure the
|
|
//hardware STALLs if the host erroneously tries to send more IN token
|
|
//packets, requesting more data than intended in the control transfer.
|
|
if(shortPacketStatus == SHORT_PKT_NOT_USED)
|
|
{
|
|
shortPacketStatus = SHORT_PKT_PENDING;
|
|
}
|
|
else if(shortPacketStatus == SHORT_PKT_PENDING)
|
|
{
|
|
shortPacketStatus = SHORT_PKT_SENT;
|
|
}
|
|
}
|
|
|
|
//Keep track of how many bytes remain to be sent in the transfer, by
|
|
//subtracting the number of bytes about to be sent from the total.
|
|
inPipes[0].wCount.Val -= byteToSend;
|
|
|
|
//Next, load the number of bytes to send to BC7..0 in buffer descriptor.
|
|
//Note: Control endpoints may never have a max packet size of > 64 bytes.
|
|
//Therefore, the BC8 and BC9 bits should always be maintained clear.
|
|
pBDTEntryIn[0]->CNT = byteToSend;
|
|
|
|
//Now copy the data from the source location, to the CtrlTrfData[] buffer,
|
|
//which we will send to the host.
|
|
pDst = (USB_VOLATILE uint8_t*)CtrlTrfData; // Set destination pointer
|
|
if(inPipes[0].info.bits.ctrl_trf_mem == USB_EP0_ROM) // Determine type of memory source
|
|
{
|
|
while(byteToSend)
|
|
{
|
|
*pDst++ = *inPipes[0].pSrc.bRom++;
|
|
byteToSend--;
|
|
}//end while(byte_to_send.Val)
|
|
}
|
|
else // RAM
|
|
{
|
|
while(byteToSend)
|
|
{
|
|
*pDst++ = *inPipes[0].pSrc.bRam++;
|
|
byteToSend--;
|
|
}//end while(byte_to_send.Val)
|
|
}//end if(usb_stat.ctrl_trf_mem == _const)
|
|
}//end USBCtrlTrfTxService
|
|
|
|
/******************************************************************************
|
|
* Function: void USBCtrlTrfRxService(void)
|
|
*
|
|
* PreCondition: pDst and wCount are setup properly.
|
|
* pSrc is always &CtrlTrfData
|
|
* usb_stat.ctrl_trf_mem is always USB_EP0_RAM.
|
|
* wCount should be set to 0 at the start of each control
|
|
* transfer.
|
|
*
|
|
* Input: None
|
|
*
|
|
* Output: None
|
|
*
|
|
* Side Effects: None
|
|
*
|
|
* Overview: This routine is used for host to device control transfers
|
|
* (uses OUT transactions). This function receives the data that arrives
|
|
* on EP0 OUT, and copies it into the appropriate outPipes[0].pDst.bRam
|
|
* buffer. Once the host has sent all the data it was intending
|
|
* to send, this function will call the appropriate outPipes[0].pFunc()
|
|
* handler (unless it is NULL), so that it can be used by the
|
|
* intended target firmware.
|
|
*
|
|
* Note: None
|
|
*****************************************************************************/
|
|
static void USBCtrlTrfRxService(void)
|
|
{
|
|
uint8_t byteToRead;
|
|
uint8_t i;
|
|
|
|
//Load byteToRead with the number of bytes the host just sent us in the
|
|
//last OUT transaction.
|
|
byteToRead = pBDTEntryEP0OutCurrent->CNT;
|
|
|
|
//Update the "outPipes[0].wCount.Val", which keeps track of the total number
|
|
//of remaining bytes expected to be received from the host, in the control
|
|
//transfer. First check to see if the host sent us more bytes than the
|
|
//application firmware was expecting to receive.
|
|
if(byteToRead > outPipes[0].wCount.Val)
|
|
{
|
|
byteToRead = outPipes[0].wCount.Val;
|
|
}
|
|
//Reduce the number of remaining bytes by the number we just received.
|
|
outPipes[0].wCount.Val -= byteToRead;
|
|
|
|
//Copy the OUT DATAx packet bytes that we just received from the host,
|
|
//into the user application buffer space.
|
|
for(i=0;i<byteToRead;i++)
|
|
{
|
|
*outPipes[0].pDst.bRam++ = CtrlTrfData[i];
|
|
}//end while(byteToRead.Val)
|
|
|
|
//If there is more data to receive, prepare EP0 OUT so that it can receive
|
|
//the next packet in the sequence.
|
|
if(outPipes[0].wCount.Val > 0)
|
|
{
|
|
pBDTEntryEP0OutNext->CNT = USB_EP0_BUFF_SIZE;
|
|
pBDTEntryEP0OutNext->ADR = ConvertToPhysicalAddress(&CtrlTrfData);
|
|
if(pBDTEntryEP0OutCurrent->STAT.DTS == 0)
|
|
{
|
|
pBDTEntryEP0OutNext->STAT.Val = _DAT1|(_DTSEN & _DTS_CHECKING_ENABLED);
|
|
pBDTEntryEP0OutNext->STAT.Val |= _USIE;
|
|
}
|
|
else
|
|
{
|
|
pBDTEntryEP0OutNext->STAT.Val = _DAT0|(_DTSEN & _DTS_CHECKING_ENABLED);
|
|
pBDTEntryEP0OutNext->STAT.Val |= _USIE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//We have received all OUT packets that we were expecting to
|
|
//receive for the control transfer. Prepare EP0 OUT to receive
|
|
//the next SETUP transaction that may arrive.
|
|
pBDTEntryEP0OutNext->CNT = USB_EP0_BUFF_SIZE;
|
|
pBDTEntryEP0OutNext->ADR = ConvertToPhysicalAddress(&SetupPkt);
|
|
//Configure EP0 OUT to receive the next SETUP transaction for any future
|
|
//control transfers. However, set BSTALL in case the host tries to send
|
|
//more data than it claims it was going to send.
|
|
pBDTEntryEP0OutNext->STAT.Val = _BSTALL;
|
|
pBDTEntryEP0OutNext->STAT.Val |= _USIE;
|
|
|
|
//All data bytes for the host to device control write (OUT) have now been
|
|
//received successfully.
|
|
//Go ahead and call the user specified callback function, to use/consume
|
|
//the control transfer data (ex: if the "void (*function)" parameter
|
|
//was non-NULL when USBEP0Receive() was called).
|
|
if(outPipes[0].pFunc != NULL)
|
|
{
|
|
#if defined(__XC8)
|
|
//Special pragmas to suppress an expected/harmless warning
|
|
//message when building with the XC8 compiler
|
|
#pragma warning push
|
|
#pragma warning disable 1088
|
|
outPipes[0].pFunc(); //Call the user's callback function
|
|
#pragma warning pop
|
|
#else
|
|
outPipes[0].pFunc(); //Call the user's callback function
|
|
#endif
|
|
}
|
|
outPipes[0].info.bits.busy = 0;
|
|
|
|
//Ready to arm status stage IN transaction now, if the application
|
|
//firmware has completed processing the request. If it is still busy
|
|
//and needs more time to finish handling the request, then the user
|
|
//callback (the one called by the outPipes[0].pFunc();) should set the
|
|
//USBDeferStatusStagePacket to true (by calling USBDeferStatusStage()). In
|
|
//this case, it is the application's firmware responsibility to call
|
|
//the USBCtrlEPAllowStatusStage() function, once it is fully done handling the request.
|
|
//Note: The application firmware must process the request and call
|
|
//USBCtrlEPAllowStatusStage() in a semi-timely fashion. "Semi-timely"
|
|
//means either 50ms, 500ms, or 5 seconds, depending on the type of
|
|
//control transfer. See the USB 2.0 specification section 9.2.6 for
|
|
//more details.
|
|
if(USBDeferStatusStagePacket == false)
|
|
{
|
|
USBCtrlEPAllowStatusStage();
|
|
}
|
|
}
|
|
|
|
}//end USBCtrlTrfRxService
|
|
|
|
|
|
/********************************************************************
|
|
* Function: void USBStdSetCfgHandler(void)
|
|
*
|
|
* PreCondition: None
|
|
*
|
|
* Input: None
|
|
*
|
|
* Output: None
|
|
*
|
|
* Side Effects: None
|
|
*
|
|
* Overview: This routine first disables all endpoints by
|
|
* clearing UEP registers. It then configures
|
|
* (initializes) endpoints by calling the callback
|
|
* function USBCBInitEP().
|
|
*
|
|
* Note: None
|
|
*******************************************************************/
|
|
static void USBStdSetCfgHandler(void)
|
|
{
|
|
uint8_t i;
|
|
|
|
// This will generate a zero length packet
|
|
inPipes[0].info.bits.busy = 1;
|
|
|
|
//Clear all of the endpoint control registers
|
|
DisableNonZeroEndpoints(USB_MAX_EP_NUMBER);
|
|
|
|
//Clear all of the BDT entries
|
|
memset((void*)&BDT[0], 0x00, sizeof(BDT));
|
|
|
|
// Assert reset request to all of the Ping Pong buffer pointers
|
|
USBPingPongBufferReset = 1;
|
|
|
|
//Re-Initialize all ping pong software state bits to 0 (which corresponds to
|
|
//the EVEN buffer being the next one that will be used), since we are also
|
|
//doing a hardware ping pong pointer reset above.
|
|
for(i = 0; i < (uint8_t)(USB_MAX_EP_NUMBER+1u); i++)
|
|
{
|
|
ep_data_in[i].Val = 0u;
|
|
ep_data_out[i].Val = 0u;
|
|
}
|
|
|
|
//clear the alternate interface settings
|
|
memset((void*)&USBAlternateInterface,0x00,USB_MAX_NUM_INT);
|
|
|
|
//Stop trying to reset ping pong buffer pointers
|
|
USBPingPongBufferReset = 0;
|
|
|
|
pBDTEntryIn[0] = (volatile BDT_ENTRY*)&BDT[EP0_IN_EVEN];
|
|
|
|
//Set the next out to the current out packet
|
|
pBDTEntryEP0OutCurrent = (volatile BDT_ENTRY*)&BDT[EP0_OUT_EVEN];
|
|
pBDTEntryEP0OutNext = pBDTEntryEP0OutCurrent;
|
|
|
|
//set the current configuration
|
|
USBActiveConfiguration = SetupPkt.bConfigurationValue;
|
|
|
|
//if the configuration value == 0
|
|
if(USBActiveConfiguration == 0)
|
|
{
|
|
//Go back to the addressed state
|
|
USBDeviceState = ADDRESS_STATE;
|
|
}
|
|
else
|
|
{
|
|
//initialize the required endpoints
|
|
USB_SET_CONFIGURATION_HANDLER(EVENT_CONFIGURED,(void*)&USBActiveConfiguration,1);
|
|
|
|
//Otherwise go to the configured state. Update the state variable last,
|
|
//after performing all of the set configuration related initialization
|
|
//tasks.
|
|
USBDeviceState = CONFIGURED_STATE;
|
|
}//end if(SetupPkt.bConfigurationValue == 0)
|
|
}//end USBStdSetCfgHandler
|
|
|
|
|
|
/********************************************************************
|
|
* Function: void USBStdGetDscHandler(void)
|
|
*
|
|
* PreCondition: None
|
|
*
|
|
* Input: None
|
|
*
|
|
* Output: None
|
|
*
|
|
* Side Effects: None
|
|
*
|
|
* Overview: This routine handles the standard GET_DESCRIPTOR
|
|
* request.
|
|
*
|
|
* Note: None
|
|
*******************************************************************/
|
|
static void USBStdGetDscHandler(void)
|
|
{
|
|
if(SetupPkt.bmRequestType == 0x80)
|
|
{
|
|
inPipes[0].info.Val = USB_EP0_ROM | USB_EP0_BUSY | USB_EP0_INCLUDE_ZERO;
|
|
|
|
switch(SetupPkt.bDescriptorType)
|
|
{
|
|
case USB_DESCRIPTOR_DEVICE:
|
|
#if !defined(USB_USER_DEVICE_DESCRIPTOR)
|
|
inPipes[0].pSrc.bRom = (const uint8_t*)&device_dsc;
|
|
#else
|
|
inPipes[0].pSrc.bRom = (const uint8_t*)USB_USER_DEVICE_DESCRIPTOR;
|
|
#endif
|
|
inPipes[0].wCount.Val = sizeof(device_dsc);
|
|
break;
|
|
case USB_DESCRIPTOR_CONFIGURATION:
|
|
//First perform error case check, to make sure the host is requesting a
|
|
//legal descriptor index. If the request index is illegal, don't do
|
|
//anything (so that the default STALL response will be sent).
|
|
if(SetupPkt.bDscIndex < USB_MAX_NUM_CONFIG_DSC)
|
|
{
|
|
#if !defined(USB_USER_CONFIG_DESCRIPTOR)
|
|
inPipes[0].pSrc.bRom = *(USB_CD_Ptr+SetupPkt.bDscIndex);
|
|
#else
|
|
inPipes[0].pSrc.bRom = *(USB_USER_CONFIG_DESCRIPTOR+SetupPkt.bDscIndex);
|
|
#endif
|
|
|
|
//This must be loaded using byte addressing. The source pointer
|
|
// may not be word aligned for the 16 or 32 bit machines resulting
|
|
// in an address error on the dereference.
|
|
inPipes[0].wCount.byte.LB = *(inPipes[0].pSrc.bRom+2);
|
|
inPipes[0].wCount.byte.HB = *(inPipes[0].pSrc.bRom+3);
|
|
}
|
|
else
|
|
{
|
|
inPipes[0].info.Val = 0;
|
|
}
|
|
break;
|
|
case USB_DESCRIPTOR_STRING:
|
|
//USB_NUM_STRING_DESCRIPTORS was introduced as optional in release v2.3. In v2.4 and
|
|
// later it is now mandatory. This should be defined in usb_config.h and should
|
|
// indicate the number of string descriptors.
|
|
if(SetupPkt.bDscIndex<USB_NUM_STRING_DESCRIPTORS)
|
|
{
|
|
//Get a pointer to the String descriptor requested
|
|
inPipes[0].pSrc.bRom = *(USB_SD_Ptr+SetupPkt.bDscIndex);
|
|
// Set data count
|
|
inPipes[0].wCount.Val = *inPipes[0].pSrc.bRom;
|
|
}
|
|
#if defined(IMPLEMENT_MICROSOFT_OS_DESCRIPTOR)
|
|
else if(SetupPkt.bDscIndex == MICROSOFT_OS_DESCRIPTOR_INDEX)
|
|
{
|
|
//Get a pointer to the special MS OS string descriptor requested
|
|
inPipes[0].pSrc.bRom = (const uint8_t*)&MSOSDescriptor;
|
|
// Set data count
|
|
inPipes[0].wCount.Val = *inPipes[0].pSrc.bRom;
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
inPipes[0].info.Val = 0;
|
|
}
|
|
break;
|
|
default:
|
|
inPipes[0].info.Val = 0;
|
|
break;
|
|
}//end switch
|
|
}//end if
|
|
}//end USBStdGetDscHandler
|
|
|
|
/********************************************************************
|
|
* Function: void USBStdGetStatusHandler(void)
|
|
*
|
|
* PreCondition: None
|
|
*
|
|
* Input: None
|
|
*
|
|
* Output: None
|
|
*
|
|
* Side Effects: None
|
|
*
|
|
* Overview: This routine handles the standard GET_STATUS request
|
|
*
|
|
* Note: None
|
|
*******************************************************************/
|
|
static void USBStdGetStatusHandler(void)
|
|
{
|
|
CtrlTrfData[0] = 0; // Initialize content
|
|
CtrlTrfData[1] = 0;
|
|
|
|
switch(SetupPkt.Recipient)
|
|
{
|
|
case USB_SETUP_RECIPIENT_DEVICE_BITFIELD:
|
|
inPipes[0].info.bits.busy = 1;
|
|
/*
|
|
* [0]: bit0: Self-Powered Status [0] Bus-Powered [1] Self-Powered
|
|
* bit1: RemoteWakeup [0] Disabled [1] Enabled
|
|
*/
|
|
if(self_power == 1) // self_power is defined in HardwareProfile.h
|
|
{
|
|
CtrlTrfData[0]|=0x01;
|
|
}
|
|
|
|
if(RemoteWakeup == true)
|
|
{
|
|
CtrlTrfData[0]|=0x02;
|
|
}
|
|
break;
|
|
case USB_SETUP_RECIPIENT_INTERFACE_BITFIELD:
|
|
inPipes[0].info.bits.busy = 1; // No data to update
|
|
break;
|
|
case USB_SETUP_RECIPIENT_ENDPOINT_BITFIELD:
|
|
inPipes[0].info.bits.busy = 1;
|
|
/*
|
|
* [0]: bit0: Halt Status [0] Not Halted [1] Halted
|
|
*/
|
|
if(SetupPkt.EPNum != 0)
|
|
{
|
|
BDT_ENTRY *p;
|
|
|
|
if(SetupPkt.EPDir == 0)
|
|
{
|
|
p = (BDT_ENTRY*)pBDTEntryOut[SetupPkt.EPNum];
|
|
}
|
|
else
|
|
{
|
|
p = (BDT_ENTRY*)pBDTEntryIn[SetupPkt.EPNum];
|
|
}
|
|
|
|
if((p->STAT.UOWN == 1) && (p->STAT.BSTALL == 1))
|
|
{
|
|
CtrlTrfData[0]=0x01; // Set bit0
|
|
}
|
|
}
|
|
break;
|
|
}//end switch
|
|
|
|
if(inPipes[0].info.bits.busy == 1)
|
|
{
|
|
inPipes[0].pSrc.bRam = (uint8_t*)&CtrlTrfData; // Set Source
|
|
inPipes[0].info.bits.ctrl_trf_mem = USB_EP0_RAM; // Set memory type
|
|
inPipes[0].wCount.v[0] = 2; // Set data count
|
|
}//end if(...)
|
|
}//end USBStdGetStatusHandler
|
|
|
|
/********************************************************************
|
|
* Function: void USBStallHandler(void)
|
|
*
|
|
* PreCondition: None
|
|
*
|
|
* Input: None
|
|
*
|
|
* Output: None
|
|
*
|
|
* Side Effects:
|
|
*
|
|
* Overview: This function handles the event of a STALL
|
|
* occurring on the bus
|
|
*
|
|
* Note: None
|
|
*******************************************************************/
|
|
static void USBStallHandler(void)
|
|
{
|
|
/*
|
|
* Does not really have to do anything here,
|
|
* even for the control endpoint.
|
|
* All BDs of Endpoint 0 are owned by SIE right now,
|
|
* but once a Setup Transaction is received, the ownership
|
|
* for EP0_OUT will be returned to CPU.
|
|
* When the Setup Transaction is serviced, the ownership
|
|
* for EP0_IN will then be forced back to CPU by firmware.
|
|
*/
|
|
|
|
if(U1EP0bits.EPSTALL == 1)
|
|
{
|
|
// UOWN - if 0, owned by CPU, if 1, owned by SIE
|
|
if((pBDTEntryEP0OutCurrent->STAT.Val == _USIE) && (pBDTEntryIn[0]->STAT.Val == (_USIE|_BSTALL)))
|
|
{
|
|
// Set ep0Bo to stall also
|
|
pBDTEntryEP0OutCurrent->STAT.Val = _DAT0|(_DTSEN & _DTS_CHECKING_ENABLED)|_BSTALL;
|
|
pBDTEntryEP0OutCurrent->STAT.Val |= _USIE;
|
|
}//end if
|
|
U1EP0bits.EPSTALL = 0; // Clear stall status
|
|
}//end if
|
|
|
|
USBClearInterruptFlag(USBStallIFReg,USBStallIFBitNum);
|
|
}
|
|
|
|
/********************************************************************
|
|
* Function: void USBSuspend(void)
|
|
*
|
|
* PreCondition: None
|
|
*
|
|
* Input: None
|
|
*
|
|
* Output: None
|
|
*
|
|
* Side Effects:
|
|
*
|
|
* Overview: This function handles if the host tries to
|
|
* suspend the device
|
|
*
|
|
* Note: None
|
|
*******************************************************************/
|
|
static void USBSuspend(void)
|
|
{
|
|
/*
|
|
* NOTE: Do not clear UIRbits.ACTVIF here!
|
|
* Reason:
|
|
* ACTVIF is only generated once an IDLEIF has been generated.
|
|
* This is a 1:1 ratio interrupt generation.
|
|
* For every IDLEIF, there will be only one ACTVIF regardless of
|
|
* the number of subsequent bus transitions.
|
|
*
|
|
* If the ACTIF is cleared here, a problem could occur when:
|
|
* [ IDLE ][bus activity ->
|
|
* <--- 3 ms -----> ^
|
|
* ^ ACTVIF=1
|
|
* IDLEIF=1
|
|
* # # # # (#=Program polling flags)
|
|
* ^
|
|
* This polling loop will see both
|
|
* IDLEIF=1 and ACTVIF=1.
|
|
* However, the program services IDLEIF first
|
|
* because ACTIVIE=0.
|
|
* If this routine clears the only ACTIVIF,
|
|
* then it can never get out of the suspend
|
|
* mode.
|
|
*/
|
|
USBActivityIE = 1; // Enable bus activity interrupt
|
|
USBClearInterruptFlag(USBIdleIFReg,USBIdleIFBitNum);
|
|
|
|
#if defined(__18CXX) || defined(_PIC14E) || defined(__XC8)
|
|
U1CONbits.SUSPND = 1; // Put USB module in power conserve
|
|
// mode, SIE clock inactive
|
|
#endif
|
|
USBBusIsSuspended = true;
|
|
USBTicksSinceSuspendEnd = 0;
|
|
|
|
/*
|
|
* At this point the PIC can go into sleep,idle, or
|
|
* switch to a slower clock, etc. This should be done in the
|
|
* USBCBSuspend() if necessary.
|
|
*/
|
|
USB_SUSPEND_HANDLER(EVENT_SUSPEND,0,0);
|
|
}
|
|
|
|
/********************************************************************
|
|
* Function: void USBWakeFromSuspend(void)
|
|
*
|
|
* PreCondition: None
|
|
*
|
|
* Input: None
|
|
*
|
|
* Output: None
|
|
*
|
|
* Side Effects: None
|
|
*
|
|
* Overview:
|
|
*
|
|
* Note: None
|
|
*******************************************************************/
|
|
static void USBWakeFromSuspend(void)
|
|
{
|
|
USBBusIsSuspended = false;
|
|
|
|
/*
|
|
* If using clock switching, the place to restore the original
|
|
* microcontroller core clock frequency is in the USBCBWakeFromSuspend() callback
|
|
*/
|
|
USB_WAKEUP_FROM_SUSPEND_HANDLER(EVENT_RESUME,0,0);
|
|
|
|
#if defined(__18CXX) || defined(_PIC14E) || defined(__XC8)
|
|
//To avoid improperly clocking the USB module, make sure the oscillator
|
|
//settings are consistent with USB operation before clearing the SUSPND bit.
|
|
//Make sure the correct oscillator settings are selected in the
|
|
//"USB_WAKEUP_FROM_SUSPEND_HANDLER(EVENT_RESUME,0,0)" handler.
|
|
U1CONbits.SUSPND = 0; // Bring USB module out of power conserve
|
|
// mode.
|
|
#endif
|
|
|
|
|
|
USBActivityIE = 0;
|
|
|
|
/********************************************************************
|
|
Bug Fix: Feb 26, 2007 v2.1
|
|
*********************************************************************
|
|
The ACTVIF bit cannot be cleared immediately after the USB module wakes
|
|
up from Suspend or while the USB module is suspended. A few clock cycles
|
|
are required to synchronize the internal hardware state machine before
|
|
the ACTIVIF bit can be cleared by firmware. Clearing the ACTVIF bit
|
|
before the internal hardware is synchronized may not have an effect on
|
|
the value of ACTVIF. Additionally, if the USB module uses the clock from
|
|
the 96 MHz PLL source, then after clearing the SUSPND bit, the USB
|
|
module may not be immediately operational while waiting for the 96 MHz
|
|
PLL to lock.
|
|
********************************************************************/
|
|
|
|
// UIRbits.ACTVIF = 0; // Removed
|
|
#if defined(__18CXX) || defined(__XC8)
|
|
while(USBActivityIF)
|
|
#endif
|
|
{
|
|
USBClearInterruptFlag(USBActivityIFReg,USBActivityIFBitNum);
|
|
} // Added
|
|
|
|
USBTicksSinceSuspendEnd = 0;
|
|
|
|
}//end USBWakeFromSuspend
|
|
|
|
/********************************************************************
|
|
* Function: void USBCtrlEPService(void)
|
|
*
|
|
* PreCondition: USTAT is loaded with a valid endpoint address.
|
|
*
|
|
* Input: None
|
|
*
|
|
* Output: None
|
|
*
|
|
* Side Effects: None
|
|
*
|
|
* Overview: USBCtrlEPService checks for three transaction
|
|
* types that it knows how to service and services
|
|
* them:
|
|
* 1. EP0 SETUP
|
|
* 2. EP0 OUT
|
|
* 3. EP0 IN
|
|
* It ignores all other types (i.e. EP1, EP2, etc.)
|
|
*
|
|
* Note: None
|
|
*******************************************************************/
|
|
static void USBCtrlEPService(void)
|
|
{
|
|
//If we get to here, that means a successful transaction has just occurred
|
|
//on EP0. This means "progress" has occurred in the currently pending
|
|
//control transfer, so we should re-initialize our timeout counter.
|
|
#if defined(USB_ENABLE_STATUS_STAGE_TIMEOUTS)
|
|
USBStatusStageTimeoutCounter = USB_STATUS_STAGE_TIMEOUT;
|
|
#endif
|
|
|
|
//Check if the last transaction was on EP0 OUT endpoint (of any kind, to either the even or odd buffer if ping pong buffers used)
|
|
if((USTATcopy.Val & USTAT_EP0_PP_MASK) == USTAT_EP0_OUT_EVEN)
|
|
{
|
|
//Point to the EP0 OUT buffer of the buffer that arrived
|
|
#if defined (_PIC14E) || defined(__18CXX) || defined(__XC8)
|
|
pBDTEntryEP0OutCurrent = (volatile BDT_ENTRY*)&BDT[(USTATcopy.Val & USTAT_EP_MASK)>>1];
|
|
#elif defined(__C30__) || defined(__C32__) || defined __XC16__
|
|
pBDTEntryEP0OutCurrent = (volatile BDT_ENTRY*)&BDT[(USTATcopy.Val & USTAT_EP_MASK)>>2];
|
|
#else
|
|
#error "unimplemented"
|
|
#endif
|
|
|
|
//Set the next out to the current out packet
|
|
pBDTEntryEP0OutNext = pBDTEntryEP0OutCurrent;
|
|
//Toggle it to the next ping pong buffer (if applicable)
|
|
pBDTEntryEP0OutNext = (volatile BDT_ENTRY*)(((uintptr_t)pBDTEntryEP0OutNext) ^ USB_NEXT_EP0_OUT_PING_PONG);
|
|
|
|
//If the current EP0 OUT buffer has a SETUP packet
|
|
if(pBDTEntryEP0OutCurrent->STAT.PID == PID_SETUP)
|
|
{
|
|
//The SETUP transaction data may have gone into the the CtrlTrfData
|
|
//buffer, or elsewhere, depending upon how the BDT was prepared
|
|
//before the transaction. Therefore, we should copy the data to the
|
|
//SetupPkt buffer so it can be processed correctly by USBCtrlTrfSetupHandler().
|
|
memcpy((uint8_t*)&SetupPkt, (uint8_t*)ConvertToVirtualAddress(pBDTEntryEP0OutCurrent->ADR), 8);
|
|
|
|
//Handle the control transfer (parse the 8-byte SETUP command and figure out what to do)
|
|
USBCtrlTrfSetupHandler();
|
|
}
|
|
else
|
|
{
|
|
//Handle the DATA transfer
|
|
USBCtrlTrfOutHandler();
|
|
}
|
|
}
|
|
else if((USTATcopy.Val & USTAT_EP0_PP_MASK) == USTAT_EP0_IN)
|
|
{
|
|
//Otherwise the transmission was and EP0 IN
|
|
// so take care of the IN transfer
|
|
USBCtrlTrfInHandler();
|
|
}
|
|
|
|
}//end USBCtrlEPService
|
|
|
|
/********************************************************************
|
|
* Function: void USBCtrlTrfSetupHandler(void)
|
|
*
|
|
* PreCondition: SetupPkt buffer is loaded with valid USB Setup Data
|
|
*
|
|
* Input: None
|
|
*
|
|
* Output: None
|
|
*
|
|
* Side Effects: None
|
|
*
|
|
* Overview: This routine is a task dispatcher and has 3 stages.
|
|
* 1. It initializes the control transfer state machine.
|
|
* 2. It calls on each of the module that may know how to
|
|
* service the Setup Request from the host.
|
|
* Module Example: USBD, HID, CDC, MSD, ...
|
|
* A callback function, USBCBCheckOtherReq(),
|
|
* is required to call other module handlers.
|
|
* 3. Once each of the modules has had a chance to check if
|
|
* it is responsible for servicing the request, stage 3
|
|
* then checks direction of the transfer to determine how
|
|
* to prepare EP0 for the control transfer.
|
|
* Refer to USBCtrlEPServiceComplete() for more details.
|
|
*
|
|
* Note: Microchip USB Firmware has three different states for
|
|
* the control transfer state machine:
|
|
* 1. WAIT_SETUP
|
|
* 2. CTRL_TRF_TX (device sends data to host through IN transactions)
|
|
* 3. CTRL_TRF_RX (device receives data from host through OUT transactions)
|
|
* Refer to firmware manual to find out how one state
|
|
* is transitioned to another.
|
|
*
|
|
* A Control Transfer is composed of many USB transactions.
|
|
* When transferring data over multiple transactions,
|
|
* it is important to keep track of data source, data
|
|
* destination, and data count. These three parameters are
|
|
* stored in pSrc,pDst, and wCount. A flag is used to
|
|
* note if the data source is from const or RAM.
|
|
*
|
|
*******************************************************************/
|
|
static void USBCtrlTrfSetupHandler(void)
|
|
{
|
|
//--------------------------------------------------------------------------
|
|
//1. Re-initialize state tracking variables related to control transfers.
|
|
//--------------------------------------------------------------------------
|
|
shortPacketStatus = SHORT_PKT_NOT_USED;
|
|
USBDeferStatusStagePacket = false;
|
|
USBDeferINDataStagePackets = false;
|
|
USBDeferOUTDataStagePackets = false;
|
|
BothEP0OutUOWNsSet = false;
|
|
controlTransferState = WAIT_SETUP;
|
|
|
|
//Abandon any previous control transfers that might have been using EP0.
|
|
//Ordinarily, nothing actually needs abandoning, since the previous control
|
|
//transfer would have completed successfully prior to the host sending the next
|
|
//SETUP packet. However, in a timeout error case, or after an EP0 STALL event,
|
|
//one or more UOWN bits might still be set. If so, we should clear the UOWN bits,
|
|
//so the EP0 IN/OUT endpoints are in a known inactive state, ready for re-arming
|
|
//by the class request handler that will be called next.
|
|
pBDTEntryIn[0]->STAT.Val &= ~(_USIE);
|
|
|
|
pBDTEntryIn[0] = (volatile BDT_ENTRY*)(((uintptr_t)pBDTEntryIn[0]) ^ USB_NEXT_EP0_IN_PING_PONG);
|
|
pBDTEntryIn[0]->STAT.Val &= ~(_USIE);
|
|
pBDTEntryIn[0] = (volatile BDT_ENTRY*)(((uintptr_t)pBDTEntryIn[0]) ^ USB_NEXT_EP0_IN_PING_PONG);
|
|
pBDTEntryEP0OutNext->STAT.Val &= ~(_USIE);
|
|
|
|
inPipes[0].info.Val = 0;
|
|
inPipes[0].wCount.Val = 0;
|
|
outPipes[0].info.Val = 0;
|
|
outPipes[0].wCount.Val = 0;
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
//2. Now find out what was in the SETUP packet, and begin handling the request.
|
|
//--------------------------------------------------------------------------
|
|
USBCheckStdRequest(); //Check for standard USB "Chapter 9" requests.
|
|
USB_NONSTANDARD_EP0_REQUEST_HANDLER(EVENT_EP0_REQUEST,0,0); //Check for USB device class specific requests
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
//3. Re-arm EP0 IN and EP0 OUT endpoints, based on the control transfer in
|
|
// progress. If one of the above handlers (in step 2) knew how to process
|
|
// the request, it will have set one of the inPipes[0].info.bits.busy or
|
|
// outPipes[0].info.bits.busy flags = 1. This lets the
|
|
// USBCtrlEPServiceComplete() function know how and which endpoints to
|
|
// arm. If both info.bits.busy flags are = 0, then no one knew how to
|
|
// process the request. In this case, the default behavior will be to
|
|
// perform protocol STALL on EP0.
|
|
//--------------------------------------------------------------------------
|
|
USBCtrlEPServiceComplete();
|
|
}//end USBCtrlTrfSetupHandler
|
|
|
|
|
|
/******************************************************************************
|
|
* Function: void USBCtrlTrfOutHandler(void)
|
|
*
|
|
* PreCondition: None
|
|
*
|
|
* Input: None
|
|
*
|
|
* Output: None
|
|
*
|
|
* Side Effects: None
|
|
*
|
|
* Overview: This routine handles an OUT transaction according to
|
|
* which control transfer state is currently active.
|
|
*
|
|
* Note: Note that if the the control transfer was from
|
|
* host to device, the session owner should be notified
|
|
* at the end of each OUT transaction to service the
|
|
* received data.
|
|
*
|
|
*****************************************************************************/
|
|
static void USBCtrlTrfOutHandler(void)
|
|
{
|
|
if(controlTransferState == CTRL_TRF_RX)
|
|
{
|
|
USBCtrlTrfRxService(); //Copies the newly received data into the appropriate buffer and configures EP0 OUT for next transaction.
|
|
}
|
|
else //In this case the last OUT transaction must have been a status stage of a CTRL_TRF_TX (<setup><in><in>...<OUT> <-- this last OUT just occurred as the status stage)
|
|
{
|
|
//If the status stage is complete, this means we are done with the
|
|
//control transfer. Go back to the idle "WAIT_SETUP" state.
|
|
controlTransferState = WAIT_SETUP;
|
|
|
|
//Prepare EP0 OUT for the next SETUP transaction, however, it may have
|
|
//already been prepared if ping-pong buffering was enabled on EP0 OUT,
|
|
//and the last control transfer was of direction: device to host, see
|
|
//USBCtrlEPServiceComplete(). If it was already prepared, do not want
|
|
//to do anything to the BDT.
|
|
if(BothEP0OutUOWNsSet == false)
|
|
{
|
|
pBDTEntryEP0OutNext->CNT = USB_EP0_BUFF_SIZE;
|
|
pBDTEntryEP0OutNext->ADR = ConvertToPhysicalAddress(&SetupPkt);
|
|
pBDTEntryEP0OutNext->STAT.Val = _DAT0|(_DTSEN & _DTS_CHECKING_ENABLED)|_BSTALL;
|
|
pBDTEntryEP0OutNext->STAT.Val |= _USIE;
|
|
}
|
|
else
|
|
{
|
|
BothEP0OutUOWNsSet = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Function: void USBCtrlTrfInHandler(void)
|
|
*
|
|
* PreCondition: None
|
|
*
|
|
* Input: None
|
|
*
|
|
* Output: None
|
|
*
|
|
* Side Effects: None
|
|
*
|
|
* Overview: This routine handles an IN transaction according to
|
|
* which control transfer state is currently active.
|
|
*
|
|
* Note: A Set Address Request must not change the actual address
|
|
* of the device until the completion of the control
|
|
* transfer. The end of the control transfer for Set Address
|
|
* Request is an IN transaction. Therefore it is necessary
|
|
* to service this unique situation when the condition is
|
|
* right. Macro mUSBCheckAdrPendingState is defined in
|
|
* usb9.h and its function is to specifically service this
|
|
* event.
|
|
*****************************************************************************/
|
|
static void USBCtrlTrfInHandler(void)
|
|
{
|
|
uint8_t lastDTS;
|
|
|
|
lastDTS = pBDTEntryIn[0]->STAT.DTS;
|
|
|
|
//switch to the next ping pong buffer
|
|
pBDTEntryIn[0] = (volatile BDT_ENTRY*)(((uintptr_t)pBDTEntryIn[0]) ^ USB_NEXT_EP0_IN_PING_PONG);
|
|
|
|
//Must check if in ADR_PENDING_STATE. If so, we need to update the address
|
|
//now, since the IN status stage of the (set address) control transfer has
|
|
//evidently completed successfully.
|
|
if(USBDeviceState == ADR_PENDING_STATE)
|
|
{
|
|
U1ADDR = (SetupPkt.bDevADR & 0x7F);
|
|
if(U1ADDR != 0u)
|
|
{
|
|
USBDeviceState=ADDRESS_STATE;
|
|
}
|
|
else
|
|
{
|
|
USBDeviceState=DEFAULT_STATE;
|
|
}
|
|
}//end if
|
|
|
|
|
|
if(controlTransferState == CTRL_TRF_TX)
|
|
{
|
|
pBDTEntryIn[0]->ADR = ConvertToPhysicalAddress(CtrlTrfData);
|
|
USBCtrlTrfTxService();
|
|
|
|
//Check if we have already sent a short packet. If so, configure
|
|
//the endpoint to STALL in response to any further IN tokens (in the
|
|
//case that the host erroneously tries to receive more data than it
|
|
//should).
|
|
if(shortPacketStatus == SHORT_PKT_SENT)
|
|
{
|
|
// If a short packet has been sent, don't want to send any more,
|
|
// stall next time if host is still trying to read.
|
|
pBDTEntryIn[0]->STAT.Val = _BSTALL;
|
|
pBDTEntryIn[0]->STAT.Val |= _USIE;
|
|
}
|
|
else
|
|
{
|
|
if(lastDTS == 0)
|
|
{
|
|
pBDTEntryIn[0]->STAT.Val = _DAT1|(_DTSEN & _DTS_CHECKING_ENABLED);
|
|
pBDTEntryIn[0]->STAT.Val |= _USIE;
|
|
}
|
|
else
|
|
{
|
|
pBDTEntryIn[0]->STAT.Val = _DAT0|(_DTSEN & _DTS_CHECKING_ENABLED);
|
|
pBDTEntryIn[0]->STAT.Val |= _USIE;
|
|
}
|
|
}//end if(...)else
|
|
}
|
|
else // must have been a CTRL_TRF_RX status stage IN packet (<setup><out><out>...<IN> <-- this last IN just occurred as the status stage)
|
|
{
|
|
//if someone is still expecting data from the control transfer
|
|
// then make sure to terminate that request and let them know that
|
|
// they are done
|
|
if(outPipes[0].info.bits.busy == 1)
|
|
{
|
|
if(outPipes[0].pFunc != NULL)
|
|
{
|
|
outPipes[0].pFunc();
|
|
}
|
|
outPipes[0].info.bits.busy = 0;
|
|
}
|
|
|
|
controlTransferState = WAIT_SETUP;
|
|
//Don't need to arm EP0 OUT here. It was already armed by the last <out> that
|
|
//got processed by the USBCtrlTrfRxService() handler.
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/********************************************************************
|
|
* Function: void USBCheckStdRequest(void)
|
|
*
|
|
* PreCondition: None
|
|
*
|
|
* Input: None
|
|
*
|
|
* Output: None
|
|
*
|
|
* Side Effects: None
|
|
*
|
|
* Overview: This routine checks the setup data packet to see
|
|
* if it knows how to handle it
|
|
*
|
|
* Note: None
|
|
*******************************************************************/
|
|
static void USBCheckStdRequest(void)
|
|
{
|
|
if(SetupPkt.RequestType != USB_SETUP_TYPE_STANDARD_BITFIELD) return;
|
|
|
|
switch(SetupPkt.bRequest)
|
|
{
|
|
case USB_REQUEST_SET_ADDRESS:
|
|
inPipes[0].info.bits.busy = 1; // This will generate a zero length packet
|
|
USBDeviceState = ADR_PENDING_STATE; // Update state only
|
|
/* See USBCtrlTrfInHandler() for the next step */
|
|
break;
|
|
case USB_REQUEST_GET_DESCRIPTOR:
|
|
USBStdGetDscHandler();
|
|
break;
|
|
case USB_REQUEST_SET_CONFIGURATION:
|
|
USBStdSetCfgHandler();
|
|
break;
|
|
case USB_REQUEST_GET_CONFIGURATION:
|
|
inPipes[0].pSrc.bRam = (uint8_t*)&USBActiveConfiguration; // Set Source
|
|
inPipes[0].info.bits.ctrl_trf_mem = USB_EP0_RAM; // Set memory type
|
|
inPipes[0].wCount.v[0] = 1; // Set data count
|
|
inPipes[0].info.bits.busy = 1;
|
|
break;
|
|
case USB_REQUEST_GET_STATUS:
|
|
USBStdGetStatusHandler();
|
|
break;
|
|
case USB_REQUEST_CLEAR_FEATURE:
|
|
case USB_REQUEST_SET_FEATURE:
|
|
USBStdFeatureReqHandler();
|
|
break;
|
|
case USB_REQUEST_GET_INTERFACE:
|
|
inPipes[0].pSrc.bRam = (uint8_t*)&USBAlternateInterface[SetupPkt.bIntfID]; // Set source
|
|
inPipes[0].info.bits.ctrl_trf_mem = USB_EP0_RAM; // Set memory type
|
|
inPipes[0].wCount.v[0] = 1; // Set data count
|
|
inPipes[0].info.bits.busy = 1;
|
|
break;
|
|
case USB_REQUEST_SET_INTERFACE:
|
|
inPipes[0].info.bits.busy = 1;
|
|
USBAlternateInterface[SetupPkt.bIntfID] = SetupPkt.bAltID;
|
|
break;
|
|
case USB_REQUEST_SET_DESCRIPTOR:
|
|
USB_SET_DESCRIPTOR_HANDLER(EVENT_SET_DESCRIPTOR,0,0);
|
|
break;
|
|
case USB_REQUEST_SYNCH_FRAME:
|
|
default:
|
|
break;
|
|
}//end switch
|
|
}//end USBCheckStdRequest
|
|
|
|
/********************************************************************
|
|
* Function: void USBStdFeatureReqHandler(void)
|
|
*
|
|
* PreCondition: None
|
|
*
|
|
* Input: None
|
|
*
|
|
* Output: Can alter BDT entries. Can also modify USB stack
|
|
* Maintained variables.
|
|
*
|
|
* Side Effects: None
|
|
*
|
|
* Overview: This routine handles the standard SET & CLEAR
|
|
* FEATURES requests
|
|
*
|
|
* Note: This is a private function, intended for internal
|
|
* use by the USB stack, when processing SET/CLEAR
|
|
* feature requests.
|
|
*******************************************************************/
|
|
static void USBStdFeatureReqHandler(void)
|
|
{
|
|
BDT_ENTRY *p;
|
|
EP_STATUS current_ep_data;
|
|
#if defined(__C32__)
|
|
uint32_t* pUEP;
|
|
#else
|
|
unsigned char* pUEP;
|
|
#endif
|
|
|
|
//Check if the host sent a valid SET or CLEAR feature (remote wakeup) request.
|
|
if((SetupPkt.bFeature == USB_FEATURE_DEVICE_REMOTE_WAKEUP)&&
|
|
(SetupPkt.Recipient == USB_SETUP_RECIPIENT_DEVICE_BITFIELD))
|
|
{
|
|
inPipes[0].info.bits.busy = 1;
|
|
if(SetupPkt.bRequest == USB_REQUEST_SET_FEATURE)
|
|
RemoteWakeup = true;
|
|
else
|
|
RemoteWakeup = false;
|
|
}//end if
|
|
|
|
//Check if the host sent a valid SET or CLEAR endpoint halt request.
|
|
if((SetupPkt.bFeature == USB_FEATURE_ENDPOINT_HALT)&&
|
|
(SetupPkt.Recipient == USB_SETUP_RECIPIENT_ENDPOINT_BITFIELD)&&
|
|
(SetupPkt.EPNum != 0) && (SetupPkt.EPNum <= USB_MAX_EP_NUMBER)&&
|
|
(USBDeviceState == CONFIGURED_STATE))
|
|
{
|
|
//The request was valid. Take control of the control transfer and
|
|
//perform the host requested action.
|
|
inPipes[0].info.bits.busy = 1;
|
|
|
|
//Fetch a pointer to the BDT that the host wants to SET/CLEAR halt on.
|
|
if(SetupPkt.EPDir == OUT_FROM_HOST)
|
|
{
|
|
p = (BDT_ENTRY*)pBDTEntryOut[SetupPkt.EPNum];
|
|
current_ep_data.Val = ep_data_out[SetupPkt.EPNum].Val;
|
|
}
|
|
else
|
|
{
|
|
p = (BDT_ENTRY*)pBDTEntryIn[SetupPkt.EPNum];
|
|
current_ep_data.Val = ep_data_in[SetupPkt.EPNum].Val;
|
|
}
|
|
|
|
//If ping pong buffering is enabled on the requested endpoint, need
|
|
//to point to the one that is the active BDT entry which the SIE will
|
|
//use for the next attempted transaction on that EP number.
|
|
#if (USB_PING_PONG_MODE == USB_PING_PONG__ALL_BUT_EP0) || (USB_PING_PONG_MODE == USB_PING_PONG__FULL_PING_PONG)
|
|
if(current_ep_data.bits.ping_pong_state == 0) //Check if even
|
|
{
|
|
p = (BDT_ENTRY*)(((uintptr_t)p) & (~USB_NEXT_PING_PONG));
|
|
}
|
|
else //else must have been odd
|
|
{
|
|
p = (BDT_ENTRY*)(((uintptr_t)p) | USB_NEXT_PING_PONG);
|
|
}
|
|
#endif
|
|
|
|
//Update the BDT pointers with the new, next entry based on the feature
|
|
// request
|
|
if(SetupPkt.EPDir == OUT_FROM_HOST)
|
|
{
|
|
pBDTEntryOut[SetupPkt.EPNum] = (volatile BDT_ENTRY *)p;
|
|
}
|
|
else
|
|
{
|
|
pBDTEntryIn[SetupPkt.EPNum] = (volatile BDT_ENTRY *)p;
|
|
}
|
|
|
|
//Check if it was a SET_FEATURE endpoint halt request
|
|
if(SetupPkt.bRequest == USB_REQUEST_SET_FEATURE)
|
|
{
|
|
if(p->STAT.UOWN == 1)
|
|
{
|
|
//Mark that we are terminating this transfer and that the user
|
|
// needs to be notified later
|
|
if(SetupPkt.EPDir == OUT_FROM_HOST)
|
|
{
|
|
ep_data_out[SetupPkt.EPNum].bits.transfer_terminated = 1;
|
|
}
|
|
else
|
|
{
|
|
ep_data_in[SetupPkt.EPNum].bits.transfer_terminated = 1;
|
|
}
|
|
}
|
|
|
|
//Then STALL the endpoint
|
|
p->STAT.Val |= _BSTALL;
|
|
p->STAT.Val |= _USIE;
|
|
}//if(SetupPkt.bRequest == USB_REQUEST_SET_FEATURE)
|
|
else
|
|
{
|
|
//Else the request must have been a CLEAR_FEATURE endpoint halt.
|
|
#if (USB_PING_PONG_MODE == USB_PING_PONG__ALL_BUT_EP0) || (USB_PING_PONG_MODE == USB_PING_PONG__FULL_PING_PONG)
|
|
//toggle over the to the non-active BDT
|
|
p = (BDT_ENTRY*)(((uintptr_t)p) ^ USB_NEXT_PING_PONG);
|
|
|
|
if(p->STAT.UOWN == 1)
|
|
{
|
|
//Clear UOWN and set DTS state so it will be correct the next time
|
|
//the application firmware uses USBTransferOnePacket() on the EP.
|
|
p->STAT.Val &= (~_USIE); //Clear UOWN bit
|
|
p->STAT.Val |= _DAT1; //Set DTS to DATA1
|
|
USB_TRANSFER_TERMINATED_HANDLER(EVENT_TRANSFER_TERMINATED,p,sizeof(p));
|
|
}
|
|
else
|
|
{
|
|
//UOWN already clear, but still need to set DTS to DATA1
|
|
p->STAT.Val |= _DAT1;
|
|
}
|
|
|
|
//toggle back to the active BDT (the one the SIE is currently looking at
|
|
//and will use for the next successful transaction to take place on the EP
|
|
p = (BDT_ENTRY*)(((uintptr_t)p) ^ USB_NEXT_PING_PONG);
|
|
|
|
//Check if we are currently terminating, or have previously terminated
|
|
//a transaction on the given endpoint. If so, need to clear UOWN,
|
|
//set DTS to the proper state, and call the application callback
|
|
//function.
|
|
if((current_ep_data.bits.transfer_terminated != 0) || (p->STAT.UOWN == 1))
|
|
{
|
|
if(SetupPkt.EPDir == OUT_FROM_HOST)
|
|
{
|
|
ep_data_out[SetupPkt.EPNum].bits.transfer_terminated = 0;
|
|
}
|
|
else
|
|
{
|
|
ep_data_in[SetupPkt.EPNum].bits.transfer_terminated = 0;
|
|
}
|
|
//clear UOWN, clear DTS to DATA0, and finally remove the STALL condition
|
|
p->STAT.Val &= ~(_USIE | _DAT1 | _BSTALL);
|
|
//Call the application event handler callback function, so it can
|
|
//decide if the endpoint should get re-armed again or not.
|
|
USB_TRANSFER_TERMINATED_HANDLER(EVENT_TRANSFER_TERMINATED,p,sizeof(p));
|
|
}
|
|
else
|
|
{
|
|
//clear UOWN, clear DTS to DATA0, and finally remove the STALL condition
|
|
p->STAT.Val &= ~(_USIE | _DAT1 | _BSTALL);
|
|
}
|
|
#else //else we must not be using ping-pong buffering on the requested endpoint
|
|
//Check if we need to call the user transfer terminated event callback function.
|
|
//We should call the callback, if the endpoint was previously terminated,
|
|
//or the endpoint is currently armed, and the host is performing clear
|
|
//endpoint halt, even though the endpoint wasn't stalled.
|
|
if((current_ep_data.bits.transfer_terminated != 0) || (p->STAT.UOWN == 1))
|
|
{
|
|
//We are going to call the user transfer terminated callback.
|
|
//Clear the flag so we know we took care of it and don't need
|
|
//to call it again later.
|
|
if(SetupPkt.EPDir == OUT_FROM_HOST)
|
|
{
|
|
ep_data_out[SetupPkt.EPNum].bits.transfer_terminated = 0;
|
|
}
|
|
else
|
|
{
|
|
ep_data_in[SetupPkt.EPNum].bits.transfer_terminated = 0;
|
|
}
|
|
|
|
//Clear UOWN and remove the STALL condition.
|
|
// In this case we also need to set the DTS bit to 1 so that
|
|
// it toggles to DATA0 the next time the application firmware
|
|
// calls USBTransferOnePacket() (or equivalent macro).
|
|
p->STAT.Val &= ~(_USIE | _BSTALL);
|
|
p->STAT.Val |= _DAT1;
|
|
//Let the application firmware know a transaction just
|
|
//got terminated by the host, and that it is now free to
|
|
//re-arm the endpoint or do other tasks if desired.
|
|
USB_TRANSFER_TERMINATED_HANDLER(EVENT_TRANSFER_TERMINATED,p,sizeof(p));
|
|
}
|
|
else
|
|
{
|
|
//Clear UOWN and remove the STALL condition.
|
|
// In this case we also need to set the DTS bit to 1 so that
|
|
// it toggles to DATA0 the next time the application firmware
|
|
// calls USBTransferOnePacket() (or equivalent macro).
|
|
p->STAT.Val &= ~(_USIE | _BSTALL);
|
|
p->STAT.Val |= _DAT1;
|
|
}
|
|
#endif //end of #if (USB_PING_PONG_MODE == USB_PING_PONG__ALL_BUT_EP0) || (USB_PING_PONG_MODE == USB_PING_PONG__FULL_PING_PONG)
|
|
|
|
//Get a pointer to the appropriate UEPn register
|
|
#if defined(__C32__)
|
|
pUEP = (uint32_t*)(&U1EP0);
|
|
pUEP += (SetupPkt.EPNum*4);
|
|
#else
|
|
pUEP = (unsigned char*)(&U1EP0+SetupPkt.EPNum);
|
|
#endif
|
|
|
|
//Clear the STALL bit in the UEP register
|
|
*pUEP &= ~UEP_STALL;
|
|
}//end if(SetupPkt.bRequest == USB_REQUEST_SET_FEATURE)
|
|
}//end if (lots of checks for set/clear endpoint halt)
|
|
}//end USBStdFeatureReqHandler
|
|
|
|
|
|
|
|
|
|
/**************************************************************************
|
|
Function:
|
|
void USBIncrement1msInternalTimers(void)
|
|
|
|
Description:
|
|
This function increments internal 1ms time base counters, which are
|
|
useful for application code (that can use a 1ms time base/counter), and
|
|
for certain USB event timing specific purposes.
|
|
|
|
In USB full speed applications, the application code does not need to (and should
|
|
not) explicitly call this function, as the USBDeviceTasks() function will
|
|
automatically call this function whenever a 1ms time interval has elapsed
|
|
(assuming the code is calling USBDeviceTasks() frequently enough in USB_POLLING
|
|
mode, or that USB interrupts aren't being masked for more than 1ms at a time
|
|
in USB_INTERRUPT mode).
|
|
|
|
In USB low speed applications, the application firmware is responsible for
|
|
periodically calling this function at a ~1ms rate. This can be done using
|
|
a general purpose microcontroller timer set to interrupt every 1ms for example.
|
|
If the low speed application code does not call this function, the internal timers
|
|
will not increment, and the USBGet1msTickCount() API function will not be available.
|
|
Additionally, certain USB stack operations (like control transfer timeouts)
|
|
may be unavailable.
|
|
|
|
Precondition:
|
|
This function should be called only after USBDeviceInit() has been
|
|
called (at least once at the start of the application). Ordinarily,
|
|
application code should never call this function, unless it is a low speed
|
|
USB device.
|
|
|
|
Parameters:
|
|
None
|
|
|
|
Return Values:
|
|
None
|
|
|
|
Remarks:
|
|
This function does not need to be called during USB suspend conditions, when
|
|
the USB module/stack is disabled, or when the USB cable is detached from the host.
|
|
***************************************************************************/
|
|
void USBIncrement1msInternalTimers(void)
|
|
{
|
|
#if(USB_SPEED_OPTION == USB_LOW_SPEED)
|
|
#warning "For low speed USB applications, read the function comments for the USBIncrement1msInternalTimers() function, and implement code to call this function periodically."
|
|
#endif
|
|
|
|
//Increment timekeeping 1ms tick counters. Useful for other APIs/code
|
|
//that needs a 1ms time base that is active during USB non-suspended operation.
|
|
USB1msTickCount++;
|
|
if(USBIsBusSuspended() == false)
|
|
{
|
|
USBTicksSinceSuspendEnd++;
|
|
//Check for 8-bit wraparound. If so, force it to saturate at 255.
|
|
if(USBTicksSinceSuspendEnd == 0)
|
|
{
|
|
USBTicksSinceSuspendEnd = 255;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/**************************************************************************
|
|
Function:
|
|
uint32_t USBGet1msTickCount(void)
|
|
|
|
Description:
|
|
This function retrieves a 32-bit unsigned integer that normally increments by
|
|
one every one millisecond. The count value starts from zero when the
|
|
USBDeviceInit() function is first called. See the remarks section for
|
|
details on special circumstances where the tick count will not increment.
|
|
|
|
Precondition:
|
|
This function should be called only after USBDeviceInit() has been
|
|
called (at least once at the start of the application).
|
|
|
|
Parameters:
|
|
None
|
|
|
|
Return Values:
|
|
uint32_t representing the approximate millisecond count, since the time the
|
|
USBDeviceInit() function was first called.
|
|
|
|
Remarks:
|
|
On 8-bit USB full speed devices, the internal counter is incremented on
|
|
every SOF packet detected. Therefore, it will not increment during suspend
|
|
or when the USB cable is detached. However, on 16-bit devices, the T1MSECIF
|
|
hardware interrupt source is used to increment the internal counter. Therefore,
|
|
on 16-bit devices, the count continue to increment during USB suspend or
|
|
detach events, so long as the application code has not put the microcontroller
|
|
to sleep during these events, and the application firmware is regularly
|
|
calling the USBDeviceTasks() function (or allowing it to execute, if using
|
|
USB_INTERRUPT mode operation).
|
|
|
|
In USB low speed applications, the host does not broadcast SOF packets to
|
|
the device, so the application firmware becomes responsible for calling
|
|
USBIncrement1msInternalTimers() periodically (ex: from a general purpose
|
|
timer interrupt handler), or else the returned value from this function will
|
|
not increment.
|
|
|
|
Prior to calling USBDeviceInit() for the first time the returned value will
|
|
be unpredictable.
|
|
|
|
This function is USB_INTERRUPT mode safe and may be called from main loop
|
|
code without risk of retrieving a partially updated 32-bit number.
|
|
|
|
However, this value only increments when the USBDeviceTasks() function is allowed
|
|
to execute. If USB_INTERRUPT mode is used, it is allowable to block on this
|
|
function. If however USB_POLLING mode is used, one must not block on this
|
|
function without also calling USBDeviceTasks() continuously for the blocking
|
|
duration (since the USB stack must still be allowed to execute, and the USB
|
|
stack is also responsible for updating the tick counter internally).
|
|
|
|
If the application is operating in USB_POLLING mode, this function should
|
|
only be called from the main loop context, and not from an interrupt handler,
|
|
as the returned value could be incorrect, if the main loop context code was in
|
|
the process of updating the internal count at the moment of the interrupt event.
|
|
***************************************************************************/
|
|
uint32_t USBGet1msTickCount(void)
|
|
{
|
|
#if defined (USB_INTERRUPT)
|
|
uint32_t localContextValue;
|
|
|
|
//Repeatedly read the interrupt context variable, until we get a stable/unchanging
|
|
//value. This ensures that the complete 32-bit value got read without
|
|
//getting interrupted in between bytes.
|
|
do
|
|
{
|
|
localContextValue = USB1msTickCount;
|
|
}while(localContextValue != USB1msTickCount);
|
|
|
|
return localContextValue;
|
|
|
|
#else
|
|
return USB1msTickCount;
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** EOF USBDevice.c *****************************************************/
|