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


相关推荐

  • 动态规划优缺点_巴西优化航空路线利用率

    动态规划优缺点_巴西优化航空路线利用率C 国有 n 个大城市和 m 条道路,每条道路连接这 n 个城市中的某两个城市。任意两个城市之间最多只有一条道路直接相连。这 m 条道路中有一部分为单向通行的道路,一部分为双向通行的道路,双向通行的道路在统计条数时也计为 1 条。C 国幅员辽阔,各地的资源分布情况各不相同,这就导致了同一种商品在不同城市的价格不一定相同。但是,同一种商品在同一个城市的买入价和卖出价始终是相同的。商人阿龙来到 C 国旅游。当他得知“同一种商品在不同城市的价格可能会不同”这一信息之后,便决定在旅游的同时,利用商品在

    2022年8月9日
    3
  • 在数组内删除指定元素_数组对象删除某一个指定对象

    在数组内删除指定元素_数组对象删除某一个指定对象一般来说,我们用数组这种数据结构最多的情况,是用来做查询,时间复杂度为O(1),那么在这里我们来看一下如何用在数组中插入元素和删除元素。数组的适用场景:一般在查询中,适用数组的情况是比较多,因为可以根据下标直接访问元素,时间复杂度是O(1),所以适用于读多写少的场景。数组的插入和删除元素,一般时间复杂度都是O(N),比较麻烦,所以对于插入和删除操作中,不建议选用数组这种数据结构,可以考虑链表…

    2022年8月10日
    6
  • java常量的定义

    java常量的定义在Java语言中,主要是利用final关键字来定义常量。当常量被设定后,一般情况下就不允许再进行更改。如可以利用如下的形式来定义一个常量:finaldoublePI=3.1315。在定义这个常量时,需要注意如下内容:一是常量在定义的时候,就需要对常量进行初始化。也就是说,必须要在常量声明时对其进行初始化。都跟局部变量或者成员变量不同。当在常量定义的时候初始化过后,在应用程序中就无法再次对这

    2022年7月8日
    23
  • 如何入侵网站数据库_数据库创建用户

    如何入侵网站数据库_数据库创建用户BrainCOSMOS:昨日在被黑客公布的csdn用户数据库中发现了自己的帐号和密码(还有各种大牛的技术博客帐号密码),立刻想到了自己其他帐号有没有其他帐号用相同密码。还好我的每个密码都有区分,不过今早听说人人网的信息也被发布出来了,顿时就火了,奉劝各位赶紧更改自己的密码,免得被人恶意使用!涉及到的网站有:人人网、U9网、百合网、开心网、天涯、世纪佳缘等一批知名网站具…

    2022年9月15日
    2
  • 出生率新低!1978-2020中国人口出生率、死亡率及自然增长率变迁「建议收藏」

    出生率新低!1978-2020中国人口出生率、死亡率及自然增长率变迁「建议收藏」根据国家统计局近日发布的《中国统计年鉴2021》,2020中国人口出生率为8.52‰,再创历史新低。同时,2020中国人口自然增长率仅为1.45‰,勉强维持正增长,同样是历史新低。下面的视…

    2025年9月13日
    20
  • BoundsChecker 使用方法[通俗易懂]

    1前言我在本文中详细介绍了测试工具NuMegaDevpartner(以下简称NuMega)的使用方法。NuMega是一个动态测试工具,主要应用于白盒测试。该工具的特点是学习简单、使用方便、功能有效。NuMega共有三个独立的子功能——BoundsChecker、TrueCoverage、TrueTime。BoundsChecker为代码检错工具,TrueCoverage为测试覆盖率统计…

    2022年4月8日
    59

发表回复

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

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