kaiyun官方注册
您所在的位置: 首页> 嵌入式技术> 设计应用> Linux下基于PCI-E时统卡的驱动程序设计
Linux下基于PCI-E时统卡的驱动程序设计
2015年微型机与应用第24期
刘 凯,胡爱兰
(华北计算机系统工程研究所,北京 100083)
摘要:简要介绍了Linux操作系统和PCI-Express(PCI-E)总线的特点以及Linux设备驱动的作用。以PEX8311时统卡为例,阐述了Linux系统下PCI-E驱动程序开发的流程和技巧,并通过DMA模式测试了驱动程序的可行性。
Abstract:
Key words :

 摘 要: 简要介绍了Linux操作系统和PCI-Express(PCI-E)总线的特点以及Linux设备驱动的作用。以PEX8311时统卡为例,阐述了Linux系统下PCI-E驱动程序开发的流程和技巧,并通过DMA模式测试了驱动程序的可行性。

关键词: 设备驱动;Linux;PCI-Express;PEX8311;DMA

0 引言

  Linux操作系统凭借其开放的源代码、良好的扩展性以及安全高效等特点,受到越来越多领域开发者的重视,并逐步成为各种计算机终端、服务器工作站及嵌入式平台的主流操作系统。PCI-Express(PCI-E)作为最新一代的总线接口,其点对点的串行设计以及双通道高带宽的传输模式,大大提高了数据的传输速率[1],它的广泛应用将全面取代PCI、AGP等总线。

  目前基于Linux平台下的PCI-E总线的应用十分广泛,小到微型嵌入式系统,大到超大型服务器系统,都可以看到二者的完美结合。而驱动程序作为硬件设备与操作系统之间的桥梁,对硬件的工作起着至关重要的作用。本文介绍的是Linux下基于PCI-E时统卡的驱动程序的开发过程。

1 PCI-E时统卡

  本文中使用的PCI-E时统卡是自主研发的一款硬件设备。该时统卡通过接受B码终端发来的信号,然后经FPGA进行解码,获得时间信息,并以1 pps脉冲为基准产生用户所需要的20 Hz、100 Hz等中断脉冲信号,最后通过PCI-E桥接芯片PEX8311进行数据交互,使得时统卡中的时间信息以及中断信息能够传到计算机终端或服务器中。而要想让安装在计算机终端或者服务器中的时统卡能够正常工作,就需要为其开发配套的驱动程序,主要就是针对PEX8311芯片的驱动。图1所示为时统卡PEX8311芯片的结构简图。数据经由PFGA传到Local Bus,然后通过内部总线再到PCI-E总线,最后传到计算机终端中。

001.jpg

2 Linux设备驱动程序

002.jpg

  Linux设备驱动程序是一种使计算机软件与硬件设备进行交互的特殊程序。图2所示为Linux设备驱动与操作系统及外设的关系。设备驱动程序位于Linux操作系统的内核空间,它相当于操作系统内核空间与物理层硬件设备之间的接口,它还为用户层提供系统调用的接口函数。用户层的应用程序不能直接访问操作物理层的外部硬件设备,只有通过系统调用才可以访问操作外部硬件设备[2]。因此可以看出设备驱动程序在操作系统中起到了相当大的作用。

3 Linux设备驱动开发过程

  Linux设备驱动程序的编写可以模块化,主要包括:设备的初始化、驱动模块的加载与卸载、设备的打开与释放、数据读写与操作、中断响应。

3.1 设备的初始化

  Linux系统启动后会自动检测计算机终端上所有的PCI-E设备的信息,并记录在pci_dev结构体中,其中包括硬件设备的厂商号、设备号等大部分的硬件信息。PCI-E驱动程序就是根据厂商号和设备号来连接设备并加载驱动的,这就需要在驱动程序中定义该驱动所支持的硬件参数信息。本文中使用的时统卡的PCI-E桥接芯片是PEX8311,其硬件参数信息定义分别为厂商号、设备号、子厂商号、子设备号、类别和类别掩码。初始化代码如下。

  static struct pci_device_id PlxPciIdTable()=

  {

  #if(PLX_CHIP==8311)

  {0x10B5,0x8311,PCI_ANY_ID,PCI_ANY_ID,0x000000,0x000000},

  {0x10B5,0x86E1,PCI_ANY_ID,PCI_ANY_ID,0x000000,0x000000},

  #endif

  };

  MODULE_DEVICE_TABLE(pci,PlxPciIdTable);

  3.2 驱动模块的加载与卸载

  硬件设备驱动的加载,必须要有一个主设备号。设备号的分配有两种方式:静态分配和动态分配。静态分配指的是由开发人员指定一个固定的设备号;动态分配则是由操作系统自动分配设备号。在不能明确某设备号是否被使用的情况下,建议使用动态分配的方式获得设备号,这样就避免了因设备号冲突导致硬件设备不能正常工作的情况出现。分配了设备号就可以注册设备并加载设备驱动了。而当该设备不再使用时,可以将该设备的驱动模块卸载掉,以此来减少系统内核的占用以及其他系统资源的开销。驱动模块加载与卸载的代码如下。

  //驱动模块的加载

  static int__init plxpci_init(void)

  {

  ……

  /*注册设备,register_chrdev函数的第一个参数为0,表示系统自动分配一个空闲的主设备号*/

  card->MajorID=register_chrdev(0,PLX_DRIVER_NAME,&plxpci_fops);

  pci_register_driver(&PlxPciDriver);

  ……

  }

  //驱动模块的卸载

  static void__exit plxpci_cleanup(void)

  {

  unregister_chrdev(major,PLX_DRIVER_NAME);

  pci_unregister_driver(&plxpci_driver);

  }

 3.3 设备的打开与释放

  Linux系统内核在驱动模块加载之后就可以打开硬件设备。设备的打开模块主要是获取设备的控制权,允许中断的产生等。而当不再使用该设备时,就需要释放该设备。设备的释放模块的任务与设备的打开模块的任务正好相反,主要是释放对设备的控制权、中断以及之前系统分配的一些资源等。设备打开与释放的代码如下。

  //设备的打开

  static int plxpci_open(struct inode*inode,struct file*file)

  {

  ……

  /*获取设备的控制权*/

  dev->open_mode|=file->f_mode&(FMODE_READ|FMODE_WRITE);

  /*允许中断产生*/

  plxpci_enable_IRQ(dev);

  return 0;

  }

  //设备的释放

  static int plxpci_release(struct inode*inode,struct file*file)

  {

  ……

  /*释放对设备的控制权*/

  dev->open_mode&=(~file->f_mode)&(FMODE_READ|FMODE_WRITE);

  free_irq(card->irq,card);

  kfree(card);

  return0;

  }

3.4 数据读写与操作

  本文中驱动程序使用的是DMA(Direct Memory Access)传输模式。DMA传输模式无需计算机或本地控制器的干预,传输效率很高,从而大大降低了控制器的工作量且提高了数据的传输速率及效率[3]。要完成DMA传输模式就需要了解时统卡上主要的PCI-E桥接芯片PEX8311的工作模式。从参考文献[4]中可知,PEX8311芯片中有几个重要的寄存器:(1)LCS_DMAMODE0,地址是80h,该寄存器主要用来设置DMA的模式。(2)LCS_DMADPR0,地址是90h,该寄存器主要用来设置DMA的传输方向。当LCS_DMADPR0[3]=1,表示传输方向从Local Bus到PCI-E,若为0,则方向相反。(3)LCS_DMACSR0,地址是A8h,该寄存器主要用来启动DMA传输。成功设置了DMA的传输模式,就可以从时统卡中读出时间信息。DMA传输的代码如下。

  //DMA传输模式

  {

  ……

  /*设置DMA传输方向*/

  PlxPci_PlxRegisterWrite(pDevice,0x90,SglPciAddress|(1<<0)|(1<<3));

  /*设置DMA模式*/

  PlxPci_PlxRegisterWrite(pDevice,0x80,0x00020642);

  /*启动DMA传输*/

  RegValue=PlxPci_PlxRegisterRead(pDevice,0xA8,NULL);

  RegValue|=(1<<0);

  PlxPci_PlxRegisterWrite(pDevice,0xA8,RegValue);

  RegValue|=(1<<1);

  PlxPci_PlxRegisterWrite(pDevice,0xA8,RegValue);

  ……

  }

3.5 中断响应

  中断是Linux系统中非常宝贵的资源,任何驱动程序都需要申请中断并注册中断处理才可以使用中断。可以使用中断的方式来读取硬件设备中的数据。而如果硬件设备不支持中断,则只能采用轮询的方式来读取数据。硬件设备中一般包含好几种不同的中断,例如1 Hz、20 Hz、100 Hz等。因此,当读取中断状态位之后还需要将不同的中断识别区分开来才能使用。另外,为方便用户层的应用软件对中断的使用,使用信号机制来向用户层发送中断信号,通知用户层的应用软件获取中断状态位。中断响应程序如下。

  //中断响应

  irq_handler_t plxpci_interrupt(int irq,void*dev_id,struct pt_regs*regs)

  {

  ……

  /*读取中断状态位,其中包含多种中断,需要在下一步识别并解析出不同的中断*/

  status=inb(PLXPCI_IRQ_REG(dev));

  if(status)

  {

  /*识别20 Hz中断*/

  if(status&IRQ_FLAG_20 Hz)

  {

  ……

  }

  ……

  /*通知调度函数向应用层软件发送中断信号*/

  tasklet_schedule(&dev->tlet);

  }

  return(IRQ_HANDLED);

  }

4 驱动程序的测试

  4.1 驱动程序的加载

  本文中开发及测试平台所使用的操作系统是中标麒麟Linux操作系统,该系统的内核版本是2.6.32。Linux下驱动程序模块的加载通常有动态加载和静态加载两种方式。静态加载就是把编译生成的驱动程序文件plx8311.ko编译到内核中,每次系统启动时自动调用,这种方式比较适合最终版本的驱动程序。动态加载就是通过insmod命令加载驱动程序,通过rmmod命令可以卸载驱动程序,这样随时可以修改驱动程序,对于还在调试阶段的程序比较方便。

  4.2 测试过程与结果

003.jpg


  测试前首先保证在计算机终端中安装好时统卡,并连接B码终端,然后加载驱动程序,使用lsmod命令查看驱动程序是否已经加载好。图3所示为plx8311驱动加载成功。当驱动程序可以正常加载,并且能够通过测试程序读出时统卡中的时间信息和中断信息,则说明编写的驱动程序是可行的。图4所示为测试结果,前面显示的是从时统卡中读出的当前时间,后面3个数字表示从启动测试程序到当前时刻所获得的1 Hz、20 Hz、100 Hz中断信号的个数。

5 结论

  Linux系统的开源性加上PCI-E总线在计算机系统中的广泛应用,使得其两者的结合越来越紧密,Linux系统下的PCI-E的驱动开发也得到了广泛的关注。本文结合实际项目开发,通过对PEX8311时统卡的驱动程序编写过程中的各模块的介绍,阐述了整个驱动的开发流程和相关技巧,并通过编写测试程序完成了驱动程序的测试工作,验证了驱动的可用性。

  参考文献

  [1] BUDRUK R, ANDERSON D, SHANLEY T. PCI Express系统体系结构标准教材[M].田玉敏,王崧,张波,译.北京:电子工业出版社,2005.

  [2] 郑强.Linux驱动开发入门与实践[M].北京:清华大学出版社,2010.

  [3] 范晶,胡爱兰.基于状态机的PEX8311的DMA实现[J].微型机与应用,2014,33(22):30-33.

  [4] PLX. PEX8311 AA data book version 1.0[OL]. [2015-04-15].http://www.plxtech.com/mydata.


此内容为AET网站原创,未经授权禁止转载。
Baidu
map