//*****************************************************************************
//
// usbenum.c - Enumeration code to handle all endpoint zero traffic.
//
// Copyright (c) 2007-2008 Luminary Micro, Inc. All rights reserved.
//
// Software License Agreement
//
// Luminary Micro, Inc. (LMI) is supplying this software for use solely and
// exclusively on LMI's microcontroller products.
//
// The software is owned by LMI and/or its suppliers, and is protected under
// applicable copyright laws. All rights are reserved. You may not combine
// this software with "viral" open-source software in order to form a larger
// program. Any use in violation of the foregoing restrictions may subject
// the user to criminal sanctions under applicable laws, as well as to civil
// liability for the breach of the terms and conditions of this license.
//
// THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED
// OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
// LMI SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR
// CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
//
// This is part of revision 2523 of the Stellaris USB Library.
//
//*****************************************************************************
#include "hw_memmap.h"
#include "hw_types.h"
#include "hw_ints.h"
#include "usb.h"
#include "interrupt.h"
#include "debug.h"
#include "usblib.h"
#include "usbdevice.h"
//*****************************************************************************
//
// Local functions prototypes.
//
//*****************************************************************************
static void USBDGetStatus(unsigned long ulIndex, tUSBRequest *pUSBRequest);
static void USBDClearFeature(unsigned long ulIndex, tUSBRequest *pUSBRequest);
static void USBDSetFeature(unsigned long ulIndex, tUSBRequest *pUSBRequest);
static void USBDSetAddress(unsigned long ulIndex, tUSBRequest *pUSBRequest);
static void USBDGetDescriptor(unsigned long ulIndex, tUSBRequest *pUSBRequest);
static void USBDSetDescriptor(unsigned long ulIndex, tUSBRequest *pUSBRequest);
static void USBDGetConfiguration(unsigned long ulIndex,
tUSBRequest *pUSBRequest);
static void USBDSetConfiguration(unsigned long ulIndex,
tUSBRequest *pUSBRequest);
static void USBDGetInterface(unsigned long ulIndex, tUSBRequest *pUSBRequest);
static void USBDSetInterface(unsigned long ulIndex, tUSBRequest *pUSBRequest);
static void USBDSyncFrame(unsigned long ulIndex, tUSBRequest *pUSBRequest);
static void USBDEP0StateTx(unsigned long ulIndex);
//*****************************************************************************
//
//! \addtogroup device_api
//! @{
//
//*****************************************************************************
//*****************************************************************************
//
// This structure holds the full state for the device enumeration.
//
//*****************************************************************************
typedef struct
{
//
// The devices current address, this also has a change pending bit in the
// MSB of this value specified by DEV_ADDR_PENDING.
//
volatile unsigned long ulDevAddress;
//
// This holds the current active configuration for this device.
//
unsigned long ulConfiguration;
//
// This holds the configuration id that will take effect after a reset.
//
unsigned long ulDefaultConfiguration;
//
// This holds the current alternate interface for this device.
//
unsigned long ulAltSetting;
//
// This is the pointer to the current data being sent out or received
// on endpoint zero.
//
unsigned char *pEP0Data;
//
// This is the number of bytes that remain to be sent from or received
// into the g_sUSBDeviceState.pEP0Data data buffer.
//
volatile unsigned long ulEP0DataRemain;
//
// The amount of data being sent/received due to a custom request.
//
unsigned long ulOUTDataSize;
//
// Holds the current device status.
//
unsigned char ucStatus;
//
// Holds the endpoint status for the HALT condition.
//
unsigned char ucHalt[3];
}
tDeviceState;
//*****************************************************************************
//
// The states for endpoint zero during enumeration.
//
//*****************************************************************************
typedef enum
{
//
// The USB device is waiting on a request from the host controller on
// endpoint zero.
//
USB_STATE_IDLE,
//
// The USB device is sending data back to the host due to an IN request.
//
USB_STATE_TX,
//
// The USB device is receiving data from the host due to an OUT
// request from the host.
//
USB_STATE_RX,
//
// The USB device has completed the IN or OUT request and is now waiting
// for the host to acknowledge the end of the IN/OUT transaction. This
// is the status phase for a USB control transaction.
//
USB_STATE_STATUS,
//
// This endpoint has signaled a stall condition and is waiting for the
// stall to be acknowledged by the host controller.
//
USB_STATE_STALL
}
tEP0State;
//*****************************************************************************
//
// Define the max packet size for endpoint zero.
//
//*****************************************************************************
#define EP0_MAX_PACKET_SIZE 64
//*****************************************************************************
//
// This is a flag used with g_sUSBDeviceState.ulDevAddress to indicate that a
// device address change is pending.
//
//*****************************************************************************
#define DEV_ADDR_PENDING 0x80000000
//*****************************************************************************
//
// 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 unsigned char g_pucDataBufferIn[EP0_MAX_PACKET_SIZE];
//*****************************************************************************
//
// The pointer to the device's information structure as passed on a call to
// USBDCDInit().
//
//*****************************************************************************
tDeviceInfo *g_psUSBDeviceInfo;
//*****************************************************************************
//
// This global holds the current state information for the USB device.
//
//*****************************************************************************
static volatile tDeviceState g_sUSBDeviceState =
{
0, DEFAULT_CONFIG_ID, DEFAULT_CONFIG_ID, 0, 0, 0, 0
};
//*****************************************************************************
//
// This global holds the current state of endpoint zero.
//
//*****************************************************************************
static volatile tEP0State g_eUSBDEP0State = USB_STATE_IDLE;
//*****************************************************************************
//
// Function table to handle standard requests.
//
//*****************************************************************************
static const tStdRequest g_psUSBDStdRequests[] =
{
USBDGetStatus,
USBDClearFeature,
0,
USBDSetFeature,
0,
USBDSetAddress,
USBDGetDescriptor,
USBDSetDescriptor,
USBDGetConfiguration,
USBDSetConfiguration,
USBDGetInterface,
USBDSetInterface,
USBDSyncFrame
};
//*****************************************************************************
//
// Functions accessible by USBLIB clients.
//
//*****************************************************************************
//*****************************************************************************
//
//! Initialize the USB library device control driver for a given hardware
//! controller.
//!
//! \param ulIndex is the index of the USB controller which is to be
//! initialized.
//! \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.
//!
//! This function must be called by any application which wishes to operate
//! as a USB device. It 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 pfnResetHandler function when a host connects to the
//! device.
//!
//! The device information structure passed in \e psDevice must remain
//! unchanged between this call and any matching call to USBDCDTerm() since
//! it is not copied by the USB library.
//!
//! \return None.
//
//*****************************************************************************
void
USBDCDInit(unsigned long ulIndex, tDeviceInfo *psDevice)
{
//
// Check the arguments.
//
ASSERT(ulIndex == 0);
ASSERT(psDevice != 0);
//
// Make sure someone else has not already claimed this controller.
//
ASSERT(g_psUSBDeviceInfo == 0);
//
// Remember the device information pointer.
//
g_psUSBDeviceInfo = psDevice;
//
// Ask for the interrupt status. As a side effect, this clears all pending
// USB interrupts.
//
USBIntStatus(USB0_BASE);
//
// Enable USB Interrupts.
//
USBIntEnable(USB0_BASE, USB_INT_RESET | USB_INT_DISCONNECT |
USB_INT_RESUME | USB_INT_SUSPEND | USB_INT_DEV_OUT |
USB_INT_DEV_IN);
//
// Attach the device using the soft connect.
//
USBDevConnect(USB0_BASE);
//
// Enable the USB interrupt.
//
IntEnable(INT_USB0);
}
//*****************************************************************************
//
//! Free the USB library device control driver for a given hardware controller.
//!
//! \param ulIndex is the index of the USB controller which is to be
//! freed.
//!
//! 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
USBDCDTerm(unsigned long ulIndex)
{
//
// Check the arguments.
//
ASSERT(ulIndex == 0);
g_psUSBDeviceInfo = (tDeviceInfo *)0;
//
// Disable the USB interrupts.
//
IntDisable(INT_USB0);
USBIntDisable(USB0_BASE, USB_INT_RESET | USB_INT_DISCONNECT |
USB_INT_RESUME | USB_INT_SUSPEND | USB_INT_DEV_OUT |
USB_INT_DEV_IN);
//
// Clear any pending interrupts.
//
USBIntStatus(USB0_BASE);
}
//*****************************************************************************
//
//! This function starts the request for data from the host on endpoint zero.
//!
//! \param ulIndex is the index of the USB controller from which the data
//! is being requested.
//! \param pucData is a pointer to the buffer to fill with data from the USB
//! host.
//! \param ulSize 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,
//! tDeviceInfo.sCallbacks.pfnDataReceived should 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.
//!
//! \return None.
//
//*****************************************************************************
void
USBDCDRequestDataEP0(unsigned long ulIndex, unsigned char *pucData,
unsigned long ulSize)
{
ASSERT(ulIndex == 0);
//
// Enter the RX state on end point 0.
//
g_eUSBDEP0State = USB_STATE_RX;
//
// Save the pointer to the data.
//
g_sUSBDeviceState.pEP0Data = pucData;
//
// Location to save the current number of bytes received.
//
g_sUSBDeviceState.ulOUTDataSize = ulSize;
//
// Bytes remaining to be received.
//
g_sUSBDeviceState.ulEP0DataRemain = ulSize;
}
//*****************************************************************************
//
//! This function requests transfer of data to the host on endpoint zero.
//!
//! \param ulIndex is the index of the USB controller which is to be used to
//! send the data.
//! \param pucData is a pointer to the buffer to send via endpoint zero.
//! \param ulSize 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,
//! tDeviceInfo.sCallbacks.pfnDataSent should contain a valid function
//! pointer. This callback could be used to free up the buffer passed into
//! this function in the \e pucData parameter. The contents of the \e pucData
//! buffer must remain unchanged until the pfnDataSent callback is
//! received.
//!
//! \return None.
//
//*****************************************************************************
void
USBDCDSendDataEP0(unsigned long ulIndex, unsigned char *pucData,
unsigned long ulSize)
{
ASSERT(ulIndex == 0);
//
// Return the externally provided device descriptor.
//
g_sUSBDeviceState.pEP0Data = pucData;
//
// The size of the device descriptor is in the first byte.
//
g_sUSBDeviceState.ulEP0DataRemain = ulSize;
//
// Save the total size of the data sent.
//
g_sUSBDeviceState.ulOUTDataSize = ulSize;
//
// Now in the transmit data state.
//
USBDEP0StateTx(0);
}
//*****************************************************************************
//
//! This function sets the default configuration for the device.
//!
//! \param ulIndex is the index of the USB controller whose default
//! configuration is to be set.
//! \param ulDefaultConfig is the configuration identifier (byte 6 of the
//! standard configuration descriptor) which is to be presented to the host
//! as the default configuration in cases where the config descriptor is
//! queried prior to any specific configuration being set.
//!
//! This function allows a device to override the default configuration
//! descriptor that will be returned to a host whenever it is queried prior
//! to a specific configuration having been set. The parameter passed must
//! equal one of the configuration identifiers found in the
//! ppConfigDescriptors array for the device.
//!
//! If this function is not called, the USB library will return the first
//! configuration in the ppConfigDescriptors array as the default
//! configuration.
//!
//! \note The USB device stack assumes that the configuration IDs (byte 6 of
//! the config descriptor, bConfigurationValue) stored within the
//! configuration descriptor array, ppConfigDescriptors,
//! are equal to the array index + 1. In other words, the first entry in the
//! array must contain a descriptor with bConfigurationValue 1, the
//! second must have bConfigurationValue 2 and so on.
//!
//! \return None.
//
//*****************************************************************************
void
USBDCDSetDefaultConfiguration(unsigned long ulIndex,
unsigned long ulDefaultConfig)
{
ASSERT(ulIndex == 0);
g_sUSBDeviceState.ulDefaultConfiguration = ulDefaultConfig;
}
//*****************************************************************************
//
//! This function generates a stall condition on endpoint zero.
//!
//! \param ulIndex is the index of the USB controller whose endpoint zero is to
//! be stalled.
//!
//! 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.
//!
//! \return None.
//
//*****************************************************************************
void
USBDCDStallEP0(unsigned long ulIndex)
{
ASSERT(ulIndex == 0);
//
// Stall the endpoint in question.
//
USBDevEndpointStall(USB0_BASE, USB_EP_0, USB_EP_DEV_OUT);
//
// Enter the stalled state.
//
g_eUSBDEP0State = USB_STATE_STALL;
}
//*****************************************************************************
//
// Internal Functions, not to be called by applications
//
//*****************************************************************************
//*****************************************************************************
//
// 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.
//
// \return None.
//
//*****************************************************************************
static void
USBDReadAndDispatchRequest(void)
{
unsigned long ulSize;
tUSBRequest *pRequest;
//
// Cast the buffer to a request structure.
//
pRequest = (tUSBRequest *)g_pucDataBufferIn;
//
// Set the buffer size.
//
ulSize = EP0_MAX_PACKET_SIZE;
//
// Get the data from the USB controller end point 0.
//
USBEndpointDataGet(USB0_BASE,
USB_EP_0,
g_pucDataBufferIn,
&ulSize);
//
// See if this is a standard request or not.
//
if((pRequest->bmRequestType & USB_RTYPE_TYPE_M) != USB_RTYPE_STANDARD)
{
//
// Since this is not a standard request, see if there is
// an external handler present.
//
if(g_psUSBDeviceInfo->sCallbacks.pfnRequestHandler)
{
g_psUSBDeviceInfo->sCallbacks.pfnRequestHandler(0, pRequest);
}
else
{
//
// If there is no handler then stall this request.
//
USBDCDStallEP0(0);
}
}
else
{
//
// Assure that the jump table is not out of bounds.
//
if ((pRequest->bRequest <
(sizeof(g_psUSBDStdRequests) / sizeof(tStdRequest))) &&
(g_psUSBDStdRequests[pRequest->bRequest] != 0))
{
//
// Jump table to the appropriate handler.
//
g_psUSBDStdRequests[pRequest->bRequest](0, pRequest);
}
else
{
//
// If there is no handler then stall this request.
//
USBDCDStallEP0(0);
}
}
}
//*****************************************************************************
//
// 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 USB_STATE_IDLE, \b USB_STATE_TX, \b USB_STATE_RX,
// \b USB_STATE_STALL, and \b USB_STATE_STATUS. In the \b USB_STATE_IDLE
// 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 USB_STATE_TX, \b USB_STATE_RX, or \b USB_STATE_STALL depending on the
// command. If the controller enters the \b USB_STATE_TX or \b USB_STATE_RX
// then once all data has been sent or received, it must pass through the
// \b USB_STATE_STATUS state to allow the host to acknowledge completion of
// the request. The \b USB_STATE_STALL is entered from \b USB_STATE_IDLE in
// the event that the USB request was not valid. Both the \b USB_STATE_STALL
// and \b USB_STATE_STATUS are transitional states that return to the
// \b USB_STATE_IDLE state.
//
// \return None.
//
// USB_STATE_IDLE -*--> USB_STATE_TX -*-> USB_STATE_STATUS -*->USB_STATE_IDLE
// | | |
// |--> USB_STATE_RX - |
// | |
// |--> USB_STATE_STALL ---------->---------
//
// ----------------------------------------------------------------
// | Current State | State 0 | State 1 |
// | --------------------|-------------------|----------------------
// | USB_STATE_IDLE | USB_STATE_TX/RX | USB_STATE_STALL |
// | USB_STATE_TX | USB_STATE_STATUS | |
// | USB_STATE_RX | USB_STATE_STATUS | |
// | USB_STATE_STATUS | USB_STATE_IDLE | |
// | USB_STATE_STALL | USB_STATE_IDLE | |
// ----------------------------------------------------------------
//
//*****************************************************************************
void
USBDeviceEnumHandler(void)
{
unsigned long ulEPStatus;
//
// Get the end point 0 status.
//
ulEPStatus = USBEndpointStatus(USB0_BASE, USB_EP_0);
switch(g_eUSBDEP0State)
{
//
// Handle the status state, this is a transitory state from
// USB_STATE_TX or USB_STATE_RX back to USB_STATE_IDLE.
//
case USB_STATE_STATUS:
{
//
// Just go back to the idle state.
//
g_eUSBDEP0State = USB_STATE_IDLE;
//
// If there is a pending address change then set the address.
//
if(g_sUSBDeviceState.ulDevAddress & DEV_ADDR_PENDING)
{
//
// Clear the pending address change and set the address.
//
g_sUSBDeviceState.ulDevAddress &= ~DEV_ADDR_PENDING;
USBDevAddrSet(USB0_BASE, g_sUSBDeviceState.ulDevAddress);
}
//
// If a new packet is already pending, we need to read it
// and handle whatever request it contains.
//
if(ulEPStatus & USB_DEV_EP0_OUT_PKTRDY)
{
//
// Process the newly arrived packet.
//
USBDReadAndDispatchRequest();
}
break;
}
//
// In the IDLE state the code is waiting to receive data from the host.
//
case USB_STATE_IDLE:
{
//
// Is there a packet waiting for us?
//
if(ulEPStatus & USB_DEV_EP0_OUT_PKTRDY)
{
//
// Yes - process it.
//
USBDReadAndDispatchRequest();
}
break;
}
//
// Data is still being sent to the host so handle this in the
// EP0StateTx() function.
//
case USB_STATE_TX:
{
USBDEP0StateTx(0);
break;
}
//
// Handle the receive state for commands that are receiving data on
// endpoint zero.
//
case USB_STATE_RX:
{
unsigned long ulDataSize;
//
// Set the number of bytes to get out of this next packet.
//
if(g_sUSBDeviceState.ulEP0DataRemain > EP0_MAX_PACKET_SIZE)
{
//
// Don't send more than EP0_MAX_PACKET_SIZE bytes.
//
ulDataSize = EP0_MAX_PACKET_SIZE;
}
else
{
//
// There was space so send the remaining bytes.
//
ulDataSize = g_sUSBDeviceState.ulEP0DataRemain;
}
//
// Get the data from the USB controller end point 0.
//
USBEndpointDataGet(USB0_BASE, USB_EP_0, g_sUSBDeviceState.pEP0Data,
&ulDataSize);
//
// 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(g_sUSBDeviceState.ulEP0DataRemain < EP0_MAX_PACKET_SIZE)
{
//
// Need to ack the data on end point 0 in this case
// without setting data end.
//
USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true);
//
// Return to the idle state.
//
g_eUSBDEP0State = USB_STATE_IDLE;
//
// If there is a receive callback then call it.
//
if((g_psUSBDeviceInfo->sCallbacks.pfnDataReceived) &&
(g_sUSBDeviceState.ulOUTDataSize != 0))
{
//
// Call the custom receive handler to handle the data
// that was received.
//
g_psUSBDeviceInfo->sCallbacks.pfnDataReceived(0,
g_sUSBDeviceState.ulOUTDataSize);
//
// Indicate that there is no longer any data being waited
// on.
//
g_sUSBDeviceState.ulOUTDataSize = 0;
}
}
else
{
//
// Need to ack the data on end point 0 in this case
// without setting data end.
//
USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false);
}
//
// Advance the pointer.
//
g_sUSBDeviceState.pEP0Data += ulDataSize;
//
// Decrement the number of bytes that are being waited on.
//
g_sUSBDeviceState.ulEP0DataRemain -= ulDataSize;
break;
}
//
// The device stalled endpoint zero so check if the stall needs to be
// cleared once it has been successfully sent.
//
case USB_STATE_STALL:
{
//
// If we sent a stall then acknowledge this interrupt.
//
if(ulEPStatus & USB_DEV_EP0_SENT_STALL)
{
//
// Clear the Setup End condition.
//
USBDevEndpointStatusClear(USB0_BASE, USB_EP_0,
USB_DEV_EP0_SENT_STALL);
//
// Reset the global end point 0 state to IDLE.
//
g_eUSBDEP0State = USB_STATE_IDLE;
}
break;
}
//
// Halt on an unknown state, but only in DEBUG mode builds.
//
default:
{
ASSERT(0);
}
}
}
//*****************************************************************************
//
// 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(void)
{
//
// Call the device dependent code to indicate a bus reset has occured.
//
if(g_psUSBDeviceInfo->sCallbacks.pfnResetHandler)
{
g_psUSBDeviceInfo->sCallbacks.pfnResetHandler(0);
}
//
// Reset the default configuration identifier and alternate function
// selections.
//
g_sUSBDeviceState.ulConfiguration =
g_sUSBDeviceState.ulDefaultConfiguration;
g_sUSBDeviceState.ulAltSetting = 0;
}
//*****************************************************************************
//
// This function handles the GET_STATUS standard USB request.
//
// \param ulIndex is the index of the USB controller which is to be
// initialized.
// \param pUSBRequest holds the request type and endpoint number if endpoint
// status is requested.
//
// This function handles responses to a Get Status request from the host
// controller. A status request can be for the device, an interface or an
// endpoint. If any other type of request is made this function will cause
// a stall condition to indicate that the command is not supported. The
// \e pUSBRequest structure holds the type of the request in the
// bmRequestType field. If the type indicates that this is a request for an
// endpoint's status, then the wIndex field holds the endpoint number.
//
// \return None.
//
//*****************************************************************************
static void
USBDGetStatus(unsigned long ulIndex, tUSBRequest *pUSBRequest)
{
unsigned long ulData;
ASSERT(ulIndex == 0);
//
// Determine what type of status was requested.
//
switch(pUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M)
{
//
// This was a Device Status request.
//
case USB_RTYPE_DEVICE:
{
//
// Return the current status for the device.
//
ulData = g_sUSBDeviceState.ucStatus;
break;
}
//
// This was a Interface status request.
//
case USB_RTYPE_INTERFACE:
{
//
// Interface status always returns 0.
//
ulData = 0;
break;
}
//
// This was an endpoint status request.
//
case USB_RTYPE_ENDPOINT:
{
//
// Check if this was a valid endpoint request.
//
if((pUSBRequest->wIndex == 0) || (pUSBRequest->wIndex > 3))
{
USBDCDStallEP0(0);
return;
}
else
{
//
// Get the current halt status for this endpoint.
//
ulData = g_sUSBDeviceState.ucHalt[pUSBRequest->wIndex];
}
break;
}
//
// This was an unknown request.
//
default:
{
//
// Anything else causes a stall condition to indicate that the
// command was not supported.
//
USBDCDStallEP0(0);
return;
}
}
//
// Return the current device status.
//
USBDCDSendDataEP0(0, (unsigned char *)&ulData, 2);
}
//*****************************************************************************
//
// This function handles the CLEAR_FEATURE standard USB request.
//
// \param ulIndex is the index of the USB controller which is to be
// initialized.
// \param pUSBRequest holds the options for the Clear Feature USB request.
//
// This function handles device or endpoint clear feature requests. The
// \e pUSBRequest structure holds the type of the request in the bmRequestType
// field and the feature is held in the wValue field. For device, the only
// clearable feature is the Remote Wake feature. This device request
// should only be made if the descriptor indicates that Remote Wake is
// implemented by the device. For endpoint requests the only clearable
// feature is the ability to clear a halt on a given endpoint. If any other
// requests are made, then the device will stall the request to indicate to
// the host that the command was not supported.
//
// \return None.
//
//*****************************************************************************
static void
USBDClearFeature(unsigned long ulIndex, tUSBRequest *pUSBRequest)
{
ASSERT(ulIndex == 0);
//
// Determine what type of status was requested.
//
switch(pUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M)
{
//
// This is a clear feature request at the device level.
//
case USB_RTYPE_DEVICE:
{
//
// Only remote wake is clearable by this function.
//
if(USB_FEATURE_REMOTE_WAKE & pUSBRequest->wValue)
{
//
// Clear the remote wake up state.
//
g_sUSBDeviceState.ucStatus &= ~USB_STATUS_REMOTE_WAKE;
}
else
{
USBDCDStallEP0(0);
}
break;
}
//
// This is a clear feature request at the endpoint level.
//
case USB_RTYPE_ENDPOINT:
{
//
// Not a valid endpoint.
//
if((pUSBRequest->wIndex == 0) || (pUSBRequest->wIndex > 3))
{
USBDCDStallEP0(0);
}
else if(USB_FEATURE_EP_HALT & pUSBRequest->wValue)
{
//
// Clear the halt condition on this endpoint.
//
g_sUSBDeviceState.ucHalt[pUSBRequest->wIndex] = 0;
}
else
{
USBDCDStallEP0(0);
}
break;
}
//
// This is an unknown request.
//
default:
{
USBDCDStallEP0(0);
return;
}
}
}
//*****************************************************************************
//
// This function handles the SET_FEATURE standard USB request.
//
// \param ulIndex is the index of the USB controller which is to be
// initialized.
// \param pUSBRequest holds the feature in the wValue field of the USB
// request.
//
// This function handles device or endpoint set feature requests. The
// \e pUSBRequest structure holds the type of the request in the bmRequestType
// field and the feature is held in the wValue field. For device, the only
// settable feature is the Remote Wake feature. This device request
// should only be made if the descriptor indicates that Remote Wake is
// implemented by the device. For endpoint requests the only settable feature
// is the ability to issue a halt on a given endpoint. If any other requests
// are made, then the device will stall the request to indicate to the host
// that the command was not supported.
//
// \return None.
//
//*****************************************************************************
static void
USBDSetFeature(unsigned long ulIndex, tUSBRequest *pUSBRequest)
{
ASSERT(ulIndex == 0);
//
// Determine what type of status was requested.
//
switch(pUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M)
{
//
// This is a set feature request at the device level.
//
case USB_RTYPE_DEVICE:
{
//
// Only remote wake is setable by this function.
//
if(USB_FEATURE_REMOTE_WAKE & pUSBRequest->wValue)
{
//
// Clear the remote wakeup state.
//
g_sUSBDeviceState.ucStatus |= USB_STATUS_REMOTE_WAKE;
}
else
{
USBDCDStallEP0(0);
}
break;
}
//
// This is a set feature request at the endpoint level.
//
case USB_RTYPE_ENDPOINT:
{
//
// Not a valid endpoint.
//
if((pUSBRequest->wIndex == 0) || (pUSBRequest->wIndex > 3))
{
USBDCDStallEP0(0);
}
//
// Only the Halt feature is settable.
//
else if(USB_FEATURE_EP_HALT & pUSBRequest->wValue)
{
//
// Clear the halt condition on this endpoint.
//
g_sUSBDeviceState.ucHalt[pUSBRequest->wIndex] = 1;
}
else
{
//
// All other requests are not supported.
//
USBDCDStallEP0(0);
}
break;
}
//
// This is an unknown request.
//
default:
{
USBDCDStallEP0(0);
return;
}
}
}
//*****************************************************************************
//
// This function handles the SET_ADDRESS standard USB request.
//
// \param ulIndex is the index of the USB controller which is to be
// initialized.
// \param pUSBRequest holds the new address to use in the wValue field of the
// USB request.
//
// This function is called to handle the change of address request from the
// host controller. This can only start the sequence as the host must
// acknowledge that the device has changed address. Thus this function sets
// the address change as pending until the status phase of the request has
// been completed successfully. This prevents the devices address from
// changing and not properly responding to the status phase.
//
// \return None.
//
//*****************************************************************************
static void
USBDSetAddress(unsigned long ulIndex, tUSBRequest *pUSBRequest)
{
ASSERT(ulIndex == 0);
//
// The data needs to be acknowledged on end point 0 without setting data
// end because there is no data coming.
//
USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true);
//
// Save the device address as we cannot change address until the status
// phase is complete.
//
g_sUSBDeviceState.ulDevAddress = pUSBRequest->wValue | DEV_ADDR_PENDING;
//
// Transition directly to the status state since there is no data phase
// for this request.
//
g_eUSBDEP0State = USB_STATE_STATUS;
}
//*****************************************************************************
//
// This function handles the GET_DESCRIPTOR standard USB request.
//
// \param ulIndex is the index of the USB controller which is to be
// initialized.
// \param pUSBRequest holds the data for this request.
//
// This function will return most of the descriptors requested by the host
// controller. The descriptor specified by \e
// g_psUSBDeviceInfo->pDeviceDescriptor will be returned when the device
// descriptor is requested. If a request for a specific configuration
// descriptor is made, then the appropriate descriptor from the \e
// g_pConfigDescriptors will be returned. When a request for a string
// descriptor is made, the appropriate string from the
// \e g_psUSBDeviceInfo->pStringDescriptors will be returned. If the \e
// g_psUSBDeviceInfo->sCallbacks.GetDescriptor is specified it will be called
// to handle the request. In this case it must call the USBDCDSendDataEP0()
// function to send the data to the host controller. If the callback is not
// specified, and the descriptor request is not for a device, configuration,
// or string descriptor then this function will stall the request to indicate
// that the request was not supported by the device.
//
// \return None.
//
//*****************************************************************************
static void
USBDGetDescriptor(unsigned long ulIndex, tUSBRequest *pUSBRequest)
{
ASSERT(ulIndex == 0);
//
// Need to ack the data on end point 0 in this case without
// setting data end.
//
USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false);
switch(pUSBRequest->wValue >> 8)
{
//
// This request was for a device descriptor.
//
case USB_DTYPE_DEVICE:
{
//
// Return the externally provided device descriptor.
//
g_sUSBDeviceState.pEP0Data =
(unsigned char *)g_psUSBDeviceInfo->pDeviceDescriptor;
//
// The size of the device descriptor is in the first byte.
//
g_sUSBDeviceState.ulEP0DataRemain =
g_psUSBDeviceInfo->pDeviceDescriptor[0];
break;
}
//
// This request was for a configuration descriptor.
//
case USB_DTYPE_CONFIGURATION:
{
//
// Return the extrenally specified configuration descriptor.
//
g_sUSBDeviceState.pEP0Data =
(unsigned char *)g_psUSBDeviceInfo->ppConfigDescriptors[
g_sUSBDeviceState.ulConfiguration - 1];
//
// The total size of the configuration descriptor is in bytes
// 3 and 4 so access them as a 16 bit value.
//
g_sUSBDeviceState.ulEP0DataRemain =
((unsigned short *)g_sUSBDeviceState.pEP0Data)[1];
break;
}
//
// This request was for a string descriptor.
//
case USB_DTYPE_STRING:
{
unsigned long ulIndex;
//
// Extract the string index from the USB request.
//
ulIndex = pUSBRequest->wValue & 0xff;
//
// If the string index was too large then stall the request to
// indicate that the request was not valid.
//
if(ulIndex > g_psUSBDeviceInfo->ulNumStringDescriptors)
{
USBDCDStallEP0(0);
break;
}
//
// Return the extrenally specified configuration descriptor.
//
g_sUSBDeviceState.pEP0Data =
(unsigned char *)g_psUSBDeviceInfo->ppStringDescriptors[ulIndex];
//
// The total size of a string descriptor is in byte 0.
//
g_sUSBDeviceState.ulEP0DataRemain =
g_psUSBDeviceInfo->ppStringDescriptors[ulIndex][0];
break;
}
//
// Any other request is not handled by the default enumeration handler
// so see if it needs to be passed on to another handler.
//
default:
{
//
// If there is a handler for requests that are not handled then
// call it.
//
if(g_psUSBDeviceInfo->sCallbacks.pfnGetDescriptor)
{
g_psUSBDeviceInfo->sCallbacks.pfnGetDescriptor(0, pUSBRequest);
}
else
{
//
// Whatever this was this handler does not understand it so
// just stall the request.
//
USBDCDStallEP0(0);
}
//
// Reset the data pointer.
//
g_sUSBDeviceState.pEP0Data = 0;
break;
}
}
//
// If this request has data to send, then send it.
//
if(g_sUSBDeviceState.pEP0Data)
{
//
// If there is more data to send than is requested then just
// send the requested amount of data.
//
if(g_sUSBDeviceState.ulEP0DataRemain > pUSBRequest->wLength)
{
g_sUSBDeviceState.ulEP0DataRemain = pUSBRequest->wLength;
}
//
// Now in the transmit data state.
//
USBDEP0StateTx(0);
}
}
//*****************************************************************************
//
// This function handles the SET_DESCRIPTOR standard USB request.
//
// \param ulIndex is the index of the USB controller which is to be
// initialized.
// \param pUSBRequest holds the data for this request.
//
// This function currently is not supported and will respond with a Stall
// to indicate that this command is not supported by the device.
//
// \return None.
//
//*****************************************************************************
static void
USBDSetDescriptor(unsigned long ulIndex, tUSBRequest *pUSBRequest)
{
ASSERT(ulIndex == 0);
//
// This function is not handled by default.
//
USBDCDStallEP0(0);
}
//*****************************************************************************
//
// This function handles the GET_CONFIGURATION standard USB request.
//
// \param ulIndex is the index of the USB controller which is to be
// initialized.
// \param pUSBRequest holds the data for this request.
//
// This function responds to a host request to return the current
// configuration of the USB device. The function will send the configuration
// response to the host and return. This value will either be 0 or the last
// value received from a call to SetConfiguration().
//
// \return None.
//
//*****************************************************************************
static void
USBDGetConfiguration(unsigned long ulIndex, tUSBRequest *pUSBRequest)
{
unsigned long ulValue;
ASSERT(ulIndex == 0);
//
// If we still have an address pending then the device is still not
// configured.
//
if(g_sUSBDeviceState.ulDevAddress & DEV_ADDR_PENDING)
{
ulValue = 0;
}
else
{
ulValue = g_sUSBDeviceState.ulConfiguration;
}
//
// Send the single byte response.
//
USBDCDSendDataEP0(0, (unsigned char *)&ulValue, 1);
}
//*****************************************************************************
//
// This function handles the SET_CONFIGURATION standard USB request.
//
// \param ulIndex is the index of the USB controller which is to be
// initialized.
// \param pUSBRequest holds the data for this request.
//
// This function responds to a host request to change the current
// configuration of the USB device. The actual configuration number is taken
// from the structure passed in via \e pUSBRequest. This number should be one
// of the configurations that was specified in the descriptors. If the
// \e ConfigChange callback is specified in \e g_psUSBDeviceInfo->sCallbacks,
// it will be called so that the application can respond to a change in
// configuration.
//
// \return None.
//
//*****************************************************************************
static void
USBDSetConfiguration(unsigned long ulIndex, tUSBRequest *pUSBRequest)
{
ASSERT(ulIndex == 0);
//
// Cannot set the configuration to one that does not exist so check the
// enumeration structure to see how many valid configurations are present.
//
if(pUSBRequest->wValue > g_psUSBDeviceInfo->pDeviceDescriptor[17])
{
//
// The passed configuration number is not valid. Stall the endpoint to
// signal the error to the host.
//
USBDCDStallEP0(0);
}
else
{
//
// Need to ack the data on end point 0 in this case without
// setting data end.
//
USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false);
//
// Save the configuration.
//
g_sUSBDeviceState.ulConfiguration = pUSBRequest->wValue;
//
// If there is a configuration change callback then call it.
//
if(g_psUSBDeviceInfo->sCallbacks.pfnConfigChange)
{
g_psUSBDeviceInfo->sCallbacks.pfnConfigChange(0,
g_sUSBDeviceState.ulConfiguration);
}
}
}
//*****************************************************************************
//
// This function handles the GET_INTERFACE standard USB request.
//
// \param ulIndex is the index of the USB controller which is to be
// initialized.
// \param pUSBRequest holds the data for this request.
//
// This function is called when the host controller request the current
// interface that is in use by the device. This simply returns the value set
// by the last call to SetInterface().
//
// \return None.
//
//*****************************************************************************
static void
USBDGetInterface(unsigned long ulIndex, tUSBRequest *pUSBRequest)
{
unsigned long ulValue;
ASSERT(ulIndex == 0);
//
// If we still have an address pending then the device is still not
// configured.
//
if(g_sUSBDeviceState.ulDevAddress & DEV_ADDR_PENDING)
{
ulValue = 0;
}
else
{
ulValue = g_sUSBDeviceState.ulAltSetting;
}
//
// Send the single byte response.
//
USBDCDSendDataEP0(0, (unsigned char *)&ulValue, 1);
}
//*****************************************************************************
//
// This function handles the SET_INTERFACE standard USB request.
//
// \param ulIndex is the index of the USB controller which is to be
// initialized.
// \param pUSBRequest holds the data for this request.
//
// This function is called when a standard request for changing the interface
// is received from the host controller. If this is a valid request the
// function will call the function specified by the InterfaceChange in the
// \e g_psUSBDeviceInfo->sCallbacks variable to notify the application that the
// interface has changed and will pass to it the new alternate interface.
//
// \return None.
//
//*****************************************************************************
static void
USBDSetInterface(unsigned long ulIndex, tUSBRequest *pUSBRequest)
{
const unsigned char *pConfigDescriptor;
int iIndex;
unsigned long ulAltIndex;
ASSERT(ulIndex == 0);
//
// Use the current configuration.
//
pConfigDescriptor =
g_psUSBDeviceInfo->ppConfigDescriptors[g_sUSBDeviceState.ulConfiguration];
//
// Reset the index for the search to past the Configuration descriptor.
//
iIndex = pConfigDescriptor[0];
//
// Which index have we found.
//
ulAltIndex = 0;
//
// While the current index is less than the total number of configurations
// keep looking for the interface requested.
//
while(iIndex < (unsigned short)pConfigDescriptor[2])
{
//
// If this is an interface descriptor, see if it is the one that is
// being searched for.
//
if(pConfigDescriptor[iIndex + 1] == USB_DTYPE_INTERFACE)
{
//
// If the alternate inteface was not found then keep looking.
//
if(pUSBRequest->wValue != ulAltIndex)
{
//
// Now looking for next alternate interface.
//
ulAltIndex++;
}
else
{
//
// The index was found so save the alternate interface index.
//
g_sUSBDeviceState.ulAltSetting = ulAltIndex;
//
// If there is a callback then notify the application of the
// change to the alternate interface.
//
if(g_psUSBDeviceInfo->sCallbacks.pfnInterfaceChange)
{
g_psUSBDeviceInfo->sCallbacks.pfnInterfaceChange(0,
g_sUSBDeviceState.ulAltSetting);
}
break;
}
}
else
{
//
// If this was not an interface descriptor, then move on to the
// next descriptor.
//
iIndex += pConfigDescriptor[iIndex];
}
}
//
// If the alternate interface was not found then stall to indicate that
// this was a bad request.
//
if(iIndex >= (unsigned short)pConfigDescriptor[2])
{
USBDCDStallEP0(0);
}
}
//*****************************************************************************
//
// This function handles the SYNC_FRAME standard USB request.
//
// \param ulIndex is the index of the USB controller which is to be
// initialized.
// \param pUSBRequest holds the data for this request.
//
// This is currently a stub function that will stall indicating that the
// command is not supported.
//
// \return None.
//
//*****************************************************************************
static void
USBDSyncFrame(unsigned long ulIndex, tUSBRequest *pUSBRequest)
{
ASSERT(ulIndex == 0);
//
// Not handled yet so stall this request.
//
USBDCDStallEP0(0);
}
//*****************************************************************************
//
// This internal function handles sending data on endpoint zero.
//
// \param ulIndex is the index of the USB controller which is to be
// initialized.
//
// \return None.
//
//*****************************************************************************
static void
USBDEP0StateTx(unsigned long ulIndex)
{
unsigned long ulNumBytes;
unsigned char *pData;
ASSERT(ulIndex == 0);
//
// In the TX state on endpoint zero.
//
g_eUSBDEP0State = USB_STATE_TX;
//
// Set the number of bytes to send this iteration.
//
ulNumBytes = g_sUSBDeviceState.ulEP0DataRemain;
//
// Limit individual transfers to 64 bytes.
//
if(ulNumBytes > 64)
{
ulNumBytes = 64;
}
//
// Save the pointer so that it can be passed to the USBEndpointDataPut()
// function.
//
pData = (unsigned char *)g_sUSBDeviceState.pEP0Data;
//
// Advance the data pointer and counter to the next data to be sent.
//
g_sUSBDeviceState.ulEP0DataRemain -= ulNumBytes;
g_sUSBDeviceState.pEP0Data += ulNumBytes;
//
// Put the data in the correct FIFO.
//
USBEndpointDataPut(USB0_BASE, USB_EP_0, pData, ulNumBytes);
//
// If this is exactly 64 then don't set the last packet yet.
//
if(ulNumBytes == 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(USB0_BASE, USB_EP_0, USB_TRANS_IN);
}
else
{
//
// Send the last bit of data.
//
USBEndpointDataSend(USB0_BASE, USB_EP_0, USB_TRANS_IN_LAST);
//
// If there is a receive callback then call it.
//
if((g_psUSBDeviceInfo->sCallbacks.pfnDataSent) &&
(g_sUSBDeviceState.ulOUTDataSize != 0))
{
//
// Call the custom handler.
//
g_psUSBDeviceInfo->sCallbacks.pfnDataSent(0,
g_sUSBDeviceState.ulOUTDataSize);
//
// There is no longer any data pending to be sent.
//
g_sUSBDeviceState.ulOUTDataSize = 0;
}
//
// Now go to the status state and wait for the transmit to complete.
//
g_eUSBDEP0State = USB_STATE_STATUS;
}
}
//*****************************************************************************
//
// Close the Doxygen group.
//! @}
//
//*****************************************************************************