IOUSBHIDDriver.cpp [plain text]
#include <libkern/OSByteOrder.h>
#include <IOKit/IOBufferMemoryDescriptor.h>
#include <IOKit/IOLib.h> // For IOLog()...
#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
OSDefineMetaClassAndStructors(IOUSBHIDDriver, super)
bool
IOUSBHIDDriver::init(OSDictionary *properties)
{
if (!super::init(properties))
{
return false;
}
_interface = NULL;
_buffer = 0;
_retryCount = kHIDDriverRetryCount;
_outstandingIO = 0;
_needToClose = false;
_maxReportSize = kMaxHIDReportSize;
_maxOutReportSize = kMaxHIDReportSize;
_outBuffer = 0;
_deviceUsage = 0;
_deviceUsagePage = 0;
return true;
}
bool
IOUSBHIDDriver::handleStart(IOService * provider)
{
HIDPreparsedDataRef parseData;
HIDCapabilities myHIDCaps;
UInt8 * myHIDDesc;
UInt32 hidDescSize;
IOReturn err = kIOReturnSuccess;
USBLog(7, "%s[%p]::handleStart", getName(), this);
if( !super::handleStart(provider))
{
return false;
}
if( !provider->open(this))
{
USBError(1, "%s[%p]::start - unable to open provider. returning false", getName(), this);
return (false);
}
_interface = OSDynamicCast(IOUSBInterface, provider);
if (!_interface)
{
return false;
}
_device = _interface->GetDevice();
if (!_device)
{
return false;
}
hidDescSize = 0;
err = GetHIDDescriptor(kUSBReportDesc, 0, NULL, &hidDescSize);
if ((err != kIOReturnSuccess) || (hidDescSize == 0))
{
return false; }
myHIDDesc = (UInt8 *)IOMalloc(hidDescSize);
if (myHIDDesc == NULL)
{
return false;
}
err = GetHIDDescriptor(kUSBReportDesc, 0, myHIDDesc, &hidDescSize);
if (err == kIOReturnSuccess)
{
err = HIDOpenReportDescriptor(myHIDDesc, hidDescSize, &parseData, 0);
if (err == kIOReturnSuccess)
{
err = HIDGetCapabilities(parseData, &myHIDCaps);
if (err == kIOReturnSuccess)
{
_deviceUsage = myHIDCaps.usage;
_deviceUsagePage = myHIDCaps.usagePage;
_maxOutReportSize = myHIDCaps.outputReportByteLength;
_maxReportSize = (myHIDCaps.inputReportByteLength > myHIDCaps.featureReportByteLength) ?
myHIDCaps.inputReportByteLength : myHIDCaps.featureReportByteLength;
}
HIDCloseReportDescriptor(parseData);
}
}
if (myHIDDesc)
{
IOFree(myHIDDesc, hidDescSize);
}
return true;
}
void
IOUSBHIDDriver::handleStop(IOService * provider)
{
USBLog(7, "%s[%p]::handleStop", getName(), this);
if (_outBuffer)
{
_outBuffer->release();
_outBuffer = NULL;
}
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);
}
super::handleStop(provider);
}
void
IOUSBHIDDriver::free()
{
USBLog(7, "%s[%p]::free", getName(), this);
super::free();
}
void
IOUSBHIDDriver::processPacket(void *data, UInt32 size)
{
IOLog("Should not be here, IOUSBHIDDriver: processPacket()\n");
return;
}
IOReturn
IOUSBHIDDriver::GetReport(UInt8 inReportType, UInt8 inReportID, UInt8 *vInBuf, UInt32 *vInSize)
{
IOUSBDevRequest requestPB;
IOReturn err;
requestPB.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBClass, kUSBInterface);
requestPB.bRequest = kHIDRqGetReport;
requestPB.wValue = (inReportType << 8) | inReportID;
requestPB.wIndex = _interface->GetInterfaceNumber();
requestPB.wLength = *vInSize;
requestPB.pData = vInBuf;
requestPB.wLenDone = 0;
err = _device->DeviceRequest(&requestPB, 5000, 0);
if ( err != kIOReturnSuccess )
IOLog("IOUSBHIDDriver::GetReport request failed; err = 0x%x)\n", err);
*vInSize = requestPB.wLenDone;
return err;
}
IOReturn
IOUSBHIDDriver::getReport( IOMemoryDescriptor * report,
IOHIDReportType reportType,
IOOptionBits options )
{
UInt8 *reportData;
UInt8 reportID;
IOByteCount reportLength, originalReportLength;
IOByteCount segmentSize;
IOReturn ret;
reportData = (UInt8 *)report->getVirtualSegment(0, &segmentSize);
reportLength = originalReportLength = report->getLength();
if ( segmentSize != originalReportLength ) {
reportData = (UInt8 *)IOMalloc( originalReportLength );
if ( reportData == 0 )
return kIOReturnNoMemory;
}
reportID = (UInt8) options;
ret = GetReport(HIDMgr2USBReportType(reportType), reportID, reportData, &reportLength);
if ( segmentSize != originalReportLength ) {
report->writeBytes(0, reportData, reportLength);
IOFree( reportData, originalReportLength );
}
return ret;
}
IOReturn
IOUSBHIDDriver::SetReport(UInt8 outReportType, UInt8 outReportID, UInt8 *vOutBuf, UInt32 vOutSize)
{
IOUSBDevRequest requestPB;
IOReturn err;
if (kHIDOutputReport == outReportType && _interruptOutPipe && _outBuffer)
{
_outBuffer->setLength(0);
if (_outBuffer->appendBytes(vOutBuf, vOutSize))
{
#if ENABLE_HIDREPORT_LOGGING
USBLog(3, "%s[%p]::SetReport sending out interrupt out pipe buffer (%p,%d):", getName(), this, vOutBuf, _outBuffer->getLength() );
LogMemReport(_outBuffer);
#endif
err = _interruptOutPipe->Write(_outBuffer);
if (err == kIOReturnSuccess)
{
return err;
}
else
{
IOLog("IOUSBHIDDriver::SetReport _interruptOutPipe->Write failed; err = 0x%x)\n", err);
}
}
}
#if ENABLE_HIDREPORT_LOGGING
USBLog(3, "%s[%p]::SetReport sending out control pipe:", getName(), this);
LogBufferReport((char *)vOutBuf, vOutSize);
#endif
requestPB.bmRequestType = USBmakebmRequestType(kUSBOut, kUSBClass, kUSBInterface);
requestPB.bRequest = kHIDRqSetReport;
requestPB.wValue = (outReportType << 8) | outReportID;
requestPB.wIndex = _interface->GetInterfaceNumber();
requestPB.wLength = vOutSize;
requestPB.pData = vOutBuf;
requestPB.wLenDone = 0;
err = _device->DeviceRequest(&requestPB, 5000, 0);
if (err != kIOReturnSuccess)
IOLog("IOUSBHIDDriver::SetReport request failed; err = 0x%x)\n", err);
return err;
}
IOReturn
IOUSBHIDDriver::setReport( IOMemoryDescriptor * report,
IOHIDReportType reportType,
IOOptionBits options)
{
UInt8 *reportData;
UInt8 reportID;
IOByteCount reportLength;
IOByteCount segmentSize;
IOReturn ret;
reportData = (UInt8 *)report->getVirtualSegment(0, &segmentSize);
reportLength = report->getLength();
if ( segmentSize != reportLength ) {
reportData = (UInt8 *)IOMalloc (reportLength);
if ( reportData == 0 )
return kIOReturnNoMemory;
report->readBytes( 0, reportData, reportLength );
}
reportID = (UInt8) options;
ret = SetReport(HIDMgr2USBReportType(reportType), reportID, reportData, reportLength);
if ( segmentSize != reportLength ) {
IOFree( reportData, reportLength );
}
return ret;
}
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::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::newTransportString() const
{
return OSString::withCString("USB");
}
OSNumber *
IOUSBHIDDriver::newPrimaryUsageNumber() const
{
return OSNumber::withNumber(_deviceUsage, 32);
}
OSNumber *
IOUSBHIDDriver::newPrimaryUsagePageNumber() const
{
return OSNumber::withNumber(_deviceUsagePage, 32);
}
OSNumber *
IOUSBHIDDriver::newVendorIDNumber() const
{
UInt16 vendorID = 0;
if (_device != NULL)
vendorID = _device->GetVendorID();
return OSNumber::withNumber(vendorID, 16);
}
OSNumber *
IOUSBHIDDriver::newProductIDNumber() const
{
UInt16 productID = 0;
if (_device != NULL)
productID = _device->GetProductID();
return OSNumber::withNumber(productID, 16);
}
OSNumber *
IOUSBHIDDriver::newVersionNumber() const
{
UInt16 releaseNum = 0;
if (_device != NULL)
releaseNum = _device->GetDeviceRelease();
return OSNumber::withNumber(releaseNum, 16);
}
UInt32
IOUSBHIDDriver::getMaxReportSize()
{
return _maxReportSize;
}
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;
}
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;
}
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;
}
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;
}
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::message( UInt32 type, IOService * provider, void * argument )
{
IOReturn err = kIOReturnSuccess;
err = super::message (type, provider, argument);
switch ( type )
{
case kIOMessageServiceIsTerminated:
USBLog(5, "%s[%p]: service is terminated - ignoring", getName(), this);
break;
case kIOUSBMessagePortHasBeenReset:
USBLog(3, "%s[%p]: received kIOUSBMessagePortHasBeenReset", getName(), this);
_retryCount = kHIDDriverRetryCount;
_deviceIsDead = FALSE;
_deviceHasBeenDisconnected = FALSE;
IncrementOutstandingIO();
err = _interruptPipe->Read(_buffer, &_completion);
if (err != kIOReturnSuccess)
{
DecrementOutstandingIO();
USBLog(3, "%s[%p]::message - err (%x) in interrupt read", getName(), this, err);
}
break;
default:
break;
}
return kIOReturnSuccess;
}
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);
}
bool
IOUSBHIDDriver::start(IOService *provider)
{
IOReturn err = kIOReturnSuccess;
IOWorkLoop *wl = NULL;
USBLog(7, "%s[%p]::start", getName(), this);
IncrementOutstandingIO(); if (super::start(provider))
do {
IOUSBFindEndpointRequest request;
USBLog(7, "%s[%p]::start - getting _gate", getName(), this);
_gate = IOCommandGate::commandGate(this);
if(!_gate)
{
USBError(1, "%s[%p]::start - unable to create command gate", getName(), this);
break;
}
wl = getWorkLoop();
if (!wl)
{
USBError(1, "%s[%p]::start - unable to find my workloop", getName(), this);
break;
}
if (wl->addEventSource(_gate) != kIOReturnSuccess)
{
USBError(1, "%s[%p]::start - unable to add gate to work loop", getName(), this);
break;
}
if ( (_device->GetVendorID()) == 0x06a3 )
SetIdleMillisecs(0);
request.type = kUSBInterrupt;
request.direction = kUSBOut;
_interruptOutPipe = _interface->FindNextPipe(NULL, &request);
if(_interruptOutPipe)
{
_maxOutReportSize = getMaxReportSize();
if (_maxOutReportSize)
{
_outBuffer = IOBufferMemoryDescriptor::withCapacity(_maxOutReportSize, kIODirectionOut);
if ( !_outBuffer )
{
USBError(2, "%s[%p]::start - unable to get create output buffer", getName(), this);
_interruptOutPipe = NULL;
}
}
}
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);
break;
}
_maxReportSize = getMaxReportSize();
if (_maxReportSize)
{
_buffer = IOBufferMemoryDescriptor::withCapacity(_maxReportSize, kIODirectionIn);
if ( !_buffer )
{
USBError(1, "%s[%p]::start - unable to get create buffer", getName(), this);
break;
}
}
_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);
if ( !_deviceDeadCheckThread || !_clearFeatureEndpointHaltThread )
{
USBError(1, "[%s]%p: could not allocate all thread functions", getName(), this);
break;
}
err = StartFinalProcessing();
if (err != kIOReturnSuccess)
{
USBError(1, "%s[%p]::start - err (%x) in StartFinalProcessing", getName(), this, err);
break;
}
USBError(1, "%s[%p]::start - USB HID Device @ %d (0x%x)", getName(), this, _device->GetAddress(), strtol(_device->getLocation(), (char **)NULL, 16));
DecrementOutstandingIO(); return true;
} while (false);
USBError(1, "%s[%p]::start - aborting startup", getName(), this);
if (_gate)
{
if (wl)
wl->removeEventSource(_gate);
_gate->release();
_gate = NULL;
}
if (_deviceDeadCheckThread)
thread_call_free(_deviceDeadCheckThread);
if (_clearFeatureEndpointHaltThread)
thread_call_free(_clearFeatureEndpointHaltThread);
if (_interface)
_interface->close(this);
DecrementOutstandingIO(); return false;
}
void
IOUSBHIDDriver::InterruptReadHandlerEntry(OSObject *target, void *param, IOReturn status, UInt32 bufferSizeRemaining)
{
IOUSBHIDDriver * me = OSDynamicCast(IOUSBHIDDriver, target);
if (!me)
return;
me->InterruptReadHandler(status, bufferSizeRemaining);
me->DecrementOutstandingIO();
}
void
IOUSBHIDDriver::InterruptReadHandler(IOReturn status, UInt32 bufferSizeRemaining)
{
bool queueAnother = true;
bool timeToGoAway = false;
IOReturn err = kIOReturnSuccess;
switch (status)
{
case kIOReturnOverrun:
USBLog(3, "%s[%p]::InterruptReadHandler kIOReturnOverrun error", getName(), this);
if (!isInactive())
{
_interruptPipe->ClearStall();
IncrementOutstandingIO();
thread_call_enter(_clearFeatureEndpointHaltThread);
}
queueAnother = false;
timeToGoAway = false;
case kIOReturnSuccess:
_retryCount = kHIDDriverRetryCount;
#if ENABLE_HIDREPORT_LOGGING
USBLog(6, "%s[%p]::InterruptReadHandler report came in:", getName(), this);
LogMemReport(_buffer);
#endif
handleReport(_buffer);
if (isInactive())
queueAnother = false;
break;
case kIOReturnNotResponding:
USBLog(3, "%s[%p]::InterruptReadHandler kIOReturnNotResponding error", getName(), this);
if ( _deviceHasBeenDisconnected || isInactive() )
{
queueAnother = false;
timeToGoAway = true;
}
else
{
USBLog(3, "%s[%p]::InterruptReadHandler Checking to see if HID device is still connected", getName(), this);
IncrementOutstandingIO();
thread_call_enter(_deviceDeadCheckThread);
_interruptPipe->ClearStall();
}
break;
case kIOReturnAborted:
if (isInactive() || _deviceIsDead )
{
USBLog(3, "%s[%p]::InterruptReadHandler error kIOReturnAborted (expected)", getName(), this);
queueAnother = false;
timeToGoAway = true;
}
else
{
USBLog(3, "%s[%p]::InterruptReadHandler error kIOReturnAborted. Try again.", getName(), this);
}
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:
USBLog(3, "%s[%p]::InterruptReadHandler OHCI error (0x%x) reading interrupt pipe", getName(), this, status);
if (!isInactive())
{
_interruptPipe->ClearStall();
IncrementOutstandingIO();
thread_call_enter(_clearFeatureEndpointHaltThread);
}
queueAnother = false;
break;
default:
USBLog(3, "%s[%p]::InterruptReadHandler error (0x%x) reading interrupt pipe", getName(), this, status);
if (isInactive())
queueAnother = false;
break;
}
if ( queueAnother )
{
IncrementOutstandingIO();
err = _interruptPipe->Read(_buffer, &_completion);
if ( err != kIOReturnSuccess)
{
USBError(1, "%s[%p]::InterruptReadHandler immediate error 0x%x queueing read\n", getName(), this, err);
DecrementOutstandingIO();
timeToGoAway = true;
}
}
}
void
IOUSBHIDDriver::CheckForDeadDeviceEntry(OSObject *target)
{
IOUSBHIDDriver * me = OSDynamicCast(IOUSBHIDDriver, target);
if (!me)
return;
me->CheckForDeadDevice();
me->DecrementOutstandingIO();
}
void
IOUSBHIDDriver::CheckForDeadDevice()
{
IOReturn err = kIOReturnSuccess;
_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 has been unplugged", getName(), this);
}
}
_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;
bzero( &request, sizeof(IOUSBDevRequest));
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 )
{
USBLog(3, "%s[%p]::ClearFeatureEndpointHalt - DeviceRequest returned: 0x%x", getName(), this, status);
}
IncrementOutstandingIO();
status = _interruptPipe->Read(_buffer, &_completion);
if ( status != kIOReturnSuccess)
{
USBLog(3, "%s[%p]::ClearFeatureEndpointHalt - immediate error %d queueing read", getName(), this, status);
DecrementOutstandingIO();
}
}
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);
}
IOReturn
IOUSBHIDDriver::StartFinalProcessing(void)
{
IOReturn err = kIOReturnSuccess;
_completion.target = (void *)this;
_completion.action = (IOUSBCompletionAction) &IOUSBHIDDriver::InterruptReadHandlerEntry;
_completion.parameter = (void *)0;
IncrementOutstandingIO();
err = _interruptPipe->Read(_buffer, &_completion);
if (err != kIOReturnSuccess)
{
DecrementOutstandingIO();
USBError(1, "%s[%p]::StartFinalProcessing - err (%x) in interrupt read, retain count %d after release", getName(), this, err, getRetainCount());
}
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]: IOUSBHIDDriver::SetIdleMillisecs returned error 0x%x",getName(), this, err);
}
return err;
}
#if ENABLE_HIDREPORT_LOGGING
void
IOUSBHIDDriver::LogBufferReport(char *report, UInt32 len)
{
IOByteCount reportSize;
char outBuffer[1024];
char *out;
char *in;
char inChar;
out = (char *)&outBuffer;
in = report;
reportSize = len;
if (reportSize > 256) reportSize = 256;
for (unsigned int i = 0; i < reportSize; i++)
{
inChar = *in++;
*out++ = ' ';
*out++ = GetHexChar(inChar >> 4);
*out++ = GetHexChar(inChar & 0x0F);
}
*out = 0;
USBLog(6, outBuffer);
}
void
IOUSBHIDDriver::LogMemReport(IOBufferMemoryDescriptor * reportBuffer)
{
IOByteCount reportSize;
char outBuffer[1024];
char *out;
char *in;
char inChar;
out = (char *)&outBuffer;
in = (char *)reportBuffer->getBytesNoCopy();
reportSize = reportBuffer->getLength();
if (reportSize > 256) reportSize = 256;
for (unsigned int i = 0; i < reportSize; i++)
{
inChar = *in++;
*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
OSMetaClassDefineReservedUnused(IOUSBHIDDriver, 0);
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);