1 /** @file
2   Helper functions to parse HID report descriptor and items.
3 
4 Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 
9 #include "UsbMouse.h"
10 
11 
12 /**
13   Get next HID item from report descriptor.
14 
15   This function retrieves next HID item from report descriptor, according to
16   the start position.
17   According to USB HID Specification, An item is piece of information
18   about the device. All items have a one-byte prefix that contains
19   the item tag, item type, and item size.
20   There are two basic types of items: short items and long items.
21   If the item is a short item, its optional data size may be 0, 1, 2, or 4 bytes.
22   Only short item is supported here.
23 
24   @param  StartPos          Start position of the HID item to get.
25   @param  EndPos            End position of the range to get the next HID item.
26   @param  HidItem           Buffer for the HID Item to return.
27 
28   @return Pointer to end of the HID item returned.
29           NULL if no HID item retrieved.
30 
31 **/
32 UINT8 *
GetNextHidItem(IN UINT8 * StartPos,IN UINT8 * EndPos,OUT HID_ITEM * HidItem)33 GetNextHidItem (
34   IN  UINT8    *StartPos,
35   IN  UINT8    *EndPos,
36   OUT HID_ITEM *HidItem
37   )
38 {
39   UINT8 Temp;
40 
41   if (EndPos <= StartPos) {
42     return NULL;
43   }
44 
45   Temp = *StartPos;
46   StartPos++;
47 
48   //
49   // Bit format of prefix byte:
50   // Bits 0-1: Size
51   // Bits 2-3: Type
52   // Bits 4-7: Tag
53   //
54   HidItem->Type = BitFieldRead8 (Temp, 2, 3);
55   HidItem->Tag  = BitFieldRead8 (Temp, 4, 7);
56 
57   if (HidItem->Tag == HID_ITEM_TAG_LONG) {
58     //
59     // Long Items are not supported, although we try to parse it.
60     //
61     HidItem->Format = HID_ITEM_FORMAT_LONG;
62 
63     if ((EndPos - StartPos) >= 2) {
64       HidItem->Size = *StartPos++;
65       HidItem->Tag  = *StartPos++;
66 
67       if ((EndPos - StartPos) >= HidItem->Size) {
68         HidItem->Data.LongData = StartPos;
69         StartPos += HidItem->Size;
70         return StartPos;
71       }
72     }
73   } else {
74     HidItem->Format = HID_ITEM_FORMAT_SHORT;
75     HidItem->Size   = BitFieldRead8 (Temp, 0, 1);
76 
77     switch (HidItem->Size) {
78     case 0:
79       //
80       // No data
81       //
82       return StartPos;
83 
84     case 1:
85       //
86       // 1-byte data
87       //
88       if ((EndPos - StartPos) >= 1) {
89         HidItem->Data.Uint8 = *StartPos++;
90         return StartPos;
91       }
92 
93     case 2:
94       //
95       // 2-byte data
96       //
97       if ((EndPos - StartPos) >= 2) {
98         CopyMem (&HidItem->Data.Uint16, StartPos, sizeof (UINT16));
99         StartPos += 2;
100         return StartPos;
101       }
102 
103     case 3:
104       //
105       // 4-byte data, adjust size
106       //
107       HidItem->Size = 4;
108       if ((EndPos - StartPos) >= 4) {
109         CopyMem (&HidItem->Data.Uint32, StartPos, sizeof (UINT32));
110         StartPos += 4;
111         return StartPos;
112       }
113     }
114   }
115 
116   return NULL;
117 }
118 
119 
120 /**
121   Get data from HID item.
122 
123   This function retrieves data from HID item.
124   It only supports short items, which has 4 types of data:
125   0, 1, 2, or 4 bytes.
126 
127   @param  HidItem       Pointer to the HID item.
128 
129   @return The data of HID item.
130 
131 **/
132 UINT32
GetItemData(IN HID_ITEM * HidItem)133 GetItemData (
134   IN  HID_ITEM *HidItem
135   )
136 {
137   //
138   // Get data from HID item.
139   //
140   switch (HidItem->Size) {
141   case 1:
142     return HidItem->Data.Uint8;
143   case 2:
144     return HidItem->Data.Uint16;
145   case 4:
146     return HidItem->Data.Uint32;
147   }
148   return 0;
149 }
150 
151 /**
152   Parse HID item from report descriptor.
153 
154   There are three item types: Main, Global, and Local.
155   This function parses these types of HID items according
156   to tag info.
157 
158   @param  UsbMouse          The instance of USB_MOUSE_DEV
159   @param  HidItem           The HID item to parse
160 
161 **/
162 VOID
ParseHidItem(IN USB_MOUSE_DEV * UsbMouse,IN HID_ITEM * HidItem)163 ParseHidItem (
164   IN  USB_MOUSE_DEV   *UsbMouse,
165   IN  HID_ITEM        *HidItem
166   )
167 {
168   UINT8  Data;
169 
170   switch (HidItem->Type) {
171 
172   case HID_ITEM_TYPE_MAIN:
173     //
174     // we don't care any main items, just skip
175     //
176     return;
177 
178   case HID_ITEM_TYPE_GLOBAL:
179     //
180     // For global items, we only care Usage Page tag for Button Page here
181     //
182     if (HidItem->Tag == HID_GLOBAL_ITEM_TAG_USAGE_PAGE) {
183       Data = (UINT8) GetItemData (HidItem);
184       if (Data == 0x09) {
185         //
186         // Button Page
187         //
188         UsbMouse->PrivateData.ButtonDetected = TRUE;
189       }
190     }
191     return;
192 
193   case HID_ITEM_TYPE_LOCAL:
194     if (HidItem->Size == 0) {
195       //
196       // No expected data for local item
197       //
198       return ;
199     }
200 
201     Data = (UINT8) GetItemData (HidItem);
202 
203     switch (HidItem->Tag) {
204     case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
205       if (UsbMouse->PrivateData.ButtonDetected) {
206         UsbMouse->PrivateData.ButtonMinIndex = Data;
207       }
208       return ;
209 
210     case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM:
211     {
212       if (UsbMouse->PrivateData.ButtonDetected) {
213         UsbMouse->PrivateData.ButtonMaxIndex = Data;
214       }
215       return ;
216     }
217 
218     default:
219       return;
220     }
221   }
222 }
223 
224 
225 /**
226   Parse Mouse Report Descriptor.
227 
228   According to USB HID Specification, report descriptors are
229   composed of pieces of information. Each piece of information
230   is called an Item. This function retrieves each item from
231   the report descriptor and updates USB_MOUSE_DEV.
232 
233   @param  UsbMouse          The instance of USB_MOUSE_DEV
234   @param  ReportDescriptor  Report descriptor to parse
235   @param  ReportSize        Report descriptor size
236 
237   @retval EFI_SUCCESS       Report descriptor successfully parsed.
238   @retval EFI_UNSUPPORTED   Report descriptor contains long item.
239 
240 **/
241 EFI_STATUS
ParseMouseReportDescriptor(OUT USB_MOUSE_DEV * UsbMouse,IN UINT8 * ReportDescriptor,IN UINTN ReportSize)242 ParseMouseReportDescriptor (
243   OUT USB_MOUSE_DEV   *UsbMouse,
244   IN  UINT8           *ReportDescriptor,
245   IN  UINTN           ReportSize
246   )
247 {
248   UINT8     *DescriptorEnd;
249   UINT8     *Ptr;
250   HID_ITEM  HidItem;
251 
252   DescriptorEnd = ReportDescriptor + ReportSize;
253 
254   Ptr = GetNextHidItem (ReportDescriptor, DescriptorEnd, &HidItem);
255   while (Ptr != NULL) {
256     if (HidItem.Format != HID_ITEM_FORMAT_SHORT) {
257       //
258       // Long Item is not supported at current HID revision
259       //
260       return EFI_UNSUPPORTED;
261     }
262 
263     ParseHidItem (UsbMouse, &HidItem);
264 
265     Ptr = GetNextHidItem (Ptr, DescriptorEnd, &HidItem);
266   }
267 
268   UsbMouse->NumberOfButtons = (UINT8) (UsbMouse->PrivateData.ButtonMaxIndex - UsbMouse->PrivateData.ButtonMinIndex + 1);
269   UsbMouse->XLogicMax = 127;
270   UsbMouse->YLogicMax = 127;
271   UsbMouse->XLogicMin = -127;
272   UsbMouse->YLogicMin = -127;
273 
274   return EFI_SUCCESS;
275 }
276