IOUSBHIDDriver.cpp [plain text]
#include <libkern/OSByteOrder.h>
#include <IOKit/IOBufferMemoryDescriptor.h>
#include <IOKit/IOMessage.h>
#include <IOKit/hid/IOHIDKeys.h>
#include <IOKit/usb/IOUSBInterface.h>
#include <IOKit/usb/IOUSBLog.h>
#include <IOKit/usb/IOUSBPipe.h>
#include <IOKit/usb/IOUSBHIDDriver.h>
#define super IOHIDDevice
#define _workLoop _usbHIDExpansionData->_workLoop
#define _handleReportThread _usbHIDExpansionData->_handleReportThread
#define _rootDomain _usbHIDExpansionData->_rootDomain
#define _wakeUpTime _usbHIDExpansionData->_wakeUpTime
#define _completionWithTimeStamp _usbHIDExpansionData->_completionWithTimeStamp
#define _checkForTimeStamp _usbHIDExpansionData->_checkForTimeStamp
#define _msToIgnoreTransactionsAfterWake _usbHIDExpansionData->_msToIgnoreTransactionsAfterWake
OSDefineMetaClassAndStructors(IOUSBHIDDriver, IOHIDDevice)
#pragma mark ееееееее IOService Methods еееееееее
bool
IOUSBHIDDriver::init(OSDictionary *properties)
{
if (!super::init(properties))
{
return false;
}
if (!_usbHIDExpansionData)
{
_usbHIDExpansionData = (IOUSBHIDDriverExpansionData *)IOMalloc(sizeof(IOUSBHIDDriverExpansionData));
if (!_usbHIDExpansionData)
return false;
bzero(_usbHIDExpansionData, sizeof(IOUSBHIDDriverExpansionData));
}
_retryCount = kHIDDriverRetryCount;
_maxReportSize = kMaxHIDReportSize;
return true;
}
void
IOUSBHIDDriver::free()
{
if (_gate)
{
if (_workLoop)
{
_workLoop->removeEventSource(_gate);
_workLoop->release();
_workLoop = NULL;
}
_gate->release();
_gate = NULL;
}
if (_usbHIDExpansionData)
{
IOFree(_usbHIDExpansionData, sizeof(IOUSBHIDDriverExpansionData));
_usbHIDExpansionData = NULL;
}
super::free();
}
bool
IOUSBHIDDriver::start(IOService *provider)
{
IOReturn err = kIOReturnSuccess;
IOWorkLoop * workLoop = NULL;
IOCommandGate * commandGate = NULL;
OSNumber * locationIDProperty;
UInt32 locationID = 0;
IOUSBFindEndpointRequest request;
bool addEventSourceSuccess = false;
UInt32 maxInputReportSize = 0;
OSNumber * inputReportSize = NULL;
USBLog(7, "%s[%p]::start", getName(), this);
IncrementOutstandingIO();
if (!super::start(provider))
{
USBError(1, "%s[%p]::start - super::start returned false!", getName(), this);
goto ErrorExit;
}
commandGate = IOCommandGate::commandGate(this);
if (!commandGate)
{
USBError(1, "%s[%p]::start - could not get a command gate", getName(), this);
goto ErrorExit;
}
workLoop = getWorkLoop();
if ( !workLoop)
{
USBError(1, "%s[%p]::start - unable to find my workloop", getName(), this);
goto ErrorExit;
}
workLoop->retain();
if (workLoop->addEventSource(commandGate) != kIOReturnSuccess)
{
USBError(1, "%s[%p]::start - unable to add gate to work loop", getName(), this);
goto ErrorExit;
}
addEventSourceSuccess = true;
request.type = kUSBInterrupt;
request.direction = kUSBOut;
_interruptOutPipe = _interface->FindNextPipe(NULL, &request);
request.type = kUSBInterrupt;
request.direction = kUSBIn;
_interruptPipe = _interface->FindNextPipe(NULL, &request);
if (!_interruptPipe)
{
USBError(1, "%s[%p]::start - unable to get interrupt pipe", getName(), this);
goto ErrorExit;
}
inputReportSize = (OSNumber *)getProperty(kIOHIDMaxInputReportSizeKey);
if ( inputReportSize )
maxInputReportSize = inputReportSize->unsigned32BitValue();
if (maxInputReportSize == 0)
maxInputReportSize = _interruptPipe->GetMaxPacketSize();
if ( maxInputReportSize > 0 )
{
_buffer = IOBufferMemoryDescriptor::withCapacity(maxInputReportSize, kIODirectionIn);
if ( !_buffer )
{
USBError(1, "%s[%p]::start - unable to get create buffer", getName(), this);
goto ErrorExit;
}
}
else
{
USBLog(5, "%s[%p]::start - Device reports maxInputReportSize of 0", getName(), this);
_buffer = NULL;
}
if ( (_device->GetVendorID()) == 0x06a3 )
SetIdleMillisecs(0);
if ( (_interface->GetInterfaceClass() == kUSBHIDClass) &&
(_interface->GetInterfaceSubClass() == kUSBHIDBootInterfaceSubClass) &&
(_interface->GetInterfaceProtocol() == kHIDKeyboardInterfaceProtocol) )
{
if (_device->GetVendorID() == kIOUSBVendorIDAppleComputer)
{
SetIdleMillisecs(0);
}
else
{
SetIdleMillisecs(24);
}
}
if (_interface->GetInterfaceSubClass() == kUSBHIDBootInterfaceSubClass)
err = SetProtocol( kHIDReportProtocolValue );
_deviceDeadCheckThread = thread_call_allocate((thread_call_func_t)CheckForDeadDeviceEntry, (thread_call_param_t)this);
_clearFeatureEndpointHaltThread = thread_call_allocate((thread_call_func_t)ClearFeatureEndpointHaltEntry, (thread_call_param_t)this);
_handleReportThread = thread_call_allocate((thread_call_func_t)HandleReportEntry, (thread_call_param_t)this);
if ( !_deviceDeadCheckThread || !_clearFeatureEndpointHaltThread || !_handleReportThread )
{
USBError(1, "[%s]%p: could not allocate all thread functions", getName(), this);
goto ErrorExit;
}
locationIDProperty = (OSNumber *) provider->getProperty(kUSBDevicePropertyLocationID);
if ( locationIDProperty )
{
locationID = locationIDProperty->unsigned32BitValue();
}
err = StartFinalProcessing();
if (err != kIOReturnSuccess)
{
USBError(1, "%s[%p]::start - err (%x) in StartFinalProcessing", getName(), this, err);
goto ErrorExit;
}
USBLog(1, "[%p]::start - USB HID Interface #%d of device %s @ %d (0x%x)",this, _interface->GetInterfaceNumber(), _device->getName(), _device->GetAddress(), locationID );
_gate = commandGate;
_workLoop = workLoop;
DecrementOutstandingIO(); return true;
ErrorExit:
USBError(1, "%s[%p]::start - aborting startup", getName(), this);
if ( commandGate != NULL )
{
if ( addEventSourceSuccess )
workLoop->removeEventSource(commandGate);
commandGate->release();
commandGate = NULL;
}
if ( workLoop != NULL )
{
workLoop->release();
workLoop = NULL;
}
if (_deviceDeadCheckThread)
thread_call_free(_deviceDeadCheckThread);
if (_clearFeatureEndpointHaltThread)
thread_call_free(_clearFeatureEndpointHaltThread);
if (_handleReportThread)
thread_call_free(_handleReportThread);
if (_interface)
_interface->close(this);
DecrementOutstandingIO(); return false;
}
IOReturn
IOUSBHIDDriver::message( UInt32 type, IOService * provider, void * argument )
{
IOReturn err;
err = super::message (type, provider, argument);
switch ( type )
{
case kIOUSBMessagePortHasBeenReset:
USBLog(3, "%s[%p]: received kIOUSBMessagePortHasBeenReset", getName(), this);
_retryCount = kHIDDriverRetryCount;
_deviceIsDead = FALSE;
_deviceHasBeenDisconnected = FALSE;
err = RearmInterruptRead();
break;
default:
break;
}
return err;
}
bool
IOUSBHIDDriver::willTerminate( IOService * provider, IOOptionBits options )
{
USBLog(3, "%s[%p]::willTerminate isInactive = %d", getName(), this, isInactive());
if (_interruptPipe)
{
_interruptPipe->Abort();
}
return super::willTerminate(provider, options);
}
bool
IOUSBHIDDriver::didTerminate( IOService * provider, IOOptionBits options, bool * defer )
{
USBLog(3, "%s[%p]::didTerminate isInactive = %d, outstandingIO = %d", getName(), this, isInactive(), _outstandingIO);
if (!_outstandingIO)
_interface->close(this);
else
_needToClose = true;
return super::didTerminate(provider, options, defer);
}
#pragma mark ееееееее IOHIDDevice Methods еееееееее
bool
IOUSBHIDDriver::handleStart(IOService * provider)
{
USBLog(7, "%s[%p]::handleStart", getName(), this);
if ( !super::handleStart(provider))
{
return false;
}
if( !provider->open(this))
{
USBError(1, "%s[%p]::handleStart - unable to open provider. returning false", getName(), this);
return (false);
}
_interface = OSDynamicCast(IOUSBInterface, provider);
if (!_interface)
{
USBError(1, "%s[%p]::handleStart - Our provider is not an IOUSBInterface!!", getName(), this);
return false;
}
_device = _interface->GetDevice();
if (!_device)
{
USBError(1, "%s[%p]::handleStart - Cannot get our provider's USB device. This is bad.", getName(), this);
return false;
}
return true;
}
void
IOUSBHIDDriver::handleStop(IOService * provider)
{
USBLog(7, "%s[%p]::handleStop", getName(), this);
if (_buffer)
{
_buffer->release();
_buffer = NULL;
}
if (_deviceDeadCheckThread)
{
thread_call_cancel(_deviceDeadCheckThread);
thread_call_free(_deviceDeadCheckThread);
}
if (_clearFeatureEndpointHaltThread)
{
thread_call_cancel(_clearFeatureEndpointHaltThread);
thread_call_free(_clearFeatureEndpointHaltThread);
}
if (_handleReportThread)
{
thread_call_cancel(_handleReportThread);
thread_call_free(_handleReportThread);
}
super::handleStop(provider);
}
IOReturn
IOUSBHIDDriver::getReport( IOMemoryDescriptor * report,
IOHIDReportType reportType,
IOOptionBits options )
{
UInt8 reportID;
IOReturn ret;
UInt8 usbReportType;
IOUSBDevRequestDesc requestPB;
if ( _device->GetVendorID() == 0x046d )
{
UInt16 prodID = _device->GetProductID();
if ( (prodID == 0xc202) || (prodID == 0xc207) || (prodID == 0xc208) ||
(prodID == 0xc209) || (prodID == 0xc20a) || (prodID == 0xc212) ||
(prodID == 0xc285) || (prodID == 0xc293) || (prodID == 0xc294) ||
(prodID == 0xc295) || (prodID == 0xc283) )
{
return kIOReturnUnsupported;
}
}
IncrementOutstandingIO();
reportID = (UInt8) ( options & 0x000000ff);
usbReportType = HIDMGR2USBREPORTTYPE(reportType);
#if ENABLE_HIDREPORT_LOGGING
USBLog(3, "%s[%p]::getReport (%d, %p)", getName(), this, reportID, usbReportType);
#endif
requestPB.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBClass, kUSBInterface);
requestPB.bRequest = kHIDRqGetReport;
requestPB.wValue = (usbReportType << 8) | reportID;
requestPB.wIndex = _interface->GetInterfaceNumber();
requestPB.wLength = report->getLength();
requestPB.pData = report;
requestPB.wLenDone = 0;
ret = _device->DeviceRequest(&requestPB);
if ( ret != kIOReturnSuccess )
USBLog(3, "%s[%p]::getReport request failed; err = 0x%x)", getName(), this, ret);
DecrementOutstandingIO();
#if ENABLE_HIDREPORT_LOGGING
USBLog(3, "%s[%p]::getReport returning", getName(), this);
#endif
return ret;
}
IOReturn
IOUSBHIDDriver::setReport( IOMemoryDescriptor * report,
IOHIDReportType reportType,
IOOptionBits options)
{
UInt8 reportID;
IOReturn ret;
UInt8 usbReportType;
IOUSBDevRequestDesc requestPB;
IncrementOutstandingIO();
reportID = (UInt8) ( options & 0x000000ff);
usbReportType = HIDMGR2USBREPORTTYPE(reportType);
if ( kHIDOutputReport == usbReportType && _interruptOutPipe )
{
#if ENABLE_HIDREPORT_LOGGING
USBLog(3, "%s[%p]::setReport sending out interrupt out pipe buffer (%p,%d):", getName(), this, report, report->getLength() );
LogMemReport(report);
#endif
ret = _interruptOutPipe->Write(report);
if (ret == kIOReturnSuccess)
{
DecrementOutstandingIO();
return ret;
}
else
{
USBLog(3, "%s[%p]::setReport _interruptOutPipe->Write failed; err = 0x%x)", getName(), this, ret);
}
}
#if ENABLE_HIDREPORT_LOGGING
USBLog(3, "%s[%p]::SetReport sending out control pipe:", getName(), this);
LogMemReport( report);
#endif
requestPB.bmRequestType = USBmakebmRequestType(kUSBOut, kUSBClass, kUSBInterface);
requestPB.bRequest = kHIDRqSetReport;
requestPB.wValue = (usbReportType << 8) | reportID;
requestPB.wIndex = _interface->GetInterfaceNumber();
requestPB.wLength = report->getLength();
requestPB.pData = report;
requestPB.wLenDone = 0;
ret = _device->DeviceRequest(&requestPB);
if (ret != kIOReturnSuccess)
USBLog(3, "%s[%p]::setReport request failed; err = 0x%x)", getName(), this, ret);
DecrementOutstandingIO();
#if ENABLE_HIDREPORT_LOGGING
USBLog(3, "%s[%p]::setReport returning", getName(), this);
#endif
return ret;
}
OSNumber *
IOUSBHIDDriver::newLocationIDNumber() const
{
OSNumber * newLocationID = NULL;
if (_interface != NULL)
{
OSNumber * locationID = (OSNumber *)_interface->getProperty(kUSBDevicePropertyLocationID);
if ( locationID )
newLocationID = OSNumber::withNumber(locationID->unsigned32BitValue(), 32);
}
return newLocationID;
}
OSString *
IOUSBHIDDriver::newManufacturerString() const
{
char manufacturerString[256];
UInt32 strSize;
UInt8 index;
IOReturn err;
manufacturerString[0] = 0;
index = _device->GetManufacturerStringIndex();
strSize = sizeof(manufacturerString);
err = GetIndexedString(index, (UInt8 *)manufacturerString, &strSize);
if ( err == kIOReturnSuccess )
return OSString::withCString(manufacturerString);
else
return NULL;
}
OSNumber *
IOUSBHIDDriver::newProductIDNumber() const
{
UInt16 productID = 0;
if (_device != NULL)
productID = _device->GetProductID();
return OSNumber::withNumber(productID, 16);
}
OSString *
IOUSBHIDDriver::newProductString() const
{
char productString[256];
UInt32 strSize;
UInt8 index;
IOReturn err;
productString[0] = 0;
index = _device->GetProductStringIndex();
strSize = sizeof(productString);
err = GetIndexedString(index, (UInt8 *)productString, &strSize);
if ( err == kIOReturnSuccess )
return OSString::withCString(productString);
else
return NULL;
}
IOReturn
IOUSBHIDDriver::newReportDescriptor(IOMemoryDescriptor ** desc) const
{
IOBufferMemoryDescriptor * bufferDesc = NULL;
IOReturn ret = kIOReturnNoMemory;
IOUSBHIDDriver * me = (IOUSBHIDDriver *) this;
UInt32 inOutSize = 0;
ret = me->GetHIDDescriptor(kUSBReportDesc, 0, NULL, &inOutSize);
if ( ret == kIOReturnSuccess && inOutSize != 0)
{
bufferDesc = IOBufferMemoryDescriptor::withCapacity(inOutSize, kIODirectionOutIn);
}
if (bufferDesc)
{
ret = me->GetHIDDescriptor(kUSBReportDesc, 0, (UInt8 *)bufferDesc->getBytesNoCopy(), &inOutSize);
if ( ret != kIOReturnSuccess )
{
bufferDesc->release();
bufferDesc = NULL;
}
}
*desc = bufferDesc;
return ret;
}
OSString *
IOUSBHIDDriver::newSerialNumberString() const
{
char serialNumberString[256];
UInt32 strSize;
UInt8 index;
IOReturn err;
serialNumberString[0] = 0;
index = _device->GetSerialNumberStringIndex();
strSize = sizeof(serialNumberString);
err = GetIndexedString(index, (UInt8 *)serialNumberString, &strSize);
if ( err == kIOReturnSuccess )
return OSString::withCString(serialNumberString);
else
return NULL;
}
OSString *
IOUSBHIDDriver::newTransportString() const
{
return OSString::withCString("USB");
}
OSNumber *
IOUSBHIDDriver::newVendorIDNumber() const
{
UInt16 vendorID = 0;
if (_device != NULL)
vendorID = _device->GetVendorID();
return OSNumber::withNumber(vendorID, 16);
}
OSNumber *
IOUSBHIDDriver::newVersionNumber() const
{
UInt16 releaseNum = 0;
if (_device != NULL)
releaseNum = _device->GetDeviceRelease();
return OSNumber::withNumber(releaseNum, 16);
}
OSNumber *
IOUSBHIDDriver::newCountryCodeNumber() const
{
IOUSBHIDDescriptor *theHIDDesc;
if (!_interface)
{
USBLog(2, "%s[%p]::newCountryCodeNumber - no _interface", getName(), this);
return NULL;
}
theHIDDesc = (IOUSBHIDDescriptor *)_interface->FindNextAssociatedDescriptor(NULL, kUSBHIDDesc);
if (theHIDDesc == NULL)
{
USBLog(2, "%s[%p]::newCountryCodeNumber - FindNextAssociatedDescriptor(NULL, kUSBHIDDesc) failed", getName(), this);
return NULL;
}
return OSNumber::withNumber((unsigned long long)theHIDDesc->hidCountryCode, 8);
}
#pragma mark ееееееее Static Methods еееееееее
void
IOUSBHIDDriver::InterruptReadHandlerEntry(OSObject *target, void *param, IOReturn status, UInt32 bufferSizeRemaining)
{
IOUSBHIDDriver * me = OSDynamicCast(IOUSBHIDDriver, target);
AbsoluteTime timeStamp;
if (!me)
return;
clock_get_uptime(&timeStamp);
me->InterruptReadHandler(status, bufferSizeRemaining, timeStamp);
me->DecrementOutstandingIO();
}
void
IOUSBHIDDriver::InterruptReadHandlerWithTimeStampEntry(OSObject *target, void *param, IOReturn status, UInt32 bufferSizeRemaining, AbsoluteTime timeStamp)
{
IOUSBHIDDriver * me = OSDynamicCast(IOUSBHIDDriver, target);
if (!me)
return;
me->InterruptReadHandler(status, bufferSizeRemaining, timeStamp);
me->DecrementOutstandingIO();
}
void
IOUSBHIDDriver::InterruptReadHandler(IOReturn status, UInt32 bufferSizeRemaining, AbsoluteTime timeStamp)
{
bool queueAnother = false; IOReturn err = kIOReturnSuccess;
UInt64 timeElapsed;
AbsoluteTime timeStop;
switch (status)
{
case kIOReturnOverrun:
USBLog(3, "%s[%p]::InterruptReadHandler kIOReturnOverrun error", getName(), this);
if (!isInactive())
{
_interruptPipe->ClearStall();
IncrementOutstandingIO();
thread_call_enter(_clearFeatureEndpointHaltThread);
}
case kIOReturnSuccess:
_retryCount = kHIDDriverRetryCount;
IncrementOutstandingIO();
thread_call_enter1(_handleReportThread, (thread_call_param_t) &timeStamp);
break;
case kIOReturnNotResponding:
USBLog(3, "%s[%p]::InterruptReadHandler kIOReturnNotResponding error", getName(), this);
if ( !_deviceHasBeenDisconnected && !isInactive() )
{
USBLog(3, "%s[%p]::InterruptReadHandler Checking to see if HID device is still connected", getName(), this);
IncrementOutstandingIO();
thread_call_enter(_deviceDeadCheckThread );
_interruptPipe->ClearStall();
queueAnother = true; }
break;
case kIOReturnAborted:
if (!isInactive() && !_deviceIsDead )
{
USBLog(3, "%s[%p]::InterruptReadHandler error kIOReturnAborted. Try again.", getName(), this);
queueAnother = true;
}
break;
case kIOReturnUnderrun:
case kIOUSBPipeStalled:
case kIOUSBLinkErr:
case kIOUSBNotSent2Err:
case kIOUSBNotSent1Err:
case kIOUSBBufferUnderrunErr:
case kIOUSBBufferOverrunErr:
case kIOUSBWrongPIDErr:
case kIOUSBPIDCheckErr:
case kIOUSBDataToggleErr:
case kIOUSBBitstufErr:
case kIOUSBCRCErr:
case kIOUSBHighSpeedSplitError:
USBLog(3, "%s[%p]::InterruptReadHandler OHCI error (0x%x) reading interrupt pipe", getName(), this, status);
if (!isInactive())
{
_interruptPipe->ClearStall();
IncrementOutstandingIO();
thread_call_enter(_clearFeatureEndpointHaltThread); }
break;
default:
USBLog(3, "%s[%p]::InterruptReadHandler Unknown error (0x%x) reading interrupt pipe", getName(), this, status);
if ( !isInactive() )
_interruptPipe->ClearStall();
queueAnother = true;
break;
}
if ( queueAnother )
{
(void) RearmInterruptRead();
}
}
void
IOUSBHIDDriver::CheckForDeadDeviceEntry(OSObject *target)
{
IOUSBHIDDriver * me = OSDynamicCast(IOUSBHIDDriver, target);
if (!me)
return;
me->CheckForDeadDevice();
me->DecrementOutstandingIO();
}
void
IOUSBHIDDriver::CheckForDeadDevice()
{
IOReturn err = kIOReturnSuccess;
if ( _deviceDeadThreadActive )
{
USBLog(3, "%s[%p]::CheckForDeadDevice already active, returning", getName(), this);
return;
}
_deviceDeadThreadActive = TRUE;
if ( _interface && _device )
{
err = _device->message(kIOUSBMessageHubIsDeviceConnected, NULL, 0);
if ( kIOReturnSuccess == err )
{
if ( --_retryCount == 0 )
{
_deviceIsDead = TRUE;
USBLog(3, "%s[%p]: Detected an kIONotResponding error but still connected. Resetting port", getName(), this);
if (_interruptPipe)
{
_interruptPipe->Abort(); }
_device->ResetDevice();
}
}
else
{
_deviceHasBeenDisconnected = TRUE;
USBLog(5, "%s[%p]: CheckForDeadDevice: device %s has been unplugged", getName(), this, _device->getName() );
}
}
_deviceDeadThreadActive = FALSE;
}
void
IOUSBHIDDriver::ClearFeatureEndpointHaltEntry(OSObject *target)
{
IOUSBHIDDriver * me = OSDynamicCast(IOUSBHIDDriver, target);
if (!me)
return;
me->ClearFeatureEndpointHalt();
me->DecrementOutstandingIO();
}
void
IOUSBHIDDriver::ClearFeatureEndpointHalt( )
{
IOReturn status;
IOUSBDevRequest request;
UInt32 retries = 2;
bzero( &request, sizeof(IOUSBDevRequest));
while ( retries > 0 )
{
retries--;
request.bmRequestType = USBmakebmRequestType(kUSBNone, kUSBStandard, kUSBEndpoint);
request.bRequest = kUSBRqClearFeature;
request.wValue = 0; request.wIndex = _interruptPipe->GetEndpointNumber() | 0x80 ; request.wLength = 0;
request.pData = NULL;
status = _device->DeviceRequest(&request, 5000, 0);
if ( status != kIOReturnSuccess )
{
USBLog(3, "%s[%p]::ClearFeatureEndpointHalt - DeviceRequest returned: 0x%x, retries = %d", getName(), this, status, retries);
IOSleep(100);
}
else
break;
}
(void) RearmInterruptRead();
}
void
IOUSBHIDDriver::HandleReportEntry(OSObject *target, thread_call_param_t timeStamp)
{
IOUSBHIDDriver * me = OSDynamicCast(IOUSBHIDDriver, target);
AbsoluteTime theTime;
if (!me)
return;
theTime = * (AbsoluteTime *)timeStamp;
me->HandleReport( theTime );
me->DecrementOutstandingIO();
}
void
IOUSBHIDDriver::HandleReport(AbsoluteTime timeStamp)
{
IOReturn status;
IOUSBDevRequest request;
#if ENABLE_HIDREPORT_LOGGING
USBLog(6, "%s[%p]::HandleReport report came in:", getName(), this);
LogMemReport(_buffer);
#endif
status = handleReport(_buffer);
if ( status != kIOReturnSuccess)
{
USBLog(1, "%s[%p]::HandleReport handleReportWithTime returned 0x%x:", getName(), this, status);
}
if ( !isInactive() )
{
(void) RearmInterruptRead();
}
}
#pragma mark ееееееее HID Driver Methods еееееееее
UInt32
IOUSBHIDDriver::getMaxReportSize()
{
UInt32 maxInputReportSize = 0;
UInt32 maxFeatureReportSize = 0;
OSNumber * inputReportSize = (OSNumber *)getProperty(kIOHIDMaxInputReportSizeKey);
if ( inputReportSize )
maxInputReportSize = inputReportSize->unsigned32BitValue();
OSNumber * featureReportSize = (OSNumber *)getProperty(kIOHIDMaxFeatureReportSizeKey);
if ( featureReportSize )
maxFeatureReportSize = featureReportSize->unsigned32BitValue();
return ( (maxInputReportSize > maxFeatureReportSize) ? maxInputReportSize : maxFeatureReportSize);
}
IOReturn
IOUSBHIDDriver::GetHIDDescriptor(UInt8 inDescriptorType, UInt8 inDescriptorIndex, UInt8 *vOutBuf, UInt32 *vOutSize)
{
IOUSBDevRequest requestPB;
IOUSBHIDDescriptor *theHIDDesc;
IOUSBHIDReportDesc *hidTypeSizePtr; UInt8 *descPtr;
UInt32 providedBufferSize;
UInt16 descSize;
UInt8 descType;
UInt8 typeIndex;
UInt8 numberOwnedDesc;
IOReturn err = kIOReturnSuccess;
Boolean foundIt;
if (!vOutSize)
return kIOReturnBadArgument;
if (!_interface)
{
USBLog(2, "%s[%p]::GetHIDDescriptor - no _interface", getName(), this);
return kIOReturnNotFound;
}
theHIDDesc = (IOUSBHIDDescriptor *)_interface->FindNextAssociatedDescriptor(NULL, kUSBHIDDesc);
if (theHIDDesc == NULL)
{
USBLog(2, "%s[%p]::GetHIDDescriptor - FindNextAssociatedDescriptor(NULL, kUSBHIDDesc) failed", getName(), this);
return kIOReturnNotFound;
}
providedBufferSize = *vOutSize;
if (inDescriptorType == kUSBHIDDesc || (inDescriptorType == 0 && inDescriptorIndex == 0))
{
descSize = theHIDDesc->descLen;
descPtr = (UInt8 *)theHIDDesc;
*vOutSize = descSize;
if (providedBufferSize == 0)
err = kIOReturnSuccess;
else if (descSize > providedBufferSize)
err = kIOReturnNoSpace;
else if (vOutBuf == NULL)
err = kIOReturnBadArgument;
else
{
memcpy(vOutBuf, descPtr, descSize);
}
}
else
{ numberOwnedDesc = ((IOUSBHIDDescriptor *)theHIDDesc)->hidNumDescriptors;
hidTypeSizePtr = (IOUSBHIDReportDesc *)&((IOUSBHIDDescriptor *)theHIDDesc)->hidDescriptorType;
typeIndex = 0;
foundIt = false;
err = kIOReturnNotFound;
for (UInt8 i = 0; i < numberOwnedDesc; i++)
{
descType = hidTypeSizePtr->hidDescriptorType;
if (inDescriptorType != 0)
{
if (inDescriptorType == descType)
{
if (inDescriptorIndex == typeIndex)
{
foundIt = true;
}
else
{
typeIndex++;
}
}
}
else if (inDescriptorIndex == i + 1)
{
typeIndex = i;
foundIt = true;
}
if (foundIt)
{
err = kIOReturnSuccess; descSize = (hidTypeSizePtr->hidDescriptorLengthHi << 8) + hidTypeSizePtr->hidDescriptorLengthLo;
*vOutSize = descSize;
if (providedBufferSize == 0)
err = kIOReturnSuccess;
else if (descSize > providedBufferSize)
err = kIOReturnNoSpace;
else if (vOutBuf == NULL)
err = kIOReturnBadArgument;
else
{
if (!_device)
{
USBLog(2, "%s[%p]::GetHIDDescriptor - no _device", getName(), this);
return kIOReturnNotFound;
}
requestPB.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBInterface);
requestPB.bRequest = kUSBRqGetDescriptor;
requestPB.wValue = (inDescriptorType << 8) + typeIndex; requestPB.wIndex = _interface->GetInterfaceNumber();
requestPB.wLength = descSize;
requestPB.pData = vOutBuf; err = _device->DeviceRequest(&requestPB, 5000, 0);
if (err != kIOReturnSuccess)
{
USBLog(3, "%s[%p]::GetHIDDescriptor Final request failed; err = 0x%x", getName(), this, err);
return err;
}
}
break; }
hidTypeSizePtr = (IOUSBHIDReportDesc *)(((UInt8 *)hidTypeSizePtr) + 3);
}
}
return err;
}
IOReturn
IOUSBHIDDriver::GetIndexedString(UInt8 index, UInt8 *vOutBuf, UInt32 *vOutSize, UInt16 lang) const
{
char strBuf[256];
UInt16 strLen = sizeof(strBuf) - 1; UInt32 outSize = *vOutSize;
IOReturn err;
if (index == 0)
{
return kIOReturnBadArgument;
}
if (lang == 0)
{
lang = 0x409; }
err = _device->GetStringDescriptor((UInt8)index, strBuf, strLen, (UInt16)lang);
if (err != kIOReturnSuccess)
{
return err;
}
strLen = (strBuf[0] == 0) ? 0 : strlen(strBuf) + 1;
if (outSize == 0)
{
*vOutSize = strLen;
return kIOReturnSuccess;
}
else if (outSize < strLen)
{
return kIOReturnMessageTooLarge;
}
strcpy((char *)vOutBuf, strBuf);
*vOutSize = strLen;
return kIOReturnSuccess;
}
OSString *
IOUSBHIDDriver::newIndexedString(UInt8 index) const
{
char string[256];
UInt32 strSize;
IOReturn err = kIOReturnSuccess;
string[0] = 0;
strSize = sizeof(string);
err = GetIndexedString(index, (UInt8 *)string, &strSize );
if ( err == kIOReturnSuccess )
return OSString::withCString(string);
else
return NULL;
}
IOReturn
IOUSBHIDDriver::StartFinalProcessing(void)
{
IOReturn err = kIOReturnSuccess;
_completionWithTimeStamp.target = (void *)this;
_completionWithTimeStamp.action = (IOUSBCompletionActionWithTimeStamp) &IOUSBHIDDriver::InterruptReadHandlerWithTimeStampEntry;
_completionWithTimeStamp.parameter = (void *)0;
_completion.target = (void *)this;
_completion.action = (IOUSBCompletionAction) &IOUSBHIDDriver::InterruptReadHandlerEntry;
_completion.parameter = (void *)0;
err = RearmInterruptRead();
return err;
}
IOReturn
IOUSBHIDDriver::SetIdleMillisecs(UInt16 msecs)
{
IOReturn err = kIOReturnSuccess;
IOUSBDevRequest request;
request.bmRequestType = USBmakebmRequestType(kUSBOut, kUSBClass, kUSBInterface);
request.bRequest = kHIDRqSetIdle; request.wValue = (msecs/4) << 8;
request.wIndex = _interface->GetInterfaceNumber();
request.wLength = 0;
request.pData = NULL;
err = _device->DeviceRequest(&request, 5000, 0);
if (err != kIOReturnSuccess)
{
USBLog(3, "%s[%p]::SetIdleMillisecs returned error 0x%x",getName(), this, err);
}
return err;
}
IOReturn
IOUSBHIDDriver::SetProtocol(UInt32 protocol)
{
IOReturn err = kIOReturnSuccess;
IOUSBDevRequest request;
request.bmRequestType = USBmakebmRequestType(kUSBOut, kUSBClass, kUSBInterface);
request.bRequest = kHIDRqSetProtocol; request.wValue = protocol;
request.wIndex = _interface->GetInterfaceNumber();
request.wLength = 0;
request.pData = NULL;
err = _device->DeviceRequest(&request, 5000, 0);
if (err != kIOReturnSuccess)
{
USBLog(3, "%s[%p]:::SetProtocol returned error 0x%x",getName(), this, err);
}
return err;
}
#pragma mark ееееееее Bookkeeping Methods еееееееее
IOReturn
IOUSBHIDDriver::ChangeOutstandingIO(OSObject *target, void *param1, void *param2, void *param3, void *param4)
{
IOUSBHIDDriver *me = OSDynamicCast(IOUSBHIDDriver, target);
UInt32 direction = (UInt32)param1;
if (!me)
{
USBLog(1, "IOUSBHIDDriver::ChangeOutstandingIO - invalid target");
return kIOReturnSuccess;
}
switch (direction)
{
case 1:
me->_outstandingIO++;
break;
case -1:
if (!--me->_outstandingIO && me->_needToClose)
{
USBLog(3, "%s[%p]::ChangeOutstandingIO isInactive = %d, outstandingIO = %d - closing device", me->getName(), me, me->isInactive(), me->_outstandingIO);
me->_interface->close(me);
}
break;
default:
USBLog(1, "%s[%p]::ChangeOutstandingIO - invalid direction", me->getName(), me);
}
return kIOReturnSuccess;
}
void
IOUSBHIDDriver::DecrementOutstandingIO(void)
{
if (!_gate)
{
if (!--_outstandingIO && _needToClose)
{
USBLog(3, "%s[%p]::DecrementOutstandingIO isInactive = %d, outstandingIO = %d - closing device", getName(), this, isInactive(), _outstandingIO);
_interface->close(this);
}
return;
}
_gate->runAction(ChangeOutstandingIO, (void*)-1);
}
void
IOUSBHIDDriver::IncrementOutstandingIO(void)
{
if (!_gate)
{
_outstandingIO++;
return;
}
_gate->runAction(ChangeOutstandingIO, (void*)1);
}
#pragma mark ееееееее Debug Methods еееееееее
#if ENABLE_HIDREPORT_LOGGING
void
IOUSBHIDDriver::LogMemReport(IOMemoryDescriptor * reportBuffer)
{
IOByteCount reportSize;
char outBuffer[1024];
char in[1024];
char *out;
char inChar;
out = (char *)&outBuffer;
reportSize = reportBuffer->getLength();
reportBuffer->readBytes(0, in, reportSize );
if (reportSize > 256) reportSize = 256;
for (unsigned int i = 0; i < reportSize; i++)
{
inChar = in[i];
*out++ = ' ';
*out++ = GetHexChar(inChar >> 4);
*out++ = GetHexChar(inChar & 0x0F);
}
*out = 0;
USBLog(6, outBuffer);
}
char
IOUSBHIDDriver::GetHexChar(char hexChar)
{
char hexChars[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
return hexChars[0x0F & hexChar];
}
#endif
#pragma mark ееееееее Obsolete Methods еееееееее
void
IOUSBHIDDriver::processPacket(void *data, UInt32 size)
{
return;
}
IOReturn
IOUSBHIDDriver::GetReport(UInt8 inReportType, UInt8 inReportID, UInt8 *vInBuf, UInt32 *vInSize)
{
return kIOReturnSuccess;
}
IOReturn
IOUSBHIDDriver::SetReport(UInt8 outReportType, UInt8 outReportID, UInt8 *vOutBuf, UInt32 vOutSize)
{
return kIOReturnSuccess;
}
#pragma mark ееееееее Padding Methods еееееееее
OSMetaClassDefineReservedUsed(IOUSBHIDDriver, 0);
IOReturn
IOUSBHIDDriver::RearmInterruptRead()
{
IOReturn err = kIOReturnSuccess;
if ( _buffer == NULL )
return err;
IncrementOutstandingIO();
err = _interruptPipe->Read(_buffer, 0, 0, _buffer->getLength(), &_completionWithTimeStamp);
if ( err != kIOReturnSuccess)
{
if ( err == kIOReturnUnsupported )
{
err = _interruptPipe->Read(_buffer, 0, 0, _buffer->getLength(), &_completion);
if ( err != kIOReturnSuccess)
{
USBError(1, "%s[%p]::InterruptReadHandler immediate error 0x%x queueing read\n", getName(), this, err);
DecrementOutstandingIO();
}
}
}
return err;
}
OSMetaClassDefineReservedUnused(IOUSBHIDDriver, 1);
OSMetaClassDefineReservedUnused(IOUSBHIDDriver, 2);
OSMetaClassDefineReservedUnused(IOUSBHIDDriver, 3);
OSMetaClassDefineReservedUnused(IOUSBHIDDriver, 4);
OSMetaClassDefineReservedUnused(IOUSBHIDDriver, 5);
OSMetaClassDefineReservedUnused(IOUSBHIDDriver, 6);
OSMetaClassDefineReservedUnused(IOUSBHIDDriver, 7);
OSMetaClassDefineReservedUnused(IOUSBHIDDriver, 8);
OSMetaClassDefineReservedUnused(IOUSBHIDDriver, 9);
OSMetaClassDefineReservedUnused(IOUSBHIDDriver, 10);
OSMetaClassDefineReservedUnused(IOUSBHIDDriver, 11);
OSMetaClassDefineReservedUnused(IOUSBHIDDriver, 12);
OSMetaClassDefineReservedUnused(IOUSBHIDDriver, 13);
OSMetaClassDefineReservedUnused(IOUSBHIDDriver, 14);
OSMetaClassDefineReservedUnused(IOUSBHIDDriver, 15);
OSMetaClassDefineReservedUnused(IOUSBHIDDriver, 16);
OSMetaClassDefineReservedUnused(IOUSBHIDDriver, 17);
OSMetaClassDefineReservedUnused(IOUSBHIDDriver, 18);
OSMetaClassDefineReservedUnused(IOUSBHIDDriver, 19);