1、PCIE开发流程PCIE开发流程前言:对于USB、PCIE设备这种挂接在总线上的设备而言,USB、PCI只是它们的”工作单位”,它们需要向”工作单位”注册(使用usb_driver,pci_driver),并接收”工作单位”的管理(被调入probe()、调出disconnect/remove()、放假suspend()/shutdown()、继续上班resume()等),但设备本身可能是一个工程师、一个前台或者一个经理,因此做好工程师,前台或者经理是其主题工作,这部分对应于字符设备驱动,tty设备驱动,网络设备驱动等。第一节 整体构成整个驱动程序的开发应该包括三个大的部分1.1驱动模块的加载与
2、卸载xxx_init_module()注册pci_driver设备。xxx_cleanup_module()注销pci_driver设备。1.2pci_driver成员函数的初始化xxx_probe()完成PCI设备初始化,注册字符设备xxx_remove()完成PCI设备释放,注销字符设备1.3字符设备file_operations成员函数用于实现上层应用程序对下层驱动程序调用时的调用函数。xxx_open()xxx_release()xxx_ioctl()xxx_read()xxx_write()第二节 PCIE设备实现细节由于PCIE设备的驱动的开发都是按照一个统一规范的框架进行的。因此
3、以一个字符设备为例说明这个框架的实现机制。在所有PCIE驱动开发的过程中2.1驱动程序的初始化和注销涉及的函数为module_init(xxx_init_module),并在init中完成的功能为注册PCIE设备,具体函数内容如下所示:注销涉及的函数为module_exit(xxx_cleanup_module)在exit中完成的功能为注销PCIE设备,具体函数内容如下所示:2.2PCIE设备的注册在模块的初始化过程中,首先是注册PCIE设备,使用函数为pci_register_driver(&xxx_pci_driver),输入变量指明了PCIE结构体,如下所示:#define XXX_MO
4、DULE_NAME xxx_audiostatic struct pci_driver xxx_pci_driver = .name = XXX_MODULE_NAME, .id_table = xxx_pci_tbl, .probe = xxx_probe, .remove = _devexit_p(xxx_remove),#ifdef CONFIG_PM .suspend = xxx_pm_suspend, .resume = xxx_pm_resume,#endif /* CONFIG_PM */;结构体中name指明PCIE模块的名称,id_table指明了PCIE的设备驱动号也就是为
5、哪个设备进行驱动等。其中probe函数完成PCI设备的初始化以及其设备本身身份(字符,TTY,网络等)的驱动注册。也是驱动注册中最重要的函数。probe函数讲解1、首先使能pci设备,pci_enable_device(pci_dev),该函数主要作用是调用底层代码初始化PCI配置空间命令寄存器的I/O位和memory位。2、设置成总线主DMA模式,pci_set_dma_mask(pci_dev, XXX_DMA_MASK)用于实现对dma设备的设置。3、读取PCI的配置信息,使用的函数是pci_resource_start (pci_dev, 1)获取设备的内存基地址和所有BAR的长度,4
6、、 调用ioremap完成配置信息的映射,可以配置PCI定义的寄存器BAR设置的空间用于映射DMA的寄存器。4、申请I/O资源,request_region(card-ac97base, 256, card_namespci_id-driver_data)5、注册字符/网络设备涉及到的函数为cdev_init(xxx_cdev,&xxx_fops);/*注册驱动*/register_chrdev_region(xxx_dev_no,1,XXX);/*申请设备号*/cdev_add(xxx_cdev);/*添加字符设备*/request_irq(card-irq, &xxx_interrupt,
7、 SA_SHIRQ, card_namespci_id-driver_data, card)/*申请中断以及注册中断处理程序*/remove函数讲解1、释放I/O资源 pci_release_regions(pdev)2、禁止PCI设备 pci_disable_device(pdev)释放占用的设备号register_chrdev_region(xxx_dev_no,1,XXX);3、注销字符设备cdev_del(&xxx_dev.cdev)。2.3设备的file_operations操作 在probe中需要注册字符设备,实现应用程序对PCIE设备的调用,例如打开,读取,控制等。这些功能都是通
8、过file_operations操作接口实现。例如用户使用该设备完成读取操作,那么用户的过程为open(),read()。而用户调用的这些函数对于linux来说这些调用都会变成系统调用,并指向改设备对应的open(),read()函数,对于该设备来说,指向了xxx_open和xxx_read。 static struct file_operations xxx_fops = .owner = THIS_MODULE, .llseek = no_llseek, .read = sgma_read, .write = xxx_write, .poll = xxx_poll, .ioctl = xx
9、x_ioctl, .mmap = xxx_mmap, .open = xxx_open, .release = xxx_release,; 接下来,需要实现上面的这些操作,然后就能实现用户对设备的调用。static int i810_open(struct inode *inode, struct file *file) 2.4其他说明 a)、中断 在PCIE中可以使用request_irq共享中断或者pci_enable_msi消息告知申请中断,不同之处在于前者在扫描PCI的时候自动为设备分配好中断号,这样存在多个设备共享中断号的情况。MSI中断是在调用初始化函数pci_enable_msi
10、()才分配中断号,可以保证设备的中断号不会与其他设备共用,从而避免了中断共享能够提高整体性能,但是MSI中断的使用需要Linux操作系统特殊的支持,不具有普遍的适用性。传统的中断,由于资源号是有限的,常常涉及到多个设备共享同一个中断号,在中断的处理过程中要依次调用每个中断处理函数来判断中断是不是目标设备发出,这会消耗系统性能。第三节 示例程序#include card.h#include #include #define DMA_MASK 0xffffffff#define test_dri_major 249 / 主设备号/#define INT_ASSERT_W 0x02 / DMA Wr
11、ite Complete/#define INT_ASSERT_R 0x10 / DMA Read Complete/* PCI 驱动基本框架 ,为下面的设备进行驱动*/static struct pci_device_id card_ids = PCI_DEVICE(PCI_VENDOR_ID_XILINX,PCI_DEVICE_ID_EP_PIPE), 0,;MODULE_DEVICE_TABLE(pci,card_ids);/*probe和remove基本函数*/static int card_probe(struct pci_dev *pci_dev, const struct pci
12、_device_id *id);static void card_remove(struct pci_dev *pdev);/*pci_driver 结构体*/static struct pci_driver card_driver = .name = DEV_NAME, .id_table = card_ids, .probe = card_probe, .remove = card_remove,;static int _init card_init(void) int result; result = pci_register_driver(&card_driver); return r
13、esult;static void _exit card_exit(void) pci_unregister_driver(&card_driver);module_init(card_init);module_exit(card_exit);/* PCI 驱动基本框架 */* 特定设备私有数据结构 */struct card_private struct pci_dev* pci_dev; void* pci_bar0; /wait_queue_head_t * dma_write_wait; /wait_queue_head_t * dma_read_wait;/* 特定设备私有数据结构
14、*/static struct card_private *adapter;/static DECLARE_WAIT_QUEUE_HEAD(dma_write_wait);/static int flag = 1;/ 将文件操作与分配的设备号相连static const struct file_operations card_fops = .owner = THIS_MODULE, /.ioctl = card_ioctl, .open = card_open, .release= card_release, .read = card_read, .write = card_write,;st
15、atic int card_probe(struct pci_dev *pdev, const struct pci_device_id *id) unsigned long phymem; void _iomem *mem; u_int8_t csz; u32 val; int result; /*配置PCI设备*/ if (pci_enable_device(pdev) return -EIO; /* XXX 32-bit addressing only */ if (pci_set_dma_mask(pdev, 0xffffffff) printk(KERN_ERR ath_pci: 3
16、2-bit DMA not availablen); goto bad; /pci_write_config_word(pdev, 0x04, 0x0007); /*配置PCI寄存器,首先调用pci_read_config_byte进行读取PCI配置空间,并将值返回给csz * Cache line size is used to size and align various * structures used to communicate with the hardware. */ pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &csz);
17、if (csz = 0) /* * Linux 2.4.18 (at least) writes the cache line size * register as a 16-bit wide register which is wrong. * We must have this setup properly for rx buffer * DMA to work so force a reasonable value here if it * comes up zero. */ csz = L1_CACHE_BYTES / sizeof(u_int32_t); pci_write_conf
18、ig_byte(pdev, PCI_CACHE_LINE_SIZE, csz); /* * The default setting of latency timer yields poor results, * set it to the value used by other systems. It may be worth * tweaking this setting more. *配置PCI配置空间 */ pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8); /*设置成总线主模式*/ pci_set_master(pdev); /*
19、读取寄存器信息 * Disable the RETRY_TIMEOUT register (0x41) to keep * PCI Tx retries from interfering with C3 CPU state. * * Code taken from ipw2100 driver - jg */ pci_read_config_dword(pdev, 0x40, &val); if (val & 0x0000ff00) != 0) pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); / 获得BAR0空间的基地址,该地址为存储
20、器域的物理地址; phymem = pci_resource_start(pdev, 0); if (!request_mem_region(phymem, pci_resource_len(pdev, 0), DEV_NAME) printk(KERN_ERR card_driver: cannot reserve PCI memory regionn); goto bad; / 将存储器域的物理地址映射为虚拟地址; mem = ioremap(phymem, pci_resource_len(pdev, 0); if (!mem) printk(KERN_ERR card_driver:
21、cannot remap PCI memory regionn) ; goto bad1; adapter = kmalloc(sizeof(struct card_private), GFP_KERNEL); if (unlikely(!adapter) return -ENOMEM; adapter - pci_dev = pdev; adapter - pci_bar0 = mem; / 注册设备驱动程序 result = register_chrdev(test_dri_major, DEV_NAME, &card_fops); if (unlikely(result) printk(
22、KERN_ERR card_driver: no memory for device staten); goto bad2; /* /init_waitqueue_head(adapter-dma_write_wait); /init_waitqueue_head(adapter-dma_read_wait); result = pci_enable_msi(pdev); if (unlikely(result) /PDEBUG(cannot enable msi . n); goto bad3; result = request_irq(pdev - irq, card_interrupt,
23、 0, DEV_NAME, NULL); if (unlikely(result) /PDEBUG(request interrupt failed . n); goto bad3; printk(KERN_DEBUG request_irq(pdev - irq, card_interrupt, 0, DEV_NAME, NULL);); */ return 0;/bad3:/ unregister_chrdev(test_dri_major, DEV_NAME); bad2: iounmap(mem);bad1: release_mem_region(phymem, pci_resourc
24、e_len(pdev, 0);bad: pci_disable_device(pdev); return (-ENODEV);static void card_remove(struct pci_dev *pdev) /pci_disable_msi(pdev); /if(pdev-irq) / free_irq(pdev-irq, pdev); iounmap(adapter - pci_bar0); release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0); pci_disable_device(pd
25、ev); unregister_chrdev(test_dri_major, DEV_NAME);/打开设备文件系统调用对应的操作static int card_open(struct inode *inode, struct file *filp) return 0;/关闭设备文件系统调用对应的操作static int card_release(struct inode *inode, struct file *filp) return 0;/读取设备信息static ssize_t card_read(struct file *file, char _user *buf, size_t c
26、ount, loff_t *f_pos) void* virt_addr = NULL; dma_addr_t dma_write_addr; u32 base, w_ddr2, w_addr,w_size,cst_32,w_counter,dma_cst; u32 ddr2; int i; i = 0; /*读取寄存器的值,0x10的位置也就是BAR的初始地址*/ pci_read_config_dword(adapter-pci_dev, 0x10, &base); printk(KERN_DEBUG pci_read_config_dword, base:%xn,base); print
27、k(KERN_DEBUG adater - pci_bar0 %lxn,(unsigned long)(adapter - pci_bar0); /*Request virt_addr(kernel) for Read(DMA write)*/ virt_addr = kmalloc(count, GFP_KERNEL|_GFP_DMA); if(unlikely(!virt_addr) /PDEBUG(cannot alloc rx memory you want . n); return -EIO; printk(KERN_DEBUG virt_addr(kernel):%xn,(u32)
28、virt_addr); /*Request virt_addr(kernel) for Read(DMA write)*/ /*dma_write_addr*/ /将存储器域的虚拟地址virt_addr转化为pci总线域的物理地址dma_write_addr,供card的DMA控制器使用。 dma_write_addr = pci_map_single(adapter-pci_dev, virt_addr, count, PCI_DMA_FROMDEVICE); if(unlikely(pci_dma_mapping_error(adapter-pci_dev, dma_write_addr)
29、 /PDEBUG(RX DMA MAPPING FAIL.n); goto err_kmalloc; printk(KERN_DEBUG dma_write_addr:%xn,dma_write_addr); /*dma_write_addr*/ /*BAR0 kong jian */ / START, w_counter读取BAR0空间 w_counter = ioread32(adapter - pci_bar0+WRITE_DMA_COUNTER_OFFSET); printk(KERN_DEBUG START, w_counter: %x,w_counter); / w_ddr2 DMA写的原地址 iowrite32(*f_pos,(adapter - pci_bar0 + WRITE_DDR2_SA_OFFSET); ddr2 = ioread32(adapter - pci_bar0 + WRITE_DDR2_SA_OFFSET); printk(KERN_DEBUG WRITE_DDR2_SA_OFFSET: %x,ddr2); / w_addr / Lower 32-bit address of system memory buffer
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1