PCI驱动编程基本框架文档格式.docx

上传人:b****5 文档编号:21099108 上传时间:2023-01-27 格式:DOCX 页数:16 大小:25.91KB
下载 相关 举报
PCI驱动编程基本框架文档格式.docx_第1页
第1页 / 共16页
PCI驱动编程基本框架文档格式.docx_第2页
第2页 / 共16页
PCI驱动编程基本框架文档格式.docx_第3页
第3页 / 共16页
PCI驱动编程基本框架文档格式.docx_第4页
第4页 / 共16页
PCI驱动编程基本框架文档格式.docx_第5页
第5页 / 共16页
点击查看更多>>
下载资源
资源描述

PCI驱动编程基本框架文档格式.docx

《PCI驱动编程基本框架文档格式.docx》由会员分享,可在线阅读,更多相关《PCI驱动编程基本框架文档格式.docx(16页珍藏版)》请在冰豆网上搜索。

PCI驱动编程基本框架文档格式.docx

unsignedint(*poll)(structfile*,structpoll_table_struct*);

long(*unlocked_ioctl)(structfile*,unsignedint,unsignedlong);

long(*compat_ioctl)(structfile*,unsignedint,unsignedlong);

int(*mmap)(structfile*,structvm_area_struct*);

int(*open)(structinode*,structfile*);

int(*flush)(structfile*,fl_owner_tid);

int(*release)(structinode*,structfile*);

int(*fsync)(structfile*,loff_t,loff_t,intdatasync);

int(*aio_fsync)(structkiocb*,intdatasync);

int(*fasync)(int,structfile*,int);

int(*lock)(structfile*,int,structfile_lock*);

ssize_t(*sendpage)(structfile*,structpage*,int,size_t,loff_t*,int);

unsignedlong(*get_unmapped_area)(structfile*,unsignedlong,unsignedlong,unsignedlong,unsignedlong);

int(*check_flags)(int);

int(*flock)(structfile*,int,structfile_lock*);

ssize_t(*splice_write)(structpipe_inode_info*,structfile*,loff_t*,size_t,unsignedint);

ssize_t(*splice_read)(structfile*,loff_t*,structpipe_inode_info*,size_t,unsignedint);

int(*setlease)(structfile*,long,structfile_lock**);

long(*fallocate)(structfile*file,intmode,loff_toffset,

loff_tlen);

};

当应用程序对设备文件进行诸如open、close、read、write等操作时,Linux内核将通过file_operations结构访问驱动程序提供的函数。

例如,当应用程序对设备文件执行读操作时,内核将调用file_operations结构中的read函数。

3.设备驱动程序模块

Linux下的设备驱动程序可以按照两种方式进行编译,一种是直接静态编译成内核的一部分,另一种则是编译成可以动态加载的模块。

如果编译进内核的话,会增加内核的大小,还要改动内核的源文件,而且不能动态地卸载,不利于调试,所有推荐使用模块方式。

从本质上来讲,模块也是内核的一部分,它不同于普通的应用程序,不能调用位于用户态下的C或者C++库函数,而只能调用Linux内核提供的函数,在/proc/ksyms中可以查看到内核提供的所有函数。

在以模块方式编写驱动程序时,要实现两个必不可少的函数init_module()和cleanup_module(),而且至少要包含和两个头文件。

一般使用LDD3例程中使用的makefile作为基本的版本,稍作改变之后用来编译驱动,编译生成的模块(一般为.ko文件)可以使用命令insmod载入Linux内核,从而成为内核的一个组成部分,此时内核会调用模块中的函数init_module()。

当不需要该模块时,可以使用rmmod命令进行卸载,此进内核会调用模块中的函数cleanup_module()。

任何时候都可以使用命令来lsmod查看目前已经加载的模块以及正在使用该模块的用户数。

4.设备驱动程序结构

了解设备驱动程序的基本结构(或者称为框架),对开发人员而言是非常重要的,Linux的设备驱动程序大致可以分为如下几个部分:

驱动程序的注册与注销、设备的打开与释放、设备的读写操作、设备的控制操作、设备的中断和轮询处理。

驱动程序的注册与注销

向系统增加一个驱动程序意味着要赋予它一个主设备号,这可以通过在驱动程序的初始化过程中调用register_chrdev()或者register_blkdev()来完成。

而在关闭字符设备或者块设备时,则需要通过调用unregister_chrdev()或unregister_blkdev()从内核中注销设备,同时释放占用的主设备号。

但是现在

程序员都倾向于动态创建设备号和设备结点,动态创建设备号和设备结点需要几个指定的函数,具体

可以参见“Linux字符驱动中动态分配设备号与动态生成设备节点”。

设备的打开与释放

打开设备是通过调用file_operations结构中的函数open()来完成的,它是驱动程序用来为今后的操作完成初始化准备工作的。

在大部分驱动程序中,open()通常需要完成下列工作:

1.检查设备相关错误,如设备尚未准备好等。

2.如果是第一次打开,则初始化硬件设备。

3.识别次设备号,如果有必要则更新读写操作的当前位置指针f_ops。

4.分配和填写要放在file->

private_data里的数据结构。

5.使用计数增1。

释放设备是通过调用file_operations结构中的函数release()来完成的,这个设备方法有时也被称为close(),它的作用正好与open()相反,通常要完成下列工作:

1.使用计数减1。

2.释放在file->

private_data中分配的内存。

3.如果使用计算为0,则关闭设备。

设备的读写操作

字符设备的读写操作相对比较简单,直接使用函数read()和write()就可以了。

但如果是块设备的话,则需要调用函数block_read()和block_write()来进行数据读写,这两个函数将向设备请求表中增加读写请求,以便Linux内核可以对请求顺序进行优化。

由于是对内存缓冲区而不是直接对设备进行操作的,因此能很大程度上加快读写速度。

如果内存缓冲区中没有所要读入的数据,或者需要执行写操作将数据写入设备,那么就要执行真正的数据传输,这是通过调用数据结构blk_dev_struct中的函数request_fn()来完成的。

设备的控制操作

除了读写操作外,应用程序有时还需要对设备进行控制,这可以通过设备驱动程序中的函数ioctl()来完成,ioctl系统调用有下面的原型:

intioctl(intfd,unsignedlongcmd,...),第一个参数是文件描述符,第二个参数是具体的命令,一般使用宏定义来确定,第三个参数一般是传递给驱动中处理设备控制操作函数的参数。

ioctl()的用法与具体设备密切关联,因此需要根据设备的实际情况进行具体分析。

设备的中断和轮询处理对于不支持中断的硬件设备,读写时需要轮流查询设备状态,以便决定是否继续进行数据传输。

如果设备支持中断,则可以按中断方式进行操作。

基本框架

在用模块方式实现PCI设备驱动程序时,通常至少要实现以下几个部分:

初始化设备模块、设备打开模块、数据读写和控制模块、中断处理模块、设备释放模块、设备卸载模块。

下面给出一个典型的PCI设备驱动程序的基本框架,从中不难体会到这几个关键模块是如何组织起来的。

/*指明该驱动程序适用于哪一些PCI设备*/

staticstructpci_device_idmy_pci_tbl[]__initdata={

{PCI_VENDOR_ID,PCI_DEVICE_ID,PCI_ANY_ID,PCI_ANY_ID,0,0,0},

{0,}

/*对特定PCI设备进行描述的数据结构*/

structdevice_private{

...

}

/*中断处理模块*/

staticirqreturn_tdevice_interrupt(intirq,void*dev_id)

{

/*...*/}

/*设备文件操作接口*/

staticstructfile_operationsdevice_fops={

owner:

THIS_MODULE,/*demo_fops所属的设备模块*/read:

device_read,/*读设备操作*/write:

device_write,/*写设备操作*/ioctl:

device_ioctl,/*控制设备操作*/mmap:

device_mmap,/*内存重映射操作*/open:

device_open,/*打开设备操作*/release:

device_release/*释放设备操作*/

/*...*/};

/*设备模块信息*/

staticstructpci_drivermy_pci_driver={

name:

DEVICE_MODULE_NAME,/*设备模块名称*/id_table:

device_pci_tbl,/*能够驱动的设备列表*/probe:

device_probe,/*查找并初始化设备*/remove:

device_remove/*卸载设备模块*/

staticint__initinit_module(void)

staticvoid__exitcleanup_module(void)

pci_unregister_driver(&

my_pci_driver);

/*加载驱动程序模块入口*/module_init(init_module);

/*卸载驱动程序模块入口*/module_exit(cleanup_module);

上面这段代码给出了一个典型的PCI设备驱动程序的框架,是一种相对固定的模式。

需要注意的是,同加载和卸载模块相关的函数或数据结构都要在前面加上__init、__exit等标志符,以使同普通函数区分开来。

构造出这样一个框架之后,接下去的工作就是如何完成框架内的各个功能模块了。

针对相应设备定义描述该PCI设备的数据结构:

structdevice_private

/*注册字符驱动和发现PCI设备的时候使用*/

structpci_dev*my_pdev;

//

structcdevmy_cdev;

dev_tmy_dev;

atomic_tcreated;

 

/*用于获取PCI设备配置空间的基本信息*/unsignedlongmmio_addr;

unsignedlongregs_len;

intirq;

//中断号

/*用于保存分配给PCI设备的内存空间的信息*/dma_addr_trx_dma_addrp;

dma_addr_ttx_dma_addrp;

/*基本的同步手段*/

spinlock_tlock_send;

spinlock_tlock_rev;

/*保存内存空间转换后的地址信息*/

void__iomem*ioaddr;

unsignedlongvirts_addr;

intopen_flag//设备打开标记

.....

初始化设备模块:

name:

DRV_NAME,//驱动的名字,一般是一个宏定义

id_table:

my_pci_tbl,//包含了相关物理PCI设备的基本信息,vendorID,deviceID等

probe:

pci_probe,//用于发现PCI设备

remove:

__devexit_p(pci_remove),//PCI设备的移除

//my_pci_tbl其实是一个structpci_device结构,该结构可以有很多项,每一项代表一个设备

//该结构可以包含很多项,每一项表明使用该结构的驱动支持的设备

//注意:

需要以一个空的项结尾,也就是:

staticstructpci_device_idmy_pci_tbl[]__initdata={

{vendor_id,device_id,PCI_ANY_ID,PCI_ANY_ID,0,0,0},

{0,}

staticint__initinit_module(void)

intresult;

printk(KERN_INFO"

my_pci_driverbuilton%s,%s\n"

__DATE__,__TIME__);

result=pci_register_driver(&

my_pci_driver);

//注册设备驱动

if(result)

returnresult;

return0;

卸载设备模块:

staticvoid__devexitmy_pci_remove(structpci_dev*pci_dev)

structdevice_private*private;

private=(structdevice_private*)pci_get_drvdata(pci_dev);

printk("

FCswitch->

irq=%d\n"

private->

irq);

//register_w32是封装的宏,便于直接操作

//#defineregister_w32(reg,val32)iowrite32((val32),device_private->

ioaddr+(reg))

//这里的作用是关中断,硬件复位

register_w32(IntrMask,0x00000001);

register_w32(Reg_reset,0x00000001);

//移除动态创建的设备号和设备

device_destroy(device_class,device->

my_dev);

class_destroy(device_class);

cdev_del(&

private->

my_cdev);

unregister_chrdev_region(priv->

my_dev,1);

//清理用于映射到用户空间的内存页面

for(private->

virts_addr=(unsignedlong)private->

rx_buf_virts;

virts_addr<

(unsignedlong)private->

rx_buf_virts+BUF_SIZE;

virts_addr+=PAGE_SIZE)

{

ClearPageReserved(virt_to_page(FCswitch->

virts_addr));

}

...

//释放分配的内存空间

pci_free_consistent(private->

my_pdev,BUF_SIZE,private->

rx_buf_virts,private->

rx_dma_addrp);

...

free_irq(private->

irq,private);

iounmap(private->

ioaddr);

pci_release_regions(pci_dev);

kfree(private);

pci_set_drvdata(pci_dev,NULL);

pci_disable_device(pci_dev);

//总之模块卸载函数的职责就是释放一切分配过的资源,根据自己代码的需要进行具体的操作

PCI设备的探测(probe):

staticint__devinitpci_probe(structpci_dev*pci_dev,conststructpci_device_id*pci_id)

unsignedlongmmio_start;

unsignedlongmmio_end;

unsignedlongmmio_flags;

unsignedlongmmio_len;

void__iomem*ioaddr1=NULL;

probefunctionisrunning\n"

);

/*启动PCI设备*/

if(pci_enable_device(pci_dev))

printk(KERN_ERR"

%s:

cannotenabledevice\n"

pci_name(pci_dev));

return-ENODEV;

printk("

enabledevice\n"

/*在内核空间中动态申请内存*/

if((private=kmalloc(sizeof(structdevice_private),GFP_KERNEL))==NULL)

pci_demo:

outofmemory\n"

return-ENOMEM;

memset(private,0,sizeof(*private));

private->

my_pdev=pci_dev;

mmio_start=pci_resource_start(pci_dev,0);

mmio_end=pci_resource_end(pci_dev,0);

mmio_flags=pci_resource_flags(pci_dev,0);

mmio_len=pci_resource_len(pci_dev,0);

mmio_startis0x%0x\n"

(unsignedint)mmio_start);

mmio_lenis0x%0x\n"

(unsignedint)mmio_len);

if(!

(mmio_flags&

IORESOURCE_MEM))

cannotfindproperPCIdevicebaseaddress,aborting.\n"

result=-ENODEV;

gotoerr_out;

/*对PCI区进行标记,标记该区域已经分配出去*/result=pci_request_regions(pci_dev,DEVICE_NAME);

/*设置成总线主DMA模式*/pci_set_master(pci_dev);

/*ioremap重映射一个物理地址范围到处理器的虚拟地址空间,使它对内核可用.*/

ioaddr1=ioremap(m

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 小学教育 > 英语

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1