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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 集合和数组相互转换[通俗易懂]

    集合和数组相互转换[通俗易懂]集合转数组【强制】使用集合转数组的方法,必须使用集合的toArray(T[]array),传入的是类型完全一样的数组,大小就是list.size()。说明:使用toArray带参方法,入参分配的数组空间不够大时,toArray方法内部将重新分配内存空间,并返回新数组地址;如果数组元素个数大于实际所需,下标为[list.size()]的数组元素将被置为…

    2022年6月29日
    26
  • Java大数据方向学习路径导航[通俗易懂]

    Java大数据方向学习路径导航[通俗易懂]Java8各技术栈笔记与相关文档链接

    2022年5月27日
    29
  • servlet和jsp中的Request转发,重定向有何区别_jsp重定向和转发的区别

    servlet和jsp中的Request转发,重定向有何区别_jsp重定向和转发的区别转发request.getRequestDispatcher("/index.jsp").forward(request,response);System.out.println("被执行了");index.jsp&lt;html&gt;&lt;body&gt;&lt;h2&gt;HelloWorld!&lt;/h2&gt;&lt;/body

    2022年9月7日
    0
  • 60道逻辑推理题及答案[通俗易懂]

    60道逻辑推理题及答案[通俗易懂]作者:billy版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处前言程序员面试题中经常会出现一些烧脑逻辑题,本人觉得这种题目非常有趣,于是收集了一些分享给大家!【1】假设有一个池塘,里面有无穷多的水。现有2个空水壶,容积分别为5升和6升。问题是如何只用这2个水壶从池塘里取得3升的水。由满6向空5倒,剩1升,把这1升倒5里,然后6剩满,倒5里面,由于5里面有1…

    2022年4月18日
    80
  • navicat premium 激活码【永久激活】[通俗易懂]

    (navicat premium 激活码)最近有小伙伴私信我,问我这边有没有免费的intellijIdea的激活码,然后我将全栈君台教程分享给他了。激活成功之后他一直表示感谢,哈哈~https://javaforall.net/100143.htmlIntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,上面是详细链接哦~MLZP…

    2022年3月21日
    574
  • MySQL基金会-基本数据库操作

    MySQL基金会-基本数据库操作

    2021年12月30日
    44

发表回复

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

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