IOHIDLibUserClient.cpp [plain text]
#include <sys/systm.h>
#include <IOKit/IOCommandGate.h>
#include <IOKit/IOMemoryDescriptor.h>
#include <IOKit/IOService.h>
#include <IOKit/IOSyncer.h>
#include <IOKit/IOWorkLoop.h>
#include "IOHIDLibUserClient.h"
#include "IOHIDDevice.h"
#include "IOHIDEventQueue.h"
#define super IOUserClient
struct AsyncParam {
OSAsyncReference fAsyncRef;
UInt32 fMax;
IOMemoryDescriptor *fMem;
IOHIDReportType reportType;
};
OSDefineMetaClassAndStructors(IOHIDLibUserClient, IOUserClient);
const IOExternalMethod IOHIDLibUserClient::
sMethods[kIOHIDLibUserClientNumCommands] = {
{ 0,
(IOMethod) &IOHIDLibUserClient::open,
kIOUCScalarIScalarO,
0,
0
},
{ 0,
(IOMethod) &IOHIDLibUserClient::close,
kIOUCScalarIScalarO,
0,
0
},
{ 0,
(IOMethod) &IOHIDLibUserClient::createQueue,
kIOUCScalarIScalarO,
2,
1
},
{ 0,
(IOMethod) &IOHIDLibUserClient::disposeQueue,
kIOUCScalarIScalarO,
1,
0
},
{ 0,
(IOMethod) &IOHIDLibUserClient::addElementToQueue,
kIOUCScalarIScalarO,
3,
0
},
{ 0,
(IOMethod) &IOHIDLibUserClient::removeElementFromQueue,
kIOUCScalarIScalarO,
2,
0
},
{ 0,
(IOMethod) &IOHIDLibUserClient::queueHasElement,
kIOUCScalarIScalarO,
2,
1
},
{ 0,
(IOMethod) &IOHIDLibUserClient::startQueue,
kIOUCScalarIScalarO,
1,
0
},
{ 0,
(IOMethod) &IOHIDLibUserClient::stopQueue,
kIOUCScalarIScalarO,
1,
0
},
{ 0,
(IOMethod) &IOHIDLibUserClient::updateElementValue,
kIOUCScalarIScalarO,
1,
0
},
{ 0,
(IOMethod) &IOHIDLibUserClient::postElementValue,
kIOUCStructIStructO,
0xffffffff,
0
},
{ 0,
(IOMethod) &IOHIDLibUserClient::getReport,
kIOUCScalarIStructO,
2,
0xffffffff
},
{ 0,
(IOMethod) &IOHIDLibUserClient::getReportOOL,
kIOUCStructIStructO,
sizeof(IOHIDReportReq),
sizeof(UInt32)
},
{ 0,
(IOMethod) &IOHIDLibUserClient::setReport,
kIOUCScalarIStructI,
2,
0xffffffff
},
{ 0,
(IOMethod) &IOHIDLibUserClient::setReportOOL,
kIOUCStructIStructO,
sizeof(IOHIDReportReq),
0
}
};
const IOExternalAsyncMethod IOHIDLibUserClient::
sAsyncMethods[kIOHIDLibUserClientNumAsyncCommands] = {
{ 0,
(IOAsyncMethod) &IOHIDLibUserClient::setAsyncPort,
kIOUCScalarIScalarO,
0,
0
},
{ 0,
(IOAsyncMethod) &IOHIDLibUserClient::setQueueAsyncPort,
kIOUCScalarIScalarO,
1,
0
},
{ 0,
(IOAsyncMethod) &IOHIDLibUserClient::asyncGetReport,
kIOUCScalarIScalarO,
5,
0
},
{ 0,
(IOAsyncMethod) &IOHIDLibUserClient::asyncSetReport,
kIOUCScalarIScalarO,
5,
0
}
};
bool IOHIDLibUserClient::
initWithTask(task_t owningTask, void * , UInt32 )
{
if (!super::init())
return false;
fClient = owningTask;
task_reference (fClient);
return true;
}
IOReturn IOHIDLibUserClient::clientClose(void)
{
if (fClient) {
task_deallocate(fClient);
fClient = 0;
}
if (fNub) {
fNub->close(this);
detach(fNub);
fNub = 0;
}
return kIOReturnSuccess;
}
bool IOHIDLibUserClient::start(IOService *provider)
{
if (!super::start(provider))
return false;
fNub = OSDynamicCast(IOHIDDevice, provider);
if (!fNub)
return false;
return true;
}
IOExternalMethod *IOHIDLibUserClient::
getTargetAndMethodForIndex(IOService **target, UInt32 index)
{
if (index < (UInt32) kIOHIDLibUserClientNumCommands)
{
*target = this;
return (IOExternalMethod *) &sMethods[index];
}
else
return 0;
}
IOExternalAsyncMethod * IOHIDLibUserClient::
getAsyncTargetAndMethodForIndex(IOService **target, UInt32 index)
{
if (index < (UInt32) kIOHIDLibUserClientNumAsyncCommands)
{
*target = this;
return (IOExternalAsyncMethod *) &sAsyncMethods[index];
}
else
return 0;
}
IOReturn IOHIDLibUserClient::
setAsyncPort(OSAsyncReference asyncRef, void *, void *, void *,
void *, void *, void *)
{
fWakePort = (mach_port_t) asyncRef[0];
return kIOReturnSuccess;
}
IOReturn IOHIDLibUserClient::
setQueueAsyncPort(OSAsyncReference asyncRef, void * vInQueue, void *, void *,
void *, void *, void *)
{
IOHIDEventQueue * eventQueue = (IOHIDEventQueue *) vInQueue;
fQueuePort = (mach_port_t) asyncRef[0];
if ( !eventQueue )
return kIOReturnBadArgument;
eventQueue->setNotificationPort(fQueuePort);
return kIOReturnSuccess;
}
IOReturn IOHIDLibUserClient::
open(void *, void *, void *, void *, void *, void *)
{
IOReturn res = kIOReturnSuccess;
IOWorkLoop *wl;
if (!fNub->open(this))
return kIOReturnExclusiveAccess;
wl = getWorkLoop();
if (!wl) {
res = kIOReturnNoResources;
goto abortOpen;
}
fGate = IOCommandGate::commandGate(this);
if (!fGate) {
res = kIOReturnNoMemory;
goto abortOpen;
}
wl->retain();
wl->addEventSource(fGate);
return kIOReturnSuccess;
abortOpen:
if (fGate) {
wl->removeEventSource(fGate);
wl->release();
fGate->release();
fGate = 0;
}
fNub->close(this);
return res;
}
IOReturn IOHIDLibUserClient::
close(void *, void *, void *, void *, void *, void *gated)
{
if ( ! (bool) gated ) {
IOReturn res;
IOWorkLoop *wl;
res = fGate->runAction(closeAction);
wl = fGate->getWorkLoop();
wl->removeEventSource(fGate);
wl->release();
wl = 0;
fGate->release();
fGate = 0;
return res;
}
else {
fNub->close(this);
return kIOReturnSuccess;
}
return kIOReturnSuccess;
}
bool
IOHIDLibUserClient::didTerminate( IOService * provider, IOOptionBits options, bool * defer )
{
fNub->close(this);
return super::didTerminate(provider, options, defer);
}
void IOHIDLibUserClient::free()
{
IOWorkLoop *wl;
if (fGate)
{
wl = fGate->getWorkLoop();
if (wl)
wl->release();
fGate->release();
}
super::free();
}
IOReturn IOHIDLibUserClient::closeAction
(OSObject *self, void *, void *, void *, void *)
{
IOHIDLibUserClient *me = (IOHIDLibUserClient *) self;
return me->close(0, 0, 0, 0, 0, (void *) true);
}
IOReturn IOHIDLibUserClient::
clientMemoryForType ( UInt32 type,
IOOptionBits * options,
IOMemoryDescriptor ** memory )
{
IOReturn ret = kIOReturnNoMemory;
IOMemoryDescriptor * memoryToShare = NULL;
if (type == IOHIDLibUserClientElementValuesType)
{
memoryToShare = fNub->getMemoryWithCurrentElementValues();
}
else
{
IOHIDEventQueue * queue = (IOHIDEventQueue *) type;
memoryToShare = queue->getMemoryDescriptor();
}
if (memoryToShare)
{
memoryToShare->retain();
ret = kIOReturnSuccess;
}
*options = 0;
*memory = memoryToShare;
return ret;
}
IOReturn IOHIDLibUserClient::
createQueue(void * vInFlags, void * vInDepth, void * vOutQueue, void *, void *, void * gated)
{
UInt32 depth = (UInt32) vInDepth;
void ** outQueue = (void **) vOutQueue;
IOHIDEventQueue * eventQueue = IOHIDEventQueue::withEntries (depth+1,
sizeof(IOHIDElementValue) + sizeof(void *));
*outQueue = eventQueue;
return kIOReturnSuccess;
}
IOReturn IOHIDLibUserClient::
disposeQueue(void * vInQueue, void *, void *, void *, void *, void * gated)
{
IOReturn ret = kIOReturnSuccess;
IOHIDEventQueue * queue = (IOHIDEventQueue *) vInQueue;
ret = fNub->stopEventDelivery (queue);
queue->release();
return kIOReturnSuccess;
}
IOReturn IOHIDLibUserClient::
addElementToQueue(void * vInQueue, void * vInElementCookie,
void * vInFlags, void *, void *, void * gated)
{
IOReturn ret = kIOReturnSuccess;
IOHIDEventQueue * queue = (IOHIDEventQueue *) vInQueue;
IOHIDElementCookie elementCookie = (IOHIDElementCookie) vInElementCookie;
ret = fNub->startEventDelivery (queue, elementCookie);
return ret;
}
IOReturn IOHIDLibUserClient::
removeElementFromQueue (void * vInQueue, void * vInElementCookie,
void *, void *, void *, void * gated)
{
IOReturn ret = kIOReturnSuccess;
IOHIDEventQueue * queue = (IOHIDEventQueue *) vInQueue;
IOHIDElementCookie elementCookie = (IOHIDElementCookie) vInElementCookie;
ret = fNub->stopEventDelivery (queue, elementCookie);
return ret;
}
IOReturn IOHIDLibUserClient::
queueHasElement (void * vInQueue, void * vInElementCookie,
void * vOutHasElement, void *, void *, void * gated)
{
IOReturn ret = kIOReturnSuccess;
IOHIDEventQueue * queue = (IOHIDEventQueue *) vInQueue;
IOHIDElementCookie elementCookie = (IOHIDElementCookie) vInElementCookie;
Boolean * outHasElement = (Boolean *) vOutHasElement;
bool hasElement = false;
ret = fNub->checkEventDelivery (queue, elementCookie, &hasElement);
*outHasElement = hasElement;
return ret;
}
IOReturn IOHIDLibUserClient::
startQueue (void * vInQueue, void *, void *,
void *, void *, void * gated)
{
IOHIDEventQueue * queue = (IOHIDEventQueue *) vInQueue;
queue->start();
return kIOReturnSuccess;
}
IOReturn IOHIDLibUserClient::
stopQueue (void * vInQueue, void *, void *,
void *, void *, void * gated)
{
IOHIDEventQueue * queue = (IOHIDEventQueue *) vInQueue;
queue->stop();
return kIOReturnSuccess;
}
IOReturn IOHIDLibUserClient::
updateElementValue (void * cookie, void *, void *,
void *, void *, void * )
{
IOReturn ret = kIOReturnError;
ret = fNub->updateElementValues(&cookie, 1);
return ret;
}
IOReturn IOHIDLibUserClient::
postElementValue (void * cookies, void * cookiesBytes, void *,
void *, void *, void * )
{
IOReturn ret = kIOReturnError;
UInt32 numCookies = ((UInt32)cookiesBytes) / sizeof(UInt32);
ret = fNub->postElementValues((IOHIDElementCookie *)cookies, numCookies);
return ret;
}
IOReturn IOHIDLibUserClient::
getReport (IOHIDReportType reportType, UInt32 reportID,
void *reportBuffer, UInt32 *reportBufferSize)
{
IOReturn ret;
IOMemoryDescriptor * mem;
if (fNub && !isInactive())
{
mem = IOMemoryDescriptor::withAddress(reportBuffer, *reportBufferSize, kIODirectionIn);
if(mem)
{
*reportBufferSize = 0;
ret = fNub->getReport(mem, reportType, reportID);
if (ret == kIOReturnSuccess)
fNub->handleReport(mem, reportType);
*reportBufferSize = mem->getLength();
mem->release();
}
else
ret = kIOReturnNoMemory;
}
else
ret = kIOReturnNotAttached;
return ret;
}
IOReturn IOHIDLibUserClient::
getReportOOL( IOHIDReportReq *reqIn,
UInt32 *sizeOut,
IOByteCount inCount,
IOByteCount *outCount)
{
IOReturn ret;
IOMemoryDescriptor * mem;
if (fNub && !isInactive())
{
*sizeOut = 0;
mem = IOMemoryDescriptor::withAddress(reqIn->reportBuffer, reqIn->reportBufferSize, kIODirectionIn, fClient);
if(mem)
{
ret = mem->prepare();
if(ret == kIOReturnSuccess)
ret = fNub->getReport(mem, reqIn->reportType, reqIn->reportID);
if (ret == kIOReturnSuccess)
fNub->handleReport(mem, reqIn->reportType);
*sizeOut = mem->getLength();
mem->complete();
mem->release();
}
else
ret = kIOReturnNoMemory;
}
else
ret = kIOReturnNotAttached;
return ret;
}
IOReturn IOHIDLibUserClient::
setReport (IOHIDReportType reportType, UInt32 reportID, void *reportBuffer,
UInt32 reportBufferSize)
{
IOReturn ret;
IOMemoryDescriptor * mem;
if (fNub && !isInactive())
{
mem = IOMemoryDescriptor::withAddress(reportBuffer, reportBufferSize, kIODirectionOut);
if(mem)
{
ret = fNub->setReport(mem, reportType, reportID);
if (ret == kIOReturnSuccess)
fNub->handleReport(mem, reportType);
mem->release();
}
else
ret = kIOReturnNoMemory;
}
else
ret = kIOReturnNotAttached;
return ret;
}
IOReturn IOHIDLibUserClient::
setReportOOL (IOHIDReportReq *req, IOByteCount inCount)
{
IOReturn ret;
IOMemoryDescriptor * mem;
if (fNub && !isInactive())
{
mem = IOMemoryDescriptor::withAddress(req->reportBuffer, req->reportBufferSize, kIODirectionOut, fClient);
if(mem)
{
ret = mem->prepare();
if(ret == kIOReturnSuccess)
ret = fNub->setReport(mem, req->reportType, req->reportID);
if (ret == kIOReturnSuccess)
fNub->handleReport(mem, req->reportType);
mem->complete();
mem->release();
}
else
ret = kIOReturnNoMemory;
}
else
ret = kIOReturnNotAttached;
return ret;
}
IOReturn IOHIDLibUserClient::
asyncGetReport (OSAsyncReference asyncRef, IOHIDReportType reportType,
UInt32 reportID, void *reportBuffer,
UInt32 reportBufferSize, UInt32 completionTimeOutMS)
{
IOReturn ret;
IOHIDCompletion tap;
IOMemoryDescriptor * mem = NULL;
AsyncParam * pb = NULL;
retain();
if (fNub && !isInactive())
{
do {
mem = IOMemoryDescriptor::withAddress((vm_address_t)reportBuffer, reportBufferSize, kIODirectionIn, fClient);
if(!mem)
{
ret = kIOReturnNoMemory;
break;
}
ret = mem->prepare();
if(ret != kIOReturnSuccess)
break;
pb = (AsyncParam *)IOMalloc(sizeof(AsyncParam));
if(!pb)
{
ret = kIOReturnNoMemory;
break;
}
bcopy(asyncRef, pb->fAsyncRef, sizeof(OSAsyncReference));
pb->fMax = reportBufferSize;
pb->fMem = mem;
pb->reportType = reportType;
tap.target = this;
tap.action = &ReqComplete;
tap.parameter = pb;
ret = fNub->getReport(mem, reportType, reportID, completionTimeOutMS, &tap);
} while (false);
}
else
ret = kIOReturnNotAttached;
if(ret != kIOReturnSuccess)
{
if(mem)
{
mem->complete();
mem->release();
}
if(pb)
IOFree(pb, sizeof(*pb));
release();
}
return ret;
}
IOReturn IOHIDLibUserClient::
asyncSetReport (OSAsyncReference asyncRef, IOHIDReportType reportType,
UInt32 reportID, void *reportBuffer,
UInt32 reportBufferSize, UInt32 completionTimeOutMS)
{
IOReturn ret;
IOHIDCompletion tap;
IOMemoryDescriptor * mem = NULL;
AsyncParam * pb = NULL;
retain();
if (fNub && !isInactive())
{
do {
mem = IOMemoryDescriptor::withAddress((vm_address_t)reportBuffer, reportBufferSize, kIODirectionOut, fClient);
if(!mem)
{
ret = kIOReturnNoMemory;
break;
}
ret = mem->prepare();
if(ret != kIOReturnSuccess)
break;
pb = (AsyncParam *)IOMalloc(sizeof(AsyncParam));
if(!pb)
{
ret = kIOReturnNoMemory;
break;
}
bcopy(asyncRef, pb->fAsyncRef, sizeof(OSAsyncReference));
pb->fMax = reportBufferSize;
pb->fMem = mem;
pb->reportType = reportType;
tap.target = this;
tap.action = &ReqComplete;
tap.parameter = pb;
ret = fNub->setReport(mem, reportType, reportID, completionTimeOutMS, &tap);
} while (false);
}
else
ret = kIOReturnNotAttached;
if(ret != kIOReturnSuccess)
{
if(mem)
{
mem->complete();
mem->release();
}
if(pb)
IOFree(pb, sizeof(*pb));
release();
}
return ret;
}
void IOHIDLibUserClient::
ReqComplete(void *obj, void *param, IOReturn res, UInt32 remaining)
{
void * args[1];
AsyncParam * pb = (AsyncParam *)param;
IOHIDLibUserClient *me = OSDynamicCast(IOHIDLibUserClient, (OSObject*)obj);
if (!me)
return;
if(res == kIOReturnSuccess)
{
args[0] = (void *)(pb->fMax - remaining);
me->fNub->handleReport(pb->fMem, pb->reportType);
}
else
{
args[0] = 0;
}
if (pb->fMem)
{
pb->fMem->complete();
pb->fMem->release();
}
sendAsyncResult(pb->fAsyncRef, res, args, 1);
IOFree(pb, sizeof(*pb));
me->release();
}