| /* |
| 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; |
| } |
| |