xref: /reactos/sdk/lib/drivers/hidparser/parser.c (revision 2196a06f)
1 /*
2  * PROJECT:     ReactOS HID Parser Library
3  * LICENSE:     GPL - See COPYING in the top level directory
4  * FILE:        lib/drivers/hidparser/parser.c
5  * PURPOSE:     HID Parser
6  * PROGRAMMERS:
7  *              Michael Martin (michael.martin@reactos.org)
8  *              Johannes Anderwald (johannes.anderwald@reactos.org)
9  */
10 
11 #include "parser.h"
12 
13 #define NDEBUG
14 #include <debug.h>
15 
16 static UCHAR ItemSize[4] = { 0, 1, 2, 4 };
17 
18 VOID
19 HidParser_DeleteReport(
20     IN PHID_REPORT Report)
21 {
22     //
23     // not implemented
24     //
25 }
26 
27 VOID
28 HidParser_FreeCollection(
29     IN PHID_COLLECTION Collection)
30 {
31     //
32     // not implemented
33     //
34 }
35 
36 NTSTATUS
37 HidParser_AllocateCollection(
38     IN PHID_COLLECTION ParentCollection,
39     IN UCHAR Type,
40     IN PLOCAL_ITEM_STATE LocalItemState,
41     OUT PHID_COLLECTION * OutCollection)
42 {
43     PHID_COLLECTION Collection;
44     USAGE_VALUE UsageValue;
45 
46     //
47     // first allocate the collection
48     //
49     Collection = (PHID_COLLECTION)AllocFunction(sizeof(HID_COLLECTION));
50     if (!Collection)
51     {
52         //
53         // no memory
54         //
55         return HIDP_STATUS_INTERNAL_ERROR;
56     }
57 
58     //
59     // init collection
60     //
61     Collection->Root = ParentCollection;
62     Collection->Type = Type;
63     Collection->StringID = LocalItemState->StringIndex;
64     Collection->PhysicalID = LocalItemState->DesignatorIndex;
65 
66     //
67     // set Usage
68     //
69     ASSERT(LocalItemState);
70     ASSERT(LocalItemState->UsageStack);
71 
72     if (LocalItemState->UsageStackUsed > 0)
73     {
74         //
75         // usage value from first local stack item
76         //
77         UsageValue.u.Extended = LocalItemState->UsageStack[0].u.Extended;
78     }
79     else if (LocalItemState->UsageMinimumSet)
80     {
81         //
82         // use value from minimum
83         //
84         UsageValue.u.Extended = LocalItemState->UsageMinimum.u.Extended;
85     }
86     else if (LocalItemState->UsageMaximumSet)
87     {
88         //
89         // use value from maximum
90         //
91         UsageValue.u.Extended = LocalItemState->UsageMaximum.u.Extended;
92     }
93     else if (Type == COLLECTION_LOGICAL)
94     {
95         //
96         // root collection
97         //
98         UsageValue.u.Extended = 0;
99     }
100     else
101     {
102         //
103         // no usage set
104         //
105         DebugFunction("HIDPARSE] No usage set\n");
106         UsageValue.u.Extended = 0;
107     }
108 
109     //
110     // store usage
111     //
112     Collection->Usage = UsageValue.u.Extended;
113 
114     //
115     // store result
116     //
117     *OutCollection = Collection;
118 
119     //
120     // done
121     //
122     return HIDP_STATUS_SUCCESS;
123 }
124 
125 NTSTATUS
126 HidParser_AddCollection(
127     IN PHID_COLLECTION CurrentCollection,
128     IN PHID_COLLECTION NewCollection)
129 {
130     PHID_COLLECTION * NewAllocCollection;
131     ULONG CollectionCount;
132 
133     //
134     // increment collection array
135     //
136     CollectionCount = CurrentCollection->NodeCount + 1;
137 
138     //
139     // allocate new collection
140     //
141     NewAllocCollection = (PHID_COLLECTION*)AllocFunction(sizeof(PHID_COLLECTION) * CollectionCount);
142     if (!NewAllocCollection)
143     {
144         //
145         // no memory
146         //
147         return HIDP_STATUS_INTERNAL_ERROR;
148     }
149 
150     if (CurrentCollection->NodeCount)
151     {
152         //
153         // copy old array
154         //
155         CopyFunction(NewAllocCollection, CurrentCollection->Nodes, CurrentCollection->NodeCount * sizeof(PHID_COLLECTION));
156 
157         //
158         // delete old array
159         //
160         FreeFunction(CurrentCollection->Nodes);
161     }
162 
163     //
164     // insert new item
165     //
166     NewAllocCollection[CurrentCollection->NodeCount] = (struct __HID_COLLECTION__*)NewCollection;
167 
168 
169     //
170     // store new array
171     //
172     CurrentCollection->Nodes = NewAllocCollection;
173     CurrentCollection->NodeCount++;
174 
175     //
176     // done
177     //
178     return HIDP_STATUS_SUCCESS;
179 }
180 
181 NTSTATUS
182 HidParser_FindReportInCollection(
183     IN PHID_COLLECTION Collection,
184     IN UCHAR ReportType,
185     IN UCHAR ReportID,
186     OUT PHID_REPORT *OutReport)
187 {
188     ULONG Index;
189     NTSTATUS Status;
190 
191     //
192     // search in local list
193     //
194     for(Index = 0; Index < Collection->ReportCount; Index++)
195     {
196         if (Collection->Reports[Index]->Type == ReportType && Collection->Reports[Index]->ReportID == ReportID)
197         {
198             //
199             // found report
200             //
201             *OutReport = Collection->Reports[Index];
202             return HIDP_STATUS_SUCCESS;
203         }
204     }
205 
206     //
207     // search in sub collections
208     //
209     for(Index = 0; Index < Collection->NodeCount; Index++)
210     {
211         Status = HidParser_FindReportInCollection(Collection->Nodes[Index], ReportType, ReportID, OutReport);
212         if (Status == HIDP_STATUS_SUCCESS)
213             return Status;
214     }
215 
216     //
217     // no such report found
218     //
219     *OutReport = NULL;
220     return HIDP_STATUS_REPORT_DOES_NOT_EXIST;
221 }
222 
223 
224 NTSTATUS
225 HidParser_FindReport(
226     IN PHID_PARSER_CONTEXT ParserContext,
227     IN UCHAR ReportType,
228     IN UCHAR ReportID,
229     OUT PHID_REPORT *OutReport)
230 {
231     //
232     // search in current top level collection
233     //
234     return HidParser_FindReportInCollection(ParserContext->RootCollection->Nodes[ParserContext->RootCollection->NodeCount-1], ReportType, ReportID, OutReport);
235 }
236 
237 NTSTATUS
238 HidParser_AllocateReport(
239     IN UCHAR ReportType,
240     IN UCHAR ReportID,
241     OUT PHID_REPORT *OutReport)
242 {
243     PHID_REPORT Report;
244 
245     //
246     // allocate report
247     //
248     Report = (PHID_REPORT)AllocFunction(sizeof(HID_REPORT));
249     if (!Report)
250     {
251         //
252         // no memory
253         //
254         return HIDP_STATUS_INTERNAL_ERROR;
255     }
256 
257     //
258     // init report
259     //
260     Report->ReportID = ReportID;
261     Report->Type = ReportType;
262 
263     //
264     // done
265     //
266     *OutReport = Report;
267     return HIDP_STATUS_SUCCESS;
268 }
269 
270 NTSTATUS
271 HidParser_AddReportToCollection(
272     IN PHID_PARSER_CONTEXT ParserContext,
273     IN PHID_COLLECTION CurrentCollection,
274     IN PHID_REPORT NewReport)
275 {
276     PHID_REPORT * NewReportArray;
277 
278     //
279     // allocate new report array
280     //
281     NewReportArray = (PHID_REPORT*)AllocFunction(sizeof(PHID_REPORT) * (CurrentCollection->ReportCount + 1));
282     if (!NewReportArray)
283     {
284         //
285         // no memory
286         //
287         return HIDP_STATUS_INTERNAL_ERROR;
288     }
289 
290     if (CurrentCollection->ReportCount)
291     {
292         //
293         // copy old array contents
294         //
295         CopyFunction(NewReportArray, CurrentCollection->Reports, sizeof(PHID_REPORT) * CurrentCollection->ReportCount);
296 
297         //
298         // free old array
299         //
300         FreeFunction(CurrentCollection->Reports);
301     }
302 
303     //
304     // store result
305     //
306     NewReportArray[CurrentCollection->ReportCount] = NewReport;
307     CurrentCollection->Reports = NewReportArray;
308     CurrentCollection->ReportCount++;
309 
310     //
311     // completed successfully
312     //
313     return HIDP_STATUS_SUCCESS;
314 }
315 
316 NTSTATUS
317 HidParser_GetReport(
318     IN PHID_PARSER_CONTEXT ParserContext,
319     IN PHID_COLLECTION Collection,
320     IN UCHAR ReportType,
321     IN UCHAR ReportID,
322     IN UCHAR CreateIfNotExists,
323     OUT PHID_REPORT *OutReport)
324 {
325     NTSTATUS Status;
326 
327     //
328     // try finding existing report
329     //
330     Status = HidParser_FindReport(ParserContext, ReportType, ReportID, OutReport);
331     if (Status == HIDP_STATUS_SUCCESS || CreateIfNotExists == FALSE)
332     {
333         //
334         // founed report
335         //
336         return Status;
337     }
338 
339     //
340     // allocate new report
341     //
342     Status = HidParser_AllocateReport(ReportType, ReportID, OutReport);
343     if (Status != HIDP_STATUS_SUCCESS)
344     {
345         //
346         // failed to allocate report
347         //
348         return Status;
349     }
350 
351     //
352     // add report
353     //
354     Status = HidParser_AddReportToCollection(ParserContext, Collection, *OutReport);
355     if (Status != HIDP_STATUS_SUCCESS)
356     {
357         //
358         // failed to allocate report
359         //
360         FreeFunction(*OutReport);
361     }
362 
363     //
364     // done
365     //
366     return Status;
367 }
368 
369 NTSTATUS
370 HidParser_ReserveReportItems(
371     IN PHID_REPORT Report,
372     IN ULONG ReportCount,
373     OUT PHID_REPORT *OutReport)
374 {
375     PHID_REPORT NewReport;
376     ULONG OldSize, Size;
377 
378     if (Report->ItemCount + ReportCount <= Report->ItemAllocated)
379     {
380         //
381         // space is already allocated
382         //
383         *OutReport = Report;
384         return HIDP_STATUS_SUCCESS;
385     }
386 
387     //
388     //calculate new size
389     //
390     OldSize = sizeof(HID_REPORT) + (Report->ItemCount) * sizeof(HID_REPORT_ITEM);
391     Size =  ReportCount * sizeof(HID_REPORT_ITEM);
392 
393     //
394     // allocate memory
395     //
396     NewReport = (PHID_REPORT)AllocFunction(Size + OldSize);
397     if (!NewReport)
398     {
399         //
400         // no memory
401         //
402         return HIDP_STATUS_INTERNAL_ERROR;
403     }
404 
405 
406     //
407     // copy old report
408     //
409     CopyFunction(NewReport, Report, OldSize);
410 
411     //
412     // increase array size
413     //
414     NewReport->ItemAllocated += ReportCount;
415 
416     //
417     // store result
418     //
419     *OutReport = NewReport;
420 
421     //
422     // completed sucessfully
423     //
424     return HIDP_STATUS_SUCCESS;
425 }
426 
427 VOID
428 HidParser_SignRange(
429     IN ULONG Minimum,
430     IN ULONG Maximum,
431     OUT PULONG NewMinimum,
432     OUT PULONG NewMaximum)
433 {
434     ULONG Mask = 0x80000000;
435     ULONG Index;
436 
437     for (Index = 0; Index < 4; Index++)
438     {
439         if (Minimum & Mask)
440         {
441             Minimum |= Mask;
442             if (Maximum & Mask)
443                 Maximum |= Mask;
444             return;
445         }
446 
447         Mask >>= 8;
448         Mask |= 0xff000000;
449     }
450 
451     *NewMinimum = Minimum;
452     *NewMaximum = Maximum;
453 }
454 
455 NTSTATUS
456 HidParser_InitReportItem(
457     IN PHID_REPORT Report,
458     IN PHID_REPORT_ITEM ReportItem,
459     IN PGLOBAL_ITEM_STATE GlobalItemState,
460     IN PLOCAL_ITEM_STATE LocalItemState,
461     IN PMAIN_ITEM_DATA ItemData,
462     IN ULONG ReportItemIndex)
463 {
464     ULONG LogicalMinimum;
465     ULONG LogicalMaximum;
466     ULONG PhysicalMinimum;
467     ULONG PhysicalMaximum;
468     ULONG UsageMinimum;
469     ULONG UsageMaximum;
470     USAGE_VALUE UsageValue;
471 
472     //
473     // get logical bounds
474     //
475     LogicalMinimum = GlobalItemState->LogicalMinimum;
476     LogicalMaximum = GlobalItemState->LogicialMaximum;
477     if (LogicalMinimum > LogicalMaximum)
478     {
479         //
480         // make them signed
481         //
482         HidParser_SignRange(LogicalMinimum, LogicalMaximum, &LogicalMinimum, &LogicalMaximum);
483     }
484     //ASSERT(LogicalMinimum <= LogicalMaximum);
485 
486     //
487     // get physical bounds
488     //
489     PhysicalMinimum = GlobalItemState->PhysicalMinimum;
490     PhysicalMaximum = GlobalItemState->PhysicalMaximum;
491     if (PhysicalMinimum > PhysicalMaximum)
492     {
493         //
494         // make them signed
495         //
496         HidParser_SignRange(PhysicalMinimum, PhysicalMaximum, &PhysicalMinimum, &PhysicalMaximum);
497     }
498     //ASSERT(PhysicalMinimum <= PhysicalMaximum);
499 
500     //
501     // get usage bounds
502     //
503     UsageMinimum = 0;
504     UsageMaximum = 0;
505     if (ItemData->ArrayVariable == FALSE)
506     {
507         //
508         // get usage bounds
509         //
510         UsageMinimum = LocalItemState->UsageMinimum.u.Extended;
511         UsageMaximum = LocalItemState->UsageMaximum.u.Extended;
512     }
513     else
514     {
515         //
516         // get usage value from stack
517         //
518         if (ReportItemIndex < LocalItemState->UsageStackUsed)
519         {
520             //
521             // use stack item
522             //
523             UsageValue = LocalItemState->UsageStack[ReportItemIndex];
524         }
525         else
526         {
527             //
528             // get usage minimum from local state
529             //
530             UsageValue = LocalItemState->UsageMinimum;
531 
532             //
533             // append item index
534             //
535             UsageValue.u.Extended += ReportItemIndex;
536 
537             if (LocalItemState->UsageMaximumSet)
538             {
539                 if (UsageValue.u.Extended > LocalItemState->UsageMaximum.u.Extended)
540                 {
541                     //
542                     // maximum reached
543                     //
544                     UsageValue.u.Extended = LocalItemState->UsageMaximum.u.Extended;
545                 }
546             }
547         }
548 
549         //
550         // usage usage bounds
551         //
552         UsageMinimum = UsageMaximum = UsageValue.u.Extended;
553     }
554 
555     //
556     // now store all values
557     //
558     ReportItem->ByteOffset = (Report->ReportSize / 8);
559     ReportItem->Shift = (Report->ReportSize % 8);
560     ReportItem->Mask = ~(0xFFFFFFFF << GlobalItemState->ReportSize);
561     ReportItem->BitCount = GlobalItemState->ReportSize;
562     ReportItem->HasData = (ItemData->DataConstant == FALSE);
563     ReportItem->Array = (ItemData->ArrayVariable == 0);
564     ReportItem->Relative = (ItemData->Relative != FALSE);
565     ReportItem->Minimum = LogicalMinimum;
566     ReportItem->Maximum = LogicalMaximum;
567     ReportItem->UsageMinimum = UsageMinimum;
568     ReportItem->UsageMaximum = UsageMaximum;
569 
570     //
571     // increment report size
572     //
573     Report->ReportSize += GlobalItemState->ReportSize;
574 
575     //
576     // completed successfully
577     //
578     return HIDP_STATUS_SUCCESS;
579 }
580 
581 BOOLEAN
582 HidParser_UpdateCurrentCollectionReport(
583     IN PHID_COLLECTION Collection,
584     IN PHID_REPORT Report,
585     IN PHID_REPORT NewReport)
586 {
587     ULONG Index;
588     BOOLEAN Found = FALSE, TempFound;
589 
590     //
591     // search in local list
592     //
593     for(Index = 0; Index < Collection->ReportCount; Index++)
594     {
595         if (Collection->Reports[Index] == Report)
596         {
597             //
598             // update report
599             //
600             Collection->Reports[Index] = NewReport;
601             Found = TRUE;
602         }
603     }
604 
605     //
606     // search in sub collections
607     //
608     for(Index = 0; Index < Collection->NodeCount; Index++)
609     {
610         //
611         // was it found
612         //
613         TempFound = HidParser_UpdateCurrentCollectionReport(Collection->Nodes[Index], Report, NewReport);
614         if (TempFound)
615         {
616             //
617             // the same report should not be found in different collections
618             //
619             ASSERT(Found == FALSE);
620             Found = TRUE;
621         }
622     }
623 
624     //
625     // done
626     //
627     return Found;
628 }
629 
630 BOOLEAN
631 HidParser_UpdateCollectionReport(
632     IN PHID_PARSER_CONTEXT ParserContext,
633     IN PHID_REPORT Report,
634     IN PHID_REPORT NewReport)
635 {
636     //
637     // update in current collection
638     //
639     return HidParser_UpdateCurrentCollectionReport(ParserContext->RootCollection->Nodes[ParserContext->RootCollection->NodeCount-1], Report, NewReport);
640 }
641 
642 
643 NTSTATUS
644 HidParser_AddMainItem(
645     IN PHID_PARSER_CONTEXT ParserContext,
646     IN PHID_REPORT Report,
647     IN PGLOBAL_ITEM_STATE GlobalItemState,
648     IN PLOCAL_ITEM_STATE LocalItemState,
649     IN PMAIN_ITEM_DATA ItemData,
650     IN PHID_COLLECTION Collection)
651 {
652     NTSTATUS Status;
653     ULONG Index;
654     PHID_REPORT NewReport;
655     BOOLEAN Found;
656 
657     //
658     // first grow report item array
659     //
660     Status = HidParser_ReserveReportItems(Report, GlobalItemState->ReportCount, &NewReport);
661     if (Status != HIDP_STATUS_SUCCESS)
662     {
663         //
664         // failed to allocate memory
665         //
666         return Status;
667     }
668 
669     if (NewReport != Report)
670     {
671         //
672         // update current top level collection
673         //
674         Found = HidParser_UpdateCollectionReport(ParserContext, Report, NewReport);
675         ASSERT(Found);
676     }
677 
678     //
679     // sanity check
680     //
681     ASSERT(NewReport->ItemCount + GlobalItemState->ReportCount <= NewReport->ItemAllocated);
682 
683     for(Index = 0; Index < GlobalItemState->ReportCount; Index++)
684     {
685         Status = HidParser_InitReportItem(NewReport, &NewReport->Items[NewReport->ItemCount], GlobalItemState, LocalItemState, ItemData, Index);
686         if (Status != HIDP_STATUS_SUCCESS)
687         {
688             //
689             // failed to init report item
690             //
691             return Status;
692         }
693 
694         //
695         // increment report item count
696         //
697         NewReport->ItemCount++;
698     }
699 
700     //
701     // done
702     //
703     return HIDP_STATUS_SUCCESS;
704 }
705 
706 NTSTATUS
707 HidParser_ParseReportDescriptor(
708     IN PUCHAR ReportDescriptor,
709     IN ULONG ReportLength,
710     OUT PVOID *OutParser)
711 {
712     PGLOBAL_ITEM_STATE LinkedGlobalItemState, NextLinkedGlobalItemState;
713     ULONG Index;
714     PUSAGE_VALUE NewUsageStack, UsageValue;
715     NTSTATUS Status;
716     PHID_COLLECTION CurrentCollection, NewCollection;
717     PUCHAR CurrentOffset, ReportEnd;
718     PITEM_PREFIX CurrentItem;
719     ULONG CurrentItemSize;
720     PLONG_ITEM CurrentLongItem;
721     PSHORT_ITEM CurrentShortItem;
722     ULONG Data;
723     UCHAR ReportType;
724     PHID_REPORT Report;
725     PMAIN_ITEM_DATA MainItemData;
726     PHID_PARSER_CONTEXT ParserContext;
727 
728     CurrentOffset = ReportDescriptor;
729     ReportEnd = ReportDescriptor + ReportLength;
730 
731     if (ReportDescriptor >= ReportEnd)
732         return HIDP_STATUS_USAGE_NOT_FOUND;
733 
734     //
735     // allocate parser
736     //
737     ParserContext = AllocFunction(sizeof(HID_PARSER_CONTEXT));
738     if (!ParserContext)
739         return HIDP_STATUS_INTERNAL_ERROR;
740 
741 
742     //
743     // allocate usage stack
744     //
745     ParserContext->LocalItemState.UsageStackAllocated = 10;
746     ParserContext->LocalItemState.UsageStack = (PUSAGE_VALUE)AllocFunction(ParserContext->LocalItemState.UsageStackAllocated * sizeof(USAGE_VALUE));
747     if (!ParserContext->LocalItemState.UsageStack)
748     {
749         //
750         // no memory
751         //
752         FreeFunction(ParserContext);
753         return HIDP_STATUS_INTERNAL_ERROR;
754     }
755 
756     //
757     // now allocate root collection
758     //
759     Status = HidParser_AllocateCollection(NULL, COLLECTION_LOGICAL, &ParserContext->LocalItemState, &ParserContext->RootCollection);
760     if (Status != HIDP_STATUS_SUCCESS)
761     {
762         //
763         // no memory
764         //
765         FreeFunction(ParserContext->LocalItemState.UsageStack);
766         ParserContext->LocalItemState.UsageStack = NULL;
767         FreeFunction(ParserContext);
768         return HIDP_STATUS_INTERNAL_ERROR;
769     }
770 
771     //
772     // start parsing
773     //
774     CurrentCollection = ParserContext->RootCollection;
775 
776     do
777     {
778         //
779         // get current item
780         //
781         CurrentItem = (PITEM_PREFIX)CurrentOffset;
782 
783         //
784         // get item size
785         //
786         CurrentItemSize = ItemSize[CurrentItem->Size];
787         Data = 0;
788 
789         if (CurrentItem->Type == ITEM_TYPE_LONG)
790         {
791             //
792             // increment item size with size of data item
793             //
794             CurrentLongItem = (PLONG_ITEM)CurrentItem;
795             CurrentItemSize += CurrentLongItem->DataSize;
796         }
797         else
798         {
799             //
800             // get short item
801             //
802             CurrentShortItem = (PSHORT_ITEM)CurrentItem;
803 
804             //
805             // get associated data
806             //
807             //ASSERT(CurrentItemSize == 1 || CurrentItemSize == 2 || CurrentItemSize == 4);
808             if (CurrentItemSize == 1)
809                 Data = CurrentShortItem->Data.UData8[0];
810             else if (CurrentItemSize == 2)
811                 Data = CurrentShortItem->Data.UData16[0];
812             else if (CurrentItemSize == 4)
813                 Data = CurrentShortItem->Data.UData32;
814             else
815             {
816                 //
817                 // invalid item size
818                 //
819                 //DebugFunction("CurrentItem invalid item size %lu\n", CurrentItemSize);
820             }
821 
822         }
823         DebugFunction("Tag %x Type %x Size %x Offset %lu Length %lu\n", CurrentItem->Tag, CurrentItem->Type, CurrentItem->Size,  ((ULONG_PTR)CurrentItem - (ULONG_PTR)ReportDescriptor), ReportLength);
824         //
825         // handle items
826         //
827         ASSERT(CurrentItem->Type >= ITEM_TYPE_MAIN && CurrentItem->Type <= ITEM_TYPE_LONG);
828         switch(CurrentItem->Type)
829         {
830             case ITEM_TYPE_MAIN:
831             {
832                 // preprocess the local state if relevant (usages for
833                 // collections and report items)
834                 if (CurrentItem->Tag != ITEM_TAG_MAIN_END_COLLECTION)
835                 {
836                     // make all usages extended for easier later processing
837                     for (Index = 0; Index < ParserContext->LocalItemState.UsageStackUsed; Index++)
838                     {
839                         //
840                         // is it already extended
841                         //
842                         if (ParserContext->LocalItemState.UsageStack[Index].IsExtended)
843                             continue;
844 
845                         //
846                         // extend usage item
847                         //
848                         ParserContext->LocalItemState.UsageStack[Index].u.s.UsagePage = ParserContext->GlobalItemState.UsagePage;
849                         ParserContext->LocalItemState.UsageStack[Index].IsExtended = TRUE;
850                     }
851 
852                     if (!ParserContext->LocalItemState.UsageMinimum.IsExtended) {
853                         // the specs say if one of them is extended they must
854                         // both be extended, so if the minimum isn't, the
855                         // maximum mustn't either.
856                         ParserContext->LocalItemState.UsageMinimum.u.s.UsagePage
857                             = ParserContext->LocalItemState.UsageMaximum.u.s.UsagePage
858                                 = ParserContext->GlobalItemState.UsagePage;
859                         ParserContext->LocalItemState.UsageMinimum.IsExtended
860                             = ParserContext->LocalItemState.UsageMaximum.IsExtended = TRUE;
861                     }
862 
863                     //LocalItemState.usage_stack = usageStack;
864                     //ParserContext->LocalItemState.UsageStackUsed = UsageStackUsed;
865                 }
866 
867                 if (CurrentItem->Tag == ITEM_TAG_MAIN_COLLECTION) {
868 
869                     //
870                     // allocate new collection
871                     //
872                     Status = HidParser_AllocateCollection(CurrentCollection, (UCHAR)Data, &ParserContext->LocalItemState, &NewCollection);
873                     ASSERT(Status == HIDP_STATUS_SUCCESS);
874 
875                     //
876                     // add new collection to current collection
877                     //
878                     Status = HidParser_AddCollection(CurrentCollection, NewCollection);
879                     ASSERT(Status == HIDP_STATUS_SUCCESS);
880 
881                     //
882                     // make new collection current
883                     //
884                     CurrentCollection = NewCollection;
885                 }
886                 else if (CurrentItem->Tag == ITEM_TAG_MAIN_END_COLLECTION)
887                 {
888                     //
889                     // assert on ending the root collection
890                     //
891                     ASSERT(CurrentCollection != ParserContext->RootCollection);
892 
893                     //
894                     // use parent of current collection
895                     //
896                     CurrentCollection = CurrentCollection->Root;
897                     ASSERT(CurrentCollection);
898                 }
899                 else
900                 {
901                     ReportType = HID_REPORT_TYPE_ANY;
902 
903                     switch (CurrentItem->Tag) {
904                         case ITEM_TAG_MAIN_INPUT:
905                             ReportType = HID_REPORT_TYPE_INPUT;
906                             break;
907 
908                         case ITEM_TAG_MAIN_OUTPUT:
909                             ReportType = HID_REPORT_TYPE_OUTPUT;
910                             break;
911 
912                         case ITEM_TAG_MAIN_FEATURE:
913                             ReportType = HID_REPORT_TYPE_FEATURE;
914                             break;
915 
916                         default:
917                             DebugFunction("[HIDPARSE] Unknown ReportType Tag %x Type %x Size %x CurrentItemSize %x\n", CurrentItem->Tag, CurrentItem->Type, CurrentItem->Size, CurrentItemSize);
918                             ASSERT(FALSE);
919                             break;
920                     }
921 
922                     if (ReportType == HID_REPORT_TYPE_ANY)
923                         break;
924 
925                     //
926                     // get report
927                     //
928                     Status = HidParser_GetReport(ParserContext, CurrentCollection, ReportType, ParserContext->GlobalItemState.ReportId, TRUE, &Report);
929                     ASSERT(Status == HIDP_STATUS_SUCCESS);
930 
931                     // fill in a sensible default if the index isn't set
932                     if (!ParserContext->LocalItemState.DesignatorIndexSet) {
933                         ParserContext->LocalItemState.DesignatorIndex
934                             = ParserContext->LocalItemState.DesignatorMinimum;
935                     }
936 
937                     if (!ParserContext->LocalItemState.StringIndexSet)
938                         ParserContext->LocalItemState.StringIndex = ParserContext->LocalItemState.StringMinimum;
939 
940                     //
941                     // get main item data
942                     //
943                     MainItemData = (PMAIN_ITEM_DATA)&Data;
944 
945                     //
946                     // add states & data to the report
947                     //
948                     Status = HidParser_AddMainItem(ParserContext, Report, &ParserContext->GlobalItemState, &ParserContext->LocalItemState, MainItemData, CurrentCollection);
949                     ASSERT(Status == HIDP_STATUS_SUCCESS);
950                 }
951 
952                 //
953                 // backup stack
954                 //
955                 Index = ParserContext->LocalItemState.UsageStackAllocated;
956                 NewUsageStack = ParserContext->LocalItemState.UsageStack;
957 
958                 //
959                 // reset the local item state and clear the usage stack
960                 //
961                 ZeroFunction(&ParserContext->LocalItemState, sizeof(LOCAL_ITEM_STATE));
962 
963                 //
964                 // restore stack
965                 //
966                 ParserContext->LocalItemState.UsageStack = NewUsageStack;
967                 ParserContext->LocalItemState.UsageStackAllocated = Index;
968                 break;
969             }
970             case ITEM_TYPE_GLOBAL:
971             {
972                 switch (CurrentItem->Tag) {
973                     case ITEM_TAG_GLOBAL_USAGE_PAGE:
974                         DebugFunction("[HIDPARSE] ITEM_TAG_GLOBAL_USAGE_PAGE %x\n", Data);
975                         ParserContext->GlobalItemState.UsagePage = Data;
976                         break;
977                     case ITEM_TAG_GLOBAL_LOGICAL_MINIMUM:
978                         DebugFunction("[HIDPARSE] ITEM_TAG_GLOBAL_LOGICAL_MINIMUM %x\n", Data);
979                         ParserContext->GlobalItemState.LogicalMinimum = Data;
980                         break;
981 
982                     case ITEM_TAG_GLOBAL_LOGICAL_MAXIMUM:
983                         DebugFunction("[HIDPARSE] ITEM_TAG_GLOBAL_LOCAL_MAXIMUM %x\n", Data);
984                         ParserContext->GlobalItemState.LogicialMaximum = Data;
985                         break;
986 
987                     case ITEM_TAG_GLOBAL_PHYSICAL_MINIMUM:
988                         DebugFunction("[HIDPARSE] ITEM_TAG_GLOBAL_PHYSICAL_MINIMUM %x\n", Data);
989                         ParserContext->GlobalItemState.PhysicalMinimum = Data;
990                         break;
991 
992                     case ITEM_TAG_GLOBAL_PHYSICAL_MAXIMUM:
993                         DebugFunction("[HIDPARSE] ITEM_TAG_GLOBAL_PHYSICAL_MAXIMUM %x\n", Data);
994                         ParserContext->GlobalItemState.PhysicalMaximum = Data;
995                         break;
996 
997                     case ITEM_TAG_GLOBAL_UNIT_EXPONENT:
998                         DebugFunction("[HIDPARSE] ITEM_TAG_GLOBAL_REPORT_UNIT_EXPONENT %x\n", Data);
999                         ParserContext->GlobalItemState.UnitExponent = Data;
1000                         break;
1001 
1002                     case ITEM_TAG_GLOBAL_UNIT:
1003                         DebugFunction("[HIDPARSE] ITEM_TAG_GLOBAL_REPORT_UNIT %x\n", Data);
1004                         ParserContext->GlobalItemState.Unit = Data;
1005                         break;
1006 
1007                     case ITEM_TAG_GLOBAL_REPORT_SIZE:
1008                         DebugFunction("[HIDPARSE] ITEM_TAG_GLOBAL_REPORT_SIZE %x\n", Data);
1009                         ParserContext->GlobalItemState.ReportSize = Data;
1010                         break;
1011 
1012                     case ITEM_TAG_GLOBAL_REPORT_ID:
1013                         DebugFunction("[HIDPARSE] ITEM_TAG_GLOBAL_REPORT_ID %x\n", Data);
1014                         ParserContext->GlobalItemState.ReportId = Data;
1015                         ParserContext->UseReportIDs = TRUE;
1016                         break;
1017 
1018                     case ITEM_TAG_GLOBAL_REPORT_COUNT:
1019                         DebugFunction("[HIDPARSE] ITEM_TAG_GLOBAL_REPORT_COUNT %x\n", Data);
1020                         ParserContext->GlobalItemState.ReportCount = Data;
1021                         break;
1022 
1023                     case ITEM_TAG_GLOBAL_PUSH:
1024                     {
1025                         DebugFunction("[HIDPARSE] ITEM_TAG_GLOBAL_PUSH\n");
1026                         //
1027                         // allocate global item state
1028                         //
1029                         LinkedGlobalItemState = (PGLOBAL_ITEM_STATE)AllocFunction(sizeof(GLOBAL_ITEM_STATE));
1030                         ASSERT(LinkedGlobalItemState);
1031 
1032                         //
1033                         // copy global item state
1034                         //
1035                         CopyFunction(LinkedGlobalItemState, &ParserContext->GlobalItemState, sizeof(GLOBAL_ITEM_STATE));
1036 
1037                         //
1038                         // store pushed item in link member
1039                         //
1040                         ParserContext->GlobalItemState.Next = (struct __GLOBAL_ITEM_STATE__*)LinkedGlobalItemState;
1041                         break;
1042                     }
1043                     case ITEM_TAG_GLOBAL_POP:
1044                     {
1045                         DebugFunction("[HIDPARSE] ITEM_TAG_GLOBAL_POP\n");
1046                         if (ParserContext->GlobalItemState.Next == NULL)
1047                         {
1048                             //
1049                             // pop without push
1050                             //
1051                             ASSERT(FALSE);
1052                             break;
1053                         }
1054 
1055                         //
1056                         // get link
1057                         //
1058                         LinkedGlobalItemState = (PGLOBAL_ITEM_STATE)ParserContext->GlobalItemState.Next;
1059 
1060                         //
1061                         // replace current item with linked one
1062                         //
1063                         CopyFunction(&ParserContext->GlobalItemState, LinkedGlobalItemState, sizeof(GLOBAL_ITEM_STATE));
1064 
1065                         //
1066                         // free item
1067                         //
1068                         FreeFunction(LinkedGlobalItemState);
1069                         break;
1070                     }
1071 
1072                     default:
1073                         //
1074                         // unknown  / unsupported tag
1075                         //
1076                         ASSERT(FALSE);
1077                         break;
1078                 }
1079 
1080                 break;
1081             }
1082             case ITEM_TYPE_LOCAL:
1083             {
1084                 switch (CurrentItem->Tag)
1085                 {
1086                     case ITEM_TAG_LOCAL_USAGE:
1087                     {
1088                         if (ParserContext->LocalItemState.UsageStackUsed >= ParserContext->LocalItemState.UsageStackAllocated)
1089                         {
1090                             //
1091                             // increment stack size
1092                             //
1093                             ParserContext->LocalItemState.UsageStackAllocated += 10;
1094 
1095                             //
1096                             // build new usage stack
1097                             //
1098                             NewUsageStack = (PUSAGE_VALUE)AllocFunction(sizeof(USAGE_VALUE) * ParserContext->LocalItemState.UsageStackAllocated);
1099                             ASSERT(NewUsageStack);
1100 
1101                             //
1102                             // copy old usage stack
1103                             //
1104                             CopyFunction(NewUsageStack, ParserContext->LocalItemState.UsageStack, sizeof(USAGE_VALUE) * (ParserContext->LocalItemState.UsageStackAllocated - 10));
1105 
1106                             //
1107                             // free old usage stack
1108                             //
1109                             FreeFunction(ParserContext->LocalItemState.UsageStack);
1110 
1111                             //
1112                             // replace with new usage stack
1113                             //
1114                             ParserContext->LocalItemState.UsageStack = NewUsageStack;
1115                         }
1116 
1117                         //
1118                         // get fresh usage value
1119                         //
1120                         UsageValue = &ParserContext->LocalItemState.UsageStack[ParserContext->LocalItemState.UsageStackUsed];
1121 
1122                         //
1123                         // init usage stack
1124                         //
1125                         UsageValue->IsExtended = CurrentItemSize == sizeof(ULONG);
1126                         UsageValue->u.Extended = Data;
1127 
1128                         //
1129                         // increment usage stack usage count
1130                         //
1131                         ParserContext->LocalItemState.UsageStackUsed++;
1132                         break;
1133                     }
1134 
1135                     case ITEM_TAG_LOCAL_USAGE_MINIMUM:
1136                         DebugFunction("[HIDPARSE] ITEM_TAG_LOCAL_USAGE_MINIMUM Data %x\n", Data);
1137                         ParserContext->LocalItemState.UsageMinimum.u.Extended = Data;
1138                         ParserContext->LocalItemState.UsageMinimum.IsExtended
1139                             = CurrentItemSize == sizeof(ULONG);
1140                         ParserContext->LocalItemState.UsageMinimumSet = TRUE;
1141                         break;
1142 
1143                     case ITEM_TAG_LOCAL_USAGE_MAXIMUM:
1144                         DebugFunction("[HIDPARSE] ITEM_TAG_LOCAL_USAGE_MAXIMUM Data %x ItemSize %x %x\n", Data, CurrentItemSize, CurrentItem->Size);
1145                         ParserContext->LocalItemState.UsageMaximum.u.Extended = Data;
1146                         ParserContext->LocalItemState.UsageMaximum.IsExtended
1147                             = CurrentItemSize == sizeof(ULONG);
1148                         ParserContext->LocalItemState.UsageMaximumSet = TRUE;
1149                         break;
1150 
1151                     case ITEM_TAG_LOCAL_DESIGNATOR_INDEX:
1152                         DebugFunction("[HIDPARSE] ITEM_TAG_LOCAL_DESIGNATOR_INDEX Data %x\n", Data);
1153                         ParserContext->LocalItemState.DesignatorIndex = Data;
1154                         ParserContext->LocalItemState.DesignatorIndexSet = TRUE;
1155                         break;
1156 
1157                     case ITEM_TAG_LOCAL_DESIGNATOR_MINIMUM:
1158                         DebugFunction("[HIDPARSE] ITEM_TAG_LOCAL_DESIGNATOR_MINIMUM Data %x\n", Data);
1159                         ParserContext->LocalItemState.DesignatorMinimum = Data;
1160                         break;
1161 
1162                     case ITEM_TAG_LOCAL_DESIGNATOR_MAXIMUM:
1163                         DebugFunction("[HIDPARSE] ITEM_TAG_LOCAL_DESIGNATOR_MAXIMUM Data %x\n", Data);
1164                         ParserContext->LocalItemState.DesignatorMaximum = Data;
1165                         break;
1166 
1167                     case ITEM_TAG_LOCAL_STRING_INDEX:
1168                         DebugFunction("[HIDPARSE] ITEM_TAG_LOCAL_STRING_INDEX Data %x\n", Data);
1169                         ParserContext->LocalItemState.StringIndex = Data;
1170                         ParserContext->LocalItemState.StringIndexSet = TRUE;
1171                         break;
1172 
1173                     case ITEM_TAG_LOCAL_STRING_MINIMUM:
1174                         DebugFunction("[HIDPARSE] ITEM_TAG_LOCAL_STRING_MINIMUM Data %x\n", Data);
1175                         ParserContext->LocalItemState.StringMinimum = Data;
1176                         break;
1177 
1178                     case ITEM_TAG_LOCAL_STRING_MAXIMUM:
1179                         DebugFunction("[HIDPARSE] ITEM_TAG_LOCAL_STRING_MAXIMUM Data %x\n", Data);
1180                         ParserContext->LocalItemState.StringMaximum = Data;
1181                         break;
1182 
1183                     default:
1184                         DebugFunction("Unknown Local Item Tag %x\n", CurrentItem->Tag);
1185                         ASSERT(FALSE);
1186                         break;
1187                 }
1188                 break;
1189             }
1190 
1191             case ITEM_TYPE_LONG:
1192             {
1193                 CurrentLongItem = (PLONG_ITEM)CurrentItem;
1194                 DebugFunction("Unsupported ITEM_TYPE_LONG Tag %x\n", CurrentLongItem->LongItemTag);
1195                 break;
1196             }
1197         }
1198 
1199         //
1200         // move to next item
1201         //
1202         CurrentOffset += CurrentItemSize + sizeof(ITEM_PREFIX);
1203 
1204     }while (CurrentOffset < ReportEnd);
1205 
1206 
1207     //
1208     // cleanup global stack
1209     //
1210     LinkedGlobalItemState = (PGLOBAL_ITEM_STATE)ParserContext->GlobalItemState.Next;
1211     while(LinkedGlobalItemState != NULL)
1212     {
1213         DebugFunction("[HIDPARSE] Freeing GlobalState %p\n", LinkedGlobalItemState);
1214         //
1215         // free global item state
1216         //
1217         NextLinkedGlobalItemState = (PGLOBAL_ITEM_STATE)LinkedGlobalItemState->Next;
1218 
1219         //
1220         // free state
1221         //
1222         FreeFunction(LinkedGlobalItemState);
1223 
1224         //
1225         // move to next global state
1226         //
1227         LinkedGlobalItemState = NextLinkedGlobalItemState;
1228     }
1229 
1230     //
1231     // free usage stack
1232     //
1233     FreeFunction(ParserContext->LocalItemState.UsageStack);
1234     ParserContext->LocalItemState.UsageStack = NULL;
1235 
1236     //
1237     // store result
1238     //
1239     *OutParser = ParserContext;
1240 
1241     //
1242     // done
1243     //
1244     return HIDP_STATUS_SUCCESS;
1245 }
1246 
1247 PHID_COLLECTION
1248 HidParser_GetCollection(
1249     PHID_PARSER_CONTEXT ParserContext,
1250     IN ULONG CollectionNumber)
1251 {
1252     //
1253     // sanity checks
1254     //
1255     ASSERT(ParserContext);
1256     ASSERT(ParserContext->RootCollection);
1257     ASSERT(ParserContext->RootCollection->NodeCount);
1258 
1259     //
1260     // is collection index out of bounds
1261     //
1262     if (CollectionNumber < ParserContext->RootCollection->NodeCount)
1263     {
1264         //
1265         // valid collection
1266         //
1267         return ParserContext->RootCollection->Nodes[CollectionNumber];
1268     }
1269 
1270     //
1271     // no such collection
1272     //
1273     DebugFunction("HIDPARSE] No such collection %lu\n", CollectionNumber);
1274     return NULL;
1275 }
1276 
1277 
1278 ULONG
1279 HidParser_NumberOfTopCollections(
1280     IN PVOID ParserCtx)
1281 {
1282     PHID_PARSER_CONTEXT ParserContext;
1283 
1284     //
1285     // get parser context
1286     //
1287     ParserContext = (PHID_PARSER_CONTEXT)ParserCtx;
1288 
1289     //
1290     // sanity checks
1291     //
1292     ASSERT(ParserContext);
1293     ASSERT(ParserContext->RootCollection);
1294     ASSERT(ParserContext->RootCollection->NodeCount);
1295 
1296     //
1297     // number of top collections
1298     //
1299     return ParserContext->RootCollection->NodeCount;
1300 }
1301 
1302 NTSTATUS
1303 HidParser_BuildContext(
1304     IN PVOID ParserContext,
1305     IN ULONG CollectionIndex,
1306     IN ULONG ContextSize,
1307     OUT PVOID *CollectionContext)
1308 {
1309     PHID_COLLECTION Collection;
1310     PVOID Context;
1311     NTSTATUS Status;
1312 
1313     //
1314     // lets get the collection
1315     //
1316     Collection = HidParser_GetCollection((PHID_PARSER_CONTEXT)ParserContext, CollectionIndex);
1317     ASSERT(Collection);
1318 
1319     //
1320     // lets allocate the context
1321     //
1322     Context = AllocFunction(ContextSize);
1323     if (Context == NULL)
1324     {
1325         //
1326         // no memory
1327         //
1328         return HIDP_STATUS_INTERNAL_ERROR;
1329     }
1330 
1331     //
1332     // lets build the context
1333     //
1334     Status = HidParser_BuildCollectionContext(Collection, Context, ContextSize);
1335     if (Status == HIDP_STATUS_SUCCESS)
1336     {
1337         //
1338         // store context
1339         //
1340         *CollectionContext = Context;
1341     }
1342 
1343     //
1344     // done
1345     //
1346     return Status;
1347 }
1348 
1349 
1350 ULONG
1351 HidParser_GetContextSize(
1352     IN PVOID ParserContext,
1353     IN ULONG CollectionIndex)
1354 {
1355     PHID_COLLECTION Collection;
1356     ULONG Size;
1357 
1358     //
1359     // lets get the collection
1360     //
1361     Collection = HidParser_GetCollection((PHID_PARSER_CONTEXT)ParserContext, CollectionIndex);
1362 
1363     //
1364     // calculate size
1365     //
1366     Size = HidParser_CalculateContextSize(Collection);
1367     return Size;
1368 }
1369 
1370