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
107 ASSERT(pabDescrip != NULL);
\r
109 bType = GET_DESC_TYPE(wTypeIndex);
\r
110 bIndex = GET_DESC_INDEX(wTypeIndex);
\r
112 pab = (unsigned char *)pabDescrip;
\r
115 while (pab[DESC_bLength] != 0) {
\r
116 if (pab[DESC_bDescriptorType] == bType) {
\r
117 if (iCurIndex == bIndex) {
\r
118 // set data pointer
\r
120 // get length from structure
\r
121 if (bType == DESC_CONFIGURATION) {
\r
122 // configuration descriptor is an exception, length is at offset 2 and 3
\r
123 *piLen = (pab[CONF_DESC_wTotalLength]) |
\r
124 (pab[CONF_DESC_wTotalLength + 1] << 8);
\r
127 // normally length is at offset 0
\r
128 *piLen = pab[DESC_bLength];
\r
134 // skip to next descriptor
\r
135 pab += pab[DESC_bLength];
\r
138 DBG("Desc %x not found!\n", wTypeIndex);
\r
144 Configures the device according to the specified configuration index and
\r
145 alternate setting by parsing the installed USB descriptor list.
\r
146 A configuration index of 0 unconfigures the device.
\r
148 @param [in] bConfigIndex Configuration index
\r
149 @param [in] bAltSetting Alternate setting number
\r
151 @todo function always returns TRUE, add stricter checking?
\r
153 @return TRUE if successfully configured, FALSE otherwise
\r
155 static BOOL USBSetConfiguration(unsigned char bConfigIndex, unsigned char bAltSetting)
\r
157 unsigned char *pab;
\r
158 unsigned char bCurConfig, bCurAltSetting;
\r
160 unsigned short wMaxPktSize;
\r
162 ASSERT(pabDescrip != NULL);
\r
164 if (bConfigIndex == 0) {
\r
165 // unconfigure device
\r
166 USBHwConfigDevice(FALSE);
\r
169 // configure endpoints for this configuration/altsetting
\r
170 pab = (unsigned char *)pabDescrip;
\r
172 bCurAltSetting = 0xFF;
\r
174 while (pab[DESC_bLength] != 0) {
\r
176 switch (pab[DESC_bDescriptorType]) {
\r
178 case DESC_CONFIGURATION:
\r
179 // remember current configuration index
\r
180 bCurConfig = pab[CONF_DESC_bConfigurationValue];
\r
183 case DESC_INTERFACE:
\r
184 // remember current alternate setting
\r
185 bCurAltSetting = pab[INTF_DESC_bAlternateSetting];
\r
188 case DESC_ENDPOINT:
\r
189 if ((bCurConfig == bConfigIndex) &&
\r
190 (bCurAltSetting == bAltSetting)) {
\r
191 // endpoint found for desired config and alternate setting
\r
192 bEP = pab[ENDP_DESC_bEndpointAddress];
\r
193 wMaxPktSize = (pab[ENDP_DESC_wMaxPacketSize]) |
\r
194 (pab[ENDP_DESC_wMaxPacketSize + 1] << 8);
\r
195 // configure endpoint
\r
196 USBHwEPConfig(bEP, wMaxPktSize);
\r
203 // skip to next descriptor
\r
204 pab += pab[DESC_bLength];
\r
207 // configure device
\r
208 USBHwConfigDevice(TRUE);
\r
216 Local function to handle a standard device request
\r
218 @param [in] pSetup The setup packet
\r
219 @param [in,out] *piLen Pointer to data length
\r
220 @param [in,out] ppbData Data buffer.
\r
222 @return TRUE if the request was handled successfully
\r
224 static BOOL HandleStdDeviceReq(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData)
\r
226 unsigned char *pbData = *ppbData;
\r
228 switch (pSetup->bRequest) {
\r
230 case REQ_GET_STATUS:
\r
231 // bit 0: self-powered
\r
232 // bit 1: remote wakeup = not supported
\r
238 case REQ_SET_ADDRESS:
\r
239 USBHwSetAddress(pSetup->wValue);
\r
242 case REQ_GET_DESCRIPTOR:
\r
243 DBG("D%x", pSetup->wValue);
\r
244 return USBGetDescriptor(pSetup->wValue, pSetup->wIndex, piLen, ppbData);
\r
246 case REQ_GET_CONFIGURATION:
\r
247 // indicate if we are configured
\r
248 pbData[0] = bConfiguration;
\r
252 case REQ_SET_CONFIGURATION:
\r
253 if (!USBSetConfiguration(pSetup->wValue & 0xFF, 0)) {
\r
254 DBG("USBSetConfiguration failed!\n");
\r
257 // configuration successful, update current configuration
\r
258 bConfiguration = pSetup->wValue & 0xFF;
\r
261 case REQ_CLEAR_FEATURE:
\r
262 case REQ_SET_FEATURE:
\r
263 if (pSetup->wValue == FEA_REMOTE_WAKEUP) {
\r
264 // put DEVICE_REMOTE_WAKEUP code here
\r
266 if (pSetup->wValue == FEA_TEST_MODE) {
\r
267 // put TEST_MODE code here
\r
271 case REQ_SET_DESCRIPTOR:
\r
272 DBG("Device req %d not implemented\n", pSetup->bRequest);
\r
276 DBG("Illegal device req %d\n", pSetup->bRequest);
\r
285 Local function to handle a standard interface request
\r
287 @param [in] pSetup The setup packet
\r
288 @param [in,out] *piLen Pointer to data length
\r
289 @param [in] ppbData Data buffer.
\r
291 @return TRUE if the request was handled successfully
\r
293 static BOOL HandleStdInterfaceReq(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData)
\r
295 unsigned char *pbData = *ppbData;
\r
297 switch (pSetup->bRequest) {
\r
299 case REQ_GET_STATUS:
\r
300 // no bits specified
\r
306 case REQ_CLEAR_FEATURE:
\r
307 case REQ_SET_FEATURE:
\r
308 // not defined for interface
\r
311 case REQ_GET_INTERFACE: // TODO use bNumInterfaces
\r
312 // there is only one interface, return n-1 (= 0)
\r
317 case REQ_SET_INTERFACE: // TODO use bNumInterfaces
\r
318 // there is only one interface (= 0)
\r
319 if (pSetup->wValue != 0) {
\r
326 DBG("Illegal interface req %d\n", pSetup->bRequest);
\r
335 Local function to handle a standard endpoint request
\r
337 @param [in] pSetup The setup packet
\r
338 @param [in,out] *piLen Pointer to data length
\r
339 @param [in] ppbData Data buffer.
\r
341 @return TRUE if the request was handled successfully
\r
343 static BOOL HandleStdEndPointReq(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData)
\r
345 unsigned char *pbData = *ppbData;
\r
347 switch (pSetup->bRequest) {
\r
348 case REQ_GET_STATUS:
\r
349 // bit 0 = endpointed halted or not
\r
350 pbData[0] = (USBHwEPGetStatus(pSetup->wIndex) & EP_STATUS_STALLED) ? 1 : 0;
\r
355 case REQ_CLEAR_FEATURE:
\r
356 if (pSetup->wValue == FEA_ENDPOINT_HALT) {
\r
357 // clear HALT by unstalling
\r
358 USBHwEPStall(pSetup->wIndex, FALSE);
\r
361 // only ENDPOINT_HALT defined for endpoints
\r
364 case REQ_SET_FEATURE:
\r
365 if (pSetup->wValue == FEA_ENDPOINT_HALT) {
\r
366 // set HALT by stalling
\r
367 USBHwEPStall(pSetup->wIndex, TRUE);
\r
370 // only ENDPOINT_HALT defined for endpoints
\r
373 case REQ_SYNCH_FRAME:
\r
374 DBG("EP req %d not implemented\n", pSetup->bRequest);
\r
378 DBG("Illegal EP req %d\n", pSetup->bRequest);
\r
387 Default handler for standard ('chapter 9') requests
\r
389 If a custom request handler was installed, this handler is called first.
\r
391 @param [in] pSetup The setup packet
\r
392 @param [in,out] *piLen Pointer to data length
\r
393 @param [in] ppbData Data buffer.
\r
395 @return TRUE if the request was handled successfully
\r
397 BOOL USBHandleStandardRequest(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData)
\r
399 // try the custom request handler first
\r
400 if ((pfnHandleCustomReq != NULL) && pfnHandleCustomReq(pSetup, piLen, ppbData)) {
\r
404 switch (REQTYPE_GET_RECIP(pSetup->bmRequestType)) {
\r
405 case REQTYPE_RECIP_DEVICE: return HandleStdDeviceReq(pSetup, piLen, ppbData);
\r
406 case REQTYPE_RECIP_INTERFACE: return HandleStdInterfaceReq(pSetup, piLen, ppbData);
\r
407 case REQTYPE_RECIP_ENDPOINT: return HandleStdEndPointReq(pSetup, piLen, ppbData);
\r
408 default: return FALSE;
\r
414 Registers a callback for custom device requests
\r
416 In USBHandleStandardRequest, the custom request handler gets a first
\r
417 chance at handling the request before it is handed over to the 'chapter 9'
\r
420 This can be used for example in HID devices, where a REQ_GET_DESCRIPTOR
\r
421 request is sent to an interface, which is not covered by the 'chapter 9'
\r
424 @param [in] pfnHandler Callback function pointer
\r
426 void USBRegisterCustomReqHandler(TFnHandleRequest *pfnHandler)
\r
428 pfnHandleCustomReq = pfnHandler;
\r