?lang_form? ?lang_select? ?lang_submit? ?lang_endform?
{HEADER END}
{BLAME START}

library

?curdirlinks? -

Blame information for rev 32

Line No. Rev Author Line
1 32 kaklik /******************************************************************************
2  
3 USB Host HID Parser
4  
5 This is the Human Interface Device Class report parser file for a USB
6 Embedded Host device. This file should be used in a project with usb_host_hid.c
7 to provided the functional interface.
8  
9 Acronyms/abbreviations used by this class:
10 * HID - Human Interface Device
11  
12 This file consists of HID parser. Report descriptor received from device is
13 validated for proper format. If the report descriptor is not in proper format
14 as mandated by USB forum the device is not allowed on the bus. Only after the
15 report descriptor is validated, the information is populated in data structures.
16 Interface functions provided in file 'usb_host_hid.c' can be
17 used to understand the device capabilities. User of this code is advised to
18 refer document "Device Class Definition for Human Interface Devices (HID)"
19 available on website 'www.usb.org' for more details on content and format
20 of report descriptor.
21  
22 FileName: usb_host_hid_parser.c
23 Dependencies: None
24 Processor: PIC24/dsPIC30/dsPIC33/PIC32MX
25 Compiler: C30/C32
26 Company: Microchip Technology, Inc.
27  
28 Software License Agreement
29  
30 The software supplied herewith by Microchip Technology Incorporated
31 (the “Company”) for its PICmicro® Microcontroller is intended and
32 supplied to you, the Company’s customer, for use solely and
33 exclusively on Microchip PICmicro Microcontroller products. The
34 software is owned by the Company and/or its supplier, and is
35 protected under applicable copyright laws. All rights are reserved.
36 Any use in violation of the foregoing restrictions may subject the
37 user to criminal sanctions under applicable laws, as well as to
38 civil liability for the breach of the terms and conditions of this
39 license.
40  
41 THIS SOFTWARE IS PROVIDED IN AN “AS IS” CONDITION. NO WARRANTIES,
42 WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED
43 TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
44 PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. THE COMPANY SHALL NOT,
45 IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL OR
46 CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
47  
48 Change History:
49 Rev Description
50 ---------- ----------------------------------------------------------
51 2.6 - 2.7 No change
52  
53 2.7a Provided macro wrapped versions of malloc() and free()
54 so that a user can override these functions easily.
55  
56 Fixed initialization issue where HID parse result information
57 wasn't cleared before loading with new parse result data.
58  
59 *******************************************************************************/
60 #include "GenericTypeDefs.h"
61 #include "HardwareProfile.h"
62 #include "usb_config.h"
63 #include "USB\usb.h"
64 #include "USB\usb_host_hid.h"
65 #include "USB\usb_host_hid_parser.h"
66 #include <stdlib.h>
67 #include <string.h>
68  
69 //#define DEBUG_MODE
70 #ifdef DEBUG_MODE
71 #include "uart2.h"
72 #endif
73  
74 // *****************************************************************************
75 // *****************************************************************************
76 // Section: Constants
77 // *****************************************************************************
78 // *****************************************************************************
79  
80  
81 //******************************************************************************
82 //******************************************************************************
83 // Section: Data Structures
84 //******************************************************************************
85 //******************************************************************************
86  
87  
88 //******************************************************************************
89 //******************************************************************************
90 // Section: Macros
91 //******************************************************************************
92 //******************************************************************************
93 #ifndef USB_MALLOC
94 #define USB_MALLOC(size) malloc(size)
95 #endif
96  
97 #ifndef USB_FREE
98 #define USB_FREE(ptr) free(ptr)
99 #endif
100  
101 #define USB_FREE_AND_CLEAR(ptr) {USB_FREE(ptr); ptr = NULL;}
102  
103 //******************************************************************************
104 //******************************************************************************
105 // Section: Local Prototypes
106 //******************************************************************************
107 //******************************************************************************
108  
109 static void _USBHostHID_InitDeviceRptInfo(void);
110 static void _USBHostHID_Parse_Collection(HID_ITEM_INFO* ptrItem);
111 static void _USBHostHID_Parse_EndCollection(HID_ITEM_INFO* ptrItem);
112 static USB_HID_RPT_DESC_ERROR _USBHostHID_Parse_ReportType(HID_ITEM_INFO* item);
113 static void _USBHostHID_ConvertDataToSigned(HID_ITEM_INFO* item);
114  
115 //******************************************************************************
116 //******************************************************************************
117 // Section: HID Host Global Variables
118 //******************************************************************************
119 //******************************************************************************
120  
121 USB_HID_DEVICE_RPT_INFO deviceRptInfo = {0};
122 USB_HID_ITEM_LIST itemListPtrs ={NULL};
123 BYTE* parsedDataMem = NULL;
124  
125 /****************************************************************************
126 Function:
127 USB_HID_RPT_DESC_ERROR _USBHostHID_Parse_Report(BYTE* hidReportDescriptor
128 ,WORD lengthOfDescriptor, WORD pollRate,
129 BYTE interfaceNum)
130  
131 Description:
132 This function is called by usb_host_hid.c after a valid configuration
133 device is found. This function parses the report descriptor and stores
134 data in data structures. Application can access these data structures
135 to understand report format and device capabilities
136  
137 Precondition:
138 None
139  
140 Parameters:
141 BYTE* hidReportDescriptor - Pointer to raw report descriptor
142 WORD lengthOfDescriptor - Length of Report Descriptor
143 WORD pollRate - Poll rate of the report
144 BYTE interfaceNum - interface number of the respective report
145 descriptor.
146  
147 Return Values:
148 USB_HID_RPT_DESC_ERROR - Returns error code(enum) if found while
149 parsing the report descriptor
150  
151 Remarks:
152 None
153 ***************************************************************************/
154 USB_HID_RPT_DESC_ERROR _USBHostHID_Parse_Report(BYTE* hidReportDescriptor , WORD lengthOfDescriptor , WORD pollRate, BYTE interfaceNum)
155 {
156 WORD sizeRequired = 0;
157 WORD len_to_be_parsed =0;
158 BYTE* currentRptDescPtr = NULL;
159 BYTE* assignMem = NULL;
160 /* Main Item Vars */
161 HID_COLLECTION *collectionLocal = NULL;
162 HID_REPORT *reportLocal = NULL;
163  
164 /* Global Item Vars */
165 HID_REPORT *lreport = NULL;
166 BYTE lreportIndex = (BYTE)NULL;
167  
168 /* Local Item Vars */
169 HID_DESIGITEM *ldesignatorItem = NULL;
170 HID_STRINGITEM *lstringItem = NULL;
171 HID_USAGEITEM *lusageItem = NULL;
172  
173 /*HID Error */
174 USB_HID_RPT_DESC_ERROR lhidError = HID_ERR;
175  
176 HID_ITEM_INFO item;
177  
178 BYTE i=0;
179 BYTE dataByte=0 ;
180 BYTE ldataSize=0;
181  
182 if((hidReportDescriptor == NULL) ||(lengthOfDescriptor == 0))
183 {
184 /* set error flag */
185 return(HID_ERR_NullPointer);
186 }
187  
188 memset( &deviceRptInfo, 0x00, sizeof( USB_HID_DEVICE_RPT_INFO ) );
189 _USBHostHID_InitDeviceRptInfo();
190  
191 deviceRptInfo.interfaceNumber = interfaceNum; // update interface number for the report
192 deviceRptInfo.reportPollingRate = pollRate;
193  
194 len_to_be_parsed = lengthOfDescriptor;
195 currentRptDescPtr = hidReportDescriptor;
196  
197 while(len_to_be_parsed > 0) /* First parse to calculate the space required for all the items */
198 {
199 item.ItemDetails.val = *currentRptDescPtr;
200 /* Data need not be parsed at this point */
201 ldataSize = item.ItemDetails.ItemSize ;
202 if(item.ItemDetails.ItemSize == 3)
203 ldataSize = 4;
204  
205 currentRptDescPtr += (ldataSize+1) ; /* point to next item i.e size of item data + 1(item detail) */
206 len_to_be_parsed -= (ldataSize+1); /* remaining bytes = current - (length of data + 1)*/
207  
208 switch (item.ItemDetails.ItemType)
209 {
210 case HIDType_Main: /* Main Items */
211 switch (item.ItemDetails.ItemTag)
212 {
213 case HIDTag_Collection:
214 deviceRptInfo.collections++;
215 deviceRptInfo.collectionNesting++;
216 if (deviceRptInfo.collectionNesting > deviceRptInfo.maxCollectionNesting)
217 deviceRptInfo.maxCollectionNesting = deviceRptInfo.collectionNesting;
218 break;
219 case HIDTag_EndCollection:
220 if (deviceRptInfo.collectionNesting-- == 0)
221 lhidError = HID_ERR_UnexpectedEndCollection ;/* Error: UnexpectedEndCollection */
222 break;
223 case HIDTag_Input:
224 case HIDTag_Output:
225 case HIDTag_Feature:
226 deviceRptInfo.reportItems++;
227 break;
228 default :
229 break;
230 }
231 break;
232 case HIDType_Global: /* Global Items */
233 switch (item.ItemDetails.ItemTag)
234 {
235 case HIDTag_ReportID:
236 deviceRptInfo.reports++;
237 break;
238 case HIDTag_Push:
239 deviceRptInfo.globalsNesting++;
240 if (deviceRptInfo.globalsNesting > deviceRptInfo.maxGlobalsNesting)
241 deviceRptInfo.maxGlobalsNesting = deviceRptInfo.globalsNesting;
242 break;
243 case HIDTag_Pop:
244 deviceRptInfo.globalsNesting--;
245 if (deviceRptInfo.globalsNesting > deviceRptInfo.maxGlobalsNesting)
246 lhidError = HID_ERR_UnexpectedPop ;/* Error: global nesting rolled to negative ... */
247 break;
248 default :
249 break;
250 }
251 break;
252 case HIDType_Local: /* Local Item */
253 switch (item.ItemDetails.ItemTag)
254 {
255 case HIDTag_Usage:
256 deviceRptInfo.usages++;
257 break;
258 case HIDTag_UsageMinimum:
259 case HIDTag_UsageMaximum:
260 deviceRptInfo.usageRanges++;
261 break;
262 case HIDTag_StringIndex:
263 deviceRptInfo.strings++;
264 break;
265 case HIDTag_StringMinimum:
266 case HIDTag_StringMaximum:
267 deviceRptInfo.stringRanges++;
268 break;
269 case HIDTag_DesignatorIndex:
270 deviceRptInfo.designators++;
271 break;
272 case HIDTag_DesignatorMinimum:
273 case HIDTag_DesignatorMaximum:
274 deviceRptInfo.designatorRanges++;
275 break;
276 default :
277 break;
278 }
279 break;
280 default :
281 break;
282  
283 }
284 }
285  
286 if(lhidError)
287 {
288 return(lhidError);
289 }
290  
291 if (deviceRptInfo.collectionNesting != 0) return(HID_ERR_MissingEndCollection) /* HID_RPT_DESC_FORMAT_IMPROPER */;
292  
293 if (deviceRptInfo.collections == 1) return(HID_ERR_MissingTopLevelCollection) /* HID_RPT_DESC_FORMAT_IMPROPER */;
294  
295 if (deviceRptInfo.reportItems == 0) return(HID_ERR_NoReports)/* HID_RPT_DESC_FORMAT_IMPROPER */;
296  
297 if ((deviceRptInfo.usageRanges & 1) == 1) return(HID_ERR_UnmatchedUsageRange)/* HID_RPT_DESC_FORMAT_IMPROPER */;
298  
299 if ((deviceRptInfo.stringRanges & 1) == 1) return(HID_ERR_UnmatchedStringRange)/* HID_RPT_DESC_FORMAT_IMPROPER */;
300  
301 if ((deviceRptInfo.designatorRanges & 1) == 1) return(HID_ERR_UnmatchedDesignatorRange)/* HID_RPT_DESC_FORMAT_IMPROPER */;
302  
303  
304 /* usages , strings & descriptors are in pair */
305 deviceRptInfo.usages += (deviceRptInfo.usageRanges/2);
306 deviceRptInfo.strings += (deviceRptInfo.stringRanges/2);
307 deviceRptInfo.designators += (deviceRptInfo.designatorRanges/2);
308  
309 /* Calculate space required */
310  
311 sizeRequired = (sizeof(HID_COLLECTION) * deviceRptInfo.collections)
312 + (sizeof(HID_REPORTITEM) * deviceRptInfo.reportItems)
313 + (sizeof(HID_REPORT) * deviceRptInfo.reports)
314 + (sizeof(HID_USAGEITEM) * deviceRptInfo.usages)
315 + (sizeof(HID_STRINGITEM) * deviceRptInfo.strings)
316 + (sizeof(HID_DESIGITEM) * deviceRptInfo.designators)
317 + (sizeof(int) * deviceRptInfo.maxCollectionNesting)
318 + (sizeof(HID_GLOBALS) * deviceRptInfo.maxGlobalsNesting);
319  
320 if (parsedDataMem != NULL)
321 {
322 USB_FREE_AND_CLEAR( parsedDataMem );
323 }
324  
325 parsedDataMem = (BYTE*) USB_MALLOC(sizeRequired);
326  
327 #ifdef DEBUG_MODE
328 UART2PrintString( "HID: Memory for Report Descriptor: " );
329 UART2PutHex( sizeRequired );
330 #endif
331  
332 if (parsedDataMem == NULL) return(HID_ERR_NotEnoughMemory); /* Error: Not enough memory */
333 assignMem = (BYTE*) parsedDataMem;
334  
335 /* Allocate Space */
336 itemListPtrs.collectionList = (HID_COLLECTION *) assignMem;
337 assignMem += (sizeof(HID_COLLECTION) * deviceRptInfo.collections);
338 itemListPtrs.reportItemList = (HID_REPORTITEM *) assignMem;
339 assignMem += (sizeof(HID_REPORTITEM) * deviceRptInfo.reportItems);
340 itemListPtrs.reportList = (HID_REPORT *) assignMem;
341 assignMem += (sizeof(HID_REPORT) * deviceRptInfo.reports);
342 itemListPtrs.usageItemList = (HID_USAGEITEM *) assignMem;
343 assignMem += (sizeof(HID_USAGEITEM) * deviceRptInfo.usages);
344 itemListPtrs.stringItemList = (HID_STRINGITEM *) assignMem;
345 assignMem += (sizeof(HID_STRINGITEM) * deviceRptInfo.strings);
346 itemListPtrs.designatorItemList = (HID_DESIGITEM *) assignMem;
347 assignMem += (sizeof(HID_DESIGITEM) * deviceRptInfo.designators);
348 itemListPtrs.collectionStack = (BYTE *) assignMem;
349 assignMem += (sizeof(int) * deviceRptInfo.maxCollectionNesting);
350 itemListPtrs.globalsStack = (HID_GLOBALS *) assignMem;
351  
352 _USBHostHID_InitDeviceRptInfo();
353  
354 // Initialize the virtual collection
355  
356 collectionLocal = itemListPtrs.collectionList;
357 collectionLocal->data = 0;
358 collectionLocal->firstChild = 0;
359 collectionLocal->firstReportItem = 0;
360 collectionLocal->firstUsageItem = 0;
361 collectionLocal->nextSibling = 0;
362 collectionLocal->parent = 0;
363 collectionLocal->reportItems = 0;
364 collectionLocal->usageItems = 0;
365 collectionLocal->usagePage = 0;
366  
367 // Initialize the default report
368  
369 reportLocal = itemListPtrs.reportList;
370 reportLocal->featureBits = 0;
371 reportLocal->inputBits = 0;
372 reportLocal->outputBits = 0;
373 reportLocal->reportID = 0;
374  
375 /* re-init ptr to Rpt Descp & Length of Descp */
376 len_to_be_parsed = lengthOfDescriptor;
377 currentRptDescPtr = hidReportDescriptor;
378  
379 #ifdef DEBUG_MODE
380 UART2PrintString( "HID-HOST: ... 2nd Parse \n" );
381 #endif
382  
383  
384 while(len_to_be_parsed > 0) /* Second parse to fill the tables with each item detail */
385 {
386 item.ItemDetails.val = *currentRptDescPtr;
387 item.Data.uItemData = 0;
388  
389 ldataSize = item.ItemDetails.ItemSize ;
390 if(item.ItemDetails.ItemSize == 3)
391 ldataSize = 4;
392  
393 currentRptDescPtr++; /* ptr points to data */
394 for (i = 0; i < ldataSize; i++)
395 {
396 dataByte = *currentRptDescPtr++; /* signed data will be taken care in ItemTag it is expected */
397 item.Data.uItemData |= ((DWORD)dataByte << (i*8));
398 }
399  
400 len_to_be_parsed -= (ldataSize+1); /* remaining bytes = current - (length of current item + 1)*/
401  
402 switch(item.ItemDetails.ItemType)
403 {
404 case HIDType_Main: /* look for Main Items*/
405 switch(item.ItemDetails.ItemTag)
406 {
407 case HIDTag_Input :
408 case HIDTag_Output :
409 case HIDTag_Feature :
410 lhidError = _USBHostHID_Parse_ReportType(&item);
411 break;
412  
413 case HIDTag_Collection :
414 _USBHostHID_Parse_Collection(&item);
415 break;
416  
417 case HIDTag_EndCollection :
418 _USBHostHID_Parse_EndCollection(&item);
419 break;
420 }
421 break;
422  
423 case HIDType_Global: /* look for Global Items*/
424 switch(item.ItemDetails.ItemTag)
425 {
426 case HIDTag_UsagePage :
427 deviceRptInfo.globals.usagePage = item.Data.uItemData;
428 break;
429  
430 case HIDTag_LogicalMinimum : /* convert to signed val */
431 // Sign extend one value
432 _USBHostHID_ConvertDataToSigned(&item);
433 deviceRptInfo.globals.logicalMinimum = item.Data.sItemData;
434 break;
435  
436 case HIDTag_LogicalMaximum :/* convert to signed val */
437 // Sign extend one value
438 _USBHostHID_ConvertDataToSigned(&item);
439 deviceRptInfo.globals.logicalMaximum = item.Data.uItemData;
440 break;
441  
442 case HIDTag_PhysicalMinimum :/* convert to signed val */
443 // Sign extend one value
444 _USBHostHID_ConvertDataToSigned(&item);
445 deviceRptInfo.globals.physicalMinimum = item.Data.uItemData;
446 break;
447  
448 case HIDTag_PhysicalMaximum :/* convert to signed val */
449 // Sign extend one value
450 _USBHostHID_ConvertDataToSigned(&item);
451 deviceRptInfo.globals.physicalMaximum = item.Data.uItemData;
452 break;
453  
454 case HIDTag_UnitExponent :
455 deviceRptInfo.globals.unitExponent = item.Data.uItemData;
456 break;
457  
458 case HIDTag_ReportSize :
459 deviceRptInfo.globals.reportsize = item.Data.uItemData;
460 if (deviceRptInfo.globals.reportsize == 0)
461 lhidError = HID_ERR_ZeroReportSize;
462 break;
463  
464 case HIDTag_ReportID :
465 if (item.Data.uItemData)
466 {
467 // Look for the Report ID in the table
468  
469 lreportIndex = 0;
470 while ((lreportIndex < deviceRptInfo.reports)
471 && (itemListPtrs.reportList[lreportIndex].reportID != item.Data.uItemData))
472 lreportIndex++;
473  
474 // initialize the entry if it's new and there's room for it
475 // Start with 8 bits for the Report ID
476  
477 if (lreportIndex == deviceRptInfo.reports)
478 {
479 lreport = &itemListPtrs.reportList[deviceRptInfo.reports++];
480 lreport->reportID = item.Data.uItemData;
481 lreport->inputBits = 8;
482 lreport->outputBits = 8;
483 lreport->featureBits = 8;
484 }
485  
486 // remember which report is being processed
487  
488 deviceRptInfo.globals.reportID = item.Data.uItemData;
489 deviceRptInfo.globals.reportIndex = lreportIndex;
490 }
491 else
492 {
493 lhidError = HID_ERR_ZeroReportID;
494 }
495 break;
496  
497 case HIDTag_ReportCount :
498 if (item.Data.uItemData)
499 {
500 deviceRptInfo.globals.reportCount = item.Data.uItemData;
501 }
502 else
503 {
504 lhidError = HID_ERR_ZeroReportCount;
505 }
506 break;
507  
508 case HIDTag_Push :
509 itemListPtrs.globalsStack[deviceRptInfo.globalsNesting++] = deviceRptInfo.globals;
510 break;
511  
512 case HIDTag_Pop :
513 deviceRptInfo.globals = itemListPtrs.globalsStack[--deviceRptInfo.globalsNesting] ;
514 break;
515  
516 }
517 break;
518  
519 case HIDType_Local: /* look for Local Items*/
520 switch(item.ItemDetails.ItemTag)
521 {
522 case HIDTag_Usage :
523 lusageItem = &itemListPtrs.usageItemList[deviceRptInfo.usageItems++];
524 lusageItem->isRange = FALSE;
525 if (item.ItemDetails.ItemSize == 3) /* 4 data bytes */
526 {
527 lusageItem->usagePage = item.Data.uItemData >> 16;
528 lusageItem->usage = item.Data.uItemData & 0x00FF;
529 }
530 else
531 {
532 lusageItem->usagePage = deviceRptInfo.globals.usagePage;
533 lusageItem->usage = item.Data.uItemData;
534 }
535 break;
536  
537 case HIDTag_UsageMinimum :
538 if(deviceRptInfo.haveUsageMax)
539 {
540 lusageItem = &itemListPtrs.usageItemList[deviceRptInfo.usageItems++];
541 lusageItem->isRange = TRUE;
542 if(item.ItemDetails.ItemSize == 3)
543 {
544 lusageItem->usagePage = item.Data.uItemData >> 16;
545 lusageItem->usageMinimum = item.Data.uItemData & 0x00FFL;
546 }
547 else
548 {
549 lusageItem->usagePage = deviceRptInfo.globals.usagePage;
550 lusageItem->usageMinimum = item.Data.uItemData;
551 }
552  
553 if (lusageItem->usagePage != deviceRptInfo.rangeUsagePage);
554 lhidError = HID_ERR_BadUsageRangePage; /* Error: BadUsageRangePage */
555  
556 lusageItem->usageMaximum = deviceRptInfo.usageMaximum;
557  
558 if (lusageItem->usageMaximum < lusageItem->usageMinimum)
559 lhidError = HID_ERR_BadUsageRange; /* Error: BadUsageRange */
560  
561 deviceRptInfo.haveUsageMax = FALSE;
562 deviceRptInfo.haveUsageMin = FALSE;
563 }
564 else
565 {
566 if(item.ItemDetails.ItemSize == 3)
567 {
568 deviceRptInfo.rangeUsagePage = item.Data.uItemData >> 16;
569 deviceRptInfo.usageMinimum = item.Data.uItemData & 0x00FFL;
570 }
571 else
572 {
573 deviceRptInfo.rangeUsagePage = deviceRptInfo.globals.usagePage;
574 deviceRptInfo.usageMinimum = item.Data.uItemData;
575 }
576  
577 deviceRptInfo.haveUsageMin = TRUE;
578 }
579 break;
580  
581 case HIDTag_UsageMaximum :
582 if(deviceRptInfo.haveUsageMin)
583 {
584 lusageItem = &itemListPtrs.usageItemList[deviceRptInfo.usageItems++];
585 lusageItem->isRange = TRUE;
586 if(item.ItemDetails.ItemSize == 3)
587 {
588 lusageItem->usagePage = item.Data.uItemData >> 16;
589 lusageItem->usageMaximum = item.Data.uItemData & 0x00FFL;
590 }
591 else
592 {
593 lusageItem->usagePage = deviceRptInfo.globals.usagePage;
594 lusageItem->usageMaximum = item.Data.uItemData;
595 }
596  
597 if (lusageItem->usagePage != deviceRptInfo.rangeUsagePage)
598 lhidError = HID_ERR_BadUsageRangePage; /* Error: BadUsageRangePage */
599  
600 lusageItem->usageMinimum = deviceRptInfo.usageMinimum;
601  
602 if (lusageItem->usageMaximum < lusageItem->usageMinimum)
603 lhidError = HID_ERR_BadUsageRange; /* Error: BadUsageRange */
604  
605 deviceRptInfo.haveUsageMax = FALSE;
606 deviceRptInfo.haveUsageMin = FALSE;
607 }
608 else
609 {
610 if(item.ItemDetails.ItemSize == 3)
611 {
612 deviceRptInfo.rangeUsagePage = item.Data.uItemData >> 16;
613 deviceRptInfo.usageMaximum = item.Data.uItemData & 0x00FFL;
614 }
615 else
616 {
617 deviceRptInfo.rangeUsagePage = deviceRptInfo.globals.usagePage;
618 deviceRptInfo.usageMaximum = item.Data.uItemData;
619 }
620  
621 deviceRptInfo.haveUsageMax = TRUE;
622 }
623 break;
624  
625 case HIDTag_DesignatorIndex :
626 ldesignatorItem = &itemListPtrs.designatorItemList[deviceRptInfo.designatorItems++];
627 ldesignatorItem->isRange = FALSE;
628 ldesignatorItem->index = item.Data.uItemData;
629  
630 break;
631  
632 case HIDTag_DesignatorMinimum :
633 if(deviceRptInfo.haveDesignatorMax)
634 {
635 ldesignatorItem = &itemListPtrs.designatorItemList[deviceRptInfo.designatorItems++];
636 ldesignatorItem->isRange = TRUE;
637 ldesignatorItem->minimum = item.Data.uItemData;
638 ldesignatorItem->maximum = deviceRptInfo.designatorMaximum;
639 deviceRptInfo.haveDesignatorMin = FALSE;
640 deviceRptInfo.haveDesignatorMax = FALSE;
641 }
642 else
643 {
644 deviceRptInfo.designatorMinimum = item.Data.uItemData;
645 deviceRptInfo.haveDesignatorMin = TRUE;
646 }
647 break;
648  
649 case HIDTag_DesignatorMaximum :
650 if(deviceRptInfo.haveDesignatorMin)
651 {
652 ldesignatorItem = &itemListPtrs.designatorItemList[deviceRptInfo.designatorItems++];
653 ldesignatorItem->isRange = TRUE;
654 ldesignatorItem->maximum = item.Data.uItemData;
655 ldesignatorItem->minimum = deviceRptInfo.designatorMinimum;
656 deviceRptInfo.haveDesignatorMin = FALSE;
657 deviceRptInfo.haveDesignatorMax = FALSE;
658 }
659 else
660 {
661 deviceRptInfo.designatorMaximum = item.Data.uItemData;
662 deviceRptInfo.haveDesignatorMax = TRUE;
663 }
664 break;
665  
666 case HIDTag_StringIndex :
667 lstringItem = &itemListPtrs.stringItemList[deviceRptInfo.stringItems++];
668 lstringItem->isRange = FALSE;
669 lstringItem->index = item.Data.uItemData;
670 break;
671  
672 case HIDTag_StringMinimum :
673 if (deviceRptInfo.haveStringMax) {
674 lstringItem = &itemListPtrs.stringItemList[deviceRptInfo.stringItems++];
675 lstringItem->isRange = TRUE;
676 lstringItem->minimum = item.Data.uItemData;
677 lstringItem->maximum = deviceRptInfo.stringMaximum;
678 deviceRptInfo.haveStringMin = FALSE;
679 deviceRptInfo.haveStringMax = FALSE;
680 }
681 else {
682 deviceRptInfo.stringMinimum = item.Data.uItemData;
683 deviceRptInfo.haveStringMin = TRUE;
684 }
685 break;
686  
687 case HIDTag_StringMaximum :
688 if (deviceRptInfo.haveStringMin) {
689 lstringItem = &itemListPtrs.stringItemList[deviceRptInfo.stringItems++];
690 lstringItem->isRange = TRUE;
691 lstringItem->maximum = item.Data.uItemData;
692 lstringItem->minimum = deviceRptInfo.stringMinimum;
693 deviceRptInfo.haveStringMin = FALSE;
694 deviceRptInfo.haveStringMax = FALSE;
695 }
696 else {
697 deviceRptInfo.stringMaximum = item.Data.uItemData;
698 deviceRptInfo.haveStringMax = TRUE;
699 }
700 break;
701 break;
702  
703 case HIDTag_SetDelimiter :
704 break;
705  
706 }
707  
708 break;
709  
710 default:
711 break;
712 }
713 /* during 2nd parse if any anomaly is found in report format abort parsing and return */
714 if(lhidError)
715 {
716 return(lhidError);
717 }
718 }
719  
720 // Update the virtual collection
721  
722 // itemListPtrs.collectionList[0].reportItems = deviceRptInfo.reportItems;
723  
724 // Remove reports that have just the report id
725  
726 for (i=1; i<deviceRptInfo.reports; i++) {
727 if (itemListPtrs.reportList[i].inputBits == 8) itemListPtrs.reportList[i].inputBits = 0;
728 if (itemListPtrs.reportList[i].outputBits == 8) itemListPtrs.reportList[i].outputBits = 0;
729 if (itemListPtrs.reportList[i].featureBits == 8) itemListPtrs.reportList[i].featureBits = 0;
730 }
731  
732 return(lhidError);
733 }
734  
735 /****************************************************************************
736 Function:
737 static void _USBHostHID_InitDeviceRptInfo(void)
738  
739 Description:
740 This function is called by _USBHostHID_Parse_Report() to Initialize
741 report information to default value before every parse. Note that not
742 all values are reset.
743  
744 Precondition:
745 None
746  
747 Parameters:
748 None
749  
750 Return Values:
751 None
752  
753 Remarks:
754 None
755 ***************************************************************************/
756 static void _USBHostHID_InitDeviceRptInfo(void)
757 {
758 deviceRptInfo.collectionNesting = 0;
759 deviceRptInfo.collections = 1;
760 deviceRptInfo.designatorRanges = 0;
761 deviceRptInfo.designators = 0;
762 deviceRptInfo.globalsNesting = 0;
763 deviceRptInfo.maxCollectionNesting = 0;
764 deviceRptInfo.maxGlobalsNesting = 0;
765 deviceRptInfo.reportItems = 0;
766 deviceRptInfo.reports = 1;
767 deviceRptInfo.stringRanges = 0;
768 deviceRptInfo.strings = 0;
769 deviceRptInfo.usages = 0;
770 deviceRptInfo.usageRanges = 0;
771 deviceRptInfo.usageItems = 0;
772  
773 deviceRptInfo.haveDesignatorMax = FALSE;
774 deviceRptInfo.haveDesignatorMin = FALSE;
775 deviceRptInfo.haveStringMax = FALSE;
776 deviceRptInfo.haveStringMin = FALSE;
777 deviceRptInfo.haveUsageMax = FALSE;
778 deviceRptInfo.haveUsageMin = FALSE;
779  
780 deviceRptInfo.globals.logicalMaximum = 0;
781 deviceRptInfo.globals.logicalMinimum = 0;
782 deviceRptInfo.globals.physicalMaximum = 0;
783 deviceRptInfo.globals.physicalMinimum = 0;
784 deviceRptInfo.globals.reportCount = 0;
785 deviceRptInfo.globals.reportID = 0;
786 deviceRptInfo.globals.reportIndex = 0;
787 deviceRptInfo.globals.reportsize = 0;
788 deviceRptInfo.globals.unit = 0;
789 deviceRptInfo.globals.unitExponent = 0;
790 deviceRptInfo.globals.usagePage = 0;
791 }
792  
793  
794 /****************************************************************************
795 Function:
796 static void _USBHostHID_Parse_Collection(HID_ITEM_INFO* ptrItem)
797  
798 Description:
799 This function is called by _USBHostHID_Parse_Report() to parse
800 collection item.
801  
802 Precondition:
803 None
804  
805 Parameters:
806 HID_ITEM_INFO* ptrItem - pointer to item structure containg raw
807 information from the report
808 Return Values:
809 None
810  
811 Remarks:
812 None
813 ***************************************************************************/
814 static void _USBHostHID_Parse_Collection(HID_ITEM_INFO* ptrItem)
815 {
816 HID_COLLECTION *lcollection;
817 WORD i;
818  
819 // Initialize the new Collection Structure
820  
821 i = deviceRptInfo.collections++;
822 lcollection = &itemListPtrs.collectionList[i];
823 lcollection->data = ptrItem->Data.uItemData;
824 lcollection->firstUsageItem = deviceRptInfo.firstUsageItem;
825 lcollection->usageItems = deviceRptInfo.usageItems - deviceRptInfo.firstUsageItem;
826 deviceRptInfo.firstUsageItem = deviceRptInfo.usageItems;
827 lcollection->nextSibling = deviceRptInfo.sibling;
828 deviceRptInfo.sibling = 0;
829 lcollection->firstChild = 0;
830 lcollection->usagePage = deviceRptInfo.globals.usagePage;
831 lcollection->firstReportItem = deviceRptInfo.reportItems;
832  
833 // Set up the relationship with the Parent Collection
834  
835 lcollection->parent = deviceRptInfo.parent;
836 itemListPtrs.collectionList[deviceRptInfo.parent].firstChild = i;
837  
838 // Save the Parent Collection Information on the stack
839 itemListPtrs.collectionStack[deviceRptInfo.collectionNesting++] = deviceRptInfo.parent;
840 deviceRptInfo.parent = i;
841 }
842  
843 /****************************************************************************
844 Function:
845 static void _USBHostHID_Parse_EndCollection(HID_ITEM_INFO* ptrItem)
846  
847 Description:
848 This function is called by _USBHostHID_Parse_Report() to parse end of
849 collection item.
850  
851 Precondition:
852 None
853  
854 Parameters:
855 HID_ITEM_INFO* ptrItem - pointer to item structure containg raw
856 information from the report
857 Return Values:
858 None
859  
860 Remarks:
861 None
862 ***************************************************************************/
863 static void _USBHostHID_Parse_EndCollection(HID_ITEM_INFO* ptrItem)
864 {
865 HID_COLLECTION *lcollection;
866 BYTE i;
867  
868 // Remember the number of reportItem MainItems in this Collection
869  
870 lcollection = &itemListPtrs.collectionList[deviceRptInfo.parent];
871 lcollection->reportItems = deviceRptInfo.reportItems - lcollection->firstReportItem;
872  
873 // Restore the Parent Collection Data
874  
875 i = itemListPtrs.collectionStack[--deviceRptInfo.collectionNesting];
876 deviceRptInfo.sibling = deviceRptInfo.parent;
877 deviceRptInfo.parent = i;
878 }
879  
880 /****************************************************************************
881 Function:
882 static USB_HID_RPT_DESC_ERROR _USBHostHID_Parse_ReportType(HID_ITEM_INFO* item)
883  
884 Description:
885 This function is called by _USBHostHID_Parse_Report() to parse
886 input, output & report item.
887  
888 Precondition:
889 None
890  
891 Parameters:
892 HID_ITEM_INFO* ptrItem - pointer to item structure containg raw
893 information from the report
894  
895 Return Values:
896 USB_HID_RPT_DESC_ERROR - Returns error code if any error is encountered
897 in report descriptor.
898  
899 Remarks:
900 None
901 ***************************************************************************/
902 static USB_HID_RPT_DESC_ERROR _USBHostHID_Parse_ReportType(HID_ITEM_INFO* item)
903 {
904 HID_REPORTITEM *lreportItem = NULL;
905 HID_REPORT *lreport = NULL;
906 WORD bits = 0;
907  
908 if(item == NULL)
909 return(HID_ERR_NullPointer);
910  
911 // Reality Check on the Report Main Item
912  
913 if (deviceRptInfo.globals.logicalMinimum >= ((LONG)1<<deviceRptInfo.globals.reportsize)) return(HID_ERR_BadLogicalMin) ;
914 if (deviceRptInfo.globals.logicalMaximum >= ((LONG)1<<deviceRptInfo.globals.reportsize))return(HID_ERR_BadLogicalMax);
915 // The barcode scanner has this issue. We'll ignore it.
916 // if (deviceRptInfo.globals.logicalMinimum > deviceRptInfo.globals.logicalMaximum)return(HID_ERR_BadLogical);
917 if (deviceRptInfo.haveUsageMin || deviceRptInfo.haveUsageMax)return(HID_ERR_UnmatchedUsageRange);
918 if (deviceRptInfo.haveStringMin || deviceRptInfo.haveStringMax)return(HID_ERR_UnmatchedStringRange);
919 if (deviceRptInfo.haveDesignatorMin || deviceRptInfo.haveDesignatorMax)return(HID_ERR_UnmatchedDesignatorRange);
920  
921 // Initialize the new Report Item structure
922  
923 lreportItem = &itemListPtrs.reportItemList[deviceRptInfo.reportItems++];
924 lreportItem->dataModes = item->Data.uItemData;
925 lreportItem->globals = deviceRptInfo.globals;
926 lreportItem->parent = deviceRptInfo.parent;
927 lreportItem->firstUsageItem = deviceRptInfo.firstUsageItem;
928 deviceRptInfo.firstUsageItem = deviceRptInfo.usageItems;
929 lreportItem->usageItems = deviceRptInfo.usageItems - lreportItem->firstUsageItem;
930 lreportItem->firstStringItem = deviceRptInfo.firstStringItem;
931 deviceRptInfo.firstStringItem = deviceRptInfo.stringItems;
932 lreportItem->stringItems = deviceRptInfo.stringItems - lreportItem->firstStringItem;
933 lreportItem->firstDesignatorItem = deviceRptInfo.firstDesignatorItem;
934 deviceRptInfo.firstDesignatorItem = deviceRptInfo.designatorItems;
935 lreportItem->designatorItems = deviceRptInfo.designatorItems - lreportItem->firstDesignatorItem;
936  
937 // Update the Report by the size of this item
938  
939 lreport = &itemListPtrs.reportList[deviceRptInfo.globals.reportIndex];
940 bits = deviceRptInfo.globals.reportsize * deviceRptInfo.globals.reportCount;
941 switch (item->ItemDetails.ItemTag)
942 {
943 case HIDTag_Feature:
944 lreportItem->reportType = hidReportFeature;
945 lreportItem->startBit = lreport->featureBits;
946 lreport->featureBits += bits;
947 break;
948 case HIDTag_Output:
949 lreportItem->reportType = hidReportOutput;
950 lreportItem->startBit = lreport->outputBits;
951 lreport->outputBits += bits;
952 break;
953 case HIDTag_Input:
954 lreportItem->reportType = hidReportInput;
955 lreportItem->startBit = lreport->inputBits;
956 lreport->inputBits += bits;
957 break;
958 default:
959 lreportItem->reportType = hidReportUnknown;
960 break;
961 }
962  
963 return HID_ERR;
964 }
965  
966 /****************************************************************************
967 Function:
968 static void _USBHostHID_ConvertDataToSigned(HID_ITEM_INFO* item)
969  
970 Description:
971 This function is called by _USBHostHID_Parse_Report() convert data
972 to signed whenever required
973  
974 Precondition:
975 None
976  
977 Parameters:
978 HID_ITEM_INFO* ptrItem - pointer to item structure containg raw
979 information from the report
980  
981 Return Values:
982 None
983  
984 Remarks:
985 None
986 ***************************************************************************/
987 static void _USBHostHID_ConvertDataToSigned(HID_ITEM_INFO* item)
988 {
989 BYTE dataByte=0;
990 BYTE index;
991  
992 index = item->ItemDetails.ItemSize;
993  
994 if(index)
995 {
996 if(item->ItemDetails.ItemSize == 3)
997 index = 4;
998  
999 dataByte = item->Data.bItemData[index-1];
1000 if ((dataByte & 0x80) != 0)
1001 {
1002 while (index < sizeof(LONG))
1003 item->Data.sItemData |= (0xFF << ((index++)*8)); /* extend one */
1004 }
1005 }
1006 }
1007  
1008  
1009 /****************************************************************************
1010 Function:
1011 BOOL USBHostHID_HasUsage(HID_REPORTITEM *reportItem,WORD usagePage,
1012 WORD usage,WORD *pindex)
1013  
1014 Description:
1015 This function is used to locate the usage in a report descriptor.
1016 Function will look into the data structures created by the HID parser
1017 and return the appropriate location.
1018  
1019 Precondition:
1020 None
1021  
1022 Parameters:
1023 HID_REPORTITEM *reportItem - Report item index to be searched
1024 WORD usagePage - Application needs to pass the usagePage as
1025 the search criteria for the usage
1026 WORD usage - Application needs to pass the usageto be
1027 searched
1028 WORD *pindex - returns index to the usage item requested.
1029  
1030 Return Values:
1031 BOOL - FALSE - If requested usage is not found
1032 TRUE - if requested usage is found
1033 Remarks:
1034 None
1035 ***************************************************************************/
1036 BOOL USBHostHID_HasUsage(HID_REPORTITEM *reportItem,WORD usagePage, WORD usage,WORD *pindex,BYTE* count)
1037 {
1038 HID_USAGEITEM *hidUsageItem;
1039 WORD usageIndex;
1040 SHORT usages;
1041 BOOL onPage;
1042 BYTE usageItem;
1043 BYTE countsLeft;
1044 BYTE i;
1045  
1046 // Disallow Null Pointers
1047  
1048 if ((reportItem == NULL)|(pindex == NULL))
1049 return FALSE;
1050  
1051 // Look through the Usage Items for this Usage
1052  
1053 usageItem = reportItem->firstUsageItem;
1054 usageIndex = 0;
1055 for (i=0; i<reportItem->usageItems; i++) {
1056 // Each Usage Item is either a Usage or a Usage Range
1057  
1058 hidUsageItem = &itemListPtrs.usageItemList[usageItem++];
1059 onPage = ((usagePage == 0) || (usagePage == hidUsageItem->usagePage));
1060 if (hidUsageItem->isRange)
1061 {
1062 // For Usage Ranges
1063 // If the index is in the range
1064 // then return the Usage
1065 // Otherwise adjust the index by the size of the range
1066  
1067 if ((usage >= hidUsageItem->usageMinimum)&& (usage <= hidUsageItem->usageMaximum))
1068 {
1069 if (pindex != NULL)
1070 *pindex = usageIndex + (usage - hidUsageItem->usageMinimum);
1071  
1072 // If this usage is the last one for this reportItem
1073 // then it gets all of the remaining ReportCount
1074  
1075 if (count != NULL)
1076 {
1077 if (((i+1) == reportItem->usageItems)&& (usage == hidUsageItem->usageMaximum))
1078 {
1079 countsLeft = reportItem->globals.reportCount - usageIndex;
1080 if (countsLeft > 1)
1081 *count = countsLeft;
1082 else
1083 *count = 1;
1084 }
1085 else
1086 *count = 1;
1087 }
1088 if(onPage)
1089 return TRUE;
1090 }
1091 usages = hidUsageItem->usageMaximum - hidUsageItem->usageMinimum + 1;
1092 if (usages < 0) usages = -usages;
1093 usageIndex += usages;
1094 }
1095 else
1096 {
1097 // For Usages
1098 // If the index is zero
1099 // then return this Usage
1100 // Otherwise one less to index through
1101  
1102 if (usage == hidUsageItem->usage)
1103 {
1104 if (pindex != NULL)
1105 *pindex = usageIndex;
1106 if (count != NULL)
1107 {
1108 if ((i+1) == reportItem->usageItems)
1109 {
1110 countsLeft = reportItem->globals.reportCount - usageIndex;
1111 if (countsLeft > 1)
1112 *count = countsLeft;
1113 else
1114 *count = 1;
1115 }
1116 else
1117 *count = 1;
1118 }
1119 if (onPage)
1120 return TRUE;
1121 }
1122 usageIndex++;
1123 }
1124 }
1125 return FALSE;
1126 }
1127  
1128 #ifdef DEBUG_MODE
1129 void USBHID_ReportDecriptor_Dump(void)
1130 {
1131 HID_COLLECTION *lcollection;
1132 HID_REPORT *lreport;
1133 HID_REPORTITEM *lreportItem;
1134 HID_USAGEITEM *lusageItem;
1135 WORD j;
1136 BYTE i;
1137  
1138  
1139 UART2PrintString("\r\n ======================= Report Descriptor Dump ======================= \n\n");
1140  
1141 UART2PrintString("\r\nCollections: ");
1142 UART2PutHex( deviceRptInfo.collections );
1143  
1144 UART2PrintString("\r\nMax Nesting: ");
1145 UART2PutHex( deviceRptInfo.maxCollectionNesting );
1146  
1147 UART2PrintString("\r\nReports: ");
1148 UART2PutHex( deviceRptInfo.reports );
1149  
1150 UART2PrintString("\r\nReportItems: ");
1151 UART2PutHex( deviceRptInfo.reportItems );
1152  
1153 UART2PrintString("\r\nUsageItems: ");
1154 UART2PutHex( deviceRptInfo.usageItems );
1155  
1156 for (i=0;i<deviceRptInfo.collections;i++)
1157 {
1158 UART2PrintString("\n\r------------------------\n");
1159 UART2PrintString("\r\nCollection : ");UART2PutHex(i);
1160 lcollection = &itemListPtrs.collectionList[i];
1161 UART2PrintString("\r\n Data : ");
1162 UART2PutHex( lcollection->data);
1163 UART2PrintString("\r\n Usage Page : ");
1164 UART2PutHex( lcollection->usagePage);
1165 UART2PrintString("\r\n 1st Usage Item : ");
1166 UART2PutHex( lcollection->firstUsageItem);
1167 UART2PrintString("\r\n # Usage Items : ");
1168 UART2PutHex( lcollection->usageItems);
1169 UART2PrintString("\r\n 1st Report Item : ");
1170 UART2PutHex( lcollection->firstReportItem);
1171 UART2PrintString("\r\n # Report Items : ");
1172 UART2PutHex( lcollection->reportItems);
1173 UART2PrintString("\r\n Parent : ");
1174 UART2PutHex( lcollection->parent);
1175 UART2PrintString("\r\n 1st Child : ");
1176 UART2PutHex( lcollection->firstChild);
1177 UART2PrintString("\r\n Next Sibling : ");
1178 UART2PutHex( lcollection->nextSibling);
1179 }
1180  
1181  
1182 for (i=0; i<deviceRptInfo.reports; i++)
1183 {
1184 UART2PrintString("\n------------------------\n");
1185 UART2PrintString("\r\nReport : ");UART2PutHex(i);
1186 lreport = &itemListPtrs.reportList[i];
1187 UART2PrintString("\r\nReportID : ");UART2PutHex(lreport->reportID);
1188 if (lreport->inputBits > 0)
1189 UART2PrintString("\r\nInbits : ");UART2PutHex(lreport->inputBits);
1190 if (lreport->outputBits > 0)
1191 UART2PrintString("\r\nOutbits : ");UART2PutHex(lreport->outputBits);
1192 if (lreport->featureBits > 0)
1193 UART2PrintString("\r\nFeatbits : ");UART2PutHex(lreport->featureBits);
1194 }
1195 for (i=0; i<deviceRptInfo.reportItems; i++) {
1196 // getchar();
1197 UART2PrintString("\n------------------------\n");
1198 UART2PrintString("\r\nReportItem : ");UART2PutHex(i);
1199 lreportItem = &itemListPtrs.reportItemList[i];
1200 UART2PrintString("\r\nReportType : ");UART2PutHex(lreportItem->reportType);
1201 // HIDGlobals globals;
1202 UART2PrintString("\r\nStart Bit : ");UART2PutHex(lreportItem->startBit);
1203 UART2PrintString("\r\nBits : ");UART2PutHex(lreportItem->globals.reportsize);
1204 UART2PrintString("\r\nParent : ");UART2PutHex(lreportItem->parent);
1205 UART2PrintString("\r\nDataModes : ");UART2PutHex(lreportItem->dataModes);
1206 UART2PrintString("\r\n1st Usage : ");UART2PutHex(lreportItem->firstUsageItem);
1207 UART2PrintString("\r\nUsage Items : ");UART2PutHex(lreportItem->usageItems);
1208 UART2PrintString("\r\n Globals ");
1209 UART2PrintString("\r\nusagePage : ");UART2PutHex(lreportItem->globals.usagePage);
1210 UART2PrintString("\r\nreportsize : ");UART2PutHex(lreportItem->globals.reportsize);
1211 UART2PrintString("\r\nreportID : ");UART2PutHex(lreportItem->globals.reportID);
1212 UART2PrintString("\r\nreportCount : ");UART2PutHex(lreportItem->globals.reportCount);
1213  
1214 }
1215  
1216 UART2PrintString("\n------------------------\n");
1217 UART2PrintString("\r\nUsageItem : ");UART2PutHex(i);
1218 for (i=0; i<deviceRptInfo.usageItems; i++)
1219 {
1220  
1221 if (itemListPtrs.usageItemList[i].isRange)
1222 {
1223 UART2PrintString("\r\nUsages Minimum Maximum \n\r");
1224 UART2PrintString("\t\t");
1225 j = itemListPtrs.usageItemList[i].usageMaximum;
1226 UART2PutHex(j);
1227 UART2PrintString("\t");
1228 j = itemListPtrs.usageItemList[i].usageMinimum;
1229 UART2PutHex(j);
1230 }
1231 else
1232 UART2PrintString("\r\nUsage: ");UART2PutHex(itemListPtrs.usageItemList[i].usage);
1233 }
1234 }
1235 #endif
1236  
{BLAME END}
{FOOTER START}

Powered by WebSVN v2.8.3