/***********************************************************************************
Filename: usb_standard_request.c
Description: Handle USB standard requests.
***********************************************************************************/
/// \addtogroup module_usb_standard_requests
/// @{
#include "usb_firmware_library_headers.h"
#include "hal_types.h"
#include "hal_board.h"
/** \brief Processes the \ref GET_STATUS request (returns status for the specified recipient)
*
* The recipient bits in \ref USB_SETUP_HEADER.requestType specify the desired recipient. This is either the
* (one and only) device, a specific interface, or a specific endpoint. Some of the status bits can be
* changed with the SET_FEATURE and CLEAR_FEATURE requests.
*
* Parameters:
* - VALUE: Always 0
* - INDEX: Depends upon the recipient:
* - DEVICE: Always 0
* - INTERFACE: Interface number
* - ENDPOINT: Endpoint address
* - LENGTH: Always 2
*
* Data (IN):
* Depends upon the recipient (the bit field illustrations are MSB first, LSB last):
* - DEVICE: 00000000.000000RS, where R(1) = DEVICE_REMOTE_WAKEUP and S(0) = SELF_POWERED
* - INTERFACE: 00000000.00000000 (all bits are reserved)
* - ENDPOINT: 00000000.0000000H, where H(0) = ENDPOINT_HALT
*/
void usbsrGetStatus(void)
{
uint8 endpoint;
static uint16 __xdata status;
// Common sanity check
if (usbSetupHeader.value || HI_UINT16(usbSetupHeader.index) || (usbSetupHeader.length != 2)) {
usbfwData.ep0Status = EP_STALL;
// Return status for device, interface, or endpoint
} else {
switch (usbSetupHeader.requestType) {
// Device status:
// Bit 0: Self powered
// Bit 1: Remote wake-up allowed
case RT_IN_DEVICE:
// Sanity check
if (LO_UINT16(usbSetupHeader.index)) {
usbfwData.ep0Status = EP_STALL;
// Get the bit values from the USBFW_DATA struct
} else {
// Self powered?
status = usbfwData.selfPowered ? 0x0001 : 0x0000;
// Remote wakeup?
if (usbfwData.remoteWakeup) status |= 0x0002;
}
break;
// Interface status:
// All bits are reserved
case RT_IN_INTERFACE:
// Sanity check
if (usbfwData.usbState != DEV_CONFIGURED) {
usbfwData.ep0Status = EP_STALL;
} else {
status = 0x0000;
}
break;
// Endpoint status:
// Bit 0: Endpoint halted
case RT_IN_ENDPOINT:
endpoint = LO_UINT16(usbSetupHeader.index) & 0x7F;
// Sanity check
if ((usbfwData.usbState != DEV_CONFIGURED) || (endpoint > 5)) {
usbfwData.ep0Status = EP_STALL;
// Translate endpoint address to status index and return the status
} else {
// IN
if (LO_UINT16(usbSetupHeader.index) & 0x80) {
status = (usbfwData.pEpInStatus[endpoint - 1] == EP_HALT) ? 0x0001 : 0x0000;
// OUT
} else {
status = (usbfwData.pEpOutStatus[endpoint - 1] == EP_HALT) ? 0x0001 : 0x0000;
}
}
break;
default:
usbfwData.ep0Status = EP_STALL;
break;
}
if (usbfwData.ep0Status != EP_STALL) {
// Send it
usbSetupData.pBuffer = (uint8 __generic *)&status;
usbSetupData.bytesLeft = 2;
usbfwData.ep0Status = EP_TX;
}
}
} // usbsrGetStatus
/** \brief Internal function used for the very similar \ref SET_FEATURE and \ref CLEAR_FEATURE requests
*
* This function either sets or clears the specified feature on the specified recipient.
*
* \param[in] set
* When TRUE, the feature is set. When FALSE, the feature is cleared.
*
* \return
* TRUE if the selected feature is supported by the USB library. FALSE to indicate that
* \ref usbsrHookClearFeature() or \ref usbsrHookSetFeature() must be called.
*/
static uint8 ChangeFeature(uint8 set)
{
uint8 endpoint;
// Sanity check
if (usbSetupHeader.length || (usbfwData.usbState != DEV_CONFIGURED) && (usbSetupHeader.index != 0)) {
usbfwData.ep0Status = EP_STALL;
// Handle based on recipient
} else {
switch (usbSetupHeader.requestType & RT_MASK_RECIP) {
// Device
case RT_RECIP_DEV:
// Sanity check
if (LO_UINT16(usbSetupHeader.value) != DEVICE_REMOTE_WAKEUP) {
return FALSE;
} else {
usbfwData.remoteWakeup = set;
usbsrHookProcessEvent(set ? USBSR_EVENT_REMOTE_WAKEUP_ENABLED : USBSR_EVENT_REMOTE_WAKEUP_DISABLED, 0);
}
break;
// Endpoint
case RT_RECIP_IF:
return FALSE;
// Endpoint
case RT_RECIP_EP:
endpoint = LO_UINT16(usbSetupHeader.index) & 0x7F;
// Sanity check
if (LO_UINT16(usbSetupHeader.value) != ENDPOINT_HALT) {
return FALSE;
} else if (endpoint > 5) {
usbfwData.ep0Status = EP_STALL;
} else {
USBFW_SELECT_ENDPOINT(endpoint);
// IN
if (LO_UINT16(usbSetupHeader.index) & 0x80) {
USBCSIL = set ? USBCSIL_SEND_STALL : USBCSIL_CLR_DATA_TOG;
usbfwData.pEpInStatus[endpoint - 1] = set ? EP_HALT : EP_IDLE;
usbsrHookProcessEvent(set ? USBSR_EVENT_EPIN_STALL_SET : USBSR_EVENT_EPIN_STALL_CLEARED, endpoint);
// OUT
} else {
USBCSOL = set ? USBCSOL_SEND_STALL : USBCSOL_CLR_DATA_TOG;
usbfwData.pEpOutStatus[endpoint - 1] = set ? EP_HALT : EP_IDLE;
usbsrHookProcessEvent(set ? USBSR_EVENT_EPOUT_STALL_SET : USBSR_EVENT_EPOUT_STALL_CLEARED, endpoint);
}
USBFW_SELECT_ENDPOINT(0);
}
break;
default:
usbfwData.ep0Status = EP_STALL;
break;
}
}
return TRUE;
} // ChangeFeature
/** \brief Processes the \ref CLEAR_FEATURE request (clears or disables a specific feature)
*
* The feature selector value must be appropriate to the recipient.
*
* Parameters:
* - VALUE: Feature selector:
* - \c DEVICE_REMOTE_WAKEUP(1): Enable remote wakeup
* - \c ENDPOINT_HALT(0): Clear the halt feature for the specified endpoint (not endpoint 0!)
* - INDEX: Depends upon the recipient:
* - DEVICE: Always 0
* - INTERFACE: Interface number
* - ENDPOINT: Endpoint address
* - LENGTH: Always 0
*/
void usbsrClearFeature()
{
if (!ChangeFeature(FALSE)) {
usbsrHookClearFeature();
}
} // usbsrClearFeature
/** \brief Processes the \ref SET_FEATURE request (sets or enables a specific feature)
*
* The feature selector value must be appropriate to the recipient.
*
* Parameters:
* - VALUE: Feature selector:
* - \c DEVICE_REMOTE_WAKEUP(1): Enable remote wakeup
* - \c ENDPOINT_HALT(0): Set the halt feature for the specified endpoint (not endpoint 0!)
* - INDEX: Depends upon the recipient:
* - DEVICE: Always 0
* - INTERFACE: Interface number
* - ENDPOINT: Endpoint address
* - LENGTH: Always 0
*/
void usbsrSetFeature(void)
{
if (!ChangeFeature(TRUE)) {
usbsrHookSetFeature();
}
} // usbsrSetFeature
/** \brief Processes the \ref SET_ADDRESS request (sets the device address for all future device
* accesses)
*
* If the value is between 1 and 127 and the device is in the default state, it will enter the address
* state. If it already is in the address state, it starts to use the newly-specified address.
*
* If the value is 0 and the device is in the address state, it will enter the default state. If it
* already is in the default state, nothing happens.
*
* Parameters:
* - VALUE: The device address (0-127)
* - INDEX: Always 0
* - LENGTH: Always 0
*/
void usbsrSetAddress(void)
{
// Sanity check
if (usbSetupHeader.index || usbSetupHeader.length || HI_UINT16(usbSetupHeader.value) || (LO_UINT16(usbSetupHeader.value) & 0x80)) {
usbfwData.ep0Status = EP_STALL;
// Update the device address
} else {
USBADDR = LO_UINT16(usbSetupHeader.value);
if (LO_UINT16(usbSetupHeader.value) != 0) {
if (usbfwData.usbState == DEV_DEFAULT) usbfwData.usbState = DEV_ADDRESS;
} else {
if (usbfwData.usbState == DEV_ADDRESS) usbfwData.usbState = DEV_DEFAULT;
}
}
} // usbsrSetAddress
/** \brief Processes the \ref GET_DESCRIPTOR request (returns the specified USB descriptor)
*
* The \ref module_usb_descriptor_parser module is used to locate device, configuration and string
* descriptors. Note that configuration descriptors also include interface, endpoint and other
* "similar" descriptor types (e.g. HID descriptor), with the total descriptor length specified by
* the \ref USB_CONFIGURATION_DESCRIPTOR.wTotalLength field.
*
* Other descriptor types that are not returned with the configuration descriptor, must be defined in
* the usbDescriptorMarker.pUsbDescLut lookup-table. This table specifies the values of the VALUE and INDEX fields, and
* gives a pointer to the descriptor along with it's length.
*
* Parameters:
* - VALUE.MSB: Descriptor type
* - VALUE.LSB: Descriptor index
* - INDEX: 0, or language ID for string descriptors (currently not supported)
* - LENGTH: Descriptor length (either the requested number of bytes, or the length of the descriptor,
* whichever is the smallest)
*
* Data (IN):
* The descriptor(s)
*/
void usbsrGetDescriptor(void)
{
uint8 n;
// Which descriptor?
switch (HI_UINT16(usbSetupHeader.value)) {
// Device descriptor
case DESC_TYPE_DEVICE:
usbSetupData.pBuffer = (uint8 __code*) usbdpGetDeviceDesc();
usbSetupData.bytesLeft = usbSetupData.pBuffer[DESC_LENGTH_IDX];
break;
// Configuration descriptor
case DESC_TYPE_CONFIG:
usbSetupData.pBuffer = (uint8 __code*) usbdpGetConfigurationDesc(0, LO_UINT16(usbSetupHeader.value));
usbSetupData.bytesLeft = usbSetupData.pBuffer[DESC_CONFIG_LENGTH_LSB_IDX] +
usbSetupData.pBuffer[DESC_CONFIG_LENGTH_MSB_IDX] * 256;
break;
// String descriptor
case DESC_TYPE_STRING:
// TODO: Implement language ID
usbSetupData.pBuffer = (uint8 *)usbdpGetStringDesc(LO_UINT16(usbSetupHeader.value));
usbSetupData.bytesLeft = usbSetupData.pBuffer[DESC_LENGTH_IDX];
break;
// Other descriptor type
default:
// Perform a table search (on index and value)
usbSetupData.pBuffer = NULL;
for (n = 0; n < ((uint16)usbDescriptorMarker.pUsbDescLutEnd - (uint16)usbDescriptorMarker.pUsbDescLut) / sizeof(DESC_LUT_INFO); n++) {
if ((usbDescriptorMarker.pUsbDescLut[n].valueMsb == HI_UINT16(usbSetupHeader.value))
&& (usbDescriptorMarker.pUsbDescLut[n].valueLsb == LO_UINT16(usbSetupHeader.value))
&& (usbDescriptorMarker.pUsbDescLut[n].indexMsb == HI_UINT16(usbSetupHeader.index))
&& (usbDescriptorMarker.pUsbDescLut[n].indexLsb == LO_UINT16(usbSetupHeader.index)) )
{
usbSetupData.pBuffer = usbDescriptorMarker.pUsbDescLut[n].pDescStart;
usbSetupData.bytesLeft = usbDescriptorMarker.pUsbDescLut[n].length;
}
}
}
// Stall EP0 if no descriptor was found
if (usbSetupData.pBuffer == NULL) usbfwData.ep0Status = EP_STALL;
if (usbfwData.ep0Status != EP_STALL) {
// Limit the returned descriptor size (the PC wants to know about sizes before
// polling the complete descriptors)
if (usbSetupData.bytesLeft > usbSetupHeader.length) {
usbSetupData.bytesLeft = usbSetupHeader.length;
}
usbfwData.ep0Status = EP_TX;
}
} // usbsrGetDescriptor
/** \brief Internally used function that configures all endpoints for the specified interface
*
* The new endpoint setup overwrites the old, without any warning. Unused endpoints keep their current
* setup. The user is responsible for ensuring that no endpoint buffers overwrite each other, and that
* interfaces do not cause conflicts. The pUsbDblbufLutInfo table must contain an entry for each
* interface descriptor to define endpoint double-buffering.
*
* \param[in] *pInterface
* A pointer to the interface descriptor
*/
static void ConfigureEndpoints(USB_INTERFACE_DESCRIPTOR __code *pInterface)
{
uint8 n;
uint16 maxpRegValue;
uint8 csRegValue;
uint8 endpoint;
USB_ENDPOINT_DESCRIPTOR __code *pEndpoint;
DBLBUF_LUT_INFO __code *pUsbDblbufLutInfo;
// Locate the double buffer settings
if (pInterface->bNumEndpoints) {
pUsbDblbufLutInfo = (DBLBUF_LUT_INFO __code*) usbDescriptorMarker.pUsbDblbufLut;
while (pUsbDblbufLutInfo->pInterface != pInterface) {
pUsbDblbufLutInfo++;
}
}
// For each endpoint in this interface
for (n = 0; n < pInterface->bNumEndpoints; n++) {
if (pEndpoint = usbdpFindNext(DESC_TYPE_ENDPOINT, 0)) {
// Get the endpoint index
endpoint = pEndpoint->bEndpointAddress & 0x0F;
USBFW_SELECT_ENDPOINT(endpoint);
csRegValue = 0x00;
maxpRegValue = (pEndpoint->wMaxPacketSize + 7) >> 3;
// For IN endpoints...
if (pEndpoint->bEndpointAddress & 0x80) {
// Clear data toggle, and flush twice (due to double buffering)
USBCSIL = USBCSIL_CLR_DATA_TOG | USBCSIL_FLUSH_PACKET;
USBCSIL = USBCSIL_FLUSH_PACKET;
// USBCSIH
if ((pEndpoint->bmAttributes & EP_ATTR_TYPE_BM) == EP_ATTR_ISO) csRegValue |= USBCSIH_ISO; // ISO flag
if (pUsbDblbufLutInfo->inMask & (1 << endpoint)) csRegValue |= USBCSIH_IN_DBL_BUF; // Double buffering
USBCSIH = csRegValue;
// Max transfer size
USBMAXI = maxpRegValue;
// Endpoint status
usbfwData.pEpInStatus[endpoint - 1] = EP_IDLE;
// For OUT endpoints...
} else {
// Clear data toggle, and flush twice (due to double buffering)
USBCSOL = USBCSOL_CLR_DATA_TOG | USBCSOL_FLUSH_PACKET;
USBCSOL = USBCSOL_FLUSH_PACKET;
// USBCSOH
if ((pEndpoint->bmAttributes & EP_ATTR_TYPE_BM) == EP_ATTR_ISO) csRegValue |= USBCSOH_ISO; // ISO flag
if (pUsbDblbufLutInfo->outMask & (1 << endpoint)) csRegValue |= USBCSOH_OUT_DBL_BUF; // Double buffering
USBCSOH = csRegValue;
// Max transfer size
USBMAXO = maxpRegValue;
// Endpoint status
usbfwData.pEpOutStatus[endpoint - 1] = EP_IDLE;
}
USBFW_SELECT_ENDPOINT(0);
}
}
} // ConfigureEndpoints
/** \brief Processes the \ref GET_CONFIGURATION request (returns the current device configuration value)
*
* If the returned value is 0, the device is not configured (not in the configured state)
*
* Parameters:
* - VALUE: Always 0
* - INDEX: Always 0
* - LENGTH: Always 1
*
* Data (IN):
* The non-zero \ref USB_CONFIGURATION_DESCRIPTOR.bConfigurationValue of the currently selected
* configuration.
*/
void usbsrGetConfiguration(void)
{
// Sanity check
if (usbSetupHeader.value || usbSetupHeader.index || (usbSetupHeader.length != 1)) {
usbfwData.ep0Status = EP_STALL;
// Return the current configuration
} else {
usbSetupData.pBuffer = &usbfwData.configurationValue;
usbSetupData.bytesLeft = 1;
usbfwData.ep0Status = EP_TX;
}
} // usbsrGetConfiguration
/** \brief Processes the \ref SET_CONFIGURATION request (sets the device configuration)
*
* The configuration value must either be 0, in which case the device enters the address state, or it
* must match a configuration value from one of the USB configuration descriptors. If there is a match,
* the device enters the configured state.
*
* This request resets all interfaces to alternate setting 0, and uses the \c ConfigureEndpoints()
* function to automatically setup all endpoint registers.
*
* Parameters:
* - VALUE: The configuration value (0-255)
* - INDEX: Always 0
* - LENGTH: Always 0
*/
void usbsrSetConfiguration(void)
{
uint8 n;
USB_CONFIGURATION_DESCRIPTOR __code *pConfiguration;
USB_INTERFACE_DESCRIPTOR __code *pInterface;
// Sanity check
if ((usbfwData.usbState == DEV_DEFAULT) || usbSetupHeader.index || usbSetupHeader.length || HI_UINT16(usbSetupHeader.value)) {
usbfwData.ep0Status = EP_STALL;
// Default endpoint setup
} else {
usbsrHookProcessEvent(USBSR_EVENT_CONFIGURATION_CHANGING, 0);
// Configure relevant endpoints
if (LO_UINT16(usbSetupHeader.value)) {
// Find the correct configuration descriptor...
pConfiguration = usbdpGetConfigurationDesc(LO_UINT16(usbSetupHeader.value), 0);
// If it exists...
if (pConfiguration) {
usbfwData.usbState = DEV_CONFIGURED;
usbfwData.configurationValue = LO_UINT16(usbSetupHeader.value);
// For each interface...
for (n = 0; n < pConfiguration->bNumInterfaces; n++) {
usbfwData.pAlternateSetting[n] = 0x00;
// Look only for alternate setting 0
do {
pInterface = usbdpFindNext(DESC_TYPE_INTERFACE, 0);
} while (pInterface->bAlternateSetting != usbfwData.pAlternateSetting[n]);
// Configure all endpoints in this interface
ConfigureEndpoints(pInterface);
}
// If not, then stall the endpoint
} else {
usbfwData.ep0Status = EP_STALL;
}
// Unconfigure endpoints
} else {
usbfwData.configurationValue = LO_UINT16(usbSetupHeader.value);
usbfwData.usbState = DEV_ADDRESS;
usbfwSetAllEpStatus(EP_HALT);
}
usbsrHookProcessEvent(USBSR_EVENT_CONFIGURATION_CHANGED, 0);
}
} // usbsrSetConfiguration
/** \brief Processes the \ref GET_INTERFACE request (returns the selected alternate setting for the
* specified interface)
*
* Some USB devices have configurations with mutually exclusive interface settings. This request allows
* the host to determine the currently selected alternate setting.
*
* Parameters:
* - VALUE: Always 0
* - INDEX: Interface number
* - LENGTH: Always 1
*
* Data (IN):
* The alternate setting for the selected interface
*/
void usbsrGetInterface(void)
{
// Sanity check
if ((usbfwData.usbState != DEV_CONFIGURED) || (usbSetupHeader.requestType != RT_IN_INTERFACE) || usbSetupHeader.value || (usbSetupHeader.length != 1)) {
usbfwData.ep0Status = EP_STALL;
// Return the current alternate setting
} else {
usbSetupData.pBuffer = &usbfwData.pAlternateSetting[usbSetupHeader.index];
usbSetupData.bytesLeft = 1;
usbfwData.ep0Status = EP_TX;
}
} // usbsrGetInterface
/** \brief Processes the \ref SET_INTERFACE request (selects an alternate setting for the specified
* interface)
*
* Some USB devices have configurations with mutually exclusive interface settings. This request allows
* the host to select the desired alternate setting.
*
* This function uses the \c ConfigureEndpoints() to automatically setup the relevant endpoint
* registers.
*
* Parameters:
* - VALUE: Alternate setting
* - INDEX: Interface number
* - LENGTH: Always 0
*/
void usbsrSetInterface(void)
{
USB_INTERFACE_DESCRIPTOR __code *pInterface;
// Sanity check
if ((usbfwData.usbState != DEV_CONFIGURED) || (usbSetupHeader.requestType != RT_OUT_INTERFACE) || usbSetupHeader.length) {
usbfwData.ep0Status = EP_STALL;
// Verify that the desired alternate setting is available, and then make the switch
} else {
if (pInterface = usbdpGetInterfaceDesc(usbfwData.configurationValue, usbSetupHeader.index, usbSetupHeader.value)) {
usbsrHookProcessEvent(USBSR_EVENT_INTERFACE_CHANGING, usbSetupHeader.index);
usbfwData.pAlternateSetting[usbSetupHeader.index] = usbSetupHeader.value;
// Configure all endpoints in this interface
ConfigureEndpoints(pInterface);
usbsrHookProcessEvent(USBSR_EVENT_INTERFACE_CHANGED, usbSetupHeader.index);
// This interface does not exist
} else {
usbfwData.ep0Status = EP_STALL;
}
}
} // usbsrSetInterface
//@}
/*
+------------------------------------------------------------------------------
| Copyright 2004-2010 Texas Instruments Incorporated. All rights reserved.
|
| IMPORTANT: Your use of this Software is limited to those specific rights
| granted under the terms of a software license agreement between the user who
| downloaded the software, his/her employer (which must be your employer) and
| Texas Instruments Incorporated (the "License"). You may not use this Software
| unless you agree to abide by the terms of the License. The License limits
| your use, and you acknowledge, that the Software may not be modified, copied
| or distributed unless embedded on a Texas Instruments microcontroller or used
| solely and exclusively in conjunction with a Texas Instruments radio
| frequency transceiver, which is integrated into your product. Other than for
| the foregoing purpose, you may not use, reproduce, copy, prepare derivative
| works of, modify, distribute, perform, display or sell this Software and/or
| its documentation for any purpose.
|
| YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE
| PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED,
| INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE,
| NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL
| TEXAS INSTRUMENTS OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT,
| NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER
| LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES INCLUDING
| BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE OR
| CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT OF
| SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES
| (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS.
|
| Should you have any questions regarding your right to use this Software,
| contact Texas Instruments Incorporated at www.TI.com.
|
+------------------------------------------------------------------------------
*/