SimpleUserClient.cpp
/* |
File: SimpleUserClient.cpp |
Description: This file shows how to implement a simple I/O Kit user client that is Rosetta-aware. |
Copyright: Copyright © 2001-2008 Apple Inc. All rights reserved. |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. |
("Apple") in consideration of your agreement to the following terms, and your |
use, installation, modification or redistribution of this Apple software |
constitutes acceptance of these terms. If you do not agree with these terms, |
please do not use, install, modify or redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and subject |
to these terms, Apple grants you a personal, non-exclusive license, under Apple’s |
copyrights in this original Apple software (the "Apple Software"), to use, |
reproduce, modify and redistribute the Apple Software, with or without |
modifications, in source and/or binary forms; provided that if you redistribute |
the Apple Software in its entirety and without modifications, you must retain |
this notice and the following text and disclaimers in all such redistributions of |
the Apple Software. Neither the name, trademarks, service marks or logos of |
Apple Computer, Inc. may be used to endorse or promote products derived from the |
Apple Software without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or implied, |
are granted by Apple herein, including but not limited to any patent rights that |
may be infringed by your derivative works or by other works in which the Apple |
Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO |
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED |
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN |
COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR |
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION |
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT |
(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN |
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
Change History (most recent first): |
2.0 08/13/2008 Add Leopard user client API for supporting 64-bit user processes. |
Now requires Xcode 3.0 or later to build. |
1.1 05/22/2007 User client performs endian swapping when called from a user process |
running using Rosetta. Updated to produce a universal binary. |
Now requires Xcode 2.2.1 or later to build. |
1.0d3 01/14/2003 New sample. |
*/ |
#include <IOKit/IOLib.h> |
#include <IOKit/IOKitKeys.h> |
#include <libkern/OSByteOrder.h> |
#include "SimpleUserClient.h" |
#define super IOUserClient |
// Even though we are defining the convenience macro super for the superclass, you must use the actual class name |
// in the OS*MetaClass macros. Note that the class name is different when supporting Mac OS X 10.4. |
#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 |
OSDefineMetaClassAndStructors(com_apple_dts_driver_SimpleUserClient_10_4, IOUserClient) |
#else |
OSDefineMetaClassAndStructors(com_apple_dts_driver_SimpleUserClient, IOUserClient) |
#endif |
#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 |
// Sentinel values for the method dispatch table |
enum { |
kMethodObjectThis = 0, |
kMethodObjectProvider |
}; |
// User client method dispatch table. |
// |
// The user client mechanism is designed to allow calls from a user process to be dispatched to |
// any IOService-based object in the kernel. Almost always this mechanism is used to dispatch calls to |
// either member functions of the user client itself or of the user client's provider. The provider is |
// the driver which the user client is connecting to the user process. |
// |
// While this sample shows one case of dispatching calls directly to the driver (ScalarIScalarO), |
// it is recommended that calls be dispatched to the user client. This allows the user client to perform |
// error checking on the parameters before passing them to the driver. It also allows the user client to |
// do any endian-swapping of parameters in the cross-endian case. (See ScalarIStructI below for further |
// discussion of this subject.) |
// |
// The dispatch table makes use of the sentinel values kMethodObjectThis and kMethodObjectProvider to |
// represent at compile time the values of the this pointer and fProvider respectively at run time. |
const IOExternalMethod SimpleUserClientClassName::sMethods[kNumberOfMethods] = { |
{ // kMyUserClientOpen |
(IOService *) kMethodObjectThis, // Target object is this user client. |
(IOMethod) &SimpleUserClientClassName::openUserClient, // Method pointer. |
kIOUCScalarIScalarO, // Scalar Input, Scalar Output. |
0, // No scalar input values. |
0 // No scalar output values. |
}, |
{ // kMyUserClientClose |
(IOService *) kMethodObjectThis, // Target object is this user client. |
(IOMethod) &SimpleUserClientClassName::closeUserClient, // Method pointer. |
kIOUCScalarIScalarO, // Scalar Input, Scalar Output. |
0, // No scalar input values. |
0 // No scalar output values. |
}, |
{ // kMyScalarIStructIMethod |
(IOService *) kMethodObjectThis, // Target object is this user client. |
(IOMethod) &SimpleUserClientClassName::ScalarIStructI, // Method pointer. |
kIOUCScalarIStructI, // Scalar Input, Struct Input. |
1, // One scalar input value. |
sizeof(MySampleStruct) // The size of the input struct. |
}, |
{ // kMyScalarIStructOMethod |
(IOService *) kMethodObjectThis, // Target object is this user client. |
(IOMethod) &SimpleUserClientClassName::ScalarIStructO, // Method pointer. |
kIOUCScalarIStructO, // Scalar Input, Struct Output. |
2, // Two scalar input values. |
sizeof(MySampleStruct) // The size of the output struct. |
}, |
{ // kMyScalarIScalarOMethod |
(IOService *) kMethodObjectProvider, // Target object is this user client's provider |
// (the driver). |
(IOMethod) &SimpleDriverClassName::ScalarIScalarO, // Method pointer. |
kIOUCScalarIScalarO, // Scalar Input, Scalar Output. |
2, // Two scalar input values. |
1 // One scalar output value. |
}, |
{ // kMyStructIStructOMethod |
(IOService *) kMethodObjectThis, // Target object is this user client. |
(IOMethod) &SimpleUserClientClassName::StructIStructO, // Method pointer. |
kIOUCStructIStructO, // Struct Input, Struct Output. |
sizeof(MySampleStruct), // The size of the input struct. |
sizeof(MySampleStruct) // The size of the output struct. |
} |
}; |
// Look up the external methods - supply a description of the parameters |
// available to be called. |
// |
// This is the legacy approach which only supports 32-bit user processes. |
IOExternalMethod* SimpleUserClientClassName::getTargetAndMethodForIndex(IOService** target, UInt32 index) |
{ |
IOLog("%s[%p]::%s(%p, %ld)\n", getName(), this, __FUNCTION__, target, index); |
// Make sure that the index of the function we're calling actually exists in the function table. |
if (index < (UInt32) kNumberOfMethods) { |
if (sMethods[index].object == (IOService *) kMethodObjectThis) { |
*target = this; |
} |
else { |
*target = fProvider; |
} |
return (IOExternalMethod *) &sMethods[index]; |
} |
else { |
*target = NULL; |
return NULL; |
} |
} |
#else |
// This is the technique which supports both 32-bit and 64-bit user processes starting with Mac OS X 10.5. |
// |
// User client method dispatch table. |
// |
// The user client mechanism is designed to allow calls from a user process to be dispatched to |
// any IOService-based object in the kernel. Almost always this mechanism is used to dispatch calls to |
// either member functions of the user client itself or of the user client's provider. The provider is |
// the driver which the user client is connecting to the user process. |
// |
// It is recommended that calls be dispatched to the user client and not directly to the provider driver. |
// This allows the user client to perform error checking on the parameters before passing them to the driver. |
// It also allows the user client to do any endian-swapping of parameters in the cross-endian case. |
// (See ScalarIStructI below for further discussion of this subject.) |
const IOExternalMethodDispatch SimpleUserClientClassName::sMethods[kNumberOfMethods] = { |
{ // kMyUserClientOpen |
(IOExternalMethodAction) &SimpleUserClientClassName::sOpenUserClient, // Method pointer. |
0, // No scalar input values. |
0, // No struct input value. |
0, // No scalar output values. |
0 // No struct output value. |
}, |
{ // kMyUserClientClose |
(IOExternalMethodAction) &SimpleUserClientClassName::sCloseUserClient, // Method pointer. |
0, // No scalar input values. |
0, // No struct input value. |
0, // No scalar output values. |
0 // No struct output value. |
}, |
{ // kMyScalarIStructIMethod |
(IOExternalMethodAction) &SimpleUserClientClassName::sScalarIStructI, // Method pointer. |
1, // One scalar input value. |
sizeof(MySampleStruct), // The size of the input struct. |
0, // No scalar output values. |
0 // No struct output value. |
}, |
{ // kMyScalarIStructOMethod |
(IOExternalMethodAction) &SimpleUserClientClassName::sScalarIStructO, // Method pointer. |
2, // Two scalar input values. |
0, // No struct input value. |
0, // No scalar output values. |
sizeof(MySampleStruct) // The size of the output struct. |
}, |
{ // kMyScalarIScalarOMethod |
(IOExternalMethodAction) &SimpleUserClientClassName::sScalarIScalarO, // Method pointer. |
2, // Two scalar input values. |
0, // No struct input value. |
1, // One scalar output value. |
0 // No struct output value. |
}, |
{ // kMyStructIStructOMethod |
(IOExternalMethodAction) &SimpleUserClientClassName::sStructIStructO, // Method pointer. |
0, // No scalar input values. |
sizeof(MySampleStruct), // The size of the input struct. |
0, // No scalar output values. |
sizeof(MySampleStruct) // The size of the output struct. |
} |
}; |
IOReturn SimpleUserClientClassName::externalMethod(uint32_t selector, IOExternalMethodArguments* arguments, |
IOExternalMethodDispatch* dispatch, OSObject* target, void* reference) |
{ |
IOLog("%s[%p]::%s(%d, %p, %p, %p, %p)\n", getName(), this, __FUNCTION__, |
selector, arguments, dispatch, target, reference); |
if (selector < (uint32_t) kNumberOfMethods) { |
dispatch = (IOExternalMethodDispatch *) &sMethods[selector]; |
if (!target) { |
if (selector == kMyScalarIScalarOMethod) { |
target = fProvider; |
} |
else { |
target = this; |
} |
} |
} |
return super::externalMethod(selector, arguments, dispatch, target, reference); |
} |
#endif |
// There are two forms of IOUserClient::initWithTask, the second of which accepts an additional OSDictionary* parameter. |
// If your user client needs to modify its behavior when it's being used by a process running using Rosetta, |
// you need to implement the form of initWithTask with this additional parameter. |
// |
// initWithTask is called as a result of the user process calling IOServiceOpen. |
bool SimpleUserClientClassName::initWithTask(task_t owningTask, void* securityToken, UInt32 type, OSDictionary* properties) |
{ |
bool success; |
success = super::initWithTask(owningTask, securityToken, type, properties); |
// This IOLog must follow super::initWithTask because getName relies on the superclass initialization. |
IOLog("%s[%p]::%s(%p, %p, %ld, %p)\n", getName(), this, __FUNCTION__, owningTask, securityToken, type, properties); |
if (success) { |
// This code will do the right thing on both PowerPC- and Intel-based systems because the cross-endian |
// property will never be set on PowerPC-based Macs. |
fCrossEndian = false; |
if (properties != NULL && properties->getObject(kIOUserClientCrossEndianKey)) { |
// A connection to this user client is being opened by a user process running using Rosetta. |
// Indicate that this user client can handle being called from cross-endian user processes by |
// setting its IOUserClientCrossEndianCompatible property in the I/O Registry. |
if (setProperty(kIOUserClientCrossEndianCompatibleKey, kOSBooleanTrue)) { |
fCrossEndian = true; |
IOLog("%s[%p]::%s(): fCrossEndian = true\n", getName(), this, __FUNCTION__); |
} |
} |
} |
fTask = owningTask; |
fProvider = NULL; |
return success; |
} |
// start is called after initWithTask as a result of the user process calling IOServiceOpen. |
bool SimpleUserClientClassName::start(IOService* provider) |
{ |
bool success; |
IOLog("%s[%p]::%s(%p)\n", getName(), this, __FUNCTION__, provider); |
// Verify that this user client is being started with a provider that it knows |
// how to communicate with. |
fProvider = OSDynamicCast(SimpleDriverClassName, provider); |
success = (fProvider != NULL); |
if (success) { |
// It's important not to call super::start if some previous condition |
// (like an invalid provider) would cause this function to return false. |
// I/O Kit won't call stop on an object if its start function returned false. |
success = super::start(provider); |
} |
return success; |
} |
// We override stop only to log that it has been called to make it easier to follow the user client's lifecycle. |
void SimpleUserClientClassName::stop(IOService* provider) |
{ |
IOLog("%s[%p]::%s(%p)\n", getName(), this, __FUNCTION__, provider); |
super::stop(provider); |
} |
// clientClose is called as a result of the user process calling IOServiceClose. |
IOReturn SimpleUserClientClassName::clientClose(void) |
{ |
IOLog("%s[%p]::%s()\n", getName(), this, __FUNCTION__); |
// Defensive coding in case the user process called IOServiceClose |
// without calling closeUserClient first. |
(void) closeUserClient(); |
// Inform the user process that this user client is no longer available. This will also cause the |
// user client instance to be destroyed. |
// |
// terminate would return false if the user process still had this user client open. |
// This should never happen in our case because this code path is only reached if the user process |
// explicitly requests closing the connection to the user client. |
bool success = terminate(); |
if (!success) { |
IOLog("%s[%p]::%s(): terminate() failed.\n", getName(), this, __FUNCTION__); |
} |
// DON'T call super::clientClose, which just returns kIOReturnUnsupported. |
return kIOReturnSuccess; |
} |
// clientDied is called if the client user process terminates unexpectedly (crashes). |
// We override clientDied only to log that it has been called to make it easier to follow the user client's lifecycle. |
// Production user clients need to override clientDied only if they need to take some alternate action if the user process |
// crashes instead of exiting normally. |
IOReturn SimpleUserClientClassName::clientDied(void) |
{ |
IOReturn result = kIOReturnSuccess; |
IOLog("%s[%p]::%s()\n", getName(), this, __FUNCTION__); |
// The default implementation of clientDied just calls clientClose. |
result = super::clientDied(); |
return result; |
} |
// willTerminate is called at the beginning of the termination process. It is a notification |
// that a provider has been terminated, sent before recursing up the stack, in root-to-leaf order. |
// |
// This is where any pending I/O should be terminated. At this point the user client has been marked |
// inactive and any further requests from the user process should be returned with an error. |
bool SimpleUserClientClassName::willTerminate(IOService* provider, IOOptionBits options) |
{ |
IOLog("%s[%p]::%s(%p, %ld)\n", getName(), this, __FUNCTION__, provider, options); |
return super::willTerminate(provider, options); |
} |
// didTerminate is called at the end of the termination process. It is a notification |
// that a provider has been terminated, sent after recursing up the stack, in leaf-to-root order. |
bool SimpleUserClientClassName::didTerminate(IOService* provider, IOOptionBits options, bool* defer) |
{ |
IOLog("%s[%p]::%s(%p, %ld, %p)\n", getName(), this, __FUNCTION__, provider, options, defer); |
// If all pending I/O has been terminated, close our provider. If I/O is still outstanding, set defer to true |
// and the user client will not have stop called on it. |
closeUserClient(); |
*defer = false; |
return super::didTerminate(provider, options, defer); |
} |
// We override terminate only to log that it has been called to make it easier to follow the user client's lifecycle. |
// Production user clients will rarely need to override terminate. Termination processing should be done in |
// willTerminate or didTerminate instead. |
bool SimpleUserClientClassName::terminate(IOOptionBits options) |
{ |
bool success; |
IOLog("%s[%p]::%s(%ld)\n", getName(), this, __FUNCTION__, options); |
success = super::terminate(options); |
return success; |
} |
// We override finalize only to log that it has been called to make it easier to follow the user client's lifecycle. |
// Production user clients will rarely need to override finalize. |
bool SimpleUserClientClassName::finalize(IOOptionBits options) |
{ |
bool success; |
IOLog("%s[%p]::%s(%ld)\n", getName(), this, __FUNCTION__, options); |
success = super::finalize(options); |
return success; |
} |
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 |
IOReturn SimpleUserClientClassName::sOpenUserClient(SimpleUserClientClassName* target, void* reference, IOExternalMethodArguments* arguments) |
{ |
return target->openUserClient(); |
} |
#endif |
IOReturn SimpleUserClientClassName::openUserClient(void) |
{ |
IOReturn result = kIOReturnSuccess; |
IOLog("%s[%p]::%s()\n", getName(), this, __FUNCTION__); |
if (fProvider == NULL || isInactive()) { |
// Return an error if we don't have a provider. This could happen if the user process |
// called openUserClient without calling IOServiceOpen first. Or, the user client could be |
// in the process of being terminated and is thus inactive. |
result = kIOReturnNotAttached; |
} |
else if (!fProvider->open(this)) { |
// The most common reason this open call will fail is because the provider is already open |
// and it doesn't support being opened by more than one client at a time. |
result = kIOReturnExclusiveAccess; |
} |
return result; |
} |
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 |
IOReturn SimpleUserClientClassName::sCloseUserClient(SimpleUserClientClassName* target, void* reference, IOExternalMethodArguments* arguments) |
{ |
return target->closeUserClient(); |
} |
#endif |
IOReturn SimpleUserClientClassName::closeUserClient(void) |
{ |
IOReturn result = kIOReturnSuccess; |
IOLog("%s[%p]::%s()\n", getName(), this, __FUNCTION__); |
if (fProvider == NULL) { |
// Return an error if we don't have a provider. This could happen if the user process |
// called closeUserClient without calling IOServiceOpen first. |
result = kIOReturnNotAttached; |
IOLog("%s[%p]::%s(): returning kIOReturnNotAttached.\n", getName(), this, __FUNCTION__); |
} |
else if (fProvider->isOpen(this)) { |
// Make sure we're the one who opened our provider before we tell it to close. |
fProvider->close(this); |
} |
else { |
result = kIOReturnNotOpen; |
IOLog("%s[%p]::%s(): returning kIOReturnNotOpen.\n", getName(), this, __FUNCTION__); |
} |
return result; |
} |
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 |
IOReturn SimpleUserClientClassName::sScalarIStructI(SimpleUserClientClassName* target, void* reference, IOExternalMethodArguments* arguments) |
{ |
return target->ScalarIStructI((uint32_t) arguments->scalarInput[0], |
(MySampleStruct*) arguments->structureInput, |
(uint32_t) arguments->structureInputSize); |
} |
#endif |
IOReturn SimpleUserClientClassName::ScalarIStructI(uint32_t inNumber, MySampleStruct* inStruct, uint32_t inStructSize) |
{ |
IOReturn result; |
IOLog("%s[%p]::%s(inNumber = %d, field1 = %lld, field2 = %lld, inStructSize = %d)\n", getName(), this, __FUNCTION__, |
inNumber, inStruct->field1, inStruct->field2, inStructSize); |
// Endian-swap structure parameters in the user client before passing them to the driver. |
// |
// This may require adding new functions to your user client and modifying the dispatch table in |
// getTargetAndMethodForIndex to point to these new functions. |
// |
// This approach is greatly preferable because it avoids the complexity of a driver which can be opened by multiple clients, |
// each of which may or may not be cross-endian. It also avoids having to change the driver to make it cross-endian-aware. |
// |
// Note that fCrossEndian will always be false if running on a PowerPC-based Mac. |
if (fProvider == NULL || isInactive()) { |
// Return an error if we don't have a provider. This could happen if the user process |
// called ScalarIStructI without calling IOServiceOpen first. Or, the user client could be |
// in the process of being terminated and is thus inactive. |
result = kIOReturnNotAttached; |
} |
else if (!fProvider->isOpen(this)) { |
// Return an error if we do not have the driver open. This could happen if the user process |
// did not call openUserClient before calling this function. |
result = kIOReturnNotOpen; |
} |
else { |
if (fCrossEndian) { |
// Structures aren't automatically swapped by the user client mechanism as it has no knowledge of how the fields |
// structure are laid out. |
// Swap the fields of the structure passed by the client user process before passing it to the driver. |
// Use the unconditional swap macros here as we know only at runtime if we're being called from a |
// cross-endian user process running using Rosetta. |
inStruct->field1 = OSSwapInt64(inStruct->field1); |
inStruct->field2 = OSSwapInt64(inStruct->field2); |
IOLog("%s[%p]::%s(after swap: inNumber = %d, field1 = %lld, field2 = %lld, inStructSize = %d)\n", getName(), this, __FUNCTION__, |
inNumber, inStruct->field1, inStruct->field2, inStructSize); |
} |
result = fProvider->ScalarIStructI(inNumber, inStruct, inStructSize); |
} |
return result; |
} |
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 |
IOReturn SimpleUserClientClassName::sScalarIStructO(SimpleUserClientClassName* target, void* reference, IOExternalMethodArguments* arguments) |
{ |
return target->ScalarIStructO((uint32_t) arguments->scalarInput[0], |
(uint32_t) arguments->scalarInput[1], |
(MySampleStruct*) arguments->structureOutput, |
(uint32_t*) &arguments->structureOutputSize); |
} |
#endif |
IOReturn SimpleUserClientClassName::ScalarIStructO(uint32_t inNumber1, uint32_t inNumber2, |
MySampleStruct* outStruct, uint32_t* outStructSize) |
{ |
IOReturn result; |
IOLog("%s[%p]::%s(inNumber1 = %d, inNumber2 = %d)\n", getName(), this, __FUNCTION__, inNumber1, inNumber2); |
if (fProvider == NULL || isInactive()) { |
// Return an error if we don't have a provider. This could happen if the user process |
// called ScalarIStructO without calling IOServiceOpen first. Or the user client could be |
// in the process of being terminated and is thus inactive. |
result = kIOReturnNotAttached; |
} |
else if (!fProvider->isOpen(this)) { |
// Return an error if we do not have the driver open. This could happen if the user process |
// did not call openUserClient before calling this function. |
result = kIOReturnNotOpen; |
} |
else { |
result = fProvider->ScalarIStructO(inNumber1, inNumber2, outStruct, outStructSize); |
// Note that fCrossEndian will always be false if running on a PowerPC-based Mac. |
if (fCrossEndian) { |
// Swap the fields of the structure returned by the driver before returning it to the client user process. |
// Use the unconditional swap macros here as we know only at runtime if we're being called from a |
// cross-endian user process running using Rosetta. |
outStruct->field1 = OSSwapInt64(outStruct->field1); |
outStruct->field2 = OSSwapInt64(outStruct->field2); |
IOLog("%s[%p]::%s(output after swap: field1 = %lld, field2 = %lld, outStructSize = %d)\n", getName(), this, __FUNCTION__, |
outStruct->field1, outStruct->field2, *outStructSize); |
} |
} |
return result; |
} |
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 |
IOReturn SimpleUserClientClassName::sScalarIScalarO(SimpleDriverClassName* target, void* reference, IOExternalMethodArguments* arguments) |
{ |
return target->ScalarIScalarO((uint32_t) arguments->scalarInput[0], |
(uint32_t) arguments->scalarInput[1], |
(uint32_t*) &arguments->scalarOutput[0]); |
} |
#endif |
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 |
IOReturn SimpleUserClientClassName::sStructIStructO(SimpleUserClientClassName* target, void* reference, IOExternalMethodArguments* arguments) |
{ |
return target->StructIStructO((MySampleStruct*) arguments->structureInput, |
(MySampleStruct*) arguments->structureOutput, |
(uint32_t) arguments->structureInputSize, |
(uint32_t*) &arguments->structureOutputSize); |
} |
#endif |
IOReturn SimpleUserClientClassName::StructIStructO(MySampleStruct* inStruct, MySampleStruct* outStruct, |
uint32_t inStructSize, uint32_t* outStructSize) |
{ |
IOReturn result; |
IOLog("%s[%p]::%s(field1 = %lld, field2 = %lld, inStructSize = %d)\n", getName(), this, __FUNCTION__, |
inStruct->field1, inStruct->field2, inStructSize); |
if (fProvider == NULL || isInactive()) { |
// Return an error if we don't have a provider. This could happen if the user process |
// called StructIStructO without calling IOServiceOpen first. Or, the user client could be |
// in the process of being terminated and is thus inactive. |
result = kIOReturnNotAttached; |
} |
else if (!fProvider->isOpen(this)) { |
// Return an error if we do not have the driver open. This could happen if the user process |
// did not call openUserClient before calling this function. |
result = kIOReturnNotOpen; |
} |
else { |
// Note that fCrossEndian will always be false if running on a PowerPC-based Mac. |
if (fCrossEndian) { |
// Swap the fields of the structure returned by the driver before returning it to the client user process. |
// Use the unconditional swap macros here as we know only at runtime if we're being called from a |
// cross-endian user process running using Rosetta. |
inStruct->field1 = OSSwapInt64(inStruct->field1); |
inStruct->field2 = OSSwapInt64(inStruct->field2); |
IOLog("%s[%p]::%s(input after swap: field1 = %lld, field2 = %lld, inStructSize = %d)\n", getName(), this, __FUNCTION__, |
inStruct->field1, inStruct->field2, inStructSize); |
} |
result = fProvider->StructIStructO(inStruct, outStruct, inStructSize, outStructSize); |
if (fCrossEndian) { |
// Swap the results returned by the driver before returning them to the client user process. |
outStruct->field1 = OSSwapInt64(outStruct->field1); |
outStruct->field2 = OSSwapInt64(outStruct->field2); |
IOLog("%s[%p]::%s(output after swap: field1 = %lld, field2 = %lld, outStructSize = %d)\n", getName(), this, __FUNCTION__, |
outStruct->field1, outStruct->field2, *outStructSize); |
} |
} |
return result; |
} |
Copyright © 2008 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2008-08-27