1/*
2 * AppleSamplePCIUserClient implementation.
3 * This class represents the user client object for the driver, which
4 * will be instantiated by IOKit to represent a connection to the client
5 * process, in response to the client's call to IOServiceOpen().
6 * It will be destroyed when the connection is closed or the client
7 * abnormally terminates, so it should track all the resources allocated
8 * to the client.
9 */
10
11#include "AppleSamplePCI.h"
12#include <IOKit/IOLib.h>
13#include <IOKit/assert.h>
14
15/*
16 * Define the metaclass information that is used for runtime
17 * typechecking of IOKit objects. We're a subclass of IOUserClient.
18 */
19
20#define super IOUserClient
21OSDefineMetaClassAndStructors( AppleSamplePCIUserClient, IOUserClient );
22
23/*
24 * Since this sample uses the IOUserClientClass property, the AppleSamplePCIUserClient
25 * is created automatically in response to IOServiceOpen(). More complex applications
26 * might have several kinds of clients each with a different IOUserClient subclass,
27 * with different enumerated types. In that case the AppleSamplePCI class must implement
28 * the newUserClient() method (see IOService.h headerdoc).
29 */
30
31bool AppleSamplePCIUserClient::initWithTask( task_t owningTask, void * securityID,
32                                             UInt32 type,  OSDictionary * properties )
33{
34    IOLog("AppleSamplePCIUserClient::initWithTask(type %ld)\n", type);
35
36    fTask = owningTask;
37
38    return( super::initWithTask( owningTask, securityID, type, properties ));
39}
40
41bool AppleSamplePCIUserClient::start( IOService * provider )
42{
43    IOLog("AppleSamplePCIUserClient::start\n");
44
45    if( !super::start( provider ))
46        return( false );
47
48    /*
49     * Our provider is the AppleSamplePCI object.
50     */
51
52    assert( OSDynamicCast( AppleSamplePCI, provider ));
53    fDriver = (AppleSamplePCI *) provider;
54
55    /*
56     * Set up some memory to be shared between this user client instance and its
57     * client process. The client will call in to map this memory, and iokit
58     * will call clientMemoryForType to obtain this memory descriptor.
59     */
60
61    fClientSharedMemory = IOBufferMemoryDescriptor::withOptions(
62                kIOMemoryKernelUserShared, sizeof( AppleSampleSharedMemory ));
63    if( !fClientSharedMemory)
64        return( false );
65
66    fClientShared = (AppleSampleSharedMemory *) fClientSharedMemory->getBytesNoCopy();
67    fClientShared->field1 = 0x11111111;
68    fClientShared->field2 = 0x22222222;
69    fClientShared->field3 = 0x33333333;
70    strcpy( fClientShared->string, "some data" );
71    fOpenCount = 1;
72
73    return( true );
74}
75
76
77/*
78 * Kill ourselves off if the client closes its connection or the client dies.
79 */
80
81IOReturn AppleSamplePCIUserClient::clientClose( void )
82{
83    if( !isInactive())
84        terminate();
85
86    return( kIOReturnSuccess );
87}
88
89/*
90 * stop will be called during the termination process, and should free all resources
91 * associated with this client.
92 */
93void AppleSamplePCIUserClient::stop( IOService * provider )
94{
95    IOLog("AppleSamplePCIUserClient::stop\n");
96
97    if( fClientSharedMemory) {
98        fClientSharedMemory->release();
99        fClientShared = 0;
100    }
101
102    super::stop( provider );
103}
104
105/*
106 * Lookup the external methods - supply a description of the parameters
107 * available to be called
108 */
109
110IOExternalMethod * AppleSamplePCIUserClient::getTargetAndMethodForIndex(
111                                                    IOService ** targetP, UInt32 index )
112{
113    static const IOExternalMethod methodDescs[kAppleSampleNumMethods] = {
114
115      { NULL, (IOMethod) &AppleSamplePCIUserClient::method1,
116        kIOUCStructIStructO, kIOUCVariableStructureSize, kIOUCVariableStructureSize },
117
118      { NULL, (IOMethod) &AppleSamplePCIUserClient::method2,
119        kIOUCStructIStructO, sizeof(AppleSampleStructForMethod2), sizeof(AppleSampleResultsForMethod2) },
120    };
121
122    *targetP = this;
123    if( index < kAppleSampleNumMethods)
124        return( (IOExternalMethod *)(methodDescs + index) );
125    else
126        return NULL;
127}
128
129IOReturn AppleSamplePCIUserClient::externalMethod(
130        uint32_t selector, IOExternalMethodArguments * arguments,
131        IOExternalMethodDispatch * dispatch, OSObject * target, void * reference )
132{
133
134    return (super::externalMethod(selector, arguments, NULL, this, NULL));
135
136    IOReturn err;
137
138    switch (selector)
139    {
140        case kAppleSampleMethod1:
141            err = method1( (UInt32 *) arguments->structureInput,
142                            (UInt32 *)  arguments->structureOutput,
143                            arguments->structureInputSize, (IOByteCount *) &arguments->structureOutputSize );
144            break;
145
146        case kAppleSampleMethod2:
147            err = method2( (AppleSampleStructForMethod2 *) arguments->structureInput,
148                            (AppleSampleResultsForMethod2 *)  arguments->structureOutput,
149                            arguments->structureInputSize, (IOByteCount *) &arguments->structureOutputSize );
150            break;
151
152        default:
153            err = kIOReturnBadArgument;
154            break;
155    }
156
157    IOLog("externalMethod(%d) 0x%x", selector, err);
158
159    return (err);
160}
161
162/*
163 * Implement each of the external methods described above.
164 */
165
166IOReturn AppleSamplePCIUserClient::method1(
167                                           UInt32 * dataIn, UInt32 * dataOut,
168                                           IOByteCount inputSize, IOByteCount * outputSize )
169{
170    IOReturn    ret;
171    IOItemCount count;
172
173    IOLog("AppleSamplePCIUserClient::method1(");
174
175    if( *outputSize < inputSize)
176        return( kIOReturnNoSpace );
177
178    count = inputSize / sizeof( UInt32 );
179    for( UInt32 i = 0; i < count; i++ ) {
180        IOLog("%08lx, ", dataIn[i]);
181        dataOut[i] = dataIn[i] ^ 0xffffffff;
182    }
183
184    ret = kIOReturnSuccess;
185    IOLog(")\n");
186    *outputSize = count * sizeof( UInt32 );
187
188    return( ret );
189}
190
191IOReturn AppleSamplePCIUserClient::method2( AppleSampleStructForMethod2 * structIn,
192                                            AppleSampleResultsForMethod2 * structOut,
193                                            IOByteCount inputSize, IOByteCount * outputSize )
194
195{
196    IOReturn err;
197    IOMemoryDescriptor * memDesc = 0;
198    UInt32 param1 = structIn->parameter1;
199    mach_vm_address_t clientAddr = structIn->data_pointer;
200    mach_vm_size_t size = structIn->data_length;
201
202    IOLog("AppleSamplePCIUserClient::method2(%lx)\n", param1);
203
204    IOLog( "fClientShared->string == \"%s\"\n", fClientShared->string );
205
206    structOut->results1 = 0x87654321;
207
208    do
209    {
210        // construct a memory descriptor for the out of line client memory
211
212        // old 32 bit API - this will fail and log a backtrace if the task is 64 bit
213        memDesc = IOMemoryDescriptor::withAddress( clientAddr, size, kIODirectionNone, fTask );
214        if( !memDesc) {
215            IOLog("IOMemoryDescriptor::withAddress failed\n");
216        } else {
217            memDesc->release();
218        }
219
220        // 64 bit API - works on all tasks, whether 64 bit or 32 bit
221        memDesc = IOMemoryDescriptor::withAddressRange( clientAddr, size, kIODirectionNone, fTask );
222        if( !memDesc) {
223            IOLog("IOMemoryDescriptor::withAddress failed\n");
224            err = kIOReturnVMError;
225            continue;
226        }
227
228        // wire it and make sure we can write it
229        err = memDesc->prepare( kIODirectionOutIn );
230        if( kIOReturnSuccess != err) {
231            IOLog("IOMemoryDescriptor::prepare failed(%x)\n", err);
232            continue;
233        }
234
235        // Generate a DMA list for the client memory
236        err = fDriver->generateDMAAddresses(memDesc);
237
238        // Other methods to access client memory:
239
240        // readBytes/writeBytes allow programmed I/O to/from an offset in the buffer
241        char pioBuffer[ 200 ];
242        memDesc->readBytes(32, &pioBuffer, sizeof( pioBuffer));
243        IOLog("readBytes: \"%s\"\n", pioBuffer);
244
245        // map() will create a mapping in the kernel address space.
246        IOMemoryMap * memMap = memDesc->map();
247        if( memMap) {
248            char * address = (char *) memMap->getVirtualAddress();
249            IOLog("kernel mapped: \"%s\"\n", address + 32);
250            memMap->release();
251        } else
252            IOLog("memDesc map(kernel) failed\n");
253
254        // this map() will create a mapping in the users (the client of this IOUserClient) address space.
255        memMap = memDesc->map(fTask, 0, kIOMapAnywhere);
256        if( memMap)
257        {
258            // old 32 bit API - this will truncate and log a backtrace if the task is 64 bit
259            IOVirtualAddress address32 = memMap->getVirtualAddress();
260            IOLog("user32 mapped: 0x%x\n", address32);
261
262            // new 64 bit API - same for 32 bit and 64 bit client tasks
263            mach_vm_address_t address64 = memMap->getAddress();
264            IOLog("user64 mapped: 0x%qx\n", address64);
265            memMap->release();
266        } else
267            IOLog("memDesc map(user) failed\n");
268
269        // Done with the I/O now.
270        memDesc->complete( kIODirectionOutIn );
271
272    } while( false );
273
274    if( memDesc)
275        memDesc->release();
276
277    return( err );
278}
279
280/*
281 * Shared memory support. Supply a IOMemoryDescriptor instance to describe
282 * each of the kinds of shared memory available to be mapped into the client
283 * process with this user client.
284 */
285
286IOReturn AppleSamplePCIUserClient::clientMemoryForType(
287                                UInt32 type,
288                                IOOptionBits * options,
289                                IOMemoryDescriptor ** memory )
290{
291    // Return a memory descriptor reference for some memory a client has requested
292    // be mapped into its address space.
293
294    IOReturn ret;
295
296    IOLog("AppleSamplePCIUserClient::clientMemoryForType(%ld)\n", type);
297
298    switch( type ) {
299
300        case kAppleSamplePCIMemoryType1:
301            // give the client access to some shared data structure
302            // (shared between this object and the client)
303            fClientSharedMemory->retain();
304            *memory  = fClientSharedMemory;
305            ret = kIOReturnSuccess;
306            break;
307
308        case kAppleSamplePCIMemoryType2:
309            // Give the client access to some of the cards memory
310            // (all clients get the same)
311            *memory  = fDriver->copyGlobalMemory();
312            ret = kIOReturnSuccess;
313            break;
314
315        default:
316            ret = kIOReturnBadArgument;
317            break;
318    }
319
320    return( ret );
321}
322
323