2 LPCUSB, an USB device driver for LPC microcontrollers
\r
3 Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl)
\r
5 Redistribution and use in source and binary forms, with or without
\r
6 modification, are permitted provided that the following conditions are met:
\r
8 1. Redistributions of source code must retain the above copyright
\r
9 notice, this list of conditions and the following disclaimer.
\r
10 2. Redistributions in binary form must reproduce the above copyright
\r
11 notice, this list of conditions and the following disclaimer in the
\r
12 documentation and/or other materials provided with the distribution.
\r
13 3. The name of the author may not be used to endorse or promote products
\r
14 derived from this software without specific prior written permission.
\r
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
\r
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
\r
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
\r
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
\r
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
\r
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
\r
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
\r
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
\r
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
\r
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\r
30 Standard request handler.
\r
32 This modules handles the 'chapter 9' processing, specifically the
\r
33 standard device requests in table 9-3 from the universal serial bus
\r
34 specification revision 2.0
\r
36 Specific types of devices may specify additional requests (for example
\r
37 HID devices add a GET_DESCRIPTOR request for interfaces), but they
\r
38 will not be part of this module.
\r
40 @todo some requests have to return a request error if device not configured:
\r
41 @todo GET_INTERFACE, GET_STATUS, SET_INTERFACE, SYNCH_FRAME
\r
42 @todo this applies to the following if endpoint != 0:
\r
43 @todo SET_FEATURE, GET_FEATURE
\r
46 #include "usbdebug.h"
\r
47 #include "usbstruct.h"
\r
50 #define MAX_DESC_HANDLERS 4 /**< device, interface, endpoint, other */
\r
53 /* general descriptor field offsets */
\r
54 #define DESC_bLength 0 /**< length offset */
\r
55 #define DESC_bDescriptorType 1 /**< descriptor type offset */
\r
57 /* config descriptor field offsets */
\r
58 #define CONF_DESC_wTotalLength 2 /**< total length offset */
\r
59 #define CONF_DESC_bConfigurationValue 5 /**< configuration value offset */
\r
60 #define CONF_DESC_bmAttributes 7 /**< configuration characteristics */
\r
62 /* interface descriptor field offsets */
\r
63 #define INTF_DESC_bAlternateSetting 3 /**< alternate setting offset */
\r
65 /* endpoint descriptor field offsets */
\r
66 #define ENDP_DESC_bEndpointAddress 2 /**< endpoint address offset */
\r
67 #define ENDP_DESC_wMaxPacketSize 4 /**< maximum packet size offset */
\r
70 /** Currently selected configuration */
\r
71 static unsigned char bConfiguration = 0;
\r
72 /** Installed custom request handler */
\r
73 static TFnHandleRequest *pfnHandleCustomReq = NULL;
\r
74 /** Pointer to registered descriptors */
\r
75 static const unsigned char *pabDescrip = NULL;
\r
79 Registers a pointer to a descriptor block containing all descriptors
\r
82 @param [in] pabDescriptors The descriptor byte array
\r
84 void USBRegisterDescriptors(const unsigned char *pabDescriptors)
\r
86 pabDescrip = pabDescriptors;
\r
91 Parses the list of installed USB descriptors and attempts to find
\r
92 the specified USB descriptor.
\r
94 @param [in] wTypeIndex Type and index of the descriptor
\r
95 @param [in] wLangID Language ID of the descriptor (currently unused)
\r
96 @param [out] *piLen Descriptor length
\r
97 @param [out] *ppbData Descriptor data
\r
99 @return TRUE if the descriptor was found, FALSE otherwise
\r
101 BOOL USBGetDescriptor(unsigned short wTypeIndex, unsigned short wLangID, int *piLen, unsigned char **ppbData)
\r
103 unsigned char bType, bIndex;
\r
104 unsigned char *pab;
\r
108 ASSERT(pabDescrip != NULL);
\r
110 bType = GET_DESC_TYPE(wTypeIndex);
\r
111 bIndex = GET_DESC_INDEX(wTypeIndex);
\r
113 pab = (unsigned char *)pabDescrip;
\r
116 while (pab[DESC_bLength] != 0) {
\r
117 if (pab[DESC_bDescriptorType] == bType) {
\r
118 if (iCurIndex == bIndex) {
\r
119 // set data pointer
\r
121 // get length from structure
\r
122 if (bType == DESC_CONFIGURATION) {
\r
123 // configuration descriptor is an exception, length is at offset 2 and 3
\r
124 *piLen = (pab[CONF_DESC_wTotalLength]) |
\r
125 (pab[CONF_DESC_wTotalLength + 1] << 8);
\r
128 // normally length is at offset 0
\r
129 *piLen = pab[DESC_bLength];
\r
135 // skip to next descriptor
\r
136 pab += pab[DESC_bLength];
\r
139 DBG("Desc %x not found!\n", wTypeIndex);
\r
145 Configures the device according to the specified configuration index and
\r
146 alternate setting by parsing the installed USB descriptor list.
\r
147 A configuration index of 0 unconfigures the device.
\r
149 @param [in] bConfigIndex Configuration index
\r
150 @param [in] bAltSetting Alternate setting number
\r
152 @todo function always returns TRUE, add stricter checking?
\r
154 @return TRUE if successfully configured, FALSE otherwise
\r
156 static BOOL USBSetConfiguration(unsigned char bConfigIndex, unsigned char bAltSetting)
\r
158 unsigned char *pab;
\r
159 unsigned char bCurConfig, bCurAltSetting;
\r
161 unsigned short wMaxPktSize;
\r
163 ASSERT(pabDescrip != NULL);
\r
165 if (bConfigIndex == 0) {
\r
166 // unconfigure device
\r
167 USBHwConfigDevice(FALSE);
\r
170 // configure endpoints for this configuration/altsetting
\r
171 pab = (unsigned char *)pabDescrip;
\r
173 bCurAltSetting = 0xFF;
\r
175 while (pab[DESC_bLength] != 0) {
\r
177 switch (pab[DESC_bDescriptorType]) {
\r
179 case DESC_CONFIGURATION:
\r
180 // remember current configuration index
\r
181 bCurConfig = pab[CONF_DESC_bConfigurationValue];
\r
184 case DESC_INTERFACE:
\r
185 // remember current alternate setting
\r
186 bCurAltSetting = pab[INTF_DESC_bAlternateSetting];
\r
189 case DESC_ENDPOINT:
\r
190 if ((bCurConfig == bConfigIndex) &&
\r
191 (bCurAltSetting == bAltSetting)) {
\r
192 // endpoint found for desired config and alternate setting
\r
193 bEP = pab[ENDP_DESC_bEndpointAddress];
\r
194 wMaxPktSize = (pab[ENDP_DESC_wMaxPacketSize]) |
\r
195 (pab[ENDP_DESC_wMaxPacketSize + 1] << 8);
\r
196 // configure endpoint
\r
197 USBHwEPConfig(bEP, wMaxPktSize);
\r
204 // skip to next descriptor
\r
205 pab += pab[DESC_bLength];
\r
208 // configure device
\r
209 USBHwConfigDevice(TRUE);
\r
217 Local function to handle a standard device request
\r
219 @param [in] pSetup The setup packet
\r
220 @param [in,out] *piLen Pointer to data length
\r
221 @param [in,out] ppbData Data buffer.
\r
223 @return TRUE if the request was handled successfully
\r
225 static BOOL HandleStdDeviceReq(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData)
\r
227 unsigned char *pbData = *ppbData;
\r
229 switch (pSetup->bRequest) {
\r
231 case REQ_GET_STATUS:
\r
232 // bit 0: self-powered
\r
233 // bit 1: remote wakeup = not supported
\r
239 case REQ_SET_ADDRESS:
\r
240 USBHwSetAddress(pSetup->wValue);
\r
243 case REQ_GET_DESCRIPTOR:
\r
244 DBG("D%x", pSetup->wValue);
\r
245 return USBGetDescriptor(pSetup->wValue, pSetup->wIndex, piLen, ppbData);
\r
247 case REQ_GET_CONFIGURATION:
\r
248 // indicate if we are configured
\r
249 pbData[0] = bConfiguration;
\r
253 case REQ_SET_CONFIGURATION:
\r
254 if (!USBSetConfiguration(pSetup->wValue & 0xFF, 0)) {
\r
255 DBG("USBSetConfiguration failed!\n");
\r
258 // configuration successful, update current configuration
\r
259 bConfiguration = pSetup->wValue & 0xFF;
\r
262 case REQ_CLEAR_FEATURE:
\r
263 case REQ_SET_FEATURE:
\r
264 if (pSetup->wValue == FEA_REMOTE_WAKEUP) {
\r
265 // put DEVICE_REMOTE_WAKEUP code here
\r
267 if (pSetup->wValue == FEA_TEST_MODE) {
\r
268 // put TEST_MODE code here
\r
272 case REQ_SET_DESCRIPTOR:
\r
273 DBG("Device req %d not implemented\n", pSetup->bRequest);
\r
277 DBG("Illegal device req %d\n", pSetup->bRequest);
\r
286 Local function to handle a standard interface request
\r
288 @param [in] pSetup The setup packet
\r
289 @param [in,out] *piLen Pointer to data length
\r
290 @param [in] ppbData Data buffer.
\r
292 @return TRUE if the request was handled successfully
\r
294 static BOOL HandleStdInterfaceReq(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData)
\r
296 unsigned char *pbData = *ppbData;
\r
298 switch (pSetup->bRequest) {
\r
300 case REQ_GET_STATUS:
\r
301 // no bits specified
\r
307 case REQ_CLEAR_FEATURE:
\r
308 case REQ_SET_FEATURE:
\r
309 // not defined for interface
\r
312 case REQ_GET_INTERFACE: // TODO use bNumInterfaces
\r
313 // there is only one interface, return n-1 (= 0)
\r
318 case REQ_SET_INTERFACE: // TODO use bNumInterfaces
\r
319 // there is only one interface (= 0)
\r
320 if (pSetup->wValue != 0) {
\r
327 DBG("Illegal interface req %d\n", pSetup->bRequest);
\r
336 Local function to handle a standard endpoint request
\r
338 @param [in] pSetup The setup packet
\r
339 @param [in,out] *piLen Pointer to data length
\r
340 @param [in] ppbData Data buffer.
\r
342 @return TRUE if the request was handled successfully
\r
344 static BOOL HandleStdEndPointReq(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData)
\r
346 unsigned char *pbData = *ppbData;
\r
348 switch (pSetup->bRequest) {
\r
349 case REQ_GET_STATUS:
\r
350 // bit 0 = endpointed halted or not
\r
351 pbData[0] = (USBHwEPGetStatus(pSetup->wIndex) & EP_STATUS_STALLED) ? 1 : 0;
\r
356 case REQ_CLEAR_FEATURE:
\r
357 if (pSetup->wValue == FEA_ENDPOINT_HALT) {
\r
358 // clear HALT by unstalling
\r
359 USBHwEPStall(pSetup->wIndex, FALSE);
\r
362 // only ENDPOINT_HALT defined for endpoints
\r
365 case REQ_SET_FEATURE:
\r
366 if (pSetup->wValue == FEA_ENDPOINT_HALT) {
\r
367 // set HALT by stalling
\r
368 USBHwEPStall(pSetup->wIndex, TRUE);
\r
371 // only ENDPOINT_HALT defined for endpoints
\r
374 case REQ_SYNCH_FRAME:
\r
375 DBG("EP req %d not implemented\n", pSetup->bRequest);
\r
379 DBG("Illegal EP req %d\n", pSetup->bRequest);
\r
388 Default handler for standard ('chapter 9') requests
\r
390 If a custom request handler was installed, this handler is called first.
\r
392 @param [in] pSetup The setup packet
\r
393 @param [in,out] *piLen Pointer to data length
\r
394 @param [in] ppbData Data buffer.
\r
396 @return TRUE if the request was handled successfully
\r
398 BOOL USBHandleStandardRequest(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData)
\r
400 // try the custom request handler first
\r
401 if ((pfnHandleCustomReq != NULL) && pfnHandleCustomReq(pSetup, piLen, ppbData)) {
\r
405 switch (REQTYPE_GET_RECIP(pSetup->bmRequestType)) {
\r
406 case REQTYPE_RECIP_DEVICE: return HandleStdDeviceReq(pSetup, piLen, ppbData);
\r
407 case REQTYPE_RECIP_INTERFACE: return HandleStdInterfaceReq(pSetup, piLen, ppbData);
\r
408 case REQTYPE_RECIP_ENDPOINT: return HandleStdEndPointReq(pSetup, piLen, ppbData);
\r
409 default: return FALSE;
\r
415 Registers a callback for custom device requests
\r
417 In USBHandleStandardRequest, the custom request handler gets a first
\r
418 chance at handling the request before it is handed over to the 'chapter 9'
\r
421 This can be used for example in HID devices, where a REQ_GET_DESCRIPTOR
\r
422 request is sent to an interface, which is not covered by the 'chapter 9'
\r
425 @param [in] pfnHandler Callback function pointer
\r
427 void USBRegisterCustomReqHandler(TFnHandleRequest *pfnHandler)
\r
429 pfnHandleCustomReq = pfnHandler;
\r