/* | |
LPCUSB, an USB device driver for LPC microcontrollers | |
Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl) | |
Redistribution and use in source and binary forms, with or without | |
modification, are permitted provided that the following conditions are met: | |
1. Redistributions of source code must retain the above copyright | |
notice, this list of conditions and the following disclaimer. | |
2. Redistributions in binary form must reproduce the above copyright | |
notice, this list of conditions and the following disclaimer in the | |
documentation and/or other materials provided with the distribution. | |
3. The name of the author may not be used to endorse or promote products | |
derived from this software without specific prior written permission. | |
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
*/ | |
/** @file | |
Control transfer handler. | |
This module handles control transfers and is normally installed on the | |
endpoint 0 callback. | |
Control transfers can be of the following type: | |
0 Standard; | |
1 Class; | |
2 Vendor; | |
3 Reserved. | |
A callback can be installed for each of these control transfers using | |
USBRegisterRequestHandler. | |
When an OUT request arrives, data is collected in the data store provided | |
with the USBRegisterRequestHandler call. When the transfer is done, the | |
callback is called. | |
When an IN request arrives, the callback is called immediately to either | |
put the control transfer data in the data store, or to get a pointer to | |
control transfer data. The data is then packetised and sent to the host. | |
*/ | |
#include "usbdebug.h" | |
#include "usbstruct.h" | |
#include "usbapi.h" | |
#define MAX_CONTROL_SIZE 128 /**< maximum total size of control transfer data */ | |
#define MAX_REQ_HANDLERS 4 /**< standard, class, vendor, reserved */ | |
static TSetupPacket Setup; /**< setup packet */ | |
static unsigned char *pbData; /**< pointer to data buffer */ | |
static int iResidue; /**< remaining bytes in buffer */ | |
static int iLen; /**< total length of control transfer */ | |
/** Array of installed request handler callbacks */ | |
static TFnHandleRequest *apfnReqHandlers[4] = {NULL, NULL, NULL, NULL}; | |
/** Array of installed request data pointers */ | |
static unsigned char *apbDataStore[4] = {NULL, NULL, NULL, NULL}; | |
/** | |
Local function to handle a request by calling one of the installed | |
request handlers. | |
In case of data going from host to device, the data is at *ppbData. | |
In case of data going from device to host, the handler can either | |
choose to write its data at *ppbData or update the data pointer. | |
@param [in] pSetup The setup packet | |
@param [in,out] *piLen Pointer to data length | |
@param [in,out] ppbData Data buffer. | |
@return TRUE if the request was handles successfully | |
*/ | |
static BOOL _HandleRequest(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData) | |
{ | |
TFnHandleRequest *pfnHandler; | |
int iType; | |
iType = REQTYPE_GET_TYPE(pSetup->bmRequestType); | |
pfnHandler = apfnReqHandlers[iType]; | |
if (pfnHandler == NULL) { | |
DBG("No handler for reqtype %d\n", iType); | |
return FALSE; | |
} | |
return pfnHandler(pSetup, piLen, ppbData); | |
} | |
/** | |
Local function to stall the control endpoint | |
@param [in] bEPStat Endpoint status | |
*/ | |
static void StallControlPipe(unsigned char bEPStat) | |
{ | |
unsigned char *pb; | |
int i; | |
USBHwEPStall(0x80, TRUE); | |
// dump setup packet | |
DBG("STALL on ["); | |
pb = (unsigned char *)&Setup; | |
for (i = 0; i < 8; i++) { | |
DBG(" %02x", *pb++); | |
} | |
DBG("] stat=%x\n", bEPStat); | |
} | |
/** | |
Sends next chunk of data (possibly 0 bytes) to host | |
*/ | |
static void DataIn(void) | |
{ | |
int iChunk; | |
if( MAX_PACKET_SIZE0 < iResidue ) | |
{ | |
iChunk = MAX_PACKET_SIZE0; | |
} | |
else | |
{ | |
iChunk = iResidue; | |
} | |
USBHwEPWrite(0x80, pbData, iChunk); | |
pbData += iChunk; | |
iResidue -= iChunk; | |
} | |
/** | |
* Handles IN/OUT transfers on EP0 | |
* | |
* @param [in] bEP Endpoint address | |
* @param [in] bEPStat Endpoint status | |
*/ | |
void USBHandleControlTransfer(unsigned char bEP, unsigned char bEPStat) | |
{ | |
int iChunk, iType; | |
if (bEP == 0x00) { | |
// OUT transfer | |
if (bEPStat & EP_STATUS_SETUP) { | |
// setup packet, reset request message state machine | |
USBHwEPRead(0x00, (unsigned char *)&Setup, sizeof(Setup)); | |
DBG("S%x", Setup.bRequest); | |
// defaults for data pointer and residue | |
iType = REQTYPE_GET_TYPE(Setup.bmRequestType); | |
pbData = apbDataStore[iType]; | |
iResidue = Setup.wLength; | |
iLen = Setup.wLength; | |
if ((Setup.wLength == 0) || | |
(REQTYPE_GET_DIR(Setup.bmRequestType) == REQTYPE_DIR_TO_HOST)) { | |
// ask installed handler to process request | |
if (!_HandleRequest(&Setup, &iLen, &pbData)) { | |
DBG("_HandleRequest1 failed\n"); | |
StallControlPipe(bEPStat); | |
return; | |
} | |
// send smallest of requested and offered length | |
if( iLen < Setup.wLength ) | |
{ | |
iResidue = iLen; | |
} | |
else | |
{ | |
iResidue = Setup.wLength; | |
} | |
// send first part (possibly a zero-length status message) | |
DataIn(); | |
} | |
} | |
else { | |
if (iResidue > 0) { | |
// store data | |
iChunk = USBHwEPRead(0x00, pbData, iResidue); | |
if (iChunk < 0) { | |
StallControlPipe(bEPStat); | |
return; | |
} | |
pbData += iChunk; | |
iResidue -= iChunk; | |
if (iResidue == 0) { | |
// received all, send data to handler | |
iType = REQTYPE_GET_TYPE(Setup.bmRequestType); | |
pbData = apbDataStore[iType]; | |
if (!_HandleRequest(&Setup, &iLen, &pbData)) { | |
DBG("_HandleRequest2 failed\n"); | |
StallControlPipe(bEPStat); | |
return; | |
} | |
// send status to host | |
DataIn(); | |
} | |
} | |
else { | |
// absorb zero-length status message | |
iChunk = USBHwEPRead(0x00, NULL, 0); | |
DBG(iChunk > 0 ? "?" : ""); | |
} | |
} | |
} | |
else if (bEP == 0x80) { | |
// IN transfer | |
// send more data if available (possibly a 0-length packet) | |
DataIn(); | |
} | |
else { | |
ASSERT(FALSE); | |
} | |
} | |
/** | |
Registers a callback for handling requests | |
@param [in] iType Type of request, e.g. REQTYPE_TYPE_STANDARD | |
@param [in] *pfnHandler Callback function pointer | |
@param [in] *pbDataStore Data storage area for this type of request | |
*/ | |
void USBRegisterRequestHandler(int iType, TFnHandleRequest *pfnHandler, unsigned char *pbDataStore) | |
{ | |
ASSERT(iType >= 0); | |
ASSERT(iType < 4); | |
apfnReqHandlers[iType] = pfnHandler; | |
apbDataStore[iType] = pbDataStore; | |
} | |