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

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 语义分割 实例分割 全景分割_语义分割转实例分割

    语义分割 实例分割 全景分割_语义分割转实例分割之前看过一篇使用分割思想进行目标检测,所以这里补习下一些分割相关的基础知识。这里重点说下语义分割、实力分割和全景分割的区别。1、semanticsegmentation(语义分割)通常意义上的目标分割指的就是语义分割,图像语义分割,简而言之就是对一张图片上的所有像素点进行分类语义分割(下图左)就是需要区分到图中每一点像素点,而不仅仅是矩形框框住了。但是同一物体的不同实例不需要单独分…

  • python中矩阵的转置怎么写_Python 矩阵转置的几种方法小结

    python中矩阵的转置怎么写_Python 矩阵转置的几种方法小结我就废话不多说了,直接上代码吧!#Python的matrix转置matrix=[[1,2,3,4],[5,6,7,8],[9,10,11,12]]defprintmatrix(m):foreleinm:foriinele:print(“%2d”%i,end=””)print()#1、利用元祖的特性进行转置deftransformMatrix(m):#此处巧妙的先按照传递…

  • navicat 链接不到虚拟机(ubuntu)的mysql

    navicat 链接不到虚拟机(ubuntu)的mysqlnavicat 链接不到虚拟机(ubuntu)的mysql

  • Web Visibilitychange

    Web Visibilitychangevisibilitychange(vc贝雷忒change)浏览器标签页被隐藏或显示的时候会触发visibilitychange事件. visible:当前页面可见.即此页面在前景标签页中,并且窗口没有最小化. hidden:页面对用户不可见.即文档处于背景标签页或者窗口处于最小化状态,或者操作系统正处于’锁屏状态’. prerender:页面此时正在渲染中,因此是不可见的.文档只能从此状态开始,永远不能从其他值变为此状态.注意:浏览器支持是可选的. unloaded

  • Docker方式安装showdoc

    Docker方式安装showdoc

  • anycast隧道_一种以anycast方式部署的CDN监控可用性的方法与流程

    anycast隧道_一种以anycast方式部署的CDN监控可用性的方法与流程本发明涉及一种CDN节点监控可用性的方法,具体涉及一种以anycast方式部署的CDN监控可用性的方法。背景技术:现有CDN可用性的检测方法,一般通过一些探测点检测目的CDN的可用性,在探测到失败时,认为目的CDN不可用;对于以anycast方式部署的CDN监控,得到的结果是不准确的;因为多个探测点发出的探测数据不一定到达了所有的目的CDN;导致探测数据未到达的CDN实际上没有被监控到。技术实现要…

发表回复

您的电子邮箱地址不会被公开。

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