// TITLE: Enumeration code to handle all endpoint zero traffic
#include <stdbool.h>
#include <stdint.h>
#include <inc/hw_ints.h>
#include <inc/hw_memmap.h>
#include <inc/hw_types.h>
//#include "debug.h"
#include "interrupt.h"
#include "sysctl.h"
#include "usb.h"
#include "usblib.h"
#include "usblibpriv.h"
#include "usbdevice.h"
static tUSBMode g_iUSBMode = eUSBModeForceDevice;
// Indices into the ppui8Halt array to select the IN or OUT endpoint group.
#define HALT_EP_IN 0
#define HALT_EP_OUT 1
// Define the max packet size for endpoint zero.
#define EP0_MAX_PACKET_SIZE 64
//*****************************************************************************
//
// This is a flag used with g_sUSBDeviceState.ui32DevAddress to indicate that a
// device address change is pending.
//
//*****************************************************************************
#define DEV_ADDR_PENDING 0x80
//*****************************************************************************
//
// This label defines the default configuration number to use after a bus
// reset. This may be overridden by calling USBDCDSetDefaultConfiguration()
// during processing of the device reset handler if required.
//
//*****************************************************************************
#define DEFAULT_CONFIG_ID 1
//*****************************************************************************
//
// The buffer for reading data coming into EP0
//
//*****************************************************************************
static uint8_t g_pui8DataBufferIn[EP0_MAX_PACKET_SIZE];
//*****************************************************************************
// This is the instance data for the USB controller itself and not a USB
// device class.
//*****************************************************************************
tDCDInstance g_psDCDInst;
//*****************************************************************************
// This is the currently active class in use by USBLib. There is only one
// of these per USB controller and no device has more than one controller.
//*****************************************************************************
tDeviceInfo *g_UsbDevice;
//*****************************************************************************
//
// Functions accessible by USBLIB clients.
//
//*****************************************************************************
//*****************************************************************************
//
//! Initialize an instance of the tDeviceInfo structure.
//!
//! \param psDeviceInfo is a pointer to the tDeviceInfo structure that needs
//! to be initialized. This function must be called by a USB device class
//! instance to initialize the basic tDeviceInfo required for all USB device
//! class modules. This is typically called in the initialization routine for
//! USB device class. For example in usbdaudio.c that supports USB device
//! audio classes, this function is called in the USBDAudioCompositeInit()
//! function which is used for both composite and non-composites instances of
//! the USB audio class.
//!
//! \note This function should not be called directly by applications.
//!
//! \return None.
//
//*****************************************************************************
void tDeviceInfo::DeviceInfoInit() {
// Save the USB interrupt number.
ui32IntNum = INT_USB;
// Initialize a couple of fields in the device state structure.
ui32Configuration = DEFAULT_CONFIG_ID;
// g_psDCDInst.ui32DefaultConfiguration = DEFAULT_CONFIG_ID;
iEP0State = eUSBStateIdle;
// Default to the state where remote wake up is disabled.
ui8Status = 0;
bRemoteWakeup = false;
}
//*****************************************************************************
//
//! Initialize the USB library device control driver for a given hardware
//! controller.
//!
//! \param psDevice is a pointer to a structure containing information that
//! the USB library requires to support operation of this application's
//! device. The structure contains event handler callbacks and pointers to the
//! various standard descriptors that the device wishes to publish to the
//! host.
//! \param pvDCDCBData is the callback data for any device callbacks.
//!
//! This function must be called by a device class which wishes to operate
//! as a USB device and is not typically called by an application. This
//! function initializes the USB device control driver for the given
//! controller and saves the device information for future use. Prior to
//! returning from this function, the device is connected to the USB bus.
//! Following return, the caller can expect to receive a callback to the
//! supplied <tt>pfnResetHandler</tt> function when a host connects to the
//! device. The \e pvDCDCBData contains a pointer to data that is returned
//! with the DCD calls back to the function in the psDevice->psCallbacks()
//! functions.
//!
//! The device information structure passed in \e psDevice must remain
//! unchanged between this call and any matching call to USBDCDTerm() because
//! it is not copied by the USB library.
//!
//! The USBStackModeSet() function can be called with eUSBModeForceDevice in
//! order to cause the USB library to force the USB operating mode to a device
//! controller. This allows the application to used the USBVBUS and USBID pins
//! as GPIOs on devices that support forcing OTG to operate as a device only
//! controller. By default the USB library will assume that the USBVBUS and
//! USBID pins are configured as USB pins and not GPIOs.
//!
//! \return None.
//
//*****************************************************************************
void tDeviceInfo::Init() {
g_UsbDevice = this;
// g_psDCDInst.pvCBData = pvDCDCBData;
// Initialize the Device Info structure for a USB device instance.
DeviceInfoInit();
//
// Should not call this if the stack is in host mode.
//
ASSERT(g_iUSBMode != eUSBModeHost);
ASSERT(g_iUSBMode != eUSBModeForceHost);
//
// Default to device mode if no mode was set.
//
if(g_iUSBMode == eUSBModeNone)
{
g_iUSBMode = eUSBModeDevice;
}
//
// Only do hardware update if the stack is in not in OTG mode.
//
if(g_iUSBMode != eUSBModeOTG)
{
//
// Reset the USB controller.
//
#ifdef __TMS320C28XX__
SysCtl_resetPeripheral(SYSCTL_PERIPH_RES_USBA);
#else
SysCtl_resetPeripheral(SYSCTL_PERIPH_RES_USB);
#endif
//
// Enable Clocking to the USB controller.
//
#ifdef __TMS320C28XX__
SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_USBA);
#else
SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_USB);
#endif
//
// Force device mode if requested.
//
// if(g_iUSBMode == eUSBModeForceDevice)
// {
USBDevMode(USB_BASE);
// }
g_iUSBMode = eUSBModeDevice;
}
//
// Initialize the USB DMA interface.
//
g_psDCDInst.psDMAInstance = USBLibDMAInit(USB_BASE);
//
// Initialize the USB tick module.
//
InternalUSBTickInit();
//
// Get a pointer to the default configuration descriptor.
//
// psHdr = psDevice->ppsConfigDescriptors[
// g_psDCDInst.ui32DefaultConfiguration - 1];
// psDesc = (const tConfigDescriptor *)(psHdr->psSections[0]->pui8Data);
// if((psDesc->bmAttributes & USB_CONF_ATTR_PWR_M) == USB_CONF_ATTR_SELF_PWR)
// {
// g_psDCDInst.ui8Status |= USB_STATUS_SELF_PWR;
// }
// else
// {
g_psDCDInst.ui8Status &= ~1; //USB_STATUS_SELF_PWR
// }
// Only do hardware update if the stack is not in OTG mode.
//
// Get the current interrupt status.to clear all pending USB
// interrupts.
//
USBIntStatusControl(USB_BASE);
USBIntStatusEndpoint(USB_BASE);
//
// Enable USB Interrupts.
//
USBIntEnableControl(USB_BASE, USB_INTCTRL_RESET |
USB_INTCTRL_DISCONNECT |
USB_INTCTRL_RESUME |
USB_INTCTRL_SUSPEND |
USB_INTCTRL_SOF);
USBIntEnableEndpoint(USB_BASE, USB_INTEP_ALL);
//
// Attach the device using the soft connect.
//
USBDevConnect(USB_BASE);
//
// Enable the USB interrupt.
//
Interrupt_enable(g_psDCDInst.ui32IntNum);
}
//*****************************************************************************
//
//! Free the USB library device control driver for a given hardware controller.
//!
//! This function should be called by an application if it no longer requires
//! the use of a given USB controller to support its operation as a USB device.
//! It frees the controller for use by another client.
//!
//! It is the caller's responsibility to remove its device from the USB bus
//! prior to calling this function.
//!
//! \return None.
//
//*****************************************************************************
void tDeviceInfo::Term() {
// Check the arguments.
// Disable the USB interrupts.
Interrupt_disable(ui32IntNum);
// Reset the tick handlers so that they can be reconfigured when and if
// USBDCDInit() is called.
InternalUSBTickReset();
// No active device.
g_UsbDevice = 0;
USBIntDisableControl(USB_BASE, USB_INTCTRL_ALL);
USBIntDisableEndpoint(USB_BASE, USB_INTEP_ALL);
// Detach the device using the soft connect.
USBDevDisconnect(USB_BASE);
// Clear any pending interrupts.
USBIntStatusControl(USB_BASE);
USBIntStatusEndpoint(USB_BASE);
// Disable the USB peripheral
#ifdef __TMS320C28XX__
SysCtl_disablePeripheral(SYSCTL_PERIPH_CLK_USBA);
#else
SysCtl_disablePeripheral(SYSCTL_PERIPH_CLK_USB);
#endif
}
//*****************************************************************************
// This internal function handles sending data on endpoint zero.
//*****************************************************************************
static void USBDEP0StateTx() {
// In the TX state on endpoint zero.
g_psDCDInst.iEP0State = eUSBStateTx;
// Set the number of bytes to send this iteration.
uint32_t ui32NumBytes = g_psDCDInst.ui32EP0DataRemain;
// Limit individual transfers to 64 bytes.
if (ui32NumBytes > 64) ui32NumBytes = 64;
// Save the pointer so that it can be passed to the USBEndpointDataPut()
// function.
uint8_t *pui8Data = g_psDCDInst.pui8EP0Data;
// Advance the data pointer and counter to the next data to be sent.
g_psDCDInst.ui32EP0DataRemain -= ui32NumBytes;
g_psDCDInst.pui8EP0Data += ui32NumBytes;
// Put the data in the correct FIFO.
USBEndpointDataPut(USB_BASE, 0, pui8Data, ui32NumBytes);
// If this is exactly 64 then don't set the last packet yet.
if (ui32NumBytes == 64) {
// There is more data to send or exactly 64 bytes were sent, this
// means that there is either more data coming or a null packet needs
// to be sent to complete the transaction.
USBEndpointDataSend(USB_BASE, 0, USB_TRANS_IN);
}else{
// Now go to the status state and wait for the transmit to complete.
g_psDCDInst.iEP0State = eUSBStateStatus;
// Send the last bit of data.
USBEndpointDataSend(USB_BASE, 0, USB_TRANS_IN_LAST);
}
}
//*****************************************************************************
//! This function starts the request for data from the host on endpoint zero.
//!
//! \param pui8Data is a pointer to the buffer to fill with data from the USB
//! host.
//! \param ui32Size is the size of the buffer or data to return from the USB
//! host.
//!
//! This function handles retrieving data from the host when a custom command
//! has been issued on endpoint zero. If the application needs notification
//! when the data has been received,
//! <tt>psCallbacks->pfnDataReceived()</tt> in the tDeviceInfo structure
//! must contain valid function pointer. In nearly all cases this is necessary
//! because the caller of this function would likely need to know that the data
//! requested was received.
//*****************************************************************************
void tDeviceInfo::RequestDataEP0(uint8_t *pui8Data, uint32_t ui32Size) {
// Enter the RX state on end point 0.
iEP0State = eUSBStateRx;
// Save the pointer to the data.
pui8EP0Data = pui8Data;
// Bytes remaining to be received.
ui32EP0DataRemain = ui32Size;
}
//*****************************************************************************
//! This function requests transfer of data to the host on endpoint zero.
//!
//! \param pui8Data is a pointer to the buffer to send via endpoint zero.
//! \param ui32Size is the amount of data to send in bytes.
//!
//! This function handles sending data to the host when a custom command is
//! issued or non-standard descriptor has been requested on endpoint zero. If
//! the application needs notification when this is complete,
//! <tt>psCallbacks->pfnDataSent</tt> in the tDeviceInfo structure must
//! contain a valid function pointer. This callback could be used to free up
//! the buffer passed into this function in the \e pui8Data parameter. The
//! contents of the \e pui8Data buffer must remain unchanged until the
//! <tt>pfnDataSent</tt> callback is received.
//*****************************************************************************
void tDeviceInfo::SendDataEP0(const uint8_t *pui8Data, uint32_t ui32Size) {
// Return the externally provided device descriptor.
pui8EP0Data = const_cast<uint8_t*>(pui8Data);
// The size of the device descriptor is in the first byte.
ui32EP0DataRemain = ui32Size;
// Now in the transmit data state.
USBDEP0StateTx();
}
void tDeviceInfo::SendDataEP0(const uint8_t *data, uint32_t size, const tUSBRequest*rq) {
rq->limitLength(size);
SendDataEP0(data, size);
}
//*****************************************************************************
//! This function generates a stall condition on endpoint zero.
//!
//! This function is typically called to signal an error condition to the host
//! when an unsupported request is received by the device. It should be
//! called from within the callback itself (in interrupt context) and not
//! deferred until later since it affects the operation of the endpoint zero
//! state machine in the USB library.
//*****************************************************************************
void tDeviceInfo::StallEP0() {
// Stall the endpoint 0
USBDevEndpointStall(USB_BASE, 0, USB_EP_DEV_OUT);
// Enter the stalled state.
iEP0State = eUSBStateStall;
}
//*****************************************************************************
//
//! Reports the device power status (bus- or self-powered) to the library.
//!
//! \param ui8Power indicates the current power status, either
//! \b USB_STATUS_SELF_PWR or \b USB_STATUS_BUS_PWR.
//!
//! Applications which support switching between bus- or self-powered
//! operation should call this function whenever the power source changes
//! to indicate the current power status to the USB library. This information
//! is required by the library to allow correct responses to be provided when
//! the host requests status from the device.
//!
//! \return None.
//
//*****************************************************************************
void tDeviceInfo::PowerStatusSet(bool selfpower) {
// Update the device status with the new power status flag.
if (selfpower) ui8Status|=1; else ui8Status &= ~1;
}
//*****************************************************************************
//
//! Requests a remote wake up to resume communication when in suspended state.
//!
//! When the bus is suspended, an application which supports remote wake up
//! (advertised to the host via the configuration descriptor) may call this
//! function to initiate remote wake up signaling to the host. If the remote
//! wake up feature has not been disabled by the host, this will cause the bus
//! to resume operation within 20mS. If the host has disabled remote wake up,
//! \b false will be returned to indicate that the wake up request was not
//! successful.
//!
//! \return Returns \b true if the remote wake up is not disabled and the
//! signaling was started or \b false if remote wake up is disabled or if
//! signaling is currently ongoing following a previous call to this function.
//
//*****************************************************************************
bool tDeviceInfo::RemoteWakeupRequest() {
// Is remote wake up signaling currently enabled?
if (!(ui8Status & 2)) return false; // Bit 1 = USB_STATUS_REMOTE_WAKE
// The host has not disabled remote wake up.
// Are we still in the middle of a previous wake up sequence?
if (bRemoteWakeup) return false;
// No - we are not in the middle of a wake up sequence so start one here.
ui8RemoteWakeupCount = 0;
bRemoteWakeup = true;
USBHostResume(USB_BASE, true);
return true;
}
//*****************************************************************************
// This internal function is called on the SOF interrupt to process any
// outstanding remote wake up requests.
//*****************************************************************************
void USBDeviceResumeTickHandler(tDCDInstance *psDevInst) {
if (!g_psDCDInst.bRemoteWakeup) return;
// Increment the millisecond counter we use to time the resume
// signaling.
g_psDCDInst.ui8RemoteWakeupCount++;
// Have we reached the 10 ms mark? If so, we need to turn the signaling off again.
if (g_psDCDInst.ui8RemoteWakeupCount == 10) {
USBHostResume(USB_BASE, false);
}
// Have we reached the point at which we can tell the client that the
// bus has resumed? The controller does not give us an interrupt if we
// initiated the wake up signaling so we just wait until 20ms have
// passed then tell the client all is well.
if (g_psDCDInst.ui8RemoteWakeupCount != 20) return;
// We are now finished with the remote wake up signaling.
g_psDCDInst.bRemoteWakeup = false;
// If the client has registered a resume callback, call it. In the
// case of a remote wake up request, we do not get a resume
// interrupt from the controller so we need to fake it here.
g_UsbDevice->cbResumeHandler();
}
//*****************************************************************************
//
// This function handles bus reset notifications.
//
// This function is called from the low level USB interrupt handler whenever
// a bus reset is detected. It performs tidy-up as required and resets the
// configuration back to defaults in preparation for descriptor queries from
// the host.
//
// \return None.
//
//*****************************************************************************
void USBDeviceEnumResetHandler(tDCDInstance *pDevInstance) {
// Disable remote wake up signaling (as per USB 2.0 spec 9.1.1.6).
pDevInstance->ui8Status &= ~2; // USB_STATUS_REMOTE_WAKE
pDevInstance->bRemoteWakeup = false;
// Call the device dependent code to indicate a bus reset has occurred.
g_UsbDevice->cbResetHandler();
// Reset the default configuration identifier and alternate function
// selections.
pDevInstance->ui32Configuration = 0;
}
//*****************************************************************************
// This internal function reads a request data packet and dispatches it to
// either a standard request handler or the registered device request
// callback depending upon the request type.
//*****************************************************************************
void tDeviceInfo::ReadAndDispatchRequest() {
// Cast the buffer to a request structure.
tUSBRequest *psRequest = (tUSBRequest *)g_pui8DataBufferIn;
// Set the buffer size.
uint32_t ui32Size = EP0_MAX_PACKET_SIZE;
// Get the data from the USB controller end point 0.
USBEndpointDataGet(USB_BASE, 0, g_pui8DataBufferIn,&ui32Size);
// If there was a null setup packet then just return.
if (!ui32Size) return;
uint8_t answer[2]={0,0};
// See if this is a standard request or not.
if (psRequest->bmRequestType & 0x60) {
// Since this is not a standard request, see if there is
// an external handler present.
if (!cbRequestHandler(psRequest)) goto stall;
return;
}
switch (psRequest->wRequest()) {
case 0x0080: { // GetStatus-Device
answer[0]=ui8Status;
USBDevEndpointDataAck(USB_BASE, 0, false);
SendDataEP0(answer,2);
}return;
case 0x0081: { // GetStatus-Interface(wIndexL)
USBDevEndpointDataAck(USB_BASE, 0, false);
SendDataEP0(answer,2);
}return;
case 0x0082: { // GetStatus-Endpoint(wIndexL)
if (psRequest->wIndexL != 0x81) goto stall;
USBDevEndpointDataAck(USB_BASE, 0, false);
//TODO: Stall-Zustand von EP1IN abfragen
SendDataEP0(answer,2);
}return;
case 0x0100: { // ClearFeature-Device
if (psRequest->wValueL != 1) goto stall;
USBDevEndpointDataAck(USB_BASE, 0, true);
ui8Status &= ~2; // Clear the remote wake up state.
}return;
case 0x0102: { // ClearFeature-Endpoint(wIndexL)
if (psRequest->wIndexL != 0x81) goto stall; // Nur EP1IN
if (psRequest->wValueL != 0) goto stall; // nur HALT
USBDevEndpointDataAck(USB_BASE, 0, true);
USBDevEndpointStallClear(USB_BASE,1<<4,USB_EP_DEV_IN);
USBEndpointDataToggleClear(USB_BASE,1<<4,USB_EP_DEV_IN);
}return;
case 0x0300: { // SetFeature-Device
if (psRequest->wValueL != 1) goto stall;
USBDevEndpointDataAck(USB_BASE, 0, true);
ui8Status |= 2; // Set the remote wake up state
}return;
case 0x0302: { // SetFeature-Endpoint(wIndexL)
if (psRequest->wIndexL != 0x81) goto stall; // Nur EP1IN
if (psRequest->wValueL != 0) goto stall; // nur HALT
USBDevEndpointDataAck(USB_BASE, 0, true);
USBDevEndpointStall(USB_BASE,1<<4,USB_EP_DEV_IN);
}return;
case 0x0500: { // SetAddress-Device
USBDevEndpointDataAck(USB_BASE, 0, true);
// Save the device address as we cannot change address until the status phase is complete.
ui32DevAddress = psRequest->wValueL | 0x80;
// Transition directly to the status state since there is no data phase for this request.
iEP0State = eUSBStateStatus;
}return;
case 0x0680: { // GetDescriptor-Device
// tDCDInstance *psUSBControl = &g_psDCDInst;
// tDeviceInfo *psDevice = g_UsbDevice;
// Need to ACK the data on end point 0 without setting last data as there will be a data phase.
USBDevEndpointDataAck(USB_BASE, 0, false);
switch (psRequest->wValueH) {
case 1: { // device descriptor
const uint8_t*d = g_UsbDevice->desc.device;
SendDataEP0(d,d[0],psRequest);
}return;
case 2: { // configuration descriptor
const uint8_t*d = g_UsbDevice->desc.config;
SendDataEP0(d, d[2] | d[3]<<8, psRequest);
}return;
case 3: switch (psRequest->wValueL) { // string descriptor
case 0:
case 1:
case 2: {
const uint8_t*d = g_UsbDevice->ppui8StringDescriptors[psRequest->wValueL];
SendDataEP0(d,d[0],psRequest);
}return;
}goto stall;
}
}goto stall;
case 0x0700: goto stall; // SetDescriptor-Device
case 0x0880: { // GetConfig-Device
USBDevEndpointDataAck(USB_BASE, 0, false);
answer[0] = ui32Configuration;
SendDataEP0(answer,1);
}return;
case 0x0900: { // SetConfig-Device
if (psRequest->wValueL>1) goto stall; // nur 0 oder 1 erlaubt
USBDevEndpointDataAck(USB_BASE, 0, true);
if (ui32Configuration = psRequest->wValueL) {
// Konfiguriere EP1IN
USBDevEndpointConfigSet(USB_BASE,1<<4,64,USB_EP_MODE_INT/*|USB_EP_AUTO_SET*/);
// EP0 verwendet die ersten 64 Bytes, EP1 kommt danach:
USBFIFOConfigSet(USB_BASE,1<<4,64,64,USB_EP_DEV_IN);
}
// If there is a configuration change callback then call it.
cbConfigChange(g_psDCDInst.ui32Configuration);
}return;
case 0x0A81: { // GetInterface-Interface(wIndexL)
if (psRequest->wIndexL) goto stall;
USBDevEndpointDataAck(USB_BASE, 0, false);
SendDataEP0(answer,1); // Einfaches Beispiel hat nur eine Alternate Setting
}return;
case 0x0B01: { // SetInterface-Interface(wIndexL)
// bmRequestType=0x01, wIndexL=Interface, wValueL=AltSetting, wLength=0
if (psRequest->wIndexL) goto stall;
if (psRequest->wValueL) goto stall;
USBDevEndpointDataAck(USB_BASE, 0, true);
}return;
case 0x0C82: goto stall; // SyncFrame-Endpoint(wIndexL)
// bmRequestType=0x82, wIndexL=Endpoint, wLength=2: Antwort = FrameNumber
}
stall:
StallEP0();
}
//*****************************************************************************
// This is interrupt handler for endpoint zero.
//
// This function handles all interrupts on endpoint zero in order to maintain
// the state needed for the control endpoint on endpoint zero. In order to
// successfully enumerate and handle all USB standard requests, all requests
// on endpoint zero must pass through this function. The endpoint has the
// following states: \b eUSBStateIdle, \b eUSBStateTx, \b eUSBStateRx,
// \b eUSBStateStall, and \b eUSBStateStatus. In the \b eUSBStateIdle
// state the USB controller has not received the start of a request, and once
// it does receive the data for the request it will either enter the
// \b eUSBStateTx, \b eUSBStateRx, or \b eUSBStateStall depending on the
// command. If the controller enters the \b eUSBStateTx or \b eUSBStateRx
// then once all data has been sent or received, it must pass through the
// \b eUSBStateStatus state to allow the host to acknowledge completion of
// the request. The \b eUSBStateStall is entered from \b eUSBStateIdle in
// the event that the USB request was not valid. Both the \b eUSBStateStall
// and \b eUSBStateStatus are transitional states that return to the
// \b eUSBStateIdle state.
//
// \return None.
//
// eUSBStateIdle -*--> eUSBStateTx -*-> eUSBStateStatus -*->eUSBStateIdle
// | | |
// |--> eUSBStateRx |
// | |
// |--> eUSBStateStall ---------->--------
//
// ----------------------------------------------------------------
// | Current State | State 0 | State 1 |
// | --------------------|-------------------|----------------------
// | eUSBStateIdle | eUSBStateTx/RX | eUSBStateStall |
// | eUSBStateTx | eUSBStateStatus | |
// | eUSBStateRx | eUSBStateStatus | |
// | eUSBStateStatus | eUSBStateIdle | |
// | eUSBStateStall | eUSBStateIdle | |
// ----------------------------------------------------------------
//
//*****************************************************************************
void tDeviceInfo::DeviceEnumHandler() {
uint32_t ui32EPStatus, ui32DataSize;
// Get the end point 0 status.
ui32EPStatus = USBEndpointStatus(USB_BASE, USB_EP_0);
switch(iEP0State) {
// Handle the status state, this is a transitory state from
// eUSBStateTx or eUSBStateRx back to eUSBStateIdle.
case eUSBStateStatus: {
// Just go back to the idle state.
iEP0State = eUSBStateIdle;
// If there is a pending address change then set the address.
if (ui32DevAddress & 0x80) {
// Clear the pending address change and set the address.
ui32DevAddress &= ~0x80;
USBDevAddrSet(USB_BASE, ui32DevAddress);
}
// If a new packet is already pending, we need to read it
// and handle whatever request it contains.
if (ui32EPStatus & USB_DEV_EP0_OUT_PKTRDY) {
// Process the newly arrived packet.
ReadAndDispatchRequest();
}
}break;
// In the IDLE state the code is waiting to receive data from the host.
case eUSBStateIdle: {
// Is there a packet waiting for us?
if (ui32EPStatus & USB_DEV_EP0_OUT_PKTRDY) {
// Yes - process it.
ReadAndDispatchRequest();
}
}break;
// Data is still being sent to the host so handle this in the
// EP0StateTx() function.
case eUSBStateTx: {
USBDEP0StateTx();
}break;
// We are still in the middle of sending the configuration descriptor
// so handle this in the EP0StateTxConfig() function.
// USBDEP0StateTxConfig();
// }break;
// Handle the receive state for commands that are receiving data on
// endpoint zero.
case eUSBStateRx: {
// Set the number of bytes to get out of this next packet.
if (ui32EP0DataRemain > EP0_MAX_PACKET_SIZE) {
// Don't send more than EP0_MAX_PACKET_SIZE bytes.
ui32DataSize = EP0_MAX_PACKET_SIZE;
}else{
// There was space so send the remaining bytes.
ui32DataSize = ui32EP0DataRemain;
}
// Get the data from the USB controller end point 0.
USBEndpointDataGet(USB_BASE, USB_EP_0,
pui8EP0Data, &ui32DataSize);
// If there we not more that EP0_MAX_PACKET_SIZE or more bytes
// remaining then this transfer is complete. If there were exactly
// EP0_MAX_PACKET_SIZE remaining then there still needs to be
// null packet sent before this is complete.
if (ui32EP0DataRemain < EP0_MAX_PACKET_SIZE) {
// Return to the idle state.
iEP0State = eUSBStateStatus;
// Need to ACK the data on end point 0 in this case and set the
// data end as this is the last of the data.
USBDevEndpointDataAck(USB_BASE, USB_EP_0, true);
}else{
// Need to ACK the data on end point 0 in this case
// without setting data end because more data is coming.
USBDevEndpointDataAck(USB_BASE, USB_EP_0, false);
}
// Advance the pointer.
pui8EP0Data += ui32DataSize;
// Decrement the number of bytes that are being waited on.
ui32EP0DataRemain -= ui32DataSize;
}break;
// The device stalled endpoint zero so check if the stall needs to be
// cleared once it has been successfully sent.
case eUSBStateStall: {
// If we sent a stall then acknowledge this interrupt.
if (ui32EPStatus & USB_DEV_EP0_SENT_STALL) {
// Clear the Setup End condition.
USBDevEndpointStatusClear(USB_BASE, USB_EP_0,
USB_DEV_EP0_SENT_STALL);
// Reset the global end point 0 state to IDLE.
iEP0State = eUSBStateIdle;
}
}break;
// Halt on an unknown state, but only in DEBUG mode builds.
default: ASSERT(0);
}
}
//*****************************************************************************
// The internal USB device interrupt handler.
//
// \param ui32Status is the current interrupt status as read via a call to
// USBIntStatus(). This is the value of USBIS.
// \param ui32IntStatusEP is the current interrupt status of the endpoint
// as read from USBIntStatus(). This is the value of RXIS and TXIS.
//
// This function is called from either \e USB0DualModeIntHandler() or
// \e USB0DeviceIntHandler() to process USB interrupts when in device mode.
// This handler will branch the interrupt off to the appropriate application or
// stack handlers depending on the current status of the USB controller.
//
// The two-tiered structure for the interrupt handler ensures that it is
// possible to use the same handler code in both device and OTG modes and
// means that host code can be excluded from applications that only require
// support for USB device mode operation.
//
//*****************************************************************************
void tDeviceInfo::USBDeviceIntHandlerInternal(uint32_t ui32Status, uint32_t ui32IntStatusEP) {
static uint32_t ui32SOFDivide = 0;
uint32_t ui32DMAIntStatus;
// If device initialization has not been performed then just disconnect
// from the USB bus and return from the handler.
if(!g_UsbDevice) {
USBDevDisconnect(USB_BASE);
return;
}
// Received a reset from the host.
if(ui32Status & USB_INTCTRL_RESET) {
USBDeviceEnumResetHandler(&g_psDCDInst);
}
// Suspend was signaled on the bus.
if(ui32Status & USB_INTCTRL_SUSPEND) {
// Call the SuspendHandler() if it was specified.
g_UsbDevice->cbSuspendHandler();
}
//
// Resume was signaled on the bus.
//
if(ui32Status & USB_INTCTRL_RESUME)
{
//
// Call the ResumeHandler() if it was specified.
//
g_UsbDevice->cbResumeHandler();
}
//
// USB device was disconnected.
//
if(ui32Status & USB_INTCTRL_DISCONNECT)
{
//
// Call the DisconnectHandler() if it was specified.
//
g_UsbDevice->cbDisconnectHandler();
}
//
// Start of Frame was received.
//
if(ui32Status & USB_INTCTRL_SOF)
{
//
// Increment the global Start of Frame counter.
//
g_ui32USBSOFCount++;
//
// Increment our SOF divider.
//
ui32SOFDivide++;
//
// Handle resume signaling if required.
//
USBDeviceResumeTickHandler(&g_psDCDInst);
//
// Have we counted enough SOFs to allow us to call the tick function?
//
if(ui32SOFDivide == USB_SOF_TICK_DIVIDE)
{
//
// Yes - reset the divider and call the SOF tick handler.
//
ui32SOFDivide = 0;
InternalUSBStartOfFrameTick(USB_SOF_TICK_DIVIDE);
}
}
//
// Use the controller interrupt status from ui32IntStatusEP.
// This is made up of the values of RXIS and TXIS.
//
ui32Status = ui32IntStatusEP;
//
// Handle end point 0 interrupts.
//
if(ui32Status & USB_INTEP_0)
{
g_UsbDevice->DeviceEnumHandler();
ui32Status &= ~USB_INTEP_0;
}
//
// Check to see if any DMA transfers are pending
//
ui32DMAIntStatus = g_psDCDInst.psDMAInstance->IntStatus();
if(ui32DMAIntStatus)
{
//
// Handle any DMA interrupt processing.
//
g_psDCDInst.psDMAInstance->IntHandler(ui32DMAIntStatus);
}
//
// Because there is no way to detect if a uDMA interrupt has occurred,
// check for an endpoint callback and call it if it is available.
//
if(ui32Status || ui32DMAIntStatus) {
g_UsbDevice->cbEndpointHandler(ui32Status);
}
}
void tDeviceInfo::USB0DeviceIntHandler() {
uint32_t ui32Status;
uint32_t ui32IntStatusEP;
// Get the controller interrupt status.
ui32Status = USBIntStatus(USB_BASE, &ui32IntStatusEP);
// Call the internal handler.
USBDeviceIntHandlerInternal(ui32Status, ui32IntStatusEP);
}
Detected encoding: ASCII (7 bit) | 2
|