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:		HIDGetButtonCaps.c
25
26	Contains:	xxx put contents here xxx
27
28	Version:	xxx put version here xxx
29
30	Copyright:	� 1999-2001 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	  <USB5>	 4/21/00	KH		Added HIDGetButtonCapabilities and
48									HIDGetSpecificButtonCapabilities that now allow users to find
49									HID report units and exponents.
50	  <USB4>	 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	  <USB3>	  5/3/99	BWS		We were not setting isStringRange, isDesignatorRange, and
55									isAbsolute
56	  <USB2>	  3/7/99	BWS		When range/notRange were made a union, we missed this case where
57									they were both being set indescriminately
58	  <USB1>	  3/5/99	BWS		first checked in
59*/
60
61#include "HIDLib.h"
62
63/*
64 *------------------------------------------------------------------------------
65 *
66 * HIDGetSpecificButtonCaps - Get the binary values for a report type
67 *
68 *	 Input:
69 *			  reportType		   - HIDP_Input, HIDP_Output, HIDP_Feature
70 *			  usagePage			   - Page Criteria or zero
71 *			  iCollection			- Collection Criteria or zero
72 *			  usage				   - usage Criteria or zero
73 *			  buttonCaps		  - ButtonCaps Array
74 *			  piButtonCapsLength	- Maximum Entries
75 *			  ptPreparsedData		- Pre-Parsed Data
76 *	 Output:
77 *			  piButtonCapsLength	- Entries Populated
78 *	 Returns:
79 *
80 *------------------------------------------------------------------------------
81*/
82OSStatus HIDGetSpecificButtonCaps(HIDReportType reportType,
83									   HIDUsage usagePage,
84									   UInt32 iCollection,
85									   HIDUsage usage,
86									   HIDButtonCapsPtr buttonCaps,
87									   UInt32 *piButtonCapsLength,
88									   HIDPreparsedDataRef preparsedDataRef)
89{
90	HIDPreparsedDataPtr ptPreparsedData = (HIDPreparsedDataPtr) preparsedDataRef;
91	HIDCollection *ptCollection;
92	HIDCollection *ptParent;
93	HIDReportItem *ptReportItem;
94	HIDP_UsageItem *ptUsageItem;
95	HIDStringItem *ptStringItem;
96	HIDDesignatorItem *ptDesignatorItem;
97	HIDP_UsageItem *ptFirstCollectionUsageItem;
98	HIDButtonCaps *ptCapability;
99	int iR, iU;
100	int parent;
101	int iReportItem, iUsageItem;
102	int iMaxCaps;
103		// There are 3 versions of HID Parser code all based on the same logic: OS 9 HID Library;
104		// OSX xnu; OSX IOKitUser. They should all be nearly the same logic. This version (xnu)
105		// is based on older OS 9 code. This version has added logic to maintain this startBit.
106		// I don't know why it is here, but believe if it is needed here, it would probably be
107		// needed in the other two implementations. Didn't have time to determine that at this
108		// time, so i'll leave this comment to remind me that we should reconcile the 3 versions.
109        UInt32 startBit;	// Added esb 9-29-99
110	/*If I remember correctly, it was an optimization.  Each time you ask for
111	a specific value capability, it would search through the entire report
112	descriptor to find it (my recollection is kind of hazy on this part).
113	The start bit allowed somebody (client maybe) to cache the information
114	on where in the report a specific value resided and the use that later
115	when fetching that value.  That way, you don't have to keep going
116	through the parse tree to find where a value exists.  I don't remember
117	if the implementation was completed or if I even used it. -esb */
118/*
119 *	Disallow Null Pointers
120*/
121	if ((buttonCaps == NULL)
122	 || (piButtonCapsLength == NULL)
123	 || (ptPreparsedData == NULL))
124		return kHIDNullPointerErr;
125	if (ptPreparsedData->hidTypeIfValid != kHIDOSType)
126		return kHIDInvalidPreparsedDataErr;
127/*
128 *	Save the buffer size
129*/
130	iMaxCaps = *piButtonCapsLength;
131	*piButtonCapsLength = 0;
132/*
133 *	The Collection must be in range
134*/
135	if (iCollection >= ptPreparsedData->collectionCount)
136		return kHIDBadParameterErr;
137/*
138 *	Search only the scope of the Collection specified
139*/
140	ptCollection = &ptPreparsedData->collections[iCollection];
141	for (iR=0; iR<ptCollection->reportItemCount; iR++)
142	{
143		iReportItem = ptCollection->firstReportItem + iR;
144		ptReportItem = &ptPreparsedData->reportItems[iReportItem];
145/*
146 *		Search only reports of the proper type
147*/
148		if ((ptReportItem->reportType == reportType)
149		 && HIDIsButton(ptReportItem, preparsedDataRef))
150		{
151                        startBit = ptReportItem->startBit;
152/*
153 *			Search the usages
154*/
155			  for (iU=0; iU<ptReportItem->usageItemCount; iU++)
156			  {
157/*
158 *				  Copy all usages if the usage above is zero
159 *					or copy all that are "match"
160*/
161				  iUsageItem = ptReportItem->firstUsageItem + iU;
162				  ptUsageItem = &ptPreparsedData->usageItems[iUsageItem];
163
164				  // �� we assume there is a 1-1 corresponence between usage items, string items, and designator items
165				  // ���this is not necessarily the case, but its better than nothing
166				  ptStringItem = &ptPreparsedData->stringItems[ptReportItem->firstStringItem + iU];
167				  ptDesignatorItem = &ptPreparsedData->desigItems[ptReportItem->firstDesigItem + iU];
168
169				  if (HIDUsageInRange(ptUsageItem,usagePage,usage))
170				  {
171/*
172 *					  Only copy if there's room
173*/
174					  if (*piButtonCapsLength >= iMaxCaps)
175						  return kHIDBufferTooSmallErr;
176					  ptCapability = &buttonCaps[(*piButtonCapsLength)++];
177/*
178 *					  Populate the Capability Structure
179*/
180					  parent = ptReportItem->parent;
181					  ptParent = &ptPreparsedData->collections[parent];
182					  ptFirstCollectionUsageItem
183						 = &ptPreparsedData->usageItems[ptParent->firstUsageItem];
184					  ptCapability->collection = parent;
185					  ptCapability->collectionUsagePage = ptParent->usagePage;
186					  ptCapability->collectionUsage = ptFirstCollectionUsageItem->usage;
187					  ptCapability->bitField =	ptReportItem->dataModes;
188					  ptCapability->reportID = ptReportItem->globals.reportID;
189					  ptCapability->usagePage = ptUsageItem->usagePage;
190
191					  ptCapability->isStringRange = false;			// �� todo: set this and stringMin,stringMax,stringIndex
192					  ptCapability->isDesignatorRange = false;		// �� todo: set this and designatorMin,designatorMax,designatorIndex
193					  ptCapability->isAbsolute = !(ptReportItem->dataModes & kHIDDataRelative);
194
195					  ptCapability->isRange = ptUsageItem->isRange;
196					  if (ptUsageItem->isRange)
197					  {
198						ptCapability->u.range.usageMin = ptUsageItem->usageMinimum;
199						ptCapability->u.range.usageMax = ptUsageItem->usageMaximum;
200					  }
201					  else
202						ptCapability->u.notRange.usage = ptUsageItem->usage;
203
204					  // if there really are that many items
205					  if (iU < ptReportItem->stringItemCount)
206					  {
207						  ptCapability->isStringRange = ptStringItem->isRange;
208
209						  if (ptStringItem->isRange)
210						  {
211							ptCapability->u.range.stringMin = ptStringItem->minimum;
212							ptCapability->u.range.stringMax = ptStringItem->maximum;
213						  }
214						  else
215							ptCapability->u.notRange.stringIndex = ptStringItem->index;
216					  }
217					  // default, clear it
218					  else
219					  {
220					  	ptCapability->isStringRange = false;
221						ptCapability->u.notRange.stringIndex = 0;
222					  }
223
224					  // if there really are that many items
225					  if (iU < ptReportItem->desigItemCount)
226					  {
227						  ptCapability->isDesignatorRange = ptDesignatorItem->isRange;
228
229						  if (ptDesignatorItem->isRange)
230						  {
231							ptCapability->u.range.designatorMin = ptDesignatorItem->minimum;
232							ptCapability->u.range.designatorMax = ptDesignatorItem->maximum;
233						  }
234						  else
235							ptCapability->u.notRange.designatorIndex = ptDesignatorItem->index;
236					  }
237					  // default, clear it
238					  else
239					  {
240					  	ptCapability->isDesignatorRange = false;
241						ptCapability->u.notRange.designatorIndex = 0;
242					  }
243                                          ptCapability->startBit = startBit;
244				  }
245                                  if ((ptReportItem->dataModes & kHIDDataVariableBit) == kHIDDataVariable)
246                                  {
247                                      // RY: Incremeneting start bit too much in case or multiple usage items
248                                      //startBit += (ptReportItem->globals.reportSize * ptReportItem->globals.reportCount);
249                                      startBit += ptReportItem->globals.reportSize;
250                                  }
251			  }
252		}
253	}
254	return kHIDSuccess;
255}
256
257/*
258 *------------------------------------------------------------------------------
259 *
260 * HIDGetButtonCaps - Get the binary values for a report type
261 *
262 *	 Input:
263 *			  reportType		   - HIDP_Input, HIDP_Output, HIDP_Feature
264 *			  buttonCaps		  - ButtonCaps Array
265 *			  piButtonCapsLength	- Maximum Entries
266 *			  ptPreparsedData		- Pre-Parsed Data
267 *	 Output:
268 *			  piButtonCapsLength	- Entries Populated
269 *	 Returns:
270 *
271 *------------------------------------------------------------------------------
272*/
273OSStatus HIDGetButtonCaps(HIDReportType reportType,
274							   HIDButtonCapsPtr buttonCaps,
275							   UInt32 *piButtonCapsLength,
276							   HIDPreparsedDataRef preparsedDataRef)
277{
278	return HIDGetSpecificButtonCaps(reportType,0,0,0,buttonCaps,
279									  piButtonCapsLength,preparsedDataRef);
280}
281
282
283/*
284 *------------------------------------------------------------------------------
285 *
286 * HIDGetSpecificButtonCapabilities - Get the binary values for a report type
287 *								This is the same as HIDGetSpecificButtonCaps,
288 *								except that it takes a HIDButtonCapabilitiesPtr
289 *								so it can return units and unitExponents.
290 *
291 *	 Input:
292 *			  reportType		   - HIDP_Input, HIDP_Output, HIDP_Feature
293 *			  usagePage			   - Page Criteria or zero
294 *			  iCollection			- Collection Criteria or zero
295 *			  usage				   - usage Criteria or zero
296 *			  buttonCaps		  - ButtonCaps Array
297 *			  piButtonCapsLength	- Maximum Entries
298 *			  ptPreparsedData		- Pre-Parsed Data
299 *	 Output:
300 *			  piButtonCapsLength	- Entries Populated
301 *	 Returns:
302 *
303 *------------------------------------------------------------------------------
304*/
305OSStatus HIDGetSpecificButtonCapabilities(HIDReportType reportType,
306									   HIDUsage usagePage,
307									   UInt32 iCollection,
308									   HIDUsage usage,
309									   HIDButtonCapabilitiesPtr buttonCaps,
310									   UInt32 *piButtonCapsLength,
311									   HIDPreparsedDataRef preparsedDataRef)
312{
313	HIDPreparsedDataPtr ptPreparsedData = (HIDPreparsedDataPtr) preparsedDataRef;
314	HIDCollection *ptCollection;
315	HIDCollection *ptParent;
316	HIDReportItem *ptReportItem;
317	HIDP_UsageItem *ptUsageItem;
318	HIDStringItem *ptStringItem;
319	HIDDesignatorItem *ptDesignatorItem;
320	HIDP_UsageItem *ptFirstCollectionUsageItem;
321	HIDButtonCapabilities *ptCapability;
322	int iR, iU;
323	int parent;
324	int iReportItem, iUsageItem;
325	int iMaxCaps;
326		// There are 3 versions of HID Parser code all based on the same logic: OS 9 HID Library;
327		// OSX xnu; OSX IOKitUser. They should all be nearly the same logic. This version (xnu)
328		// is based on older OS 9 code. This version has added logic to maintain this startBit.
329		// I don't know why it is here, but believe if it is needed here, it would probably be
330		// needed in the other two implementations. Didn't have time to determine that at this
331		// time, so i'll leave this comment to remind me that we should reconcile the 3 versions.
332        UInt32 startBit;
333/*
334 *	Disallow Null Pointers
335*/
336	if ((buttonCaps == NULL)
337	 || (piButtonCapsLength == NULL)
338	 || (ptPreparsedData == NULL))
339		return kHIDNullPointerErr;
340	if (ptPreparsedData->hidTypeIfValid != kHIDOSType)
341		return kHIDInvalidPreparsedDataErr;
342/*
343 *	Save the buffer size
344*/
345	iMaxCaps = *piButtonCapsLength;
346	*piButtonCapsLength = 0;
347/*
348 *	The Collection must be in range
349*/
350	if (iCollection >= ptPreparsedData->collectionCount)
351		return kHIDBadParameterErr;
352/*
353 *	Search only the scope of the Collection specified
354*/
355	ptCollection = &ptPreparsedData->collections[iCollection];
356	for (iR=0; iR<ptCollection->reportItemCount; iR++)
357	{
358		iReportItem = ptCollection->firstReportItem + iR;
359		ptReportItem = &ptPreparsedData->reportItems[iReportItem];
360/*
361 *		Search only reports of the proper type
362*/
363		if ((ptReportItem->reportType == reportType)
364		 && HIDIsButton(ptReportItem, preparsedDataRef))
365		{
366                        startBit = ptReportItem->startBit;
367/*
368 *			Search the usages
369*/
370			  for (iU=0; iU<ptReportItem->usageItemCount; iU++)
371			  {
372/*
373 *				  Copy all usages if the usage above is zero
374 *					or copy all that are "match"
375*/
376				  iUsageItem = ptReportItem->firstUsageItem + iU;
377				  ptUsageItem = &ptPreparsedData->usageItems[iUsageItem];
378
379				  // �� we assume there is a 1-1 corresponence between usage items, string items, and designator items
380				  // ���this is not necessarily the case, but its better than nothing
381				  ptStringItem = &ptPreparsedData->stringItems[ptReportItem->firstStringItem + iU];
382				  ptDesignatorItem = &ptPreparsedData->desigItems[ptReportItem->firstDesigItem + iU];
383
384				  if (HIDUsageInRange(ptUsageItem,usagePage,usage))
385				  {
386/*
387 *					  Only copy if there's room
388*/
389					  if (*piButtonCapsLength >= iMaxCaps)
390						  return kHIDBufferTooSmallErr;
391					  ptCapability = &buttonCaps[(*piButtonCapsLength)++];
392/*
393 *					  Populate the Capability Structure
394*/
395					  parent = ptReportItem->parent;
396					  ptParent = &ptPreparsedData->collections[parent];
397					  ptFirstCollectionUsageItem
398						 = &ptPreparsedData->usageItems[ptParent->firstUsageItem];
399					  ptCapability->collection = parent;
400					  ptCapability->collectionUsagePage = ptParent->usagePage;
401					  ptCapability->collectionUsage = ptFirstCollectionUsageItem->usage;
402					  ptCapability->bitField =	ptReportItem->dataModes;
403					  ptCapability->reportID = ptReportItem->globals.reportID;
404					  ptCapability->usagePage = ptUsageItem->usagePage;
405
406                                          // *** HACK FOR ARRAY ITEMS ***
407                                          if ((ptReportItem->dataModes & kHIDDataArrayBit) == kHIDDataArray)
408                                          {
409                                            // RY: Introducing a hack to allow the HID Manager
410                                            // gain access to the report Size and report Count
411                                            // for array items.  Trust me this makes sense
412                                            // becuase these fields are not used by an array.
413                                            ptCapability->unitExponent = ptReportItem->globals.reportSize;
414                                            ptCapability->units = ptReportItem->globals.reportCount;
415
416                                            // RY: For array items, we need to know the logical
417                                            // min/max.  Since we don't have any space, we need
418                                            // to use 2 reserved fields of the non usage union
419
420                                            if (ptReportItem->flags & kHIDReportItemFlag_Reversed)
421                                            {
422                                                ptCapability->u.notRange.reserved2 = ptReportItem->globals.logicalMaximum;
423                                                ptCapability->u.notRange.reserved3 = ptReportItem->globals.logicalMinimum;
424                                            }
425                                            else
426                                            {
427                                                ptCapability->u.notRange.reserved2 = ptReportItem->globals.logicalMinimum;
428                                                ptCapability->u.notRange.reserved3 = ptReportItem->globals.logicalMaximum;
429                                            }
430
431                                          }
432                                          else
433                                          {
434                                            ptCapability->unitExponent = ptReportItem->globals.unitExponent;
435                                            ptCapability->units = ptReportItem->globals.units;
436                                          }
437                                          // End hack
438
439//					  ptCapability->reserved = 0;							// for future OS 9 expansion
440					  ptCapability->startBit = 0;		// init esb added field.
441//					  ptCapability->pbVersion = kHIDCurrentCapabilitiesPBVersion;
442					  ptCapability->pbVersion = 2;
443
444					  ptCapability->isStringRange = false;			// �� todo: set this and stringMin,stringMax,stringIndex
445					  ptCapability->isDesignatorRange = false;		// �� todo: set this and designatorMin,designatorMax,designatorIndex
446					  ptCapability->isAbsolute = !(ptReportItem->dataModes & kHIDDataRelative);
447
448					  ptCapability->isRange = ptUsageItem->isRange;
449					  if (ptUsageItem->isRange)
450					  {
451						ptCapability->u.range.usageMin = ptUsageItem->usageMinimum;
452						ptCapability->u.range.usageMax = ptUsageItem->usageMaximum;
453					  }
454					  else
455						ptCapability->u.notRange.usage = ptUsageItem->usage;
456
457					  // if there really are that many items
458					  if (iU < ptReportItem->stringItemCount)
459					  {
460						  ptCapability->isStringRange = ptStringItem->isRange;
461
462						  if (ptStringItem->isRange)
463						  {
464							ptCapability->u.range.stringMin = ptStringItem->minimum;
465							ptCapability->u.range.stringMax = ptStringItem->maximum;
466						  }
467						  else
468							ptCapability->u.notRange.stringIndex = ptStringItem->index;
469					  }
470					  // default, clear it
471					  else
472					  {
473					  	ptCapability->isStringRange = false;
474						ptCapability->u.notRange.stringIndex = 0;
475					  }
476
477					  // if there really are that many items
478					  if (iU < ptReportItem->desigItemCount)
479					  {
480						  ptCapability->isDesignatorRange = ptDesignatorItem->isRange;
481
482						  if (ptDesignatorItem->isRange)
483						  {
484							ptCapability->u.range.designatorMin = ptDesignatorItem->minimum;
485							ptCapability->u.range.designatorMax = ptDesignatorItem->maximum;
486						  }
487						  else
488							ptCapability->u.notRange.designatorIndex = ptDesignatorItem->index;
489					  }
490					  // default, clear it
491					  else
492					  {
493					  	ptCapability->isDesignatorRange = false;
494						ptCapability->u.notRange.designatorIndex = 0;
495					  }
496                                          ptCapability->startBit = startBit;
497				  }
498                                  // For array items, we want the startBit left alone;
499                                  if ((ptReportItem->dataModes & kHIDDataVariableBit) == kHIDDataVariable)
500                                  {
501                                    // RY: Incremeneting start bit too much in case or multiple usage items
502                                    //startBit += (ptReportItem->globals.reportSize * ptReportItem->globals.reportCount);
503                                    startBit += ptReportItem->globals.reportSize;
504                                  }
505			  }
506		}
507	}
508	return kHIDSuccess;
509}
510
511/*
512 *------------------------------------------------------------------------------
513 *
514 * HIDGetButtonCapabilities - Get the binary values for a report type
515 *								This is the same as HIDGetButtonCaps,
516 *								except that it takes a HIDButtonCapabilitiesPtr
517 *								so it can return units and unitExponents.
518 *
519 *	 Input:
520 *			  reportType		   - HIDP_Input, HIDP_Output, HIDP_Feature
521 *			  buttonCaps		  - ButtonCaps Array
522 *			  piButtonCapsLength	- Maximum Entries
523 *			  ptPreparsedData		- Pre-Parsed Data
524 *	 Output:
525 *			  piButtonCapsLength	- Entries Populated
526 *	 Returns:
527 *
528 *------------------------------------------------------------------------------
529*/
530OSStatus HIDGetButtonCapabilities(HIDReportType reportType,
531							   HIDButtonCapabilitiesPtr buttonCaps,
532							   UInt32 *piButtonCapsLength,
533							   HIDPreparsedDataRef preparsedDataRef)
534{
535	return HIDGetSpecificButtonCapabilities(reportType,0,0,0,buttonCaps,
536									  piButtonCapsLength,preparsedDataRef);
537}
538