2 LPCUSB, an USB device driver for LPC microcontrollers
3 Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl)
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
8 1. Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission.
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 Standard request handler.
32 This modules handles the 'chapter 9' processing, specifically the
33 standard device requests in table 9-3 from the universal serial bus
34 specification revision 2.0
36 Specific types of devices may specify additional requests (for example
37 HID devices add a GET_DESCRIPTOR request for interfaces), but they
38 will not be part of this module.
40 @todo some requests have to return a request error if device not configured:
41 @todo GET_INTERFACE, GET_STATUS, SET_INTERFACE, SYNCH_FRAME
42 @todo this applies to the following if endpoint != 0:
43 @todo SET_FEATURE, GET_FEATURE
47 #include "usbstruct.h"
50 #define MAX_DESC_HANDLERS 4 /**< device, interface, endpoint, other */
53 /* general descriptor field offsets */
54 #define DESC_bLength 0 /**< length offset */
55 #define DESC_bDescriptorType 1 /**< descriptor type offset */
57 /* config descriptor field offsets */
58 #define CONF_DESC_wTotalLength 2 /**< total length offset */
59 #define CONF_DESC_bConfigurationValue 5 /**< configuration value offset */
60 #define CONF_DESC_bmAttributes 7 /**< configuration characteristics */
62 /* interface descriptor field offsets */
63 #define INTF_DESC_bAlternateSetting 3 /**< alternate setting offset */
65 /* endpoint descriptor field offsets */
66 #define ENDP_DESC_bEndpointAddress 2 /**< endpoint address offset */
67 #define ENDP_DESC_wMaxPacketSize 4 /**< maximum packet size offset */
70 /** Currently selected configuration */
71 static unsigned char bConfiguration = 0;
72 /** Installed custom request handler */
73 static TFnHandleRequest *pfnHandleCustomReq = NULL;
74 /** Pointer to registered descriptors */
75 static const unsigned char *pabDescrip = NULL;
79 Registers a pointer to a descriptor block containing all descriptors
82 @param [in] pabDescriptors The descriptor byte array
84 void USBRegisterDescriptors(const unsigned char *pabDescriptors)
86 pabDescrip = pabDescriptors;
91 Parses the list of installed USB descriptors and attempts to find
92 the specified USB descriptor.
94 @param [in] wTypeIndex Type and index of the descriptor
95 @param [in] wLangID Language ID of the descriptor (currently unused)
96 @param [out] *piLen Descriptor length
97 @param [out] *ppbData Descriptor data
99 @return TRUE if the descriptor was found, FALSE otherwise
101 BOOL USBGetDescriptor(unsigned short wTypeIndex, unsigned short wLangID, int *piLen, unsigned char **ppbData)
103 unsigned char bType, bIndex;
107 ASSERT(pabDescrip != NULL);
109 bType = GET_DESC_TYPE(wTypeIndex);
110 bIndex = GET_DESC_INDEX(wTypeIndex);
112 pab = (unsigned char *)pabDescrip;
115 while (pab[DESC_bLength] != 0) {
116 if (pab[DESC_bDescriptorType] == bType) {
117 if (iCurIndex == bIndex) {
120 // get length from structure
121 if (bType == DESC_CONFIGURATION) {
122 // configuration descriptor is an exception, length is at offset 2 and 3
123 *piLen = (pab[CONF_DESC_wTotalLength]) |
124 (pab[CONF_DESC_wTotalLength + 1] << 8);
127 // normally length is at offset 0
128 *piLen = pab[DESC_bLength];
134 // skip to next descriptor
135 pab += pab[DESC_bLength];
138 DBG("Desc %x not found!\n", wTypeIndex);
144 Configures the device according to the specified configuration index and
145 alternate setting by parsing the installed USB descriptor list.
146 A configuration index of 0 unconfigures the device.
148 @param [in] bConfigIndex Configuration index
149 @param [in] bAltSetting Alternate setting number
151 @todo function always returns TRUE, add stricter checking?
153 @return TRUE if successfully configured, FALSE otherwise
155 static BOOL USBSetConfiguration(unsigned char bConfigIndex, unsigned char bAltSetting)
158 unsigned char bCurConfig, bCurAltSetting;
160 unsigned short wMaxPktSize;
162 ASSERT(pabDescrip != NULL);
164 if (bConfigIndex == 0) {
165 // unconfigure device
166 USBHwConfigDevice(FALSE);
169 // configure endpoints for this configuration/altsetting
170 pab = (unsigned char *)pabDescrip;
172 bCurAltSetting = 0xFF;
174 while (pab[DESC_bLength] != 0) {
176 switch (pab[DESC_bDescriptorType]) {
178 case DESC_CONFIGURATION:
179 // remember current configuration index
180 bCurConfig = pab[CONF_DESC_bConfigurationValue];
184 // remember current alternate setting
185 bCurAltSetting = pab[INTF_DESC_bAlternateSetting];
189 if ((bCurConfig == bConfigIndex) &&
190 (bCurAltSetting == bAltSetting)) {
191 // endpoint found for desired config and alternate setting
192 bEP = pab[ENDP_DESC_bEndpointAddress];
193 wMaxPktSize = (pab[ENDP_DESC_wMaxPacketSize]) |
194 (pab[ENDP_DESC_wMaxPacketSize + 1] << 8);
195 // configure endpoint
196 USBHwEPConfig(bEP, wMaxPktSize);
203 // skip to next descriptor
204 pab += pab[DESC_bLength];
208 USBHwConfigDevice(TRUE);
216 Local function to handle a standard device request
218 @param [in] pSetup The setup packet
219 @param [in,out] *piLen Pointer to data length
220 @param [in,out] ppbData Data buffer.
222 @return TRUE if the request was handled successfully
224 static BOOL HandleStdDeviceReq(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData)
226 unsigned char *pbData = *ppbData;
228 switch (pSetup->bRequest) {
231 // bit 0: self-powered
232 // bit 1: remote wakeup = not supported
238 case REQ_SET_ADDRESS:
239 USBHwSetAddress(pSetup->wValue);
242 case REQ_GET_DESCRIPTOR:
243 DBG("D%x", pSetup->wValue);
244 return USBGetDescriptor(pSetup->wValue, pSetup->wIndex, piLen, ppbData);
246 case REQ_GET_CONFIGURATION:
247 // indicate if we are configured
248 pbData[0] = bConfiguration;
252 case REQ_SET_CONFIGURATION:
253 if (!USBSetConfiguration(pSetup->wValue & 0xFF, 0)) {
254 DBG("USBSetConfiguration failed!\n");
257 // configuration successful, update current configuration
258 bConfiguration = pSetup->wValue & 0xFF;
261 case REQ_CLEAR_FEATURE:
262 case REQ_SET_FEATURE:
263 if (pSetup->wValue == FEA_REMOTE_WAKEUP) {
264 // put DEVICE_REMOTE_WAKEUP code here
266 if (pSetup->wValue == FEA_TEST_MODE) {
267 // put TEST_MODE code here
271 case REQ_SET_DESCRIPTOR:
272 DBG("Device req %d not implemented\n", pSetup->bRequest);
276 DBG("Illegal device req %d\n", pSetup->bRequest);
285 Local function to handle a standard interface request
287 @param [in] pSetup The setup packet
288 @param [in,out] *piLen Pointer to data length
289 @param [in] ppbData Data buffer.
291 @return TRUE if the request was handled successfully
293 static BOOL HandleStdInterfaceReq(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData)
295 unsigned char *pbData = *ppbData;
297 switch (pSetup->bRequest) {
306 case REQ_CLEAR_FEATURE:
307 case REQ_SET_FEATURE:
308 // not defined for interface
311 case REQ_GET_INTERFACE: // TODO use bNumInterfaces
312 // there is only one interface, return n-1 (= 0)
317 case REQ_SET_INTERFACE: // TODO use bNumInterfaces
318 // there is only one interface (= 0)
319 if (pSetup->wValue != 0) {
326 DBG("Illegal interface req %d\n", pSetup->bRequest);
335 Local function to handle a standard endpoint request
337 @param [in] pSetup The setup packet
338 @param [in,out] *piLen Pointer to data length
339 @param [in] ppbData Data buffer.
341 @return TRUE if the request was handled successfully
343 static BOOL HandleStdEndPointReq(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData)
345 unsigned char *pbData = *ppbData;
347 switch (pSetup->bRequest) {
349 // bit 0 = endpointed halted or not
350 pbData[0] = (USBHwEPGetStatus(pSetup->wIndex) & EP_STATUS_STALLED) ? 1 : 0;
355 case REQ_CLEAR_FEATURE:
356 if (pSetup->wValue == FEA_ENDPOINT_HALT) {
357 // clear HALT by unstalling
358 USBHwEPStall(pSetup->wIndex, FALSE);
361 // only ENDPOINT_HALT defined for endpoints
364 case REQ_SET_FEATURE:
365 if (pSetup->wValue == FEA_ENDPOINT_HALT) {
366 // set HALT by stalling
367 USBHwEPStall(pSetup->wIndex, TRUE);
370 // only ENDPOINT_HALT defined for endpoints
373 case REQ_SYNCH_FRAME:
374 DBG("EP req %d not implemented\n", pSetup->bRequest);
378 DBG("Illegal EP req %d\n", pSetup->bRequest);
387 Default handler for standard ('chapter 9') requests
389 If a custom request handler was installed, this handler is called first.
391 @param [in] pSetup The setup packet
392 @param [in,out] *piLen Pointer to data length
393 @param [in] ppbData Data buffer.
395 @return TRUE if the request was handled successfully
397 BOOL USBHandleStandardRequest(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData)
399 // try the custom request handler first
400 if ((pfnHandleCustomReq != NULL) && pfnHandleCustomReq(pSetup, piLen, ppbData)) {
404 switch (REQTYPE_GET_RECIP(pSetup->bmRequestType)) {
405 case REQTYPE_RECIP_DEVICE: return HandleStdDeviceReq(pSetup, piLen, ppbData);
406 case REQTYPE_RECIP_INTERFACE: return HandleStdInterfaceReq(pSetup, piLen, ppbData);
407 case REQTYPE_RECIP_ENDPOINT: return HandleStdEndPointReq(pSetup, piLen, ppbData);
408 default: return FALSE;
414 Registers a callback for custom device requests
416 In USBHandleStandardRequest, the custom request handler gets a first
417 chance at handling the request before it is handed over to the 'chapter 9'
420 This can be used for example in HID devices, where a REQ_GET_DESCRIPTOR
421 request is sent to an interface, which is not covered by the 'chapter 9'
424 @param [in] pfnHandler Callback function pointer
426 void USBRegisterCustomReqHandler(TFnHandleRequest *pfnHandler)
428 pfnHandleCustomReq = pfnHandler;