/******************************************************************************* * * Function : Plx_cleanup_module * * Description: Unload the driver * ******************************************************************************/ void Plx_cleanup_module( void ) { DebugPrintf_Cont(("\n")); DebugPrintf(("Unload driver...\n")); // Release common buffer if (pGbl_DriverObject->CommonBuffer.Size != 0) { DebugPrintf(("De-allocate Common Buffer\n")); // Release the buffer Plx_dma_buffer_free( pGbl_DriverObject->DeviceObject->DeviceExtension, // First device &(pGbl_DriverObject->CommonBuffer) ); } // De-register driver if (pGbl_DriverObject->bPciDriverReg) pci_unregister_driver( &PlxPciDriver ); DebugPrintf(( "De-register driver (MajorID = %03d)\n", pGbl_DriverObject->MajorID )); /********************************************************* * De-register the driver with the OS * * NOTE: This driver still uses the old method for de-registering * the device (unregister_chrdev) for compatability with 2.4 kernels. * A future version of the driver may use the new interface * (cdev_init, cdev_add, & cdev_del). ********************************************************/ unregister_chrdev( pGbl_DriverObject->MajorID, PLX_DRIVER_NAME ); DebugPrintf(( "Release global driver object (%p)\n", pGbl_DriverObject )); // Release driver object kfree( pGbl_DriverObject ); pGbl_DriverObject = NULL; DebugPrintf(("...driver unloaded\n")); }
/****************************************************************************** * * Function : Dispatch_release * * Description: Handle close() call, which closes the connection between the * application and drivers. * ******************************************************************************/ int Dispatch_release( struct inode *inode, struct file *filp ) { DEVICE_OBJECT *fdo; DebugPrintf_Cont(("\n")); DebugPrintf(("Received message ==> CLOSE_DEVICE\n")); if (iminor(inode) == PLX_MNGMT_INTERFACE) { DebugPrintf(("Close Management interface...\n")); // Clear the driver object from the private data filp->private_data = NULL; } else { // Get the device object fdo = (DEVICE_OBJECT *)(filp->private_data); DebugPrintf(( "Close device (%s)...\n", fdo->DeviceExtension->LinkName )); // Release any pending notifications owned by proccess PlxNotificationCancel( fdo->DeviceExtension, NULL, filp ); // Close DMA channels owned by the process PlxDmaChannelCleanup( fdo->DeviceExtension, filp ); // Release any physical memory allocated by process PlxPciPhysicalMemoryFreeAll_ByOwner( fdo->DeviceExtension, filp ); } DebugPrintf(("...device closed\n")); return 0; }
/****************************************************************************** * * Function : Dispatch_open * * Description: Handle open() which allows applications to create a * connection to the driver * ******************************************************************************/ int Dispatch_open( struct inode *inode, struct file *filp ) { U8 i; DEVICE_OBJECT *fdo; DebugPrintf_Cont(("\n")); DebugPrintf(("Received message ==> OPEN_DEVICE\n")); if (iminor(inode) == PLX_MNGMT_INTERFACE) { DebugPrintf(("Open Management interface...\n")); // Store the driver object in the private data filp->private_data = pGbl_DriverObject; } else { // Select desired device from device list i = iminor(inode); fdo = pGbl_DriverObject->DeviceObject; while (i-- && fdo != NULL) fdo = fdo->NextDevice; if (fdo == NULL) { ErrorPrintf(("WARNING - Attempt to open non-existent device\n")); return (-ENODEV); } DebugPrintf(( "Open device (%s)...\n", fdo->DeviceExtension->LinkName )); // Store device object for future calls filp->private_data = fdo; } DebugPrintf(("...device opened\n")); return 0; }
/****************************************************************************** * * Function : Dispatch_mmap * * Description: Maps a PCI space into user virtual space * ******************************************************************************/ int Dispatch_mmap( struct file *filp, struct vm_area_struct *vma ) { int rc; off_t offset; BOOLEAN bDeviceMem; PLX_UINT_PTR AddressToMap; DEVICE_EXTENSION *pdx; DebugPrintf_Cont(("\n")); DebugPrintf(("Received message ===> MMAP\n")); // Get device extension pdx = ((DEVICE_OBJECT*)(filp->private_data))->DeviceExtension; // Get the supplied offset offset = vma->vm_pgoff; // Determine if mapping to a PCI BAR or system memory switch (offset) { case 0: case 1: case 2: case 3: case 4: case 5: // Verify space is not I/O if (pdx->PciBar[offset].Properties.Flags & PLX_BAR_FLAG_IO) { DebugPrintf(( "ERROR - PCI BAR %d is an I/O space, cannot map to user space\n", (U8)offset )); return -ENODEV; } DebugPrintf(( "Map PCI BAR %d...\n", (U8)offset )); // Use the BAR physical address for the mapping AddressToMap = (PLX_UINT_PTR)pdx->PciBar[offset].Properties.Physical; // Flag that the mapping is to IO memory bDeviceMem = TRUE; break; default: // Use provided offset as CPU physical address for mapping AddressToMap = (PLX_UINT_PTR)offset << PAGE_SHIFT; // Flag that the mapping is to system memory bDeviceMem = FALSE; break; } // Verify physical address if (AddressToMap == 0) { DebugPrintf(( "ERROR - Invalid physical (%08lx), cannot map to user space\n", AddressToMap )); return -ENODEV; } /*********************************************************** * Attempt to map the region * * NOTE: * * Due to variations in the remap function between kernel releases * and distributions, a PLX macro is used to simplify code * readability. For additional information about the macro * expansions, refer to the file "Plx_sysdep.h". **********************************************************/ // Set the region as page-locked vma->vm_flags |= VM_RESERVED; if (bDeviceMem) { // Set flag for I/O resource vma->vm_flags |= VM_IO; // The region must be marked as non-cached vma->vm_page_prot = pgprot_noncached( vma->vm_page_prot ); // Map device memory rc = Plx_io_remap_pfn_range( vma, vma->vm_start, AddressToMap >> PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot ); } else {
/******************************************************************************* * * Function : Plx_init_module * * Description: Entry point for the driver * ******************************************************************************/ int __init Plx_init_module( void ) { int status; PLX_PHYSICAL_MEM PhysicalMem; DebugPrintf_Cont(("\n")); DebugPrintf(("<========================================================>\n")); printk( "PLX driver v%d.%02d (%d-bit) - built on %s %s\n", PLX_SDK_VERSION_MAJOR, PLX_SDK_VERSION_MINOR, (U32)(sizeof(PLX_UINT_PTR) * 8), __DATE__, __TIME__ ); printk( "Supports Linux kernel version %s\n", UTS_RELEASE ); // Allocate memory for the Driver Object pGbl_DriverObject = kmalloc( sizeof(DRIVER_OBJECT), GFP_KERNEL ); if (pGbl_DriverObject == NULL) { ErrorPrintf(("ERROR - memory allocation for Driver Object failed\n")); return (-ENOMEM); } printk( "Allocated global driver object (%p)\n", pGbl_DriverObject ); // Clear the driver object RtlZeroMemory( pGbl_DriverObject, sizeof(DRIVER_OBJECT) ); // Initialize driver object pGbl_DriverObject->DeviceObject = NULL; pGbl_DriverObject->DeviceCount = 0; // Fill in the appropriate dispatch handlers pGbl_DriverObject->DispatchTable.owner = THIS_MODULE; pGbl_DriverObject->DispatchTable.mmap = Dispatch_mmap; pGbl_DriverObject->DispatchTable.open = Dispatch_open; pGbl_DriverObject->DispatchTable.release = Dispatch_release; #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)) pGbl_DriverObject->DispatchTable.ioctl = Dispatch_IoControl; #else pGbl_DriverObject->DispatchTable.unlocked_ioctl = Dispatch_IoControl; #endif // Initialize spin locks spin_lock_init( &(pGbl_DriverObject->Lock_DeviceList) ); /********************************************************* * Register the driver with the OS * * NOTE: This driver still uses the old method for registering * the device (register_chrdev) for compatability with 2.4 kernels. * A future version of the driver may use the new interface * (cdev_init, cdev_add, & cdev_del). ********************************************************/ pGbl_DriverObject->MajorID = register_chrdev( 0, // 0 = system chooses Major ID PLX_DRIVER_NAME, &(pGbl_DriverObject->DispatchTable) ); printk( "Registered driver (MajorID = %03d)\n", pGbl_DriverObject->MajorID ); // Register the driver status = pci_register_driver( &PlxPciDriver ); if (status != 0) { ErrorPrintf(("ERROR: Unable to register driver (status=%d)\n", status)); Plx_cleanup_module(); return status; } // Flag driver was registered pGbl_DriverObject->bPciDriverReg = TRUE; // Check if any devices were found if (pGbl_DriverObject->DeviceCount == 0) { ErrorPrintf(("ERROR - No supported devices found\n")); Plx_cleanup_module(); return (-ENODEV); } // Initialize common buffer pGbl_DriverObject->CommonBuffer.Size = 0; // Set requested size PhysicalMem.Size = DEFAULT_SIZE_COMMON_BUFFER; // Allocate common buffer if (PhysicalMem.Size != 0) { // Allocate common buffer PlxPciPhysicalMemoryAllocate( pGbl_DriverObject->DeviceObject->DeviceExtension, // Assign buffer to first device &PhysicalMem, TRUE, // Smaller buffer is ok pGbl_DriverObject // Assign Driver object as owner ); } DebugPrintf(( "Added: %d supported device(s)\n", pGbl_DriverObject->DeviceCount )); DebugPrintf(("...driver loaded\n\n")); return 0; }