1/* 2 * This is a tiny driver that attaches to a PCI device and logs information 3 * about it. It doesn't alter the device in any way. It also supports a 4 * generic IOUserClient subclass that allows driver specific client code to 5 * make various kinds of calls into the driver, and map shared memory 6 * or portions of hardware memory. 7 */ 8 9#include "AppleSamplePCI.h" 10#include <IOKit/IOLib.h> 11#include <IOKit/assert.h> 12 13/* 14 * Define the metaclass information that is used for runtime 15 * typechecking of IOKit objects. We're a subclass of IOService, 16 * but usually we would subclass from a family class. 17 */ 18 19#define super IOService 20OSDefineMetaClassAndStructors( AppleSamplePCI, IOService ); 21 22bool AppleSamplePCI::start( IOService * provider ) 23{ 24 IOMemoryDescriptor * mem; 25 IOMemoryMap * map; 26 27 IOLog("AppleSamplePCI::start\n"); 28 29 if( !super::start( provider )) 30 return( false ); 31 32 /* 33 * Our provider class is specified in the driver property table 34 * as IOPCIDevice, so the provider must be of that class. 35 * The assert is just to make absolutely sure for debugging. 36 */ 37 38 assert( OSDynamicCast( IOPCIDevice, provider )); 39 fPCIDevice = (IOPCIDevice *) provider; 40 41 /* 42 * Enable memory response from the card 43 */ 44 fPCIDevice->setMemoryEnable( true ); 45 46 47 /* 48 * Log some info about the device 49 */ 50 51 /* print all the device's memory ranges */ 52 for( UInt32 index = 0; 53 index < fPCIDevice->getDeviceMemoryCount(); 54 index++ ) { 55 56 mem = fPCIDevice->getDeviceMemoryWithIndex( index ); 57 assert( mem ); 58 IOLog("Range[%ld] %08lx:%08lx\n", index, 59 mem->getPhysicalAddress(), mem->getLength()); 60 } 61 62 /* look up a range based on its config space base address register */ 63 mem = fPCIDevice->getDeviceMemoryWithRegister( 64 kIOPCIConfigBaseAddress0 ); 65 if( mem ) 66 IOLog("Range@0x%x %08lx:%08lx\n", kIOPCIConfigBaseAddress0, 67 mem->getPhysicalAddress(), mem->getLength()); 68 69 /* map a range based on its config space base address register, 70 * this is how the driver gets access to its memory mapped registers 71 * the getVirtualAddress() method returns a kernel virtual address 72 * for the register mapping */ 73 74 map = fPCIDevice->mapDeviceMemoryWithRegister( 75 kIOPCIConfigBaseAddress0 ); 76 if( map ) { 77 IOLog("Range@0x%x (%08lx) mapped to kernel virtual address %08x\n", 78 kIOPCIConfigBaseAddress0, 79 map->getPhysicalAddress(), 80 map->getVirtualAddress()); 81 /* release the map object, and the mapping itself */ 82 map->release(); 83 } 84 85 /* read a config space register */ 86 IOLog("Config register@0x%x = %08lx\n", kIOPCIConfigCommand, 87 fPCIDevice->configRead32(kIOPCIConfigCommand) ); 88 89 // construct a memory descriptor for a buffer below the 4Gb line & 90 // so addressable by 32 bit DMA. This could be used for a 91 // DMA program buffer for example 92 93 IOBufferMemoryDescriptor * bmd = 94 IOBufferMemoryDescriptor::inTaskWithPhysicalMask( 95 // task to hold the memory 96 kernel_task, 97 // options 98 kIOMemoryPhysicallyContiguous, 99 // size 100 64*1024, 101 // physicalMask - 32 bit addressable and page aligned 102 0x00000000FFFFF000ULL); 103 104 if (bmd) { 105 generateDMAAddresses(bmd); 106 } else { 107 IOLog("IOBufferMemoryDescriptor::inTaskWithPhysicalMask failed\n"); 108 } 109 fLowMemory = bmd; 110 111 /* publish ourselves so clients can find us */ 112 registerService(); 113 114 return( true ); 115} 116 117/* 118 * We'll come here when the device goes away, or the driver is unloaded. 119 */ 120 121void AppleSamplePCI::stop( IOService * provider ) 122{ 123 IOLog("AppleSamplePCI::stop\n"); 124 super::stop( provider ); 125} 126 127/* 128 * Method to supply an IOMemoryDescriptor for the user client to map into 129 * the client process. This sample just supplies all of the hardware memory 130 * associated with the PCI device's Base Address Register 0. 131 * In a real driver mapping hardware memory would only ever be used in some 132 * limited high performance scenarios where the device range can be safely 133 * accessed by client code with compromising system stability. 134 */ 135 136IOMemoryDescriptor * AppleSamplePCI::copyGlobalMemory( void ) 137{ 138 IOMemoryDescriptor * memory; 139 140 memory = fPCIDevice->getDeviceMemoryWithRegister( kIOPCIConfigBaseAddress0 ); 141 if( memory) 142 memory->retain(); 143 144 return( memory ); 145} 146 147IOReturn AppleSamplePCI::generateDMAAddresses( IOMemoryDescriptor * memDesc ) 148{ 149 // Get the physical segment list. These could be used to generate a scatter gather 150 // list for hardware. 151 152 // This is the old getPhysicalSegment() loop calling IOMemoryDescriptor, 153 // it will fail (panic) on new machines with memory above the 4Gb line 154 155 IODMACommand * cmd; 156 IOReturn err = kIOReturnSuccess; 157 IOByteCount offset = 0; 158 IOPhysicalAddress physicalAddr; 159 IOPhysicalLength segmentLength; 160 UInt32 index = 0; 161 162 while( (physicalAddr = memDesc->getPhysicalSegment( offset, &segmentLength ))) { 163 IOLog("Physical segment(%ld) %08lx:%08lx\n", index, physicalAddr, segmentLength); 164 offset += segmentLength; 165 index++; 166 } 167 168 // 64 bit physical address generation using IODMACommand 169 do 170 { 171 cmd = IODMACommand::withSpecification( 172 // outSegFunc - Host endian since we read the address data with the cpu 173 // and 64 bit wide quantities 174 kIODMACommandOutputHost64, 175 // numAddressBits 176 64, 177 // maxSegmentSize - zero for unrestricted physically contiguous chunks 178 0, 179 // mappingOptions - kMapped for DMA addresses 180 IODMACommand::kMapped, 181 // maxTransferSize - no restriction 182 0, 183 // alignment - no restriction 184 1 ); 185 if (!cmd) 186 { 187 IOLog("IODMACommand::withSpecification failed\n"); 188 break; 189 } 190 191 // point at the memory descriptor and use the auto prepare option 192 // to prepare the entire range 193 err = cmd->setMemoryDescriptor(memDesc); 194 if (kIOReturnSuccess != err) 195 { 196 IOLog("setMemoryDescriptor failed (0x%x)\n", err); 197 break; 198 } 199 200 UInt64 offset = 0; 201 while ((kIOReturnSuccess == err) && (offset < memDesc->getLength())) 202 { 203 // use the 64 bit variant to match outSegFunc 204 IODMACommand::Segment64 segments[1]; 205 UInt32 numSeg = 1; 206 207 // use the 64 bit variant to match outSegFunc 208 err = cmd->gen64IOVMSegments(&offset, &segments[0], &numSeg); 209 IOLog("gen64IOVMSegments(%x) addr 0x%qx, len 0x%qx, nsegs %ld\n", 210 err, segments[0].fIOVMAddr, segments[0].fLength, numSeg); 211 } 212 213 // if we had a DMA controller, kick off the DMA here 214 215 // when the DMA has completed, 216 217 // clear the memory descriptor and use the auto complete option 218 // to complete the transaction 219 err = cmd->clearMemoryDescriptor(); 220 if (kIOReturnSuccess != err) 221 { 222 IOLog("clearMemoryDescriptor failed (0x%x)\n", err); 223 } 224 } 225 while (false); 226 if (cmd) 227 cmd->release(); 228 // end 64 bit loop 229 230 231 // 32 bit physical address generation using IODMACommand 232 // any memory above 4Gb in the memory descriptor will be buffered 233 // to memory below the 4G line, on machines without remapping HW support 234 do 235 { 236 cmd = IODMACommand::withSpecification( 237 // outSegFunc - Host endian since we read the address data with the cpu 238 // and 32 bit wide quantities 239 kIODMACommandOutputHost32, 240 // numAddressBits 241 32, 242 // maxSegmentSize - zero for unrestricted physically contiguous chunks 243 0, 244 // mappingOptions - kMapped for DMA addresses 245 IODMACommand::kMapped, 246 // maxTransferSize - no restriction 247 0, 248 // alignment - no restriction 249 1 ); 250 if (!cmd) 251 { 252 IOLog("IODMACommand::withSpecification failed\n"); 253 break; 254 } 255 256 // point at the memory descriptor and use the auto prepare option 257 // to prepare the entire range 258 err = cmd->setMemoryDescriptor(memDesc); 259 if (kIOReturnSuccess != err) 260 { 261 IOLog("setMemoryDescriptor failed (0x%x)\n", err); 262 break; 263 } 264 265 UInt64 offset = 0; 266 while ((kIOReturnSuccess == err) && (offset < memDesc->getLength())) 267 { 268 // use the 32 bit variant to match outSegFunc 269 IODMACommand::Segment32 segments[1]; 270 UInt32 numSeg = 1; 271 272 // use the 32 bit variant to match outSegFunc 273 err = cmd->gen32IOVMSegments(&offset, &segments[0], &numSeg); 274 IOLog("gen32IOVMSegments(%x) addr 0x%lx, len 0x%lx, nsegs %ld\n", 275 err, segments[0].fIOVMAddr, segments[0].fLength, numSeg); 276 } 277 278 // if we had a DMA controller, kick off the DMA here 279 280 // when the DMA has completed, 281 282 // clear the memory descriptor and use the auto complete option 283 // to complete the transaction 284 err = cmd->clearMemoryDescriptor(); 285 if (kIOReturnSuccess != err) 286 { 287 IOLog("clearMemoryDescriptor failed (0x%x)\n", err); 288 } 289 } 290 while (false); 291 if (cmd) 292 cmd->release(); 293 // end 32 bit loop 294 295 return (err); 296} 297