PCI设备驱动程序「建议收藏」

PCI设备驱动程序「建议收藏」PCI总线是现在非常流行的计算机总线,学会它的驱动设计方法很重要。相信曾经想学习PCI总线驱动的人有这么一个经历,就是去看那些讲解PCI总线驱动的书籍和资料的时候,会被里面繁杂的内容所击败,又是什么配置空间又是什么枚举的,还没开始真正的去写PCI的驱动,到这里就已经开始打退堂鼓了。其实,只要你认真下去,虽然有些东西看不明白,但是对于你写PCI的驱动来说,似乎“不那么重要”。因为,Linux内核对P…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

版权声明:本文为博主原创文章,未经博主允许不得转载。
https://blog.csdn.net/huangweiqing80/article/details/83347495

一、PCI设备驱动编写

PCI总线是现在非常流行的计算机总线,学会它的驱动设计方法很重要。相信曾经想学习PCI总线驱动的人有这么一个经历,就是去看那些讲解PCI总线驱动的书籍和资料的时候,会被里面繁杂的内容所击败,又是什么配置空间又是什么枚举的,还没开始真正的去写PCI的驱动,到这里就已经开始打退堂鼓了。其实,只要你认真下去,虽然有些东西看不明白,但是对于你写PCI的驱动来说,似乎“不那么重要”。因为,Linux内核对PCI总线已经有了完美的支持,你所需要做的内容是非常小的一部份。

Linux下的PCI总线,在系统上电的时候会逐一的扫描系统中存在的设备(包括设备和桥),总线号中断号都是这个时候分配给设备的,如果你是初学者,这个过程如果不是很明白,你大可以先略过,去找一个带有PCI总线的开发板,接上PCI的设备,让系统重启扫描一遍,再配合下面会给出的PCI总线驱动框架,你就会明白很多。

众所周知,Linux 2.6内核引入了总线驱动模型这一概念,如此,很多基于总线的设备驱动就分成了总线驱动和设备驱动两部分。其实PCI总线驱动跟2.6内核里面的platform总线有类似之处,只不过platform总线的匹配方式是名字匹配,也就是设备名和驱动名一致。PCI总线匹配的是id_table;但匹配方式不只一种,最常见的就是厂商号和设备号。当你加载PCI驱动的时候,驱动程序会把系统中已经存在的设备的厂商号和设备号与驱动程序中的对比,如果一致,则会注册PCI总线驱动并进行下一步操作。

对于PCI总线上电扫描过程,推荐去看一篇博客,http://blog.csdn.net/linuxdrivers/article/details/5849698,他讲的详细一点。

下面是我写的一个PCI总线的驱动程序,注意是PCI设备识别时的驱动程序,这里并没有实现具体的功能驱动。PCI设备的驱动分成两个部分,一部分是总线的,就是PCI设备识别、调用驱动程序probe函数的部分,另一部分就是具体的功能驱动,比如网卡。基于PCI总线的设备有很多种,但就PCI总线驱动这一块来说,都大同小异,实现了PCI总线驱动之后,再去继续做具体的设备驱动。

程序如下(在2.6.31至3.1.4内核都可以运行成功):

#include <linux/module.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
 
//设备相关
#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 = %d\n",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");

Jetbrains全家桶1年46,售后保障稳定

以上这个程序是我在开发板中插入了一个PCIE的网卡设备,系统重启之后,加载这个驱动模块,就会进行注册驱动等一系列的操作。
加载模块后的结果:

[root@board /] insmod ar9280.ko

Probe succeeds.PCIE ioport addr start at 98000000, mypci->ioaddr is 0xd4fa0000,interrupt No.17.

看到上面Probe 成功,说明系统找到了我的网卡,98000000正是系统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中的中断

下面来讲一下PCI中断:
首先看一下pci 设备的pin list
在这里插入图片描述

扯点题外话,里面大部分信号是低电平有效。据说是因为低电平阻抗低,抗干扰能力强。

可以看到,它有四个中断pin,但是它是放在右边作为optional 的。

在PCI 里面,中断是电平触发的,低电平有效,如果不是走MSI方式,当Device 有需要的时候,Device driver 会去拉低INTx line. 一旦这个信号被拉低,它会持续为低,直到Driver 没有了pending 请求。如果是单功能设备,那么只需要用到INT A,多功能设备可以把INT A, B, C ,D 都用完。

对于多功能设备而言,上的的逻辑设备可以使用A, B, C ,D 中的任何一根。

从上面我们可以看出,每个PCI设备都含有四个IO口INTA# – INTD#,设备的中断引脚( INTA# – INTD#)会连接到系统中断控制器的引脚(1RQO – IRQ15中)上去,这样当INTA# – INTD#引脚拉低时,就相当于把连接到中断控制器中的中断引脚拉低了,从而产生中断。

关于PCI中断还可以参考:
https://blog.csdn.net/shanghaiqianlun/article/details/7100825?utm_source=blogxgwz9

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/209845.html原文链接:https://javaforall.net

(0)
上一篇 2025年7月31日 下午3:43
下一篇 2025年7月31日 下午4:15


相关推荐

  • 深入理解sun.misc.Unsafe原理

    深入理解sun.misc.Unsafe原理前言 Unsafe 类在 JDK 源码中被广泛使用 在 Spark 使用 off heapmemory 时也会使用到 该类功能很强大 涉及到类加载机制 深入理解 ClassLoader 工作机制 其实例一般情况是获取不到的 源码中的设计是采用单例模式 不是系统加载初始化就会抛出 SecurityExce 异常 这个类的提供了一些绕开 JVM 的更底层功能 基于它的实现可以提高效率 但是 它是一把双刃剑 正如

    2026年3月19日
    2
  • kali linux破解wifi密码-超详细过程

    前期准备VMwareWorkstation虚拟机在虚拟机安装好kaillinunx系统无线网卡(芯片为3070或者1887L都支持Linux)一.打开终端,用airmon-ng命令查看如果出现无法访问…没有那个文件或目录解决办法:先在window主机上按win+r键然后输入services.msc回车找到VMwareUSBArbitrationService,双击…

    2022年4月8日
    1.6K
  • 使用命令查看Linux内核版本的三种方法

    使用命令查看Linux内核版本的三种方法想知道您的系统使用哪个 Linux 内核版本吗 以下是在 Linux 终端中检查内核版本的几种方法 当您需要了解系统上正在使用的确切 Linux 内核版本时 您可能会遇到这种情况 借助功能强大的 Linux 命令行 您可以轻松地找到答案 如何找到 Linux 内核版本在撰写本文时 我正在使用 Ubuntu18 04 但是这些命令是通用的 可以在 Fedora Debian CentOS SUSELin

    2025年6月13日
    7
  • pycharm conmunity 2022.1没有mange repositories,只能使用命令方式修改镜像源(长期可信)

    pycharm conmunity 2022.1没有mange repositories,只能使用命令方式修改镜像源(长期可信)修改为清华大学的镜像源 并置为可信 阿里云的好像有点问题但不知到出在哪里 pipconfigset index urlhttps pypi tuna tsinghua edu cn simpletruste hostpypi tuna tsinghua edu cnPSD Python project Cycle Gan gt pipconfigset index urlhttps pypi tuna tsinghua edu cn

    2026年3月27日
    3
  • 2021年最好用&完全免费的图片压缩网站、软件推荐(包括GIF)

    2021年最好用&完全免费的图片压缩网站、软件推荐(包括GIF)最近我有遇到一个很奇怪的问题因为我不是转用AppleMusic本地化听歌了????所以很多歌的歌曲信息都是我自己补充的,当然也包括封面但我在用iTunes把歌传到iPhone上来听的时候,有首歌的封面怎么都同步不过来我来回同步了几遍,还重新连接了几次,甚至换回了有线来同步,这个封面始终都还是同步不上…我就一直奇了怪了直到我想重新编辑一下封面,重新添加,我才发现…好家伙,一张封面竟然有18M!?比我MP3本身都要大了,难怪我添加不上呢完全被它小小的外表给欺骗了我后来把图片

    2022年6月18日
    30
  • Tiled 编辑地形后 输出简化

    Tiled 编辑地形后 输出简化做 2d 游戏 用 Tiled 编辑地形信息 另存为 json 后信息太复杂 这里简单将地形输出 第一个是 html 文件浏览器打开将 tiled 输出的 json 导入 会自动生成一个原文件名 Cut json 的文件里面只有地形 想另行定义就改红色字体部分即可 lt doctypehtml gt lt htmllang en gt lt head gt nbsp nbsp lt me

    2026年3月17日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号