1 |
581 |
jeremybenn |
/*
|
2 |
|
|
LPCUSB, an USB device driver for LPC microcontrollers
|
3 |
|
|
Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl)
|
4 |
|
|
|
5 |
|
|
Redistribution and use in source and binary forms, with or without
|
6 |
|
|
modification, are permitted provided that the following conditions are met:
|
7 |
|
|
|
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.
|
15 |
|
|
|
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.
|
26 |
|
|
*/
|
27 |
|
|
|
28 |
|
|
|
29 |
|
|
/** @file
|
30 |
|
|
Standard request handler.
|
31 |
|
|
|
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
|
35 |
|
|
|
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.
|
39 |
|
|
|
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
|
44 |
|
|
*/
|
45 |
|
|
|
46 |
|
|
#include "usbdebug.h"
|
47 |
|
|
#include "usbstruct.h"
|
48 |
|
|
#include "usbapi.h"
|
49 |
|
|
|
50 |
|
|
#define MAX_DESC_HANDLERS 4 /**< device, interface, endpoint, other */
|
51 |
|
|
|
52 |
|
|
|
53 |
|
|
/* general descriptor field offsets */
|
54 |
|
|
#define DESC_bLength 0 /**< length offset */
|
55 |
|
|
#define DESC_bDescriptorType 1 /**< descriptor type offset */
|
56 |
|
|
|
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 */
|
61 |
|
|
|
62 |
|
|
/* interface descriptor field offsets */
|
63 |
|
|
#define INTF_DESC_bAlternateSetting 3 /**< alternate setting offset */
|
64 |
|
|
|
65 |
|
|
/* endpoint descriptor field offsets */
|
66 |
|
|
#define ENDP_DESC_bEndpointAddress 2 /**< endpoint address offset */
|
67 |
|
|
#define ENDP_DESC_wMaxPacketSize 4 /**< maximum packet size offset */
|
68 |
|
|
|
69 |
|
|
|
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;
|
76 |
|
|
|
77 |
|
|
|
78 |
|
|
/**
|
79 |
|
|
Registers a pointer to a descriptor block containing all descriptors
|
80 |
|
|
for the device.
|
81 |
|
|
|
82 |
|
|
@param [in] pabDescriptors The descriptor byte array
|
83 |
|
|
*/
|
84 |
|
|
void USBRegisterDescriptors(const unsigned char *pabDescriptors)
|
85 |
|
|
{
|
86 |
|
|
pabDescrip = pabDescriptors;
|
87 |
|
|
}
|
88 |
|
|
|
89 |
|
|
|
90 |
|
|
/**
|
91 |
|
|
Parses the list of installed USB descriptors and attempts to find
|
92 |
|
|
the specified USB descriptor.
|
93 |
|
|
|
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
|
98 |
|
|
|
99 |
|
|
@return TRUE if the descriptor was found, FALSE otherwise
|
100 |
|
|
*/
|
101 |
|
|
BOOL USBGetDescriptor(unsigned short wTypeIndex, unsigned short wLangID, int *piLen, unsigned char **ppbData)
|
102 |
|
|
{
|
103 |
|
|
unsigned char bType, bIndex;
|
104 |
|
|
unsigned char *pab;
|
105 |
|
|
int iCurIndex;
|
106 |
|
|
|
107 |
|
|
ASSERT(pabDescrip != NULL);
|
108 |
|
|
|
109 |
|
|
bType = GET_DESC_TYPE(wTypeIndex);
|
110 |
|
|
bIndex = GET_DESC_INDEX(wTypeIndex);
|
111 |
|
|
|
112 |
|
|
pab = (unsigned char *)pabDescrip;
|
113 |
|
|
iCurIndex = 0;
|
114 |
|
|
|
115 |
|
|
while (pab[DESC_bLength] != 0) {
|
116 |
|
|
if (pab[DESC_bDescriptorType] == bType) {
|
117 |
|
|
if (iCurIndex == bIndex) {
|
118 |
|
|
// set data pointer
|
119 |
|
|
*ppbData = pab;
|
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);
|
125 |
|
|
}
|
126 |
|
|
else {
|
127 |
|
|
// normally length is at offset 0
|
128 |
|
|
*piLen = pab[DESC_bLength];
|
129 |
|
|
}
|
130 |
|
|
return TRUE;
|
131 |
|
|
}
|
132 |
|
|
iCurIndex++;
|
133 |
|
|
}
|
134 |
|
|
// skip to next descriptor
|
135 |
|
|
pab += pab[DESC_bLength];
|
136 |
|
|
}
|
137 |
|
|
// nothing found
|
138 |
|
|
DBG("Desc %x not found!\n", wTypeIndex);
|
139 |
|
|
return FALSE;
|
140 |
|
|
}
|
141 |
|
|
|
142 |
|
|
|
143 |
|
|
/**
|
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.
|
147 |
|
|
|
148 |
|
|
@param [in] bConfigIndex Configuration index
|
149 |
|
|
@param [in] bAltSetting Alternate setting number
|
150 |
|
|
|
151 |
|
|
@todo function always returns TRUE, add stricter checking?
|
152 |
|
|
|
153 |
|
|
@return TRUE if successfully configured, FALSE otherwise
|
154 |
|
|
*/
|
155 |
|
|
static BOOL USBSetConfiguration(unsigned char bConfigIndex, unsigned char bAltSetting)
|
156 |
|
|
{
|
157 |
|
|
unsigned char *pab;
|
158 |
|
|
unsigned char bCurConfig, bCurAltSetting;
|
159 |
|
|
unsigned char bEP;
|
160 |
|
|
unsigned short wMaxPktSize;
|
161 |
|
|
|
162 |
|
|
ASSERT(pabDescrip != NULL);
|
163 |
|
|
|
164 |
|
|
if (bConfigIndex == 0) {
|
165 |
|
|
// unconfigure device
|
166 |
|
|
USBHwConfigDevice(FALSE);
|
167 |
|
|
}
|
168 |
|
|
else {
|
169 |
|
|
// configure endpoints for this configuration/altsetting
|
170 |
|
|
pab = (unsigned char *)pabDescrip;
|
171 |
|
|
bCurConfig = 0xFF;
|
172 |
|
|
bCurAltSetting = 0xFF;
|
173 |
|
|
|
174 |
|
|
while (pab[DESC_bLength] != 0) {
|
175 |
|
|
|
176 |
|
|
switch (pab[DESC_bDescriptorType]) {
|
177 |
|
|
|
178 |
|
|
case DESC_CONFIGURATION:
|
179 |
|
|
// remember current configuration index
|
180 |
|
|
bCurConfig = pab[CONF_DESC_bConfigurationValue];
|
181 |
|
|
break;
|
182 |
|
|
|
183 |
|
|
case DESC_INTERFACE:
|
184 |
|
|
// remember current alternate setting
|
185 |
|
|
bCurAltSetting = pab[INTF_DESC_bAlternateSetting];
|
186 |
|
|
break;
|
187 |
|
|
|
188 |
|
|
case DESC_ENDPOINT:
|
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);
|
197 |
|
|
}
|
198 |
|
|
break;
|
199 |
|
|
|
200 |
|
|
default:
|
201 |
|
|
break;
|
202 |
|
|
}
|
203 |
|
|
// skip to next descriptor
|
204 |
|
|
pab += pab[DESC_bLength];
|
205 |
|
|
}
|
206 |
|
|
|
207 |
|
|
// configure device
|
208 |
|
|
USBHwConfigDevice(TRUE);
|
209 |
|
|
}
|
210 |
|
|
|
211 |
|
|
return TRUE;
|
212 |
|
|
}
|
213 |
|
|
|
214 |
|
|
|
215 |
|
|
/**
|
216 |
|
|
Local function to handle a standard device request
|
217 |
|
|
|
218 |
|
|
@param [in] pSetup The setup packet
|
219 |
|
|
@param [in,out] *piLen Pointer to data length
|
220 |
|
|
@param [in,out] ppbData Data buffer.
|
221 |
|
|
|
222 |
|
|
@return TRUE if the request was handled successfully
|
223 |
|
|
*/
|
224 |
|
|
static BOOL HandleStdDeviceReq(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData)
|
225 |
|
|
{
|
226 |
|
|
unsigned char *pbData = *ppbData;
|
227 |
|
|
|
228 |
|
|
switch (pSetup->bRequest) {
|
229 |
|
|
|
230 |
|
|
case REQ_GET_STATUS:
|
231 |
|
|
// bit 0: self-powered
|
232 |
|
|
// bit 1: remote wakeup = not supported
|
233 |
|
|
pbData[0] = 0;
|
234 |
|
|
pbData[1] = 0;
|
235 |
|
|
*piLen = 2;
|
236 |
|
|
break;
|
237 |
|
|
|
238 |
|
|
case REQ_SET_ADDRESS:
|
239 |
|
|
USBHwSetAddress(pSetup->wValue);
|
240 |
|
|
break;
|
241 |
|
|
|
242 |
|
|
case REQ_GET_DESCRIPTOR:
|
243 |
|
|
DBG("D%x", pSetup->wValue);
|
244 |
|
|
return USBGetDescriptor(pSetup->wValue, pSetup->wIndex, piLen, ppbData);
|
245 |
|
|
|
246 |
|
|
case REQ_GET_CONFIGURATION:
|
247 |
|
|
// indicate if we are configured
|
248 |
|
|
pbData[0] = bConfiguration;
|
249 |
|
|
*piLen = 1;
|
250 |
|
|
break;
|
251 |
|
|
|
252 |
|
|
case REQ_SET_CONFIGURATION:
|
253 |
|
|
if (!USBSetConfiguration(pSetup->wValue & 0xFF, 0)) {
|
254 |
|
|
DBG("USBSetConfiguration failed!\n");
|
255 |
|
|
return FALSE;
|
256 |
|
|
}
|
257 |
|
|
// configuration successful, update current configuration
|
258 |
|
|
bConfiguration = pSetup->wValue & 0xFF;
|
259 |
|
|
break;
|
260 |
|
|
|
261 |
|
|
case REQ_CLEAR_FEATURE:
|
262 |
|
|
case REQ_SET_FEATURE:
|
263 |
|
|
if (pSetup->wValue == FEA_REMOTE_WAKEUP) {
|
264 |
|
|
// put DEVICE_REMOTE_WAKEUP code here
|
265 |
|
|
}
|
266 |
|
|
if (pSetup->wValue == FEA_TEST_MODE) {
|
267 |
|
|
// put TEST_MODE code here
|
268 |
|
|
}
|
269 |
|
|
return FALSE;
|
270 |
|
|
|
271 |
|
|
case REQ_SET_DESCRIPTOR:
|
272 |
|
|
DBG("Device req %d not implemented\n", pSetup->bRequest);
|
273 |
|
|
return FALSE;
|
274 |
|
|
|
275 |
|
|
default:
|
276 |
|
|
DBG("Illegal device req %d\n", pSetup->bRequest);
|
277 |
|
|
return FALSE;
|
278 |
|
|
}
|
279 |
|
|
|
280 |
|
|
return TRUE;
|
281 |
|
|
}
|
282 |
|
|
|
283 |
|
|
|
284 |
|
|
/**
|
285 |
|
|
Local function to handle a standard interface request
|
286 |
|
|
|
287 |
|
|
@param [in] pSetup The setup packet
|
288 |
|
|
@param [in,out] *piLen Pointer to data length
|
289 |
|
|
@param [in] ppbData Data buffer.
|
290 |
|
|
|
291 |
|
|
@return TRUE if the request was handled successfully
|
292 |
|
|
*/
|
293 |
|
|
static BOOL HandleStdInterfaceReq(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData)
|
294 |
|
|
{
|
295 |
|
|
unsigned char *pbData = *ppbData;
|
296 |
|
|
|
297 |
|
|
switch (pSetup->bRequest) {
|
298 |
|
|
|
299 |
|
|
case REQ_GET_STATUS:
|
300 |
|
|
// no bits specified
|
301 |
|
|
pbData[0] = 0;
|
302 |
|
|
pbData[1] = 0;
|
303 |
|
|
*piLen = 2;
|
304 |
|
|
break;
|
305 |
|
|
|
306 |
|
|
case REQ_CLEAR_FEATURE:
|
307 |
|
|
case REQ_SET_FEATURE:
|
308 |
|
|
// not defined for interface
|
309 |
|
|
return FALSE;
|
310 |
|
|
|
311 |
|
|
case REQ_GET_INTERFACE: // TODO use bNumInterfaces
|
312 |
|
|
// there is only one interface, return n-1 (= 0)
|
313 |
|
|
pbData[0] = 0;
|
314 |
|
|
*piLen = 1;
|
315 |
|
|
break;
|
316 |
|
|
|
317 |
|
|
case REQ_SET_INTERFACE: // TODO use bNumInterfaces
|
318 |
|
|
// there is only one interface (= 0)
|
319 |
|
|
if (pSetup->wValue != 0) {
|
320 |
|
|
return FALSE;
|
321 |
|
|
}
|
322 |
|
|
*piLen = 0;
|
323 |
|
|
break;
|
324 |
|
|
|
325 |
|
|
default:
|
326 |
|
|
DBG("Illegal interface req %d\n", pSetup->bRequest);
|
327 |
|
|
return FALSE;
|
328 |
|
|
}
|
329 |
|
|
|
330 |
|
|
return TRUE;
|
331 |
|
|
}
|
332 |
|
|
|
333 |
|
|
|
334 |
|
|
/**
|
335 |
|
|
Local function to handle a standard endpoint request
|
336 |
|
|
|
337 |
|
|
@param [in] pSetup The setup packet
|
338 |
|
|
@param [in,out] *piLen Pointer to data length
|
339 |
|
|
@param [in] ppbData Data buffer.
|
340 |
|
|
|
341 |
|
|
@return TRUE if the request was handled successfully
|
342 |
|
|
*/
|
343 |
|
|
static BOOL HandleStdEndPointReq(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData)
|
344 |
|
|
{
|
345 |
|
|
unsigned char *pbData = *ppbData;
|
346 |
|
|
|
347 |
|
|
switch (pSetup->bRequest) {
|
348 |
|
|
case REQ_GET_STATUS:
|
349 |
|
|
// bit 0 = endpointed halted or not
|
350 |
|
|
pbData[0] = (USBHwEPGetStatus(pSetup->wIndex) & EP_STATUS_STALLED) ? 1 : 0;
|
351 |
|
|
pbData[1] = 0;
|
352 |
|
|
*piLen = 2;
|
353 |
|
|
break;
|
354 |
|
|
|
355 |
|
|
case REQ_CLEAR_FEATURE:
|
356 |
|
|
if (pSetup->wValue == FEA_ENDPOINT_HALT) {
|
357 |
|
|
// clear HALT by unstalling
|
358 |
|
|
USBHwEPStall(pSetup->wIndex, FALSE);
|
359 |
|
|
break;
|
360 |
|
|
}
|
361 |
|
|
// only ENDPOINT_HALT defined for endpoints
|
362 |
|
|
return FALSE;
|
363 |
|
|
|
364 |
|
|
case REQ_SET_FEATURE:
|
365 |
|
|
if (pSetup->wValue == FEA_ENDPOINT_HALT) {
|
366 |
|
|
// set HALT by stalling
|
367 |
|
|
USBHwEPStall(pSetup->wIndex, TRUE);
|
368 |
|
|
break;
|
369 |
|
|
}
|
370 |
|
|
// only ENDPOINT_HALT defined for endpoints
|
371 |
|
|
return FALSE;
|
372 |
|
|
|
373 |
|
|
case REQ_SYNCH_FRAME:
|
374 |
|
|
DBG("EP req %d not implemented\n", pSetup->bRequest);
|
375 |
|
|
return FALSE;
|
376 |
|
|
|
377 |
|
|
default:
|
378 |
|
|
DBG("Illegal EP req %d\n", pSetup->bRequest);
|
379 |
|
|
return FALSE;
|
380 |
|
|
}
|
381 |
|
|
|
382 |
|
|
return TRUE;
|
383 |
|
|
}
|
384 |
|
|
|
385 |
|
|
|
386 |
|
|
/**
|
387 |
|
|
Default handler for standard ('chapter 9') requests
|
388 |
|
|
|
389 |
|
|
If a custom request handler was installed, this handler is called first.
|
390 |
|
|
|
391 |
|
|
@param [in] pSetup The setup packet
|
392 |
|
|
@param [in,out] *piLen Pointer to data length
|
393 |
|
|
@param [in] ppbData Data buffer.
|
394 |
|
|
|
395 |
|
|
@return TRUE if the request was handled successfully
|
396 |
|
|
*/
|
397 |
|
|
BOOL USBHandleStandardRequest(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData)
|
398 |
|
|
{
|
399 |
|
|
// try the custom request handler first
|
400 |
|
|
if ((pfnHandleCustomReq != NULL) && pfnHandleCustomReq(pSetup, piLen, ppbData)) {
|
401 |
|
|
return TRUE;
|
402 |
|
|
}
|
403 |
|
|
|
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;
|
409 |
|
|
}
|
410 |
|
|
}
|
411 |
|
|
|
412 |
|
|
|
413 |
|
|
/**
|
414 |
|
|
Registers a callback for custom device requests
|
415 |
|
|
|
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'
|
418 |
|
|
request handler.
|
419 |
|
|
|
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'
|
422 |
|
|
specification.
|
423 |
|
|
|
424 |
|
|
@param [in] pfnHandler Callback function pointer
|
425 |
|
|
*/
|
426 |
|
|
void USBRegisterCustomReqHandler(TFnHandleRequest *pfnHandler)
|
427 |
|
|
{
|
428 |
|
|
pfnHandleCustomReq = pfnHandler;
|
429 |
|
|
}
|
430 |
|
|
|