static void nysa_pcie_remove(struct pci_dev *pdev) { //---------------------------- // Destroy Character Device //---------------------------- nysa_pcie_dev_t *dev; int index = 0; struct cdev *cdv = NULL; mod_info_dbg("Entered\n"); mod_info_dbg("Removing Character Device\n"); dev = pci_get_drvdata(pdev); index = get_nysa_pcie_dev_index(dev); //cdv = (struct cdev *)get_nysa_pcie_private(index); cdv = &cdevs[index]; //---------------------------- // Destroy PCIE Controller //---------------------------- destroy_pcie_device(pdev); mod_info_dbg("Deleted Character Device\n"); cdev_del(cdv); mod_info_dbg("Destroyed PCIE Device\n"); atomic_dec(&device_count); }
int nysa_pcie_open(struct inode *inode, struct file *filp) { filp->private_data = get_nysa_pcie_dev(iminor(inode)); mod_info_dbg("Opened!\n"); mod_info_dbg("Minor Number: %d\n", iminor(inode)); return SUCCESS; }
loff_t nysa_pcie_llseek (struct file * filp, loff_t off, int whence) { nysa_pcie_dev_t *dev; dev = filp->private_data; enable_command_mode(dev, false); mod_info_dbg("in llseek\n"); switch (whence) { case 0: //Set mod_info_dbg("disable command mode!\n"); break; case 1: //Current Position mod_info_dbg("disable command mode!\n"); break; case 2: //End mod_info_dbg("enable command mode!\n"); enable_command_mode(dev, true); break; default: return -EINVAL; } return 0; }
static void __exit nysa_pcie_exit(void) { mod_info_dbg("Cleanup Module\n"); //Tell the kernel we are not listenning for PCI devices mod_info_dbg("Unregistering Driver\n"); pci_unregister_driver(&pcidriver); mod_info_dbg("Unregistering Character Driver\n"); unregister_chrdev_region(pci_driver_chrdev_num, MAX_DEVICES); destroy_pcie_ctr(); atomic_set(&device_count, 0); mod_info_dbg("Finished Cleanup Module, Exiting\n"); return; }
ssize_t nysa_pcie_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos) { int retval = SUCCESS; u32 value; u32 address; u32 device_address; nysa_pcie_dev_t *dev; u8 kernel_buf[12]; dev = filp->private_data; if (is_command_mode_enabled(dev)) { mod_info_dbg("Command Mode!\n"); if (count > 12){ mod_info_dbg("Copy only the first 12-bytes\n"); copy_from_user(kernel_buf, buf, 12); } else copy_from_user(kernel_buf, buf, count); address = (kernel_buf[0] << 24) | (kernel_buf[1] << 16) | (kernel_buf[2] << 8) | (kernel_buf[3]); value = (kernel_buf[4] << 24) | (kernel_buf[5] << 16) | (kernel_buf[6] << 8) | (kernel_buf[7]); device_address = (kernel_buf[8] << 24) | (kernel_buf[9] << 16) | (kernel_buf[10] << 8) | (kernel_buf[11]); mod_info_dbg("Write: 0x%08X 0x%08X 0x%08X\n", (unsigned int) address, (unsigned int)value, (unsigned int)device_address); //Need to determine if this is a register write or a command, if it is a command see if it takes an address if (address < CMD_OFFSET) { //Write a Register write_register(dev, address, value); } else { //Write Command mod_info_dbg("Not Command Mode!\n"); write_command(dev, address, device_address, value); } } else { mod_info_dbg("Write Data: Count: 0x%08X\n", (unsigned int) count); //return retval; return nysa_pcie_write_data(dev, buf, count); } //Check to see if this is a command or just a register return retval; }
static int __init nysa_pcie_init(void) { int i = 0; int retval = SUCCESS; atomic_set(&device_count, 0); //Request a set of character device numbers mod_info_dbg("Registering Driver\n"); if ((retval = alloc_chrdev_region(&pci_driver_chrdev_num, MINOR_NUM_START, MAX_DEVICES, MODULE_NAME)) != 0) { mod_info_dbg("Failed to create chrdev region"); goto init_fail; } major_num = MAJOR(pci_driver_chrdev_num); //Create a reference to all the pci devices we will be interfacing with if ((retval = construct_pcie_ctr(MAX_DEVICES)) != 0) { goto unregister_chrdev_region; } //Initialize each of the possible character devices for (i = 0; i < MAX_DEVICES; i++) { cdevs[i].owner = THIS_MODULE; } //Register the PCI IDs with the kernel if ((retval = pci_register_driver(&pcidriver)) != 0) { mod_info_dbg("Failed to register PCI Driver\n"); goto register_fail; } mod_info_dbg("Driver Initialized, waiting for probe...\n"); return SUCCESS; //Handle Fail register_fail: destroy_pcie_ctr(); unregister_chrdev_region: unregister_chrdev_region(MAJOR(pci_driver_chrdev_num), MAX_DEVICES); init_fail: return retval; }
//----------------------------------------------------------------------------- // Device Detect/Remove //----------------------------------------------------------------------------- static int nysa_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) { int retval = 0; int minor = 0; dev_t devno; mod_info_dbg("Found PCI Device: %04X:%04X %s\n", id->vendor, id->device, dev_name(&pdev->dev)); //Increment the Device ID minor = atomic_inc_return(&device_count) - 1; devno = MKDEV(major_num, minor); if (minor >= MAX_DEVICES) { mod_info_dbg("Maximum Number of Devices Reached! Increase MAX_DEVICES.\n"); goto probe_fail; } //---------------------------- // Configure PCIE //---------------------------- if ((retval = construct_pcie_device(pdev, devno)) != 0) { mod_info_dbg("Failed to create device.\n"); goto probe_decrement_minor; } //---------------------------- // Configure Character Devices //---------------------------- cdev_init(&cdevs[minor], &nysa_pcie_fops); if ((retval = cdev_add(&cdevs[minor], devno, 1)) != 0) { mod_info_dbg("Error %d while trying to add cdev for minor: %d\n", retval, minor); goto probe_destroy_pcie_device; } set_nysa_pcie_private(minor, (void *) &cdevs[minor]); return SUCCESS; //Handle Fails probe_destroy_pcie_device: destroy_pcie_device(pdev); probe_decrement_minor: atomic_dec(&device_count); probe_fail: return retval; }
ssize_t nysa_pcie_read(struct file *filp, char __user * buf, size_t count, loff_t *f_pos) { nysa_pcie_dev_t *dev; dev = filp->private_data; mod_info_dbg("Buffer Pointer: %p\n", buf); return nysa_pcie_read_data(dev, buf, count); }
/** * * This function is the entry point for mmap() and calls either pcidriver_mmap_pci * or pcidriver_mmap_kmem * * @see pcidriver_mmap_pci * @see pcidriver_mmap_kmem * */ int pcidriver_mmap(struct file *filp, struct vm_area_struct *vma) { pcidriver_privdata_t *privdata; int ret = 0, bar; mod_info_dbg("Entering mmap\n"); /* Get the private data area */ privdata = filp->private_data; /* Check the current mmap mode */ switch (privdata->mmap_mode) { case PCIDRIVER_MMAP_PCI: /* Mmap a PCI region */ switch (privdata->mmap_area) { case PCIDRIVER_BAR0: bar = 0; break; case PCIDRIVER_BAR1: bar = 1; break; case PCIDRIVER_BAR2: bar = 2; break; case PCIDRIVER_BAR3: bar = 3; break; case PCIDRIVER_BAR4: bar = 4; break; case PCIDRIVER_BAR5: bar = 5; break; default: mod_info("Attempted to mmap a PCI area with the wrong mmap_area value: %d\n",privdata->mmap_area); return -EINVAL; /* invalid parameter */ break; } ret = pcidriver_mmap_pci(privdata, vma, bar); break; case PCIDRIVER_MMAP_KMEM: /* mmap a Kernel buffer */ ret = pcidriver_mmap_kmem(privdata, vma); break; default: mod_info( "Invalid mmap_mode value (%d)\n",privdata->mmap_mode ); return -EINVAL; /* Invalid parameter (mode) */ } return ret; }
/* Internal driver functions */ int pcidriver_mmap_pci(pcidriver_privdata_t *privdata, struct vm_area_struct *vmap, int bar) { int ret = 0; unsigned long bar_addr; unsigned long bar_length, vma_size; unsigned long bar_flags; mod_info_dbg("Entering mmap_pci\n"); /* Get info of the BAR to be mapped */ bar_addr = pci_resource_start(privdata->pdev, bar); bar_length = pci_resource_len(privdata->pdev, bar); bar_flags = pci_resource_flags(privdata->pdev, bar); /* Check sizes */ vma_size = (vmap->vm_end - vmap->vm_start); if ((vma_size != bar_length) && ((bar_length < PAGE_SIZE) && (vma_size != PAGE_SIZE))) { mod_info( "mmap size is not correct! bar: %lu - vma: %lu\n", bar_length, vma_size ); return -EINVAL; } if (bar_flags & IORESOURCE_IO) { /* Unlikely case, we will mmap a IO region */ /* IO regions are never cacheable */ #ifdef pgprot_noncached vmap->vm_page_prot = pgprot_noncached(vmap->vm_page_prot); #endif /* Map the BAR */ ret = io_remap_pfn_range_compat( vmap, vmap->vm_start, bar_addr, bar_length, vmap->vm_page_prot); } else { /* Normal case, mmap a memory region */ /* Ensure this VMA is non-cached, if it is not flaged as prefetchable. * If it is prefetchable, caching is allowed and will give better performance. * This should be set properly by the BIOS, but we want to be sure. */ /* adapted from drivers/char/mem.c, mmap function. */ #ifdef pgprot_noncached /* Setting noncached disables MTRR registers, and we want to use them. * So we take this code out. This can lead to caching problems if and only if * the System BIOS set something wrong. Check LDDv3, page 425. */ // if (!(bar_flags & IORESOURCE_PREFETCH)) // vmap->vm_page_prot = pgprot_noncached(vmap->vm_page_prot); #endif /* Map the BAR */ ret = remap_pfn_range_compat( vmap, vmap->vm_start, bar_addr, bar_length, vmap->vm_page_prot); } if (ret) { mod_info("remap_pfn_range failed\n"); return -EAGAIN; } return 0; /* success */ }