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