1/* 2 * @APPLE_LICENSE_HEADER_START@ 3 * 4 * Copyright (c) 2012 Apple, 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#include <AssertMacros.h> 25#include <IOKit/IOLib.h> 26 27#ifdef enqueue 28 #undef enqueue 29#endif 30 31#include "IOHIDResourceUserClient.h" 32 33#define kHIDClientTimeoutUS 1000000ULL 34 35#define kHIDQueueSize 16384 36 37#define super IOUserClient 38 39 40OSDefineMetaClassAndStructors( IOHIDResourceDeviceUserClient, IOUserClient ) 41 42 43//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 44// IOHIDResourceDeviceUserClient::_methods 45//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 46const IOExternalMethodDispatch IOHIDResourceDeviceUserClient::_methods[kIOHIDResourceDeviceUserClientMethodCount] = { 47 { // kIOHIDResourceDeviceUserClientMethodCreate 48 (IOExternalMethodAction) &IOHIDResourceDeviceUserClient::_createDevice, 49 1, -1, /* 1 struct input : the report descriptor */ 50 0, 0 51 }, 52 { // kIOHIDResourceDeviceUserClientMethodTerminate 53 (IOExternalMethodAction) &IOHIDResourceDeviceUserClient::_terminateDevice, 54 0, 0, 55 0, 0 56 }, 57 { // kIOHIDResourceDeviceUserClientMethodHandleReport 58 (IOExternalMethodAction) &IOHIDResourceDeviceUserClient::_handleReport, 59 1, -1, /* 1 struct input : the buffer */ 60 0, 0 61 }, 62 { // kIOHIDResourceDeviceUserClientMethodPostReportResult 63 (IOExternalMethodAction) &IOHIDResourceDeviceUserClient::_postReportResult, 64 kIOHIDResourceUserClientResponseIndexCount, -1, /* 1 scalar input: the result, 1 struct input : the buffer */ 65 0, 0 66 } 67}; 68 69 70 71//---------------------------------------------------------------------------------------------------- 72// IOHIDResourceDeviceUserClient::initWithTask 73//---------------------------------------------------------------------------------------------------- 74bool IOHIDResourceDeviceUserClient::initWithTask(task_t owningTask, void * security_id, UInt32 type) 75{ 76 bool result = false; 77 78#if !TARGET_OS_EMBEDDED 79 require_noerr_action(clientHasPrivilege(owningTask, kIOClientPrivilegeAdministrator), exit, result=false); 80#endif 81 82 result = super::initWithTask(owningTask, security_id, type); 83 require_action(result, exit, IOLog("%s failed\n", __FUNCTION__)); 84 85 _pending = OSSet::withCapacity(4); 86 _maxClientTimeoutUS = kHIDClientTimeoutUS; 87 88exit: 89 return result; 90} 91 92//---------------------------------------------------------------------------------------------------- 93// IOHIDResourceDeviceUserClient::start 94//---------------------------------------------------------------------------------------------------- 95bool IOHIDResourceDeviceUserClient::start(IOService * provider) 96{ 97 IOWorkLoop * workLoop; 98 bool result; 99 100 _owner = OSDynamicCast(IOHIDResource, provider); 101 require_action(_owner, exit, result=false); 102 _owner->retain(); 103 104 require_action(super::start(provider), exit, result=false); 105 106 workLoop = getWorkLoop(); 107 require_action(workLoop, exit, result=false); 108 109 _createDeviceTimer = IOTimerEventSource::timerEventSource(this, OSMemberFunctionCast(IOTimerEventSource::Action, this, &IOHIDResourceDeviceUserClient::createAndStartDeviceAsyncCallback)); 110 require_action(_createDeviceTimer, exit, result=false); 111 require_noerr_action(workLoop->addEventSource(_createDeviceTimer), exit, result=false); 112 113 _commandGate = IOCommandGate::commandGate(this); 114 require_action(_commandGate, exit, result=false); 115 require_noerr_action(workLoop->addEventSource(_commandGate), exit, result=false); 116 117 result = true; 118 119exit: 120 if ( result==false ) { 121 IOLog("%s failed\n", __FUNCTION__); 122 stop(provider); 123 } 124 125 return result; 126} 127 128//---------------------------------------------------------------------------------------------------- 129// IOHIDResourceDeviceUserClient::stop 130//---------------------------------------------------------------------------------------------------- 131void IOHIDResourceDeviceUserClient::stop(IOService * provider) 132{ 133 IOWorkLoop * workLoop = getWorkLoop(); 134 135 require(workLoop, exit); 136 137 if ( _createDeviceTimer ) { 138 _createDeviceTimer->cancelTimeout(); 139 workLoop->removeEventSource(_createDeviceTimer); 140 } 141 142 if ( _commandGate ) { 143 cleanupPendingReports(); 144 145 workLoop->removeEventSource(_commandGate); 146 } 147 148exit: 149 super::stop(provider); 150} 151 152//---------------------------------------------------------------------------------------------------- 153// IOHIDResourceDeviceUserClient::free 154//---------------------------------------------------------------------------------------------------- 155void IOHIDResourceDeviceUserClient::free() 156{ 157 if ( _properties ) 158 _properties->release(); 159 160 if ( _commandGate ) { 161 _commandGate->release(); 162 } 163 164 if ( _createDeviceTimer ) 165 _createDeviceTimer->release(); 166 167 if ( _device ) 168 _device->release(); 169 170 if ( _queue ) 171 _queue->release(); 172 173 if ( _owner ) 174 _owner->release(); 175 176 return super::free(); 177} 178 179//---------------------------------------------------------------------------------------------------- 180// IOHIDResourceDeviceUserClient::registerNotificationPort 181//---------------------------------------------------------------------------------------------------- 182IOReturn IOHIDResourceDeviceUserClient::registerNotificationPort(mach_port_t port, UInt32 type __unused, io_user_reference_t refCon __unused) 183{ 184 IOReturn result; 185 186 require_action(!isInactive(), exit, result=kIOReturnOffline); 187 188 result = _commandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &IOHIDResourceDeviceUserClient::registerNotificationPortGated), port); 189 190exit: 191 return result; 192} 193 194//---------------------------------------------------------------------------------------------------- 195// IOHIDResourceDeviceUserClient::registerNotificationPortGated 196//---------------------------------------------------------------------------------------------------- 197IOReturn IOHIDResourceDeviceUserClient::registerNotificationPortGated(mach_port_t port) 198{ 199 IOReturn result; 200 201 require_action(!isInactive(), exit, result=kIOReturnOffline); 202 203 _port = port; 204 _queue->setNotificationPort(port); 205 206 result = kIOReturnSuccess; 207exit: 208 return result; 209} 210 211//---------------------------------------------------------------------------------------------------- 212// IOHIDResourceDeviceUserClient::clientMemoryForType 213//---------------------------------------------------------------------------------------------------- 214IOReturn IOHIDResourceDeviceUserClient::clientMemoryForType(UInt32 type __unused, IOOptionBits * options, IOMemoryDescriptor ** memory ) 215{ 216 IOReturn result; 217 218 require_action(!isInactive(), exit, result=kIOReturnOffline); 219 220 result = _commandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &IOHIDResourceDeviceUserClient::clientMemoryForTypeGated), options, memory); 221 222exit: 223 return result; 224} 225 226//---------------------------------------------------------------------------------------------------- 227// IOHIDResourceDeviceUserClient::clientMemoryForTypeGated 228//---------------------------------------------------------------------------------------------------- 229IOReturn IOHIDResourceDeviceUserClient::clientMemoryForTypeGated(IOOptionBits * options, IOMemoryDescriptor ** memory ) 230{ 231 IOReturn ret; 232 IOMemoryDescriptor * memoryToShare = NULL; 233 234 require_action(!isInactive(), exit, ret=kIOReturnOffline); 235 236 if ( !_queue ) { 237 _queue = IOHIDResourceQueue::withCapacity(kHIDQueueSize); 238 } 239 240 require_action(_queue, exit, ret = kIOReturnNoMemory); 241 242 memoryToShare = _queue->getMemoryDescriptor(); 243 require_action(memoryToShare, exit, ret = kIOReturnNoMemory); 244 245 memoryToShare->retain(); 246 247 ret = kIOReturnSuccess; 248 249exit: 250 // set the result 251 *options = 0; 252 *memory = memoryToShare; 253 254 return ret; 255} 256//---------------------------------------------------------------------------------------------------- 257// IOHIDResourceDeviceUserClient::externalMethod 258//---------------------------------------------------------------------------------------------------- 259IOReturn IOHIDResourceDeviceUserClient::externalMethod( 260 uint32_t selector, 261 IOExternalMethodArguments * arguments, 262 IOExternalMethodDispatch * dispatch, 263 OSObject * target, 264 void * reference) 265{ 266 ExternalMethodGatedArguments gatedArguments = {selector, arguments, dispatch, target, reference}; 267 IOReturn result; 268 269 require_action(!isInactive(), exit, result=kIOReturnOffline); 270 271 result = _commandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &IOHIDResourceDeviceUserClient::externalMethodGated), &gatedArguments); 272 273exit: 274 return result; 275} 276 277//---------------------------------------------------------------------------------------------------- 278// IOHIDResourceDeviceUserClient::externalMethodGated 279//---------------------------------------------------------------------------------------------------- 280IOReturn IOHIDResourceDeviceUserClient::externalMethodGated(ExternalMethodGatedArguments *arguments) 281{ 282 IOReturn result; 283 284 require_action(!isInactive(), exit, result=kIOReturnOffline); 285 286 require_action(arguments->selector < (uint32_t) kIOHIDResourceDeviceUserClientMethodCount, exit, result=kIOReturnBadArgument); 287 288 arguments->dispatch = (IOExternalMethodDispatch *) &_methods[arguments->selector]; 289 if (!arguments->target) 290 arguments->target = this; 291 292 result = super::externalMethod(arguments->selector, arguments->arguments, arguments->dispatch, arguments->target, arguments->reference); 293 294exit: 295 return result; 296} 297 298//---------------------------------------------------------------------------------------------------- 299// IOHIDResourceDeviceUserClient::createMemoryDescriptorFromInputArguments 300//---------------------------------------------------------------------------------------------------- 301IOMemoryDescriptor * IOHIDResourceDeviceUserClient::createMemoryDescriptorFromInputArguments( 302 IOExternalMethodArguments * arguments) 303{ 304 IOMemoryDescriptor * report = NULL; 305 306 if ( arguments->structureInputDescriptor ) { 307 report = arguments->structureInputDescriptor; 308 report->retain(); 309 } else { 310 report = IOMemoryDescriptor::withAddress((void *)arguments->structureInput, arguments->structureInputSize, kIODirectionOut); 311 } 312 313 return report; 314} 315 316 317//---------------------------------------------------------------------------------------------------- 318// IOHIDResourceDeviceUserClient::getService 319//---------------------------------------------------------------------------------------------------- 320IOService * IOHIDResourceDeviceUserClient::getService(void) 321{ 322 return _owner ? _owner : NULL; 323} 324 325//---------------------------------------------------------------------------------------------------- 326// IOHIDResourceDeviceUserClient::clientClose 327//---------------------------------------------------------------------------------------------------- 328IOReturn IOHIDResourceDeviceUserClient::clientClose(void) 329{ 330 terminate(); 331 return kIOReturnSuccess; 332} 333 334//---------------------------------------------------------------------------------------------------- 335// IOHIDResourceDeviceUserClient::createAndStartDevice 336//---------------------------------------------------------------------------------------------------- 337IOReturn IOHIDResourceDeviceUserClient::createAndStartDevice() 338{ 339 IOReturn result; 340 OSNumber * number = NULL; 341 342 number = OSDynamicCast(OSNumber, _properties->getObject(kIOHIDRequestTimeoutKey)); 343 if ( number ) 344 _maxClientTimeoutUS = number->unsigned32BitValue(); 345 346 // If after all the unwrapping we have a dictionary, let's create the device 347 _device = IOHIDUserDevice::withProperties(_properties); 348 require_action(_device, exit, result=kIOReturnNoResources); 349 350 require_action(_device->attach(this), exit, result=kIOReturnInternalError); 351 352 require_action(_device->start(this), exit, _device->detach(this); result=kIOReturnInternalError); 353 354 result = kIOReturnSuccess; 355 356exit: 357 if ( result!=kIOReturnSuccess ) { 358 IOLog("%s: result=0x%08x\n", __FUNCTION__, result); 359 OSSafeReleaseNULL(_device); 360 } 361 362 return result; 363} 364 365//---------------------------------------------------------------------------------------------------- 366// IOHIDResourceDeviceUserClient::createAndStartDeviceAsyncCallback 367//---------------------------------------------------------------------------------------------------- 368void IOHIDResourceDeviceUserClient::createAndStartDeviceAsyncCallback() 369{ 370 createAndStartDevice(); 371} 372 373//---------------------------------------------------------------------------------------------------- 374// IOHIDResourceDeviceUserClient::createAndStartDeviceAsync 375//---------------------------------------------------------------------------------------------------- 376IOReturn IOHIDResourceDeviceUserClient::createAndStartDeviceAsync() 377{ 378 _createDeviceTimer->setTimeoutMS(0); 379 return kIOReturnSuccess; 380} 381 382//---------------------------------------------------------------------------------------------------- 383// IOHIDResourceDeviceUserClient::createDevice 384//---------------------------------------------------------------------------------------------------- 385IOReturn IOHIDResourceDeviceUserClient::createDevice(IOExternalMethodArguments * arguments) 386{ 387 IOMemoryDescriptor * propertiesDesc = NULL; 388 void * propertiesData = NULL; 389 IOByteCount propertiesLength = 0; 390 OSObject * object = NULL; 391 IOReturn result; 392 393 // Report descriptor is static and thus can only be set on creation 394 require_action(_device==NULL, exit, result=kIOReturnInternalError); 395 396 // Let's deal with our device properties from data 397 propertiesDesc = createMemoryDescriptorFromInputArguments(arguments); 398 require_action(propertiesDesc, exit, result=kIOReturnNoMemory); 399 400 propertiesLength = propertiesDesc->getLength(); 401 require_action(propertiesLength, exit, result=kIOReturnNoResources); 402 403 propertiesData = IOMalloc(propertiesLength); 404 require_action(propertiesData, exit, result=kIOReturnNoMemory); 405 406 propertiesDesc->readBytes(0, propertiesData, propertiesLength); 407 408 require_action(strnlen((const char *) propertiesData, propertiesLength) < propertiesLength, exit, result=kIOReturnInternalError); 409 410 object = OSUnserializeXML((const char *)propertiesData, propertiesLength); 411 require_action(object, exit, result=kIOReturnInternalError); 412 413 _properties = OSDynamicCast(OSDictionary, object); 414 require_action(_properties, exit, result=kIOReturnNoMemory); 415 416 _properties->retain(); 417 418 if ( arguments->scalarInput[0] ) 419 result = createAndStartDeviceAsync(); 420 else 421 result = createAndStartDevice(); 422 423 require_noerr(result, exit); 424 425exit: 426 427 if ( object ) 428 object->release(); 429 430 if ( propertiesData && propertiesLength ) 431 IOFree(propertiesData, propertiesLength); 432 433 if ( propertiesDesc ) 434 propertiesDesc->release(); 435 436 return result; 437} 438 439//---------------------------------------------------------------------------------------------------- 440// IOHIDResourceDeviceUserClient::_createDevice 441//---------------------------------------------------------------------------------------------------- 442IOReturn IOHIDResourceDeviceUserClient::_createDevice( 443 IOHIDResourceDeviceUserClient * target, 444 void * reference __unused, 445 IOExternalMethodArguments * arguments) 446{ 447 return target->createDevice(arguments); 448} 449 450struct IOHIDResourceDeviceUserClientAsyncParamBlock { 451 OSAsyncReference64 fAsyncRef; 452 uint32_t fAsyncCount; 453}; 454 455void IOHIDResourceDeviceUserClient::ReportComplete(void *param, IOReturn res, UInt32 remaining __unused) 456{ 457 IOHIDResourceDeviceUserClientAsyncParamBlock *pb = (IOHIDResourceDeviceUserClientAsyncParamBlock *)param; 458 459 io_user_reference_t args[1]; 460 args[0] = 0; 461 462 sendAsyncResult64(pb->fAsyncRef, res, args, 0); 463 IOFree(pb, sizeof(*pb)); 464 465 release(); 466} 467 468//---------------------------------------------------------------------------------------------------- 469// IOHIDResourceDeviceUserClient::handleReport 470//---------------------------------------------------------------------------------------------------- 471IOReturn IOHIDResourceDeviceUserClient::handleReport(IOExternalMethodArguments * arguments) 472{ 473 AbsoluteTime timestamp; 474 475 if (_device == NULL) { 476 IOLog("%s failed : device is NULL\n", __FUNCTION__); 477 return kIOReturnNotOpen; 478 } 479 480 IOReturn ret; 481 IOMemoryDescriptor * report; 482 483 report = createMemoryDescriptorFromInputArguments(arguments); 484 if ( !report ) { 485 IOLog("%s failed : could not create descriptor\n", __FUNCTION__); 486 return kIOReturnNoMemory; 487 } 488 489 if ( arguments->scalarInput[0] ) 490 AbsoluteTime_to_scalar(×tamp) = arguments->scalarInput[0]; 491 else 492 clock_get_uptime( ×tamp ); 493 494 if ( !arguments->asyncWakePort ) { 495 ret = _device->handleReportWithTime(timestamp, report); 496 report->release(); 497 } else { 498 IOHIDCompletion tap; 499 500 IOHIDResourceDeviceUserClientAsyncParamBlock *pb = 501 (IOHIDResourceDeviceUserClientAsyncParamBlock *)IOMalloc(sizeof(IOHIDResourceDeviceUserClientAsyncParamBlock)); 502 503 if (!pb) { 504 report->release(); 505 return kIOReturnNoMemory; // need to release report 506 } 507 508 retain(); 509 510 bcopy(arguments->asyncReference, pb->fAsyncRef, sizeof(OSAsyncReference64)); 511 pb->fAsyncCount = arguments->asyncReferenceCount; 512 513 tap.target = this; 514 tap.action = OSMemberFunctionCast(IOHIDCompletionAction, this, &IOHIDResourceDeviceUserClient::ReportComplete); 515 tap.parameter = pb; 516 517 ret = _device->handleReportWithTimeAsync(timestamp, report, kIOHIDReportTypeInput, 0, 0, &tap); 518 519 report->release(); 520 521 if (ret != kIOReturnSuccess) { 522 IOFree(pb, sizeof(*pb)); 523 release(); 524 } 525 } 526 527 return ret; 528} 529 530//---------------------------------------------------------------------------------------------------- 531// IOHIDResourceDeviceUserClient::_handleReport 532//---------------------------------------------------------------------------------------------------- 533IOReturn IOHIDResourceDeviceUserClient::_handleReport(IOHIDResourceDeviceUserClient *target, 534 void *reference __unused, 535 IOExternalMethodArguments *arguments) 536{ 537 return target->handleReport(arguments); 538} 539 540typedef struct { 541 IOReturn ret; 542 IOMemoryDescriptor * descriptor; 543} __ReportResult; 544 545//---------------------------------------------------------------------------------------------------- 546// IOHIDResourceDeviceUserClient::getReport 547//---------------------------------------------------------------------------------------------------- 548IOReturn IOHIDResourceDeviceUserClient::getReport(IOMemoryDescriptor *report, IOHIDReportType reportType, IOOptionBits options) 549{ 550 ReportGatedArguments arguments = {report, reportType, options}; 551 IOReturn result; 552 553 require_action(!isInactive(), exit, result=kIOReturnOffline); 554 555 result = _commandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &IOHIDResourceDeviceUserClient::getReportGated), &arguments); 556exit: 557 return result; 558} 559 560//---------------------------------------------------------------------------------------------------- 561// IOHIDResourceDeviceUserClient::getReport 562//---------------------------------------------------------------------------------------------------- 563IOReturn IOHIDResourceDeviceUserClient::getReportGated(ReportGatedArguments * arguments) 564{ 565 IOHIDResourceDataQueueHeader header; 566 __ReportResult result; 567 AbsoluteTime ts; 568 IOReturn ret; 569 OSData * retData = NULL; 570 571 require_action(!isInactive(), exit, ret=kIOReturnOffline); 572 573 result.descriptor = arguments->report; 574 575 retData = OSData::withBytesNoCopy(&result, sizeof(__ReportResult)); 576 require_action(retData, exit, ret=kIOReturnNoMemory); 577 578 header.direction = kIOHIDResourceReportDirectionIn; 579 header.type = arguments->reportType; 580 header.reportID = arguments->options&0xff; 581 header.length = (uint32_t)arguments->report->getLength(); 582 header.token = (intptr_t)retData; 583 584 _pending->setObject(retData); 585 586 require_action(_queue && _queue->enqueueReport(&header), exit, ret=kIOReturnNoMemory); 587 588 // if we successfully enqueue, let's sleep till we get a result from postReportResult 589 clock_interval_to_deadline(kMicrosecondScale, _maxClientTimeoutUS, &ts); 590 591 switch ( _commandGate->commandSleep(retData, ts, THREAD_ABORTSAFE) ) { 592 case THREAD_AWAKENED: 593 ret = result.ret; 594 break; 595 case THREAD_TIMED_OUT: 596 ret = kIOReturnTimeout; 597 break; 598 default: 599 ret = kIOReturnError; 600 break; 601 } 602 603exit: 604 if ( retData ) { 605 _pending->removeObject(retData); 606 _commandGate->commandWakeup(&_pending); 607 retData->release(); 608 } 609 610 return ret; 611} 612 613//---------------------------------------------------------------------------------------------------- 614// IOHIDResourceDeviceUserClient::setReport 615//---------------------------------------------------------------------------------------------------- 616IOReturn IOHIDResourceDeviceUserClient::setReport(IOMemoryDescriptor *report, IOHIDReportType reportType, IOOptionBits options) 617{ 618 ReportGatedArguments arguments={report, reportType, options}; 619 IOReturn result; 620 621 require_action(!isInactive(), exit, result=kIOReturnOffline); 622 623 result = _commandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &IOHIDResourceDeviceUserClient::setReportGated), &arguments); 624exit: 625 return result; 626} 627 628//---------------------------------------------------------------------------------------------------- 629// IOHIDResourceDeviceUserClient::setReportGated 630//---------------------------------------------------------------------------------------------------- 631IOReturn IOHIDResourceDeviceUserClient::setReportGated(ReportGatedArguments * arguments) 632{ 633 IOHIDResourceDataQueueHeader header; 634 __ReportResult result; 635 AbsoluteTime ts; 636 IOReturn ret; 637 OSData * retData = NULL; 638 639 require_action(!isInactive(), exit, ret=kIOReturnOffline); 640 641 bzero(&result, sizeof(result)); 642 643 retData = OSData::withBytesNoCopy(&result, sizeof(result)); 644 require_action(retData, exit, ret=kIOReturnNoMemory); 645 646 header.direction = kIOHIDResourceReportDirectionOut; 647 header.type = arguments->reportType; 648 header.reportID = arguments->options&0xff; 649 header.length = (uint32_t)arguments->report->getLength(); 650 header.token = (intptr_t)retData; 651 652 _pending->setObject(retData); 653 654 require_action(_queue && _queue->enqueueReport(&header, arguments->report), exit, ret=kIOReturnNoMemory); 655 656 // if we successfully enqueue, let's sleep till we get a result from postReportResult 657 clock_interval_to_deadline(kMicrosecondScale, _maxClientTimeoutUS, (uint64_t *)&ts); 658 659 switch ( _commandGate->commandSleep(retData, ts, THREAD_ABORTSAFE) ) { 660 case THREAD_AWAKENED: 661 ret = result.ret; 662 break; 663 case THREAD_TIMED_OUT: 664 ret = kIOReturnTimeout; 665 break; 666 default: 667 ret = kIOReturnError; 668 break; 669 } 670 671exit: 672 if ( retData ) { 673 _pending->removeObject(retData); 674 _commandGate->commandWakeup(&_pending); 675 retData->release(); 676 } 677 678 return ret; 679} 680 681//---------------------------------------------------------------------------------------------------- 682// IOHIDResourceDeviceUserClient::postReportResult 683//---------------------------------------------------------------------------------------------------- 684IOReturn IOHIDResourceDeviceUserClient::postReportResult(IOExternalMethodArguments * arguments) 685{ 686 OSObject * tokenObj = (OSObject*)arguments->scalarInput[kIOHIDResourceUserClientResponseIndexToken]; 687 688 if ( tokenObj && _pending->containsObject(tokenObj) ) { 689 OSData * data = OSDynamicCast(OSData, tokenObj); 690 if ( data ) { 691 __ReportResult * pResult = (__ReportResult*)data->getBytesNoCopy(); 692 693 // RY: HIGHLY UNLIKELY > 4K 694 if ( pResult->descriptor && arguments->structureInput ) { 695 pResult->descriptor->writeBytes(0, arguments->structureInput, arguments->structureInputSize); 696 697 // 12978252: If we get an IOBMD passed in, set the length to be the # of bytes that were transferred 698 IOBufferMemoryDescriptor * buffer = OSDynamicCast(IOBufferMemoryDescriptor, pResult->descriptor); 699 if (buffer) 700 buffer->setLength((vm_size_t)arguments->structureInputSize); 701 702 } 703 704 pResult->ret = (IOReturn)arguments->scalarInput[kIOHIDResourceUserClientResponseIndexResult]; 705 706 _commandGate->commandWakeup(data); 707 } 708 709 } 710 711 return kIOReturnSuccess; 712} 713 714//---------------------------------------------------------------------------------------------------- 715// IOHIDResourceDeviceUserClient::_postReportResult 716//---------------------------------------------------------------------------------------------------- 717IOReturn IOHIDResourceDeviceUserClient::_postReportResult(IOHIDResourceDeviceUserClient *target, 718 void *reference __unused, 719 IOExternalMethodArguments *arguments) 720{ 721 return target->postReportResult(arguments); 722} 723 724//---------------------------------------------------------------------------------------------------- 725// IOHIDResourceDeviceUserClient::cleanupPendingReports 726//---------------------------------------------------------------------------------------------------- 727void IOHIDResourceDeviceUserClient::cleanupPendingReports() 728{ 729 OSCollectionIterator * iterator; 730 OSObject * object; 731 732 iterator = OSCollectionIterator::withCollection(_pending); 733 if ( !iterator ) 734 return; 735 736 while ( (object = iterator->getNextObject()) ) { 737 __ReportResult * pResult = (__ReportResult*)((OSData*)object)->getBytesNoCopy(); 738 739 pResult->ret = kIOReturnAborted; 740 741 _commandGate->commandWakeup(object); 742 } 743 744 iterator->release(); 745 746 while ( _pending->getCount() ) { 747 _commandGate->commandSleep(&_pending); 748 } 749} 750 751//---------------------------------------------------------------------------------------------------- 752// IOHIDResourceDeviceUserClient::terminateDevice 753//---------------------------------------------------------------------------------------------------- 754IOReturn IOHIDResourceDeviceUserClient::terminateDevice() 755{ 756 if (_device) { 757 _device->terminate(); 758 } 759 OSSafeRelease(_device); 760 761 return kIOReturnSuccess; 762} 763 764//---------------------------------------------------------------------------------------------------- 765// IOHIDResourceDeviceUserClient::_terminateDevice 766//---------------------------------------------------------------------------------------------------- 767IOReturn IOHIDResourceDeviceUserClient::_terminateDevice( 768 IOHIDResourceDeviceUserClient *target, 769 void *reference __unused, 770 IOExternalMethodArguments *arguments __unused) 771{ 772 return target->terminateDevice(); 773} 774 775 776//==================================================================================================== 777// IOHIDResourceQueue 778//==================================================================================================== 779#include <IOKit/IODataQueueShared.h> 780OSDefineMetaClassAndStructors( IOHIDResourceQueue, IOSharedDataQueue ) 781 782IOHIDResourceQueue *IOHIDResourceQueue::withCapacity(UInt32 capacity) 783{ 784 IOHIDResourceQueue *dataQueue = new IOHIDResourceQueue; 785 786 if (dataQueue) { 787 if (!dataQueue->initWithCapacity(capacity)) { 788 dataQueue->release(); 789 dataQueue = 0; 790 } 791 } 792 793 return dataQueue; 794} 795 796void IOHIDResourceQueue::free() 797{ 798 if ( _descriptor ) 799 { 800 _descriptor->release(); 801 _descriptor = 0; 802 } 803 804 IOSharedDataQueue::free(); 805} 806 807#define ALIGNED_DATA_SIZE(data_size,align_size) ((((data_size - 1) / align_size) + 1) * align_size) 808 809Boolean IOHIDResourceQueue::enqueueReport(IOHIDResourceDataQueueHeader * header, IOMemoryDescriptor * report) 810{ 811 UInt32 headerSize = sizeof(IOHIDResourceDataQueueHeader); 812 UInt32 reportSize = report ? (UInt32)report->getLength() : 0; 813 UInt32 dataSize = ALIGNED_DATA_SIZE(headerSize + reportSize, sizeof(uint32_t)); 814 const UInt32 head = dataQueue->head; // volatile 815 const UInt32 tail = dataQueue->tail; 816 const UInt32 entrySize = dataSize + DATA_QUEUE_ENTRY_HEADER_SIZE; 817 IODataQueueEntry * entry; 818 819 if ( tail >= head ) 820 { 821 // Is there enough room at the end for the entry? 822 if ( (tail + entrySize) <= getQueueSize() ) 823 { 824 entry = (IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail); 825 826 entry->size = dataSize; 827 828 bcopy(header, &entry->data, headerSize); 829 830 if ( report ) 831 report->readBytes(0, ((UInt8*)&entry->data) + headerSize, reportSize); 832 833 // The tail can be out of bound when the size of the new entry 834 // exactly matches the available space at the end of the queue. 835 // The tail can range from 0 to getQueueSize() inclusive. 836 837 dataQueue->tail += entrySize; 838 } 839 else if ( head > entrySize ) // Is there enough room at the beginning? 840 { 841 // Wrap around to the beginning, but do not allow the tail to catch 842 // up to the head. 843 844 dataQueue->queue->size = dataSize; 845 846 // We need to make sure that there is enough room to set the size before 847 // doing this. The user client checks for this and will look for the size 848 // at the beginning if there isn't room for it at the end. 849 850 if ( ( getQueueSize() - tail ) >= DATA_QUEUE_ENTRY_HEADER_SIZE ) 851 { 852 ((IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail))->size = dataSize; 853 } 854 855 bcopy(header, &dataQueue->queue->data, sizeof(IOHIDResourceDataQueueHeader)); 856 if ( report ) 857 report->readBytes(0, ((UInt8*)&dataQueue->queue->data) + headerSize, reportSize); 858 dataQueue->tail = entrySize; 859 } 860 else 861 { 862 return false; // queue is full 863 } 864 } 865 else 866 { 867 // Do not allow the tail to catch up to the head when the queue is full. 868 // That's why the comparison uses a '>' rather than '>='. 869 870 if ( (head - tail) > entrySize ) 871 { 872 entry = (IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail); 873 874 entry->size = dataSize; 875 876 bcopy(header, &entry->data, sizeof(IOHIDResourceDataQueueHeader)); 877 if ( report ) 878 report->readBytes(0, ((UInt8*)&entry->data) + headerSize, reportSize); 879 dataQueue->tail += entrySize; 880 } 881 else 882 { 883 return false; // queue is full 884 } 885 } 886 887 // Send notification (via mach message) that data is available if either the 888 // queue was empty prior to enqueue() or queue was emptied during enqueue() 889 if ( ( head == tail ) || ( dataQueue->head == tail ) ) 890 sendDataAvailableNotification(); 891 892 return true; 893} 894 895 896void IOHIDResourceQueue::setNotificationPort(mach_port_t port) 897{ 898 IOSharedDataQueue::setNotificationPort(port); 899 900 if (dataQueue->head != dataQueue->tail) 901 sendDataAvailableNotification(); 902} 903 904IOMemoryDescriptor * IOHIDResourceQueue::getMemoryDescriptor() 905{ 906 if (!_descriptor) 907 _descriptor = IOSharedDataQueue::getMemoryDescriptor(); 908 909 return _descriptor; 910} 911