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