深入了解PCI设备驱动程序:原理、功能与应用

2024-11-23 0 719

PCI总线在计算机界颇受欢迎,掌握其驱动程序设计的重要性不言而喻。Linux系统对PCI总线的支持相当到位,这表面上似乎减少了开发者的负担,然而对于新手而言,仍有许多疑惑有待解开。

PCI总线在Linux下的上电扫描

在Linux系统中,系统启动时会对PCI总线进行设备扫描,这一过程涵盖了设备和桥接器等。比如在某个特定的开发板上,系统启动后会为设备分配总线号和中断号。这个过程对于初学者来说可能有些难以理解。不过,初学者可以先暂时跳过这一步骤。可以选择一块带有PCI总线的开发板,连接PCI设备后重启系统进行扫描,然后再结合驱动程序框架来学习。比如我在实验室使用的开发板,在连接网卡并重启后,可以看到设备被扫描并识别出来。这实际上就是电脑启动时内部的工作流程之一。

实际操作中,若对扫描过程的细节有所好奇,不妨查阅相关资料进行深入研究。理论结合实际,这能让我们更深入地理解和掌握这一关键扫描过程。

Linux2.6内核下的总线驱动模型

Linux2.6内核推出的总线驱动模型颇具特色。此后,总线设备驱动被细分为总线驱动和设备驱动两个独立部分。以PCI总线驱动为例,它与2.6内核中的总线驱动存在相似之处。其匹配原则是名称对应,即设备名称需与驱动名称相同。若要使某PCI设备正常运行,必须确保设备名与驱动名相符。有开发者在测试过程中,因名称不匹配,导致驱动无法正确加载。这种匹配机制有效保障了系统的稳定性和准确性。

正确理解这种模式对于开发PCI总线驱动来说至关重要。若不依照这种匹配方式,实际使用时驱动程序将难以找到设备,或者设备无法识别驱动程序,这些问题将严重妨碍设备的正常运行。

PCI驱动加载过程

在安装PCI驱动过程中,需要进行一系列的判断。首先,它会核对系统内现有设备的厂商号和设备号,与驱动程序中的数据相对比。如果匹配成功,便会注册PCI总线驱动,并继续执行后续步骤。这个过程颇似一个严谨的身份验证过程。在我的工作中,曾经遇到过因厂商号出错,导致驱动程序无法成功注册的情况。经过仔细检查,我发现错误源于设备厂商号在生产过程中被错误录入。

这一过程极为严格,稍有不慎,小错误也可能引发驱动加载失败。因此,开发人员必须细心操作。比如,他们需要多次检查设备信息与驱动数据是否一致,这样才能有效降低出错率。

PCI总线驱动的组成部分

PCI设备驱动主要由两部分构成。首先是总线相关部分,涉及PCI设备识别、调用驱动程序的probe函数等操作。其次是具体的功能驱动,比如网卡驱动。在众多采用PCI总线的设备中,如声卡、显卡等,总线驱动模块的设计是相似的。我们首先完成了PCI总线驱动的开发,随后具体设备的驱动开发工作也就变得较为顺利。在测试PCI声卡设备时,我已成功处理了PCI总线驱动,这使得后续的功能驱动开发过程变得更加顺畅。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
 
//设备相关
#define MY_VENDOR_ID 0x168c    //厂商号
#define MY_DEVICE_ID 0x002a     //设备号
#define MY_PCI_NAME "MYPCIE"  //自己起的设备名
static int debug = 1;
module_param(debug,int,S_IRUGO);
#define DBG(msg...) do{ 
   if(debug) 
      printk(msg); 
   }while(0)
 
struct pcie_card
{
   //端口读写变量
   int io;
   long range,flags;
   void __iomem *ioaddr;
   int irq;
};
/* 设备中断服务*/
static irqreturn_t mypci_interrupt(int irq, void *dev_id)
{
   struct pcie_card *mypci = (struct pcie_card *)dev_id;
   printk("irq = %d,mypci_irq = %dn",irq,mypci->irq);
   return IRQ_HANDLED;
}
 
/* 探测PCI设备*/
static int __init mypci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
{
   int retval=0;//, intport, intmask;
   struct pcie_card *mypci;
 
   if ( pci_enable_device (dev) )
   {
    printk (KERN_ERR "IO Error.n");
    return -EIO;
   }
   /*分配设备结构*/
   mypci = kmalloc(sizeof(struct pcie_card),GFP_KERNEL);
   if(!mypci)
   {
      printk("In %s,kmalloc err!",__func__);
      return -ENOMEM;
   }
   /*设定端口地址及其范围,指定中断IRQ*/
   mypci->irq = dev->irq;
   if(mypci->irq < 0)
   {
      printk("IRQ is %d, it's invalid!n",mypci->irq);
      goto out_mypci;
   }
   mypci->io = pci_resource_start(dev, 0);
   mypci->range = pci_resource_end(dev, 0) - mypci->io;
   mypci->flags = pci_resource_flags(dev,0);
   DBG("PCI base addr 0 is io%s.n",(mypci->flags & IORESOURCE_MEM)? "mem":"port");
   /*检查申请IO端口*/
   retval = check_region(mypci->io,mypci->range);
   if(retval)
   {
      printk(KERN_ERR "I/O %d is not free.n",mypci->io);
      goto out_mypci;
   }
   //request_region(mypci->io,mypci->range, MY_PCI_NAME);
   retval = pci_request_regions(dev,MY_PCI_NAME);
   if(retval)
   {
      printk("PCI request regions err!n");
      goto out_mypci;
   }
   mypci->ioaddr = ioremap(mypci->io,mypci->range);
   if(!mypci->ioaddr)
   {
      printk("ioremap err!n");
      retval = -ENOMEM;
      goto out_regions;
   }
   //申请中断IRQ并设定中断服务子函数
   retval = request_irq(mypci->irq, mypci_interrupt, IRQF_SHARED, MY_PCI_NAME, mypci);
   if(retval)
   {
      printk (KERN_ERR "Can't get assigned IRQ %d.n",mypci->irq);
      goto out_iounmap;
   }
   pci_set_drvdata(dev,mypci);
   DBG("Probe succeeds.PCIE ioport addr start at %X, mypci->ioaddr is 0x%p,interrupt No. %d.n",mypci->io,mypci->ioaddr,mypci->irq);
   return 0;
  
   out_iounmap:
      iounmap(mypci->ioaddr);
   out_regions:
      pci_release_regions(dev);
   out_mypci:
      kfree(mypci);
      return retval;
}
 
/* 移除PCI设备 */
static void __devexit mypci_remove(struct pci_dev *dev)
{
   struct pcie_card *mypci = pci_get_drvdata(dev);
   free_irq (mypci->irq, mypci);
   iounmap(mypci->ioaddr);
   //release_region(mypci->io,mypci->range);
   pci_release_regions(dev);
   kfree(mypci);
   DBG("Device is removed successfully.n");
}
 
/* 指明驱动程序适用的PCI设备ID */
static struct pci_device_id mypci_table[] __initdata =
{
   {
     MY_VENDOR_ID,  //厂商ID
     MY_DEVICE_ID,  //设备ID
     PCI_ANY_ID,   //子厂商ID
     PCI_ANY_ID,   //子设备ID
   },
   {0, },
};
MODULE_DEVICE_TABLE(pci, mypci_table);
 
/* 设备模块信息 */
static struct pci_driver mypci_driver_ops =
{
   name: MY_PCI_NAME,   //设备模块名称
   id_table: mypci_table, //驱动设备表
   probe: mypci_probe,   //查找并初始化设备
   remove: mypci_remove  //卸载设备模块
};
 
static int __init mypci_init(void)
{
   //注册硬件驱动程序
   if ( pci_register_driver(&mypci_driver_ops) )
   {
      printk (KERN_ERR "Can't register driver!n");
      return -ENODEV;
   }
   return 0;
}
 
static void __exit mypci_exit(void)
{
   pci_unregister_driver(&mypci_driver_ops);
}
 
module_init(mypci_init);
module_exit(mypci_exit);
MODULE_LICENSE("GPL");

这两个部分的开发顺序是有一定逻辑的。若顺序颠倒,开发过程中很容易出现逻辑上的混乱,进而影响开发效率,甚至可能导致设备驱动失败。

PCI设备识别实例

我进行了一项实验,在开发板上接入了一块PCIE网卡。系统重启后,加载相应的驱动模块,便启动了驱动注册等步骤。当系统显示“探测成功”时,说明系统已识别出我的网卡。这正显示了成功识别PCI设备的过程。这一成果表明,系统的PCI总线物理起始地址已被正确识别。这标志着开发过程中的一个关键节点,若识别失败,就必须重新检查设备、驱动、硬件连接等问题。

PCI设备在识别上或许各有不同,但它们的识别逻辑大体相近。通过这个例子,我们可以更深入地了解PCI设备识别的运作原理。

[root@board /] cat /proc/interrupts
           CPU0      
 17:          0   UIC   Level     MYPCIE
 18:         24   UIC   Level     MAL TX EOB
 19:        225   UIC   Level     MAL RX EOB
 20:          0   UIC   Level     MAL SERR
 21:          0   UIC   Level     MAL TX DE
 22:          0   UIC   Level     MAL RX DE
 24:          0   UIC   Level     EMAC
 26:       1194   UIC   Level     serial
BAD:          0
[root@board /] cat /proc/iomem               //注意:查看iomem时出现了自己的设备占用的iomem,说明是IO内存
90000000-97ffffff : /plb/pciex@0a0000000
98000000-9fffffff : /plb/pciex@0c0000000
  98000000-980fffff : PCI Bus 0001:41
    98000000-9800ffff : 0001:41:00.0
      98000000-9800ffff : MYPCIE
ef600200-ef600207 : serial
ef600300-ef600307 : serial
fc000000-ffffffff : fc000000.nor_flash

PCI中的中断机制

在PCI中,中断是由电平触发,且低电平为有效信号。如果不采用MSI方式,一旦有需求,就会将INTx线路拉低,并保持低电平状态,直到请求结束。每个PCI设备都配备了四个IO口,即INTA#至INTD#,这些中断引脚连接到系统中的中断控制器,以产生中断。这种机制对于设备运行至关重要。以我的网卡设备为例,当它需要处理数据请求时,就会通过触发中断机制与系统进行交互。

合理运用中断机制能提升PCI设备的表现,若对这一机制不甚了解,在设备发生中断故障时,可能难以准确找到问题所在。

深入了解PCI设备驱动程序:原理、功能与应用

PCI总线的驱动设计在Linux系统下得到了较好的支持,简化了不少工作,但其中仍有许多细节需要我们特别留意。在开发PCI总线驱动时,你是否遇到过一些令人印象深刻的问题?欢迎点赞、分享这篇文章,并踊跃留言评论。

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

七爪网 行业资讯 深入了解PCI设备驱动程序:原理、功能与应用 https://www.7claw.com/2797147.html

七爪网源码交易平台

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务