223 lines
6.2 KiB
C
223 lines
6.2 KiB
C
/*
|
|
LPCUSB, an USB device driver for LPC microcontrollers
|
|
Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl)
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
/** @file
|
|
Control transfer handler.
|
|
|
|
In case of a control-write (host-to-device), this module collects the full
|
|
control message in a local buffer, then sends it off to an installed
|
|
request handler.
|
|
|
|
In case of a control-read (device-to-host), an installed request handler
|
|
is asked to handle the request and provide return data. The handler can
|
|
either put the data in the control data buffer through the supplied pointer,
|
|
or it can supply a new data pointer. In both cases, the handler is required
|
|
to update the data length in *piLen;
|
|
|
|
|
|
Currently, control transfers are handled in a very simple way, keeping
|
|
almost no state about the control transfer progress (setup stage, data
|
|
stage, status stage). We simply follow the host: if it sends data, we store
|
|
it in the control data buffer and if it asks for data, we just send the next
|
|
block.
|
|
|
|
*/
|
|
|
|
#include "type.h"
|
|
#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 U8 *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 U8 *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, U8 **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(U8 bEPStat)
|
|
{
|
|
U8 *pb;
|
|
int i;
|
|
|
|
USBHwEPStall(0x80, TRUE);
|
|
|
|
// dump setup packet
|
|
DBG("STALL on [");
|
|
pb = (U8 *)&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;
|
|
|
|
iChunk = MIN(MAX_PACKET_SIZE0, 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(U8 bEP, U8 bEPStat)
|
|
{
|
|
int iChunk, iType;
|
|
|
|
if (bEP == 0x00) {
|
|
// OUT transfer
|
|
if (bEPStat & EP_STATUS_SETUP) {
|
|
// setup packet, reset request message state machine
|
|
USBHwEPRead(0x00, (U8 *)&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
|
|
iResidue = MIN(iLen, 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, U8 *pbDataStore)
|
|
{
|
|
ASSERT(iType >= 0);
|
|
ASSERT(iType < 4);
|
|
apfnReqHandlers[iType] = pfnHandler;
|
|
apbDataStore[iType] = pbDataStore;
|
|
}
|
|
|