您好,欢迎访问三七文档
PCIE开发流程前言:对于USB、PCIE设备这种挂接在总线上的设备而言,USB、PCI只是它们的”工作单位”,它们需要向”工作单位”注册(使用usb_driver,pci_driver),并接收”工作单位”的管理(被调入probe()、调出disconnect/remove()、放假suspend()/shutdown()、继续上班resume()等),但设备本身可能是一个工程师、一个前台或者一个经理,因此做好工程师,前台或者经理是其主题工作,这部分对应于字符设备驱动,tty设备驱动,网络设备驱动等。第一节整体构成整个驱动程序的开发应该包括三个大的部分1.1驱动模块的加载与卸载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设备的驱动的开发都是按照一个统一规范的框架进行的。因此以一个字符设备为例说明这个框架的实现机制。在所有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结构体,如下所示:#defineXXX_MODULE_NAMExxx_audiostaticstructpci_driverxxx_pci_driver={.name=XXX_MODULE_NAME,.id_table=xxx_pci_tbl,.probe=xxx_probe,.remove=__devexit_p(xxx_remove),#ifdefCONFIG_PM.suspend=xxx_pm_suspend,.resume=xxx_pm_resume,#endif/*CONFIG_PM*/};结构体中name指明PCIE模块的名称,id_table指明了PCIE的设备驱动号也就是为哪个设备进行驱动等。其中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、调用ioremap完成配置信息的映射,可以配置PCI定义的寄存器BAR设置的空间用于映射DMA的寄存器。4、申请I/O资源,request_region(card-ac97base,256,card_names[pci_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,SA_SHIRQ,card_names[pci_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设备的调用,例如打开,读取,控制等。这些功能都是通过file_operations操作接口实现。例如用户使用该设备完成读取操作,那么用户的过程为open(),read()。而用户调用的这些函数对于linux来说这些调用都会变成系统调用,并指向改设备对应的open(),read()函数,对于该设备来说,指向了xxx_open和xxx_read。staticstructfile_operationsxxx_fops={.owner=THIS_MODULE,.llseek=no_llseek,.read=sgma_read,.write=xxx_write,.poll=xxx_poll,.ioctl=xxx_ioctl,.mmap=xxx_mmap,.open=xxx_open,.release=xxx_release,};接下来,需要实现上面的这些操作,然后就能实现用户对设备的调用。staticinti810_open(structinode*inode,structfile*file){^^^^^^^^}2.4其他说明a)、中断在PCIE中可以使用request_irq共享中断或者pci_enable_msi消息告知申请中断,不同之处在于前者在扫描PCI的时候自动为设备分配好中断号,这样存在多个设备共享中断号的情况。MSI中断是在调用初始化函数pci_enable_msi()才分配中断号,可以保证设备的中断号不会与其他设备共用,从而避免了中断共享能够提高整体性能,但是MSI中断的使用需要Linux操作系统特殊的支持,不具有普遍的适用性。传统的中断,由于资源号是有限的,常常涉及到多个设备共享同一个中断号,在中断的处理过程中要依次调用每个中断处理函数来判断中断是不是目标设备发出,这会消耗系统性能。第三节示例程序#includecard.h#includelinux/time.h#includelinux/spinlock.h#defineDMA_MASK0xffffffff#definetest_dri_major249//主设备号//#defineINT_ASSERT_W0x02//DMAWriteComplete//#defineINT_ASSERT_R0x10//DMAReadComplete/*PCI驱动基本框架,为下面的设备进行驱动*/staticstructpci_device_idcard_ids[]={{PCI_DEVICE(PCI_VENDOR_ID_XILINX,PCI_DEVICE_ID_EP_PIPE),},{0,}};MODULE_DEVICE_TABLE(pci,card_ids);/*probe和remove基本函数*/staticintcard_probe(structpci_dev*pci_dev,conststructpci_device_id*id);staticvoidcard_remove(structpci_dev*pdev);/*pci_driver结构体*/staticstructpci_drivercard_driver={.name=DEV_NAME,.id_table=card_ids,.probe=card_probe,.remove=card_remove,};staticint__initcard_init(void){intresult;result=pci_register_driver(&card_driver);returnresult;}staticvoid__exitcard_exit(void){pci_unregister_driver(&card_driver);}module_init(card_init);module_exit(card_exit);/*PCI驱动基本框架*//*特定设备私有数据结构*/structcard_private{structpci_dev*pci_dev;void*pci_bar0;//wait_queue_head_t*dma_write_wait;//wait_queue_head_t*dma_read_wait;};/*特定设备私有数据结构*/staticstructcard_private*adapter;//staticDECLARE_WAIT_QUEUE_HEAD(dma_write_wait);//staticintflag=1;//将文件操作与分配的设备号相连staticconststructfile_operationscard_fops={.owner=THIS_MODULE,//.ioctl=card_ioctl,.open=card_open,.release=card_release,.read=card_read,.write=card_write,};staticintcard_probe(structpci_dev*pdev,conststructpci_device_id*id){unsignedlongphymem;void__iomem*mem;u_int8_tcsz;u32val;intresult;/*配置PCI设备*/if(pci_enable_device(pdev))return-EIO;/*XXX32-bitaddressingonly*/if(pci_set_dma_mask(pdev,0xffffffff)){printk(KERN_ERRath_pci:32-bitDMAnotavailable\n);gotobad;}//pci_write_config_word(pdev,0x04,0x0007);/*配置PCI寄存器,首先调用pci_read_config_byte进行读取PCI配置空间,并将值返回给csz*Cachelinesizeisusedtosizeandalignvarious*structuresusedtocommunicatewiththehardware.*/pci_read_config_byte(pdev,PCI_CACHE_LINE_SIZE,&csz);if(csz==0){/**Linux2.4.18(atleast)writesthecachelinesize*registerasa16-bitwideregisterwhichiswrong.*Wemusthavethissetupproperlyforrxbuffer*DMAtoworksoforceareasonablevaluehereifit*comesupzero.*/csz=L1_CACHE_BYTES/sizeof(u_int32_t);pci_write_config_byte(pdev,PCI_CACHE_LINE_SIZE,csz);}/**Thedefaultsettingoflatencytimeryieldspoorresults,*setittothevalueusedbyothersystems.Itmaybeworth*tweaki
本文标题:PCIE开发流程
链接地址:https://www.777doc.com/doc-4712321 .html