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