]> git.saurik.com Git - apple/xnu.git/blob - iokit/Families/IOHIDSystem/IOHIDDescriptorParser/HIDGetValueCaps.c
1af6f746712c1630c4ef2f8bd458b16cabbe22b6
[apple/xnu.git] / iokit / Families / IOHIDSystem / IOHIDDescriptorParser / HIDGetValueCaps.c
1 /*
2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /*
23 File: HIDGetValueCaps.c
24
25 Contains: xxx put contents here xxx
26
27 Version: xxx put version here xxx
28
29 Copyright: © 1999-2000 by Apple Computer, Inc., all rights reserved.
30
31 File Ownership:
32
33 DRI: xxx put dri here xxx
34
35 Other Contact: xxx put other contact here xxx
36
37 Technology: xxx put technology here xxx
38
39 Writers:
40
41 (KH) Keithen Hayenga
42 (BWS) Brent Schorsch
43
44 Change History (most recent first):
45
46 <USB8> 12/12/00 KH range count off by 1.
47 <USB7> 4/21/00 KH Added HIDGetValueCapabilities and
48 HIDGetSpecificValueCapabilities that now allow users to find HID
49 report units and exponents.
50 <USB6> 11/1/99 BWS [2405720] We need a better check for 'bit padding' items,
51 rather than just is constant. We will check to make sure the
52 item is constant, and has no usage, or zero usage. This means we
53 need to pass an additional parameter to some internal functions
54 <USB5> 5/3/99 BWS Fix typo
55 <USB4> 5/3/99 BWS We were not setting isStringRange, isDesignatorRange, and
56 isAbsolute
57 <USB3> 3/7/99 BWS When range/notRange were made a union, we missed this case where
58 they were both being set indescriminately
59 <USB2> 3/7/99 BWS [2311411] Had added missing fields to caps structure, but they
60 were not being filled in
61 <USB1> 3/5/99 BWS first checked in
62 */
63
64 #include "HIDLib.h"
65
66 /*
67 *------------------------------------------------------------------------------
68 *
69 * HIDGetSpecificValueCaps - Get the binary values for a report type
70 *
71 * Input:
72 * reportType - HIDP_Input, HIDP_Output, HIDP_Feature
73 * usagePage - Page Criteria or zero
74 * iCollection - Collection Criteria or zero
75 * usage - usage Criteria or zero
76 * valueCaps - ValueCaps Array
77 * piValueCapsLength - Maximum Entries
78 * ptPreparsedData - Pre-Parsed Data
79 * Output:
80 * piValueCapsLength - Entries Populated
81 * Returns:
82 *
83 *------------------------------------------------------------------------------
84 */
85 OSStatus HIDGetSpecificValueCaps(HIDReportType reportType,
86 HIDUsage usagePage,
87 UInt32 iCollection,
88 HIDUsage usage,
89 HIDValueCapsPtr valueCaps,
90 UInt32 *piValueCapsLength,
91 HIDPreparsedDataRef preparsedDataRef)
92 {
93 HIDPreparsedDataPtr ptPreparsedData = (HIDPreparsedDataPtr) preparsedDataRef;
94 HIDCollection *ptCollection;
95 HIDCollection *ptParent;
96 HIDReportItem *ptReportItem;
97 HIDP_UsageItem *ptUsageItem;
98 HIDStringItem *ptStringItem;
99 HIDDesignatorItem *ptDesignatorItem;
100 HIDP_UsageItem *ptFirstCollectionUsageItem;
101 HIDValueCaps *ptCapability;
102 int iR, iU;
103 int parent;
104 int iReportItem, iUsageItem;
105 int iMaxCaps;
106 UInt32 iCount;
107 // There are 3 versions of HID Parser code all based on the same logic: OS 9 HID Library;
108 // OSX xnu; OSX IOKitUser. They should all be nearly the same logic. This version (xnu)
109 // is based on older OS 9 code. This version has added logic to maintain this startBit.
110 // I don't know why it is here, but believe if it is needed here, it would probably be
111 // needed in the other two implementations. Didn't have time to determine that at this
112 // time, so i'll leave this comment to remind me that we should reconcile the 3 versions.
113 UInt32 startBit; // Added esb 9-29-99
114 /*If I remember correctly, it was an optimization. Each time you ask for
115 a specific value capability, it would search through the entire report
116 descriptor to find it (my recollection is kind of hazy on this part).
117 The start bit allowed somebody (client maybe) to cache the information
118 on where in the report a specific value resided and the use that later
119 when fetching that value. That way, you don't have to keep going
120 through the parse tree to find where a value exists. I don't remember
121 if the implementation was completed or if I even used it. -esb */
122 /*
123 * Disallow Null Pointers
124 */
125 if ((valueCaps == NULL)
126 || (piValueCapsLength == NULL)
127 || (ptPreparsedData == NULL))
128 return kHIDNullPointerErr;
129 if (ptPreparsedData->hidTypeIfValid != kHIDOSType)
130 return kHIDInvalidPreparsedDataErr;
131 /*
132 * Save the buffer size
133 */
134 iMaxCaps = *piValueCapsLength;
135 *piValueCapsLength = 0;
136 /*
137 * The Collection must be in range
138 */
139 if ((iCollection < 0) || (iCollection >= ptPreparsedData->collectionCount))
140 return kHIDBadParameterErr;
141 /*
142 * Search only the scope of the Collection specified
143 */
144 ptCollection = &ptPreparsedData->collections[iCollection];
145 for (iR=0; iR<ptCollection->reportItemCount; iR++)
146 {
147 iReportItem = ptCollection->firstReportItem + iR;
148 ptReportItem = &ptPreparsedData->reportItems[iReportItem];
149 /*
150 * Search only reports of the proper type
151 */
152 if ((ptReportItem->reportType == reportType)
153 && ((ptReportItem->globals.usagePage == usagePage)
154 || (usagePage == 0))
155 && HIDIsVariable(ptReportItem, preparsedDataRef))
156 {
157 startBit = ptReportItem->startBit; // Added esb 9-28-99
158 /*
159 * Search the usages
160 */
161 for (iU=0; iU<ptReportItem->usageItemCount; iU++)
162 {
163 /*
164 * Copy all usages if the usage above is zero
165 * or copy all that "match"
166 */
167 iUsageItem = ptReportItem->firstUsageItem + iU;
168 ptUsageItem = &ptPreparsedData->usageItems[iUsageItem];
169
170 // ¥¥ we assume there is a 1-1 corresponence between usage items, string items, and designator items
171 // ¥¥Êthis is not necessarily the case, but its better than nothing
172 ptStringItem = &ptPreparsedData->stringItems[ptReportItem->firstStringItem + iU];
173 ptDesignatorItem = &ptPreparsedData->desigItems[ptReportItem->firstDesigItem + iU];
174
175 if (HIDUsageInRange(ptUsageItem,usagePage,usage))
176 {
177 /*
178 * Only copy if there's room
179 */
180 if (*piValueCapsLength >= iMaxCaps)
181 return kHIDBufferTooSmallErr;
182 ptCapability = &valueCaps[(*piValueCapsLength)++];
183 /*
184 * Populate the Capability Structure
185 */
186 parent = ptReportItem->parent;
187 ptParent = &ptPreparsedData->collections[parent];
188 ptFirstCollectionUsageItem = &ptPreparsedData->usageItems[ptParent->firstUsageItem];
189 ptCapability->collection = parent;
190 ptCapability->collectionUsagePage = ptParent->usagePage;
191 ptCapability->collectionUsage = ptFirstCollectionUsageItem->usage;
192 ptCapability->bitField = ptReportItem->dataModes;
193 ptCapability->reportID = ptReportItem->globals.reportID;
194 ptCapability->usagePage = ptUsageItem->usagePage;
195
196 ptCapability->isAbsolute = !(ptReportItem->dataModes & kHIDDataRelative);
197
198 ptCapability->isRange = ptUsageItem->isRange;
199 if (ptUsageItem->isRange)
200 {
201 ptCapability->u.range.usageMin = ptUsageItem->usageMinimum;
202 ptCapability->u.range.usageMax = ptUsageItem->usageMaximum;
203 }
204 else
205 ptCapability->u.notRange.usage = ptUsageItem->usage;
206
207 // if there really are that many items
208 if (iU < ptReportItem->stringItemCount)
209 {
210 ptCapability->isStringRange = ptStringItem->isRange;
211
212 if (ptStringItem->isRange)
213 {
214 ptCapability->u.range.stringMin = ptStringItem->minimum;
215 ptCapability->u.range.stringMax = ptStringItem->maximum;
216 }
217 else
218 ptCapability->u.notRange.stringIndex = ptStringItem->index;
219 }
220 // default, clear it
221 else
222 {
223 ptCapability->isStringRange = false;
224 ptCapability->u.notRange.stringIndex = 0;
225 }
226
227 // if there really are that many items
228 if (iU < ptReportItem->desigItemCount)
229 {
230 ptCapability->isDesignatorRange = ptDesignatorItem->isRange;
231
232 if (ptDesignatorItem->isRange)
233 {
234 ptCapability->u.range.designatorMin = ptDesignatorItem->minimum;
235 ptCapability->u.range.designatorMax = ptDesignatorItem->maximum;
236 }
237 else
238 ptCapability->u.notRange.designatorIndex = ptDesignatorItem->index;
239 }
240 // default, clear it
241 else
242 {
243 ptCapability->isDesignatorRange = false;
244 ptCapability->u.notRange.designatorIndex = 0;
245 }
246
247 ptCapability->bitSize = ptReportItem->globals.reportSize;
248
249 ptCapability->logicalMin = ptReportItem->globals.logicalMinimum;
250 ptCapability->logicalMax = ptReportItem->globals.logicalMaximum;
251 ptCapability->physicalMin = ptReportItem->globals.physicalMinimum;
252 ptCapability->physicalMax = ptReportItem->globals.physicalMaximum;
253
254 if (ptUsageItem->isRange)
255 {
256 iCount = ptUsageItem->usageMaximum - ptUsageItem->usageMinimum;
257 if (iCount < 0)
258 iCount = -iCount;
259 iCount++; // Range count was off by one.
260 }
261 else
262 // If we're not in a range, then there should be just one usage.
263 // Why do we have to call this function to determine that? Are we checking
264 // that there is that usage before we decide if usage count is 0 or 1?
265 // But haven't we already verified that we have this usage by the time we
266 // got here?
267 HIDHasUsage(preparsedDataRef,ptReportItem,
268 ptUsageItem->usagePage,ptUsageItem->usage,
269 NULL,&iCount);
270 ptCapability->reportCount = iCount;
271 ptCapability->startBit = startBit;
272 startBit += (ptCapability->bitSize * ptCapability->reportCount);
273 }
274 }
275 }
276 }
277 return kHIDSuccess;
278 }
279
280 /*
281 *------------------------------------------------------------------------------
282 *
283 * HIDGetValueCaps - Get the binary values for a report type
284 *
285 * Input:
286 * reportType - HIDP_Input, HIDP_Output, HIDP_Feature
287 * valueCaps - ValueCaps Array
288 * piValueCapsLength - Maximum Entries
289 * ptPreparsedData - Pre-Parsed Data
290 * Output:
291 * piValueCapsLength - Entries Populated
292 * Returns:
293 *
294 *------------------------------------------------------------------------------
295 */
296 OSStatus HIDGetValueCaps(HIDReportType reportType,
297 HIDValueCapsPtr valueCaps,
298 UInt32 *piValueCapsLength,
299 HIDPreparsedDataRef preparsedDataRef)
300 {
301 return HIDGetSpecificValueCaps(reportType,0,0,0,valueCaps,
302 piValueCapsLength,preparsedDataRef);
303 }
304
305
306 /*
307 *------------------------------------------------------------------------------
308 *
309 * HIDGetSpecificValueCapabilities - Get the binary values for a report type
310 * This is the same as HIDGetSpecificValueCaps,
311 * except that it takes a HIDValueCapabilitiesPtr
312 * so it can return units and unitExponents.
313 *
314 * Input:
315 * reportType - HIDP_Input, HIDP_Output, HIDP_Feature
316 * usagePage - Page Criteria or zero
317 * iCollection - Collection Criteria or zero
318 * usage - usage Criteria or zero
319 * valueCaps - ValueCaps Array
320 * piValueCapsLength - Maximum Entries
321 * ptPreparsedData - Pre-Parsed Data
322 * Output:
323 * piValueCapsLength - Entries Populated
324 * Returns:
325 *
326 *------------------------------------------------------------------------------
327 */
328 OSStatus HIDGetSpecificValueCapabilities(HIDReportType reportType,
329 HIDUsage usagePage,
330 UInt32 iCollection,
331 HIDUsage usage,
332 HIDValueCapabilitiesPtr valueCaps,
333 UInt32 *piValueCapsLength,
334 HIDPreparsedDataRef preparsedDataRef)
335 {
336 HIDPreparsedDataPtr ptPreparsedData = (HIDPreparsedDataPtr) preparsedDataRef;
337 HIDCollection *ptCollection;
338 HIDCollection *ptParent;
339 HIDReportItem *ptReportItem;
340 HIDP_UsageItem *ptUsageItem;
341 HIDStringItem *ptStringItem;
342 HIDDesignatorItem *ptDesignatorItem;
343 HIDP_UsageItem *ptFirstCollectionUsageItem;
344 HIDValueCapabilities *ptCapability;
345 int iR, iU;
346 int parent;
347 int iReportItem, iUsageItem;
348 int iMaxCaps;
349 UInt32 iCount;
350 // There are 3 versions of HID Parser code all based on the same logic: OS 9 HID Library;
351 // OSX xnu; OSX IOKitUser. They should all be nearly the same logic. This version (xnu)
352 // is based on older OS 9 code. This version has added logic to maintain this startBit.
353 // I don't know why it is here, but believe if it is needed here, it would probably be
354 // needed in the other two implementations. Didn't have time to determine that at this
355 // time, so i'll leave this comment to remind me that we should reconcile the 3 versions.
356 UInt32 startBit; // Carried esb's logic down here when we added HIDGetSpecificValueCapabilities().
357 /*
358 * Disallow Null Pointers
359 */
360 if ((valueCaps == NULL)
361 || (piValueCapsLength == NULL)
362 || (ptPreparsedData == NULL))
363 return kHIDNullPointerErr;
364 if (ptPreparsedData->hidTypeIfValid != kHIDOSType)
365 return kHIDInvalidPreparsedDataErr;
366 /*
367 * Save the buffer size
368 */
369 iMaxCaps = *piValueCapsLength;
370 *piValueCapsLength = 0;
371 /*
372 * The Collection must be in range
373 */
374 if ((iCollection < 0) || (iCollection >= ptPreparsedData->collectionCount))
375 return kHIDBadParameterErr;
376 /*
377 * Search only the scope of the Collection specified
378 */
379 ptCollection = &ptPreparsedData->collections[iCollection];
380 for (iR=0; iR<ptCollection->reportItemCount; iR++)
381 {
382 iReportItem = ptCollection->firstReportItem + iR;
383 ptReportItem = &ptPreparsedData->reportItems[iReportItem];
384 /*
385 * Search only reports of the proper type
386 */
387 if ((ptReportItem->reportType == reportType)
388 && ((ptReportItem->globals.usagePage == usagePage)
389 || (usagePage == 0))
390 && HIDIsVariable(ptReportItem, preparsedDataRef))
391 {
392 startBit = ptReportItem->startBit; // Same logic as Added esb 9-28-99
393 /*
394 * Search the usages
395 */
396 for (iU=0; iU<ptReportItem->usageItemCount; iU++)
397 {
398 /*
399 * Copy all usages if the usage above is zero
400 * or copy all that "match"
401 */
402 iUsageItem = ptReportItem->firstUsageItem + iU;
403 ptUsageItem = &ptPreparsedData->usageItems[iUsageItem];
404
405 // ¥¥ we assume there is a 1-1 corresponence between usage items, string items, and designator items
406 // ¥¥Êthis is not necessarily the case, but its better than nothing
407 ptStringItem = &ptPreparsedData->stringItems[ptReportItem->firstStringItem + iU];
408 ptDesignatorItem = &ptPreparsedData->desigItems[ptReportItem->firstDesigItem + iU];
409
410 if (HIDUsageInRange(ptUsageItem,usagePage,usage))
411 {
412 /*
413 * Only copy if there's room
414 */
415 if (*piValueCapsLength >= iMaxCaps)
416 return kHIDBufferTooSmallErr;
417 ptCapability = &valueCaps[(*piValueCapsLength)++];
418
419 /*
420 * Populate the Capability Structure
421 */
422 parent = ptReportItem->parent;
423 ptParent = &ptPreparsedData->collections[parent];
424 ptFirstCollectionUsageItem = &ptPreparsedData->usageItems[ptParent->firstUsageItem];
425 ptCapability->collection = parent;
426 ptCapability->collectionUsagePage = ptParent->usagePage;
427 ptCapability->collectionUsage = ptFirstCollectionUsageItem->usage;
428 ptCapability->bitField = ptReportItem->dataModes;
429 ptCapability->reportID = ptReportItem->globals.reportID;
430 ptCapability->usagePage = ptUsageItem->usagePage;
431 ptCapability->unitExponent = ptReportItem->globals.unitExponent;
432 ptCapability->units = ptReportItem->globals.units;
433 // ptCapability->reserved = 0; // for future OS 9 expansion
434 ptCapability->startBit = 0; // init esb added field.
435 // ptCapability->pbVersion = kHIDCurrentCapabilitiesPBVersion;
436 ptCapability->pbVersion = 2;
437
438 ptCapability->isAbsolute = !(ptReportItem->dataModes & kHIDDataRelative);
439
440 ptCapability->isRange = ptUsageItem->isRange;
441 if (ptUsageItem->isRange)
442 {
443 ptCapability->u.range.usageMin = ptUsageItem->usageMinimum;
444 ptCapability->u.range.usageMax = ptUsageItem->usageMaximum;
445 }
446 else
447 ptCapability->u.notRange.usage = ptUsageItem->usage;
448
449 // if there really are that many items
450 if (iU < ptReportItem->stringItemCount)
451 {
452 ptCapability->isStringRange = ptStringItem->isRange;
453
454 if (ptStringItem->isRange)
455 {
456 ptCapability->u.range.stringMin = ptStringItem->minimum;
457 ptCapability->u.range.stringMax = ptStringItem->maximum;
458 }
459 else
460 ptCapability->u.notRange.stringIndex = ptStringItem->index;
461 }
462 // default, clear it
463 else
464 {
465 ptCapability->isStringRange = false;
466 ptCapability->u.notRange.stringIndex = 0;
467 }
468
469 // if there really are that many items
470 if (iU < ptReportItem->desigItemCount)
471 {
472 ptCapability->isDesignatorRange = ptDesignatorItem->isRange;
473
474 if (ptDesignatorItem->isRange)
475 {
476 ptCapability->u.range.designatorMin = ptDesignatorItem->minimum;
477 ptCapability->u.range.designatorMax = ptDesignatorItem->maximum;
478 }
479 else
480 ptCapability->u.notRange.designatorIndex = ptDesignatorItem->index;
481 }
482 // default, clear it
483 else
484 {
485 ptCapability->isDesignatorRange = false;
486 ptCapability->u.notRange.designatorIndex = 0;
487 }
488
489 ptCapability->bitSize = ptReportItem->globals.reportSize;
490
491 ptCapability->logicalMin = ptReportItem->globals.logicalMinimum;
492 ptCapability->logicalMax = ptReportItem->globals.logicalMaximum;
493 ptCapability->physicalMin = ptReportItem->globals.physicalMinimum;
494 ptCapability->physicalMax = ptReportItem->globals.physicalMaximum;
495
496 if (ptUsageItem->isRange)
497 {
498 iCount = ptUsageItem->usageMaximum - ptUsageItem->usageMinimum;
499 if (iCount < 0)
500 iCount = -iCount;
501 iCount++; // Range count was off by one.
502 }
503 else
504 HIDHasUsage(preparsedDataRef,ptReportItem,
505 ptUsageItem->usagePage,ptUsageItem->usage,
506 NULL,&iCount);
507 ptCapability->reportCount = iCount;
508 ptCapability->startBit = startBit; // more of same logic.
509 startBit += (ptCapability->bitSize * ptCapability->reportCount);
510 }
511 }
512 }
513 }
514 return kHIDSuccess;
515 }
516
517 /*
518 *------------------------------------------------------------------------------
519 *
520 * HIDGetValueCapabilities - Get the binary values for a report type
521 * This is the same as HIDGetValueCaps,
522 * except that it takes a HIDValueCapabilitiesPtr
523 * so it can return units and unitExponents.
524 *
525 * Input:
526 * reportType - HIDP_Input, HIDP_Output, HIDP_Feature
527 * valueCaps - ValueCaps Array
528 * piValueCapsLength - Maximum Entries
529 * ptPreparsedData - Pre-Parsed Data
530 * Output:
531 * piValueCapsLength - Entries Populated
532 * Returns:
533 *
534 *------------------------------------------------------------------------------
535 */
536 OSStatus HIDGetValueCapabilities(HIDReportType reportType,
537 HIDValueCapabilitiesPtr valueCaps,
538 UInt32 *piValueCapsLength,
539 HIDPreparsedDataRef preparsedDataRef)
540 {
541 return HIDGetSpecificValueCapabilities(reportType,0,0,0,valueCaps,
542 piValueCapsLength,preparsedDataRef);
543 }