#ifndef PCI_MODULE_C #define PCI_MODULE_C #include #include #include #include #include #include #include "pciDrv.h" #define _GPL_ #define stringify(s) tostring(s) #define tostring(s) #s static int pci_major; static struct class *pci_module_class; #define PCI_E_LINK_STAT_OFFSET 0x12 /* PCI-Express link status register offset(to PCI-Express capability) */ #define PCI_E_LINK_CAP_OFFSET 0x0C /* PCI-Express link capabilities register offset(to PCI-Express capability) */ //#define PCI_VENDOR_ID_PLX 0x10B5 /* defined in linux/pci.h */ #define PCI_VENDOR_ID_LSI 0x1000 #define PCI_DEVICE_ID_SAS3008 0x0097 #define PCI_DEVICE_ID_I2101 0x1533 #define PCI_DEVICE_ID_I2102 0x1537 #define PCI_DEVICE_ID_HASWELL_DMI 0x2F00 #define PCI_DEVICE_ID_WBG_LPC 0x8D44 #define PCI_DEVICE_ID_WBG_SSATA 0x8D62 #define PCI_DEVICE_ID_WBG_SATA1 0x8D02 #define PCI_DEVICE_ID_WBG_USB 0x8D26 #define PCI_VENDOR_ID_NETAPP 0x1275 #define PCI_DEVICE_ID_FPGA 0x7110 #define PCI_DEVICE_ID_NIC82574 0x10D3 //test int pci_open (struct inode *inod, struct file *filp) { int major, minor; major = imajor(inod); minor = iminor(inod); try_module_get(THIS_MODULE); return 0; } int pci_release(struct inode *inod, struct file *filp) { module_put(THIS_MODULE); return 0; } int read_pci_config_byte(void *arg) { int ret = -EINVAL; struct pci_dev *pdev; config_pci_dev *fpcidev; config_pci_dev kerdev; fpcidev = (config_pci_dev *)arg; ret = copy_from_user(&kerdev, fpcidev, sizeof(config_pci_dev)); if(ret) { printk("pci_read_byte: Copy from user space error!\n"); return ret; } pdev = pci_get_bus_and_slot(kerdev.bus_num, PCI_DEVFN(kerdev.dev_num, kerdev.func_num)); if(!pdev) { ret = -EINVAL; return ret; } pci_read_config_byte(pdev, kerdev.offset, (u8 *)(&kerdev.data)); ret = copy_to_user(fpcidev, &kerdev, sizeof(config_pci_dev)); if(ret) printk("pci_read_byte: Copy to user space error!\n"); return ret; } int read_pci_config_word(void *arg) { int ret = -EINVAL; struct pci_dev *pdev; config_pci_dev *fpcidev; config_pci_dev kerdev; fpcidev = (config_pci_dev *)arg; ret = copy_from_user(&kerdev, fpcidev, sizeof(config_pci_dev)); if(ret) { printk("read_pci_config_word: Copy from user space error!\n"); return ret; } pdev = pci_get_bus_and_slot(kerdev.bus_num, PCI_DEVFN(kerdev.dev_num, kerdev.func_num)); if(!pdev) { ret = -EINVAL; return ret; } pci_read_config_word(pdev, kerdev.offset, (u16 *)(&kerdev.data)); ret = copy_to_user(fpcidev, &kerdev, sizeof(config_pci_dev)); if(ret) printk("read_pci_config_word: Copy to user space error!\n"); return ret; } int read_pci_config_dword(void *arg) { int ret = -EINVAL; struct pci_dev *pdev; config_pci_dev *fpcidev; config_pci_dev kerdev; fpcidev = (config_pci_dev *)arg; ret = copy_from_user(&kerdev, fpcidev, sizeof(config_pci_dev)); if(ret) { printk("read_pci_config_dword: Copy from user space error!\n"); return ret; } pdev = pci_get_bus_and_slot(kerdev.bus_num, PCI_DEVFN(kerdev.dev_num, kerdev.func_num)); if(!pdev) { ret = -EINVAL; return ret; } pci_read_config_dword(pdev, kerdev.offset, &kerdev.data); ret = copy_to_user(fpcidev, &kerdev, sizeof(config_pci_dev)); if(ret) printk("read_pci_config_dword: Copy to user space error!\n"); return ret; } int read_pcie_cap_byte(void *arg) { int ret = -EINVAL; struct pci_dev *pdev; config_pci_dev *fpcidev; config_pci_dev kerdev; int cap_offset; fpcidev = (config_pci_dev *)arg; ret = copy_from_user(&kerdev, fpcidev, sizeof(config_pci_dev)); if(ret) { printk("read_pcie_cap_dword: Copy from user space error!\n"); return ret; } pdev = pci_get_bus_and_slot(kerdev.bus_num, PCI_DEVFN(kerdev.dev_num, kerdev.func_num)); if(!pdev) { ret = -EINVAL; return ret; } /*According to cap id, find the address of this specific device */ cap_offset = pci_find_capability(pdev, kerdev.cap_id); if(!cap_offset) { printk("read_pcie_cap_dword: there is no this capability ID = 0x%x\n", kerdev.cap_id ); return -EINVAL; } kerdev.reg_addr = cap_offset + kerdev.offset; pci_read_config_byte(pdev, cap_offset + kerdev.offset, (u8*)&kerdev.data); ret = copy_to_user(fpcidev, &kerdev, sizeof(config_pci_dev)); if(ret) printk("read_pcie_cap_dword: Copy to user space error!\n"); return ret; } int read_pcie_ext_cap_byte(void *arg) { int ret = -EINVAL; struct pci_dev *pdev; config_pci_dev *fpcidev; config_pci_dev kerdev; int ext_cap_offset; fpcidev = (config_pci_dev *)arg; ret = copy_from_user(&kerdev, fpcidev, sizeof(config_pci_dev)); if(ret) { printk("read_pcie_cap_dword: Copy from user space error!\n"); return ret; } pdev = pci_get_bus_and_slot(kerdev.bus_num, PCI_DEVFN(kerdev.dev_num, kerdev.func_num)); if(!pdev) { ret = -EINVAL; return ret; } /*According to cap id, find the address of this specific device */ ext_cap_offset = pci_find_ext_capability(pdev, kerdev.cap_id); if(!ext_cap_offset) { printk("read_pcie_cap_dword: there is no this capability ID = 0x%x\n", kerdev.cap_id ); return -EINVAL; } kerdev.reg_addr = ext_cap_offset + kerdev.offset; pci_read_config_byte(pdev, ext_cap_offset + kerdev.offset, (u8*)&kerdev.data); ret = copy_to_user(fpcidev, &kerdev, sizeof(config_pci_dev)); if(ret) printk("read_pcie_cap_dword: Copy to user space error!\n"); return ret; } int read_pcie_ext_cap_dword(void *arg) { int ret = -EINVAL; struct pci_dev *pdev; config_pci_dev *fpcidev; config_pci_dev kerdev; int ext_cap_offset; fpcidev = (config_pci_dev *)arg; ret = copy_from_user(&kerdev, fpcidev, sizeof(config_pci_dev)); if(ret) { printk("read_pcie_cap_dword: Copy from user space error!\n"); return ret; } pdev = pci_get_bus_and_slot(kerdev.bus_num, PCI_DEVFN(kerdev.dev_num, kerdev.func_num)); if(!pdev) { ret = -EINVAL; return ret; } /*According to cap id, find the address of this specific device */ ext_cap_offset = pci_find_ext_capability(pdev, kerdev.cap_id); if(!ext_cap_offset) { printk("read_pcie_cap_dword: there is no this capability ID = 0x%x\n", kerdev.cap_id ); return -EINVAL; } kerdev.reg_addr = ext_cap_offset + kerdev.offset; pci_read_config_dword(pdev, ext_cap_offset + kerdev.offset, &kerdev.data); ret = copy_to_user(fpcidev, &kerdev, sizeof(config_pci_dev)); if(ret) printk("read_pcie_cap_dword: Copy to user space error!\n"); return ret; } int write_pci_config_byte(void *arg) { int ret = -EINVAL; struct pci_dev *pdev; config_pci_dev *fpcidev; config_pci_dev kerdev; fpcidev = (config_pci_dev *)arg; ret = copy_from_user(&kerdev, fpcidev, sizeof(config_pci_dev)); if(ret) { printk("write_pci_config_byte: Copy from user space error!\n"); return ret; } printk("pciDrv write byte: bus=0x%x, dev=0x%x, func=0x%x, offset=0x%x, cap_id=0x%x, data=0x%x", kerdev.bus_num, kerdev.dev_num, kerdev.func_num, kerdev.offset, kerdev.cap_id, kerdev.data); pdev = pci_get_bus_and_slot(kerdev.bus_num, PCI_DEVFN(kerdev.dev_num, kerdev.func_num)); if(!pdev) { printk("write byte error\n"); ret = -EINVAL; return ret; } pci_write_config_byte(pdev, kerdev.offset, (u8)kerdev.data); ret = copy_to_user(fpcidev, &kerdev, sizeof(config_pci_dev)); if(ret) printk("write_pci_config_byte: Copy to user space error!\n"); return ret; } int write_pci_config_word(void *arg) { int ret = -EINVAL; struct pci_dev *pdev; config_pci_dev *fpcidev; config_pci_dev kerdev; fpcidev = (config_pci_dev *)arg; ret = copy_from_user(&kerdev, fpcidev, sizeof(config_pci_dev)); if(ret) { printk("write_pci_config_word: Copy from user space error!\n"); return ret; } pdev = pci_get_bus_and_slot(kerdev.bus_num, PCI_DEVFN(kerdev.dev_num, kerdev.func_num)); if(!pdev) { ret = -EINVAL; return ret; } pci_write_config_word(pdev, kerdev.offset, (u16)kerdev.data); ret = copy_to_user(fpcidev, &kerdev, sizeof(config_pci_dev)); if(ret) printk("write_pci_config_word: Copy to user space error!\n"); return ret; } int write_pci_config_dword(void *arg) { int ret = -EINVAL; struct pci_dev *pdev; config_pci_dev *fpcidev; config_pci_dev kerdev; fpcidev = (config_pci_dev *)arg; ret = copy_from_user(&kerdev, fpcidev, sizeof(config_pci_dev)); if(ret) { printk("write_pci_config_dword: Copy from user space error!\n"); return ret; } pdev = pci_get_bus_and_slot(kerdev.bus_num, PCI_DEVFN(kerdev.dev_num, kerdev.func_num)); if(!pdev) { ret = -EINVAL; return ret; } pci_write_config_dword(pdev, kerdev.offset, kerdev.data); ret = copy_to_user(fpcidev, &kerdev, sizeof(config_pci_dev)); if(ret) printk("write_pci_config_dword: Copy to user space error!\n"); return ret; } int write_pcie_cap_byte(void *arg) { int ret = -EINVAL; struct pci_dev *pdev; config_pci_dev *fpcidev; config_pci_dev kerdev; int cap_offset; fpcidev = (config_pci_dev *)arg; ret = copy_from_user(&kerdev, fpcidev, sizeof(config_pci_dev)); if(ret) { printk("write_pcie_cap_dword: Copy from user space error!\n"); return ret; } pdev = pci_get_bus_and_slot(kerdev.bus_num, PCI_DEVFN(kerdev.dev_num, kerdev.func_num)); if(!pdev) { ret = -EINVAL; return ret; } /*According to cap id, find the address of this specific device */ cap_offset = pci_find_capability(pdev, kerdev.cap_id); if(!cap_offset) { printk("read_pcie_cap_dword: there is no this capability ID = 0x%x\n", kerdev.cap_id ); return -EINVAL;; } kerdev.reg_addr = cap_offset + kerdev.offset; pci_write_config_byte(pdev, cap_offset + kerdev.offset, (u8)kerdev.data); ret = copy_to_user(fpcidev, &kerdev, sizeof(config_pci_dev)); if(ret) printk("write_pcie_cap_dword: Copy to user space error!\n"); return ret; } int write_pcie_ext_cap_byte(void *arg) { int ret = -EINVAL; struct pci_dev *pdev; config_pci_dev *fpcidev; config_pci_dev kerdev; int ext_cap_offset; fpcidev = (config_pci_dev *)arg; ret = copy_from_user(&kerdev, fpcidev, sizeof(config_pci_dev)); if(ret) { printk("write_pcie_cap_dword: Copy from user space error!\n"); return ret; } pdev = pci_get_bus_and_slot(kerdev.bus_num, PCI_DEVFN(kerdev.dev_num, kerdev.func_num)); if(!pdev) { ret = -EINVAL; return ret; } /*According to cap id, find the address of this specific device */ ext_cap_offset = pci_find_ext_capability(pdev, kerdev.cap_id); if(!ext_cap_offset) { printk("read_pcie_cap_dword: there is no this capability ID = 0x%x\n", kerdev.cap_id ); return -EINVAL;; } kerdev.reg_addr = ext_cap_offset + kerdev.offset; pci_write_config_byte(pdev, ext_cap_offset + kerdev.offset, (u8)kerdev.data); ret = copy_to_user(fpcidev, &kerdev, sizeof(config_pci_dev)); if(ret) printk("write_pcie_cap_dword: Copy to user space error!\n"); return ret; } /* * The PCI interface treats multi-function devices as independent * devices. The slot/function address of each device is encoded * in a single byte as follows: * * 7:3 = slot * 2:0 = function * * #define PCI_DEVFN(slot,func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) * #define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f) * #define PCI_FUNC(devfn) ((devfn) & 0x07) * */ int searchPciByVendorId(void *arg) { int ret = -EINVAL; struct pci_dev *pdev = NULL; config_pci_dev *fpcidev; config_pci_dev *fpcidev_ptr; config_pci_dev kerdev; unsigned int value = 0; int lsi_index = 0; unsigned short VID = 0; fpcidev = (config_pci_dev *)arg; fpcidev_ptr = fpcidev; ret = copy_from_user(&kerdev, fpcidev_ptr, sizeof(config_pci_dev)); if(ret) { printk("searchPciByVendorId: Copy from user space error!\n"); return ret; } VID = kerdev.vendor_id; do { memset(&kerdev, 0, sizeof(config_pci_dev)); ret = copy_from_user(&kerdev, fpcidev_ptr, sizeof(config_pci_dev)); if(ret) { printk("searchPciByVendorId: Copy from user space error!\n"); return ret; } /* * If pdev = NULL, it will find device from BEGIN. * else, it will continue to find next device if call it again. */ pdev = pci_get_device(VID, PCI_ANY_ID, pdev); if(!pdev) break; kerdev.bus_num = pdev->bus->number; kerdev.dev_num = PCI_SLOT(pdev->devfn); kerdev.func_num = PCI_FUNC(pdev->devfn); kerdev.vendor_id = pdev->vendor; kerdev.device_id = pdev->device; pci_read_config_word(pdev, pdev->pcie_cap + PCI_E_LINK_STAT_OFFSET, (u16 *)(&value)); kerdev.link_speed = value & 0x000f; kerdev.link_width = (value & 0x03F0) >> 4; kerdev.pcie_cap = pdev->pcie_cap; kerdev.msi_cap = pdev->msi_cap; kerdev.msix_cap = pdev->msix_cap; kerdev.dev_type = pci_pcie_type(pdev); kerdev.class_type = pdev->class; ret = copy_to_user(fpcidev_ptr, &kerdev, sizeof(config_pci_dev)); if(ret) { printk("searchPciByVendorId: Copy to user space error!\n"); return ret; } fpcidev_ptr++; }while (1); return ret; } int checkPcieDevices(void *arg) { int ret = -EINVAL; struct pci_dev *pdev; unsigned int value = 0; config_pci_dev *fpcidev; config_pci_dev kerdev; fpcidev = (config_pci_dev *)arg; ret = copy_from_user(&kerdev, fpcidev, sizeof(config_pci_dev)); if(ret) { printk("checkPcieDevices: Copy from user space error!\n"); return ret; } pdev = pci_get_bus_and_slot(kerdev.bus_num, PCI_DEVFN(kerdev.dev_num, kerdev.func_num)); if(!pdev) { ret = -EINVAL; return ret; } //check if the PCI device is PCI Express capable if(!pci_is_pcie(pdev)) { printk("checkPcieDevices: It's not pcie devices! vid[%x] did[%x]\n", pdev->vendor, pdev->device); ret = -ERR_PCIDRV_NOTPCIE; return ret; } kerdev.bus_num = pdev->bus->number; kerdev.dev_num = PCI_SLOT(pdev->devfn); kerdev.func_num = PCI_FUNC(pdev->devfn); kerdev.vendor_id = pdev->vendor; kerdev.device_id = pdev->device; pci_read_config_word(pdev, pdev->pcie_cap + PCI_E_LINK_STAT_OFFSET, (u16 *)(&value)); kerdev.link_speed = value & 0x000f; kerdev.link_width = (value & 0x03F0) >> 4; kerdev.pcie_cap = pdev->pcie_cap; kerdev.msi_cap = pdev->msi_cap; kerdev.msix_cap = pdev->msix_cap; kerdev.dev_type = pci_pcie_type(pdev); kerdev.class_type = pdev->class; ret = copy_to_user(fpcidev, &kerdev, sizeof(config_pci_dev)); if(ret) { printk("checkPcieDevices: Copy to user space error!\n"); } return ret; } /** Function Name : pci_ioctl() ** Parameter : cmd: ioctl command; ** arg: user space parameter pointer; ** Return : ** Function : the ioctl() syscall implementation ** Auther : Katrina Liu ** Date : June 27, 2012 **/ //int pci_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) long pci_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { int err = 0; int ret = 0; /* ** extract the type and number bitfields, and don't decode ** wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok() */ if (_IOC_TYPE(cmd) != PCI_MODULE_IOC_MAGIC) return -ENOTTY; if (_IOC_NR(cmd) > PCI_MODULE_IOC_MAXNUM) return -ENOTTY; /* ** The direction is a bitmask, and VERIFY_WRITE catches R/W transfers. ** 'Type' is user-oriented, while access_ok is kernel-oriented, so the ** concept of "read" and "write" is reversed */ if (_IOC_DIR(cmd) & _IOC_READ) { err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd)); } else if (_IOC_DIR(cmd) & _IOC_WRITE) { err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)); } if (err) return -EFAULT; switch (cmd) { case READ_PCI_CONFIG_BYTE: ret = read_pci_config_byte((void *)arg); break; case READ_PCI_CONFIG_WORD: ret = read_pci_config_word((void *)arg); break; case READ_PCI_CONFIG_DWORD: ret = read_pci_config_dword((void *)arg); break; case WRITE_PCI_CONFIG_BYTE: ret = write_pci_config_byte((void *)arg); break; case WRITE_PCI_CONFIG_WORD: ret = write_pci_config_word((void *)arg); break; case WRITE_PCI_CONFIG_DWORD: ret = write_pci_config_dword((void *)arg); break; case SEARCH_PCI_BY_VENDOR_ID: ret = searchPciByVendorId((void *)arg); break; case CHECK_PCI_E_DEVICES: ret = checkPcieDevices((void *)arg); break; case PCIE_CAP_READ_BYTE: read_pcie_cap_byte((void *)arg); break; case PCIE_CAP_WRITE_BYTE: write_pcie_cap_byte((void *)arg); break; case PCIE_EXT_CAP_READ_BYTE: read_pcie_ext_cap_byte((void *)arg); break; case PCIE_EXT_CAP_READ_DWORD: read_pcie_ext_cap_dword((void *)arg); break; case PCIE_EXT_CAP_WRITE_BYTE: write_pcie_ext_cap_byte((void *)arg); break; default: return -ENOTTY; } return ret; } struct file_operations pci_fops = { owner: THIS_MODULE, unlocked_ioctl: pci_ioctl, //ioctl: pci_ioctl, open : pci_open, release : pci_release, }; int pci_init_module(void) { int ret_num = 0; pci_major = 0; ret_num = register_chrdev(PCI_DYNAMIC_MAJOR, DEVICE_NAME, &pci_fops); pci_major = ret_num; if (ret_num < 0) { printk(KERN_ERR "PCI MODULE: Can not get major device Number !\n"); return ret_num; } if (ret_num >= 0) { pci_module_class = class_create(THIS_MODULE, DEVICE_NAME); device_create(pci_module_class, NULL, MKDEV(pci_major, 0), NULL, "%s", DEVICE_NAME); printk(KERN_INFO "PCI module have been loaded successfully (MAJOR_NUM=%d)!\n", ret_num); } return 0; } void pci_cleanup_module(void) { unregister_chrdev(pci_major, DEVICE_NAME); device_destroy(pci_module_class, MKDEV(pci_major, 0)); class_destroy(pci_module_class); } module_init(pci_init_module); module_exit(pci_cleanup_module); /* MODULE LICENSE DEFINE */ #ifdef _GPL_ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Netapp"); #else MODULE_LICENSE("Copyright 2012-2020, Netapp"); MODULE_AUTHOR("Netapp"); #endif #endif /* PCI_MODULE_C */