国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

PCI驅(qū)動(dòng)編程基本框架

2019-11-10 17:01:47
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

linux將所有外部設(shè)備看成是一類(lèi)特殊文件,稱(chēng)之為“設(shè)備文件”,如果說(shuō)系統(tǒng)調(diào)用是Linux內(nèi)核和應(yīng)用程序之間的接口,那么設(shè)備驅(qū)動(dòng)程序則可以看成是 Linux內(nèi)核與外部設(shè)備之間的接口。設(shè)備驅(qū)動(dòng)程序向應(yīng)用程序屏蔽了硬件在實(shí)現(xiàn)上的細(xì)節(jié),使得應(yīng)用程序可以像操作普通文件一樣來(lái)操作外部設(shè)備。

1. 字符設(shè)備和塊設(shè)備

Linux抽象了對(duì)硬件的處理,所有的硬件設(shè)備都可以像普通文件一樣來(lái)看待:它們可以使用和操作文件相同的、標(biāo)準(zhǔn)的系統(tǒng)調(diào)用接口來(lái)完成打開(kāi)、關(guān)閉、讀寫(xiě)和 I/O控制操作,而驅(qū)動(dòng)程序的主要任務(wù)也就是要實(shí)現(xiàn)這些系統(tǒng)調(diào)用函數(shù)。Linux系統(tǒng)中的所有硬件設(shè)備都使用一個(gè)特殊的設(shè)備文件來(lái)表示,例如,系統(tǒng)中的第 一個(gè)IDE硬盤(pán)使用/dev/hda表示。每個(gè)設(shè)備文件對(duì)應(yīng)有兩個(gè)設(shè)備號(hào):一個(gè)是主設(shè)備號(hào),標(biāo)識(shí)該設(shè)備的種類(lèi),也標(biāo)識(shí)了該設(shè)備所使用的驅(qū)動(dòng)程序;另一個(gè)是 次設(shè)備號(hào),標(biāo)識(shí)使用同一設(shè)備驅(qū)動(dòng)程序的不同硬件設(shè)備。設(shè)備文件的主設(shè)備號(hào)必須與設(shè)備驅(qū)動(dòng)程序在登錄該設(shè)備時(shí)申請(qǐng)的主設(shè)備號(hào)一致,否則用戶(hù)進(jìn)程將無(wú)法訪問(wèn)到 設(shè)備驅(qū)動(dòng)程序。

在Linux操作系統(tǒng)下有兩類(lèi)主要的設(shè)備文件:一類(lèi)是字符設(shè)備,另一類(lèi)則是塊設(shè)備。字符設(shè)備是以字節(jié)為單位逐個(gè)進(jìn)行I/O操作的設(shè)備,在對(duì)字符設(shè)備發(fā)出讀 寫(xiě)請(qǐng)求時(shí),實(shí)際的硬件I/O緊接著就發(fā)生了,一般來(lái)說(shuō)字符設(shè)備中的緩存是可有可無(wú)的,而且也不支持隨機(jī)訪問(wèn)。塊設(shè)備則是利用一塊系統(tǒng)內(nèi)存作為緩沖區(qū),當(dāng)用 戶(hù)進(jìn)程對(duì)設(shè)備進(jìn)行讀寫(xiě)請(qǐng)求時(shí),驅(qū)動(dòng)程序先查看緩沖區(qū)中的內(nèi)容,如果緩沖區(qū)中的數(shù)據(jù)能滿足用戶(hù)的要求就返回相應(yīng)的數(shù)據(jù),否則就調(diào)用相應(yīng)的請(qǐng)求函數(shù)來(lái)進(jìn)行實(shí)際 的I/O操作。塊設(shè)備主要是針對(duì)磁盤(pán)等慢速設(shè)備設(shè)計(jì)的,其目的是避免耗費(fèi)過(guò)多的CPU時(shí)間來(lái)等待操作的完成。一般說(shuō)來(lái),PCI卡通常都屬于字符設(shè)備。

2.設(shè)備驅(qū)動(dòng)程序接口

Linux中的I/O子系統(tǒng)向內(nèi)核中的其他部分提供了一個(gè)統(tǒng)一的標(biāo)準(zhǔn)設(shè)備接口,這是通過(guò)include/linux/fs.h中的數(shù)據(jù)結(jié)構(gòu)file_Operations來(lái)完成的:

struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *, fl_owner_t id); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, loff_t, loff_t, int datasync); int (*aio_fsync) (struct kiocb *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*check_flags)(int); int (*flock) (struct file *, int, struct file_lock *); ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); int (*setlease)(struct file *, long, struct file_lock **); long (*fallocate)(struct file *file, int mode, loff_t offset,loff_t len);};

當(dāng)應(yīng)用程序?qū)υO(shè)備文件進(jìn)行諸如open、close、read、write等操作時(shí),Linux內(nèi)核將通過(guò)file_operations結(jié)構(gòu)訪問(wèn)驅(qū)動(dòng)程 序提供的函數(shù)。例如,當(dāng)應(yīng)用程序?qū)υO(shè)備文件執(zhí)行讀操作時(shí),內(nèi)核將調(diào)用file_operations結(jié)構(gòu)中的read函數(shù)。

3.設(shè)備驅(qū)動(dòng)程序模塊

Linux下的設(shè)備驅(qū)動(dòng)程序可以按照兩種方式進(jìn)行編譯,一種是直接靜態(tài)編譯成內(nèi)核的一部分,另一種則是編譯成可以動(dòng)態(tài)加載的模塊。如果編譯進(jìn)內(nèi)核的話,會(huì)增加內(nèi)核的大小,還要改動(dòng)內(nèi)核的源文件,而且不能動(dòng)態(tài)地卸載,不利于調(diào)試,所有推薦使用模塊方式。

從本質(zhì)上來(lái)講,模塊也是內(nèi)核的一部分,它不同于普通的應(yīng)用程序,不能調(diào)用位于用戶(hù)態(tài)下的C或者C++庫(kù)函數(shù),而只能調(diào)用Linux內(nèi)核提供的函數(shù),在/PRoc/ksyms中可以查看到內(nèi)核提供的所有函數(shù)。

在以模塊方式編寫(xiě)驅(qū)動(dòng)程序時(shí),要實(shí)現(xiàn)兩個(gè)必不可少的函數(shù)init_module( )和cleanup_module( ),而且至少要包含和兩 個(gè)頭文件。一般使用LDD3 例程中使用的makefile 作為基本的版本,稍作改變之后用來(lái)編譯驅(qū)動(dòng),編譯生成的模塊(一般為.ko文件)可以使用命令insmod載入Linux內(nèi)核,從而成為內(nèi)核的一個(gè)組成部分,此時(shí)內(nèi)核會(huì)調(diào)用 模塊中的函數(shù)init_module( )。當(dāng)不需要該模塊時(shí),可以使用rmmod命令進(jìn)行卸載,此進(jìn)內(nèi)核會(huì)調(diào)用模塊中的函數(shù)cleanup_module( )。任何時(shí)候都可以使用命令來(lái)lsmod查看目前已經(jīng)加載的模塊以及正在使用該模塊的用戶(hù)數(shù)。

4.設(shè)備驅(qū)動(dòng)程序結(jié)構(gòu)

了解設(shè)備驅(qū)動(dòng)程序的基本結(jié)構(gòu)(或者稱(chēng)為框架),對(duì)開(kāi)發(fā)人員而言是非常重要的,Linux的設(shè)備驅(qū)動(dòng)程序大致可以分為如下幾個(gè)部分:驅(qū)動(dòng)程序的注冊(cè)與注銷(xiāo)、設(shè)備的打開(kāi)與釋放、設(shè)備的讀寫(xiě)操作、設(shè)備的控制操作、設(shè)備的中斷和輪詢(xún)處理。

驅(qū)動(dòng)程序的注冊(cè)與注銷(xiāo)

向系統(tǒng)增加一個(gè)驅(qū)動(dòng)程序意味著要賦予它一個(gè)主設(shè)備號(hào),這可以通過(guò)在驅(qū)動(dòng)程序的初始化過(guò)程中調(diào)用register_chrdev( )或者register_blkdev( )來(lái)完成。而在關(guān)閉字符設(shè)備或者塊設(shè)備時(shí),則需要通過(guò)調(diào)用unregister_chrdev( )或unregister_blkdev( )從內(nèi)核中注銷(xiāo)設(shè)備,同時(shí)釋放占用的主設(shè)備號(hào)。但是現(xiàn)在 程序員都傾向于動(dòng)態(tài)創(chuàng)建設(shè)備號(hào)和設(shè)備結(jié)點(diǎn),動(dòng)態(tài)創(chuàng)建設(shè)備號(hào)和設(shè)備結(jié)點(diǎn)需要幾個(gè)指定的函數(shù),具體 可以參見(jiàn)“Linux字符驅(qū)動(dòng)中動(dòng)態(tài)分配設(shè)備號(hào)與動(dòng)態(tài)生成設(shè)備節(jié)點(diǎn)”。

設(shè)備的打開(kāi)與釋放

打開(kāi)設(shè)備是通過(guò)調(diào)用file_operations結(jié)構(gòu)中的函數(shù)open( )來(lái)完成的,它是驅(qū)動(dòng)程序用來(lái)為今后的操作完成初始化準(zhǔn)備工作的。在大部分驅(qū)動(dòng)程序中,open( )通常需要完成下列工作:

1.檢查設(shè)備相關(guān)錯(cuò)誤,如設(shè)備尚未準(zhǔn)備好等。

2.如果是第一次打開(kāi),則初始化硬件設(shè)備。

3.識(shí)別次設(shè)備號(hào),如果有必要?jiǎng)t更新讀寫(xiě)操作的當(dāng)前位置指針f_ops。

4.分配和填寫(xiě)要放在file->private_data里的數(shù)據(jù)結(jié)構(gòu)。

5.使用計(jì)數(shù)增1。

釋放設(shè)備是通過(guò)調(diào)用file_operations結(jié)構(gòu)中的函數(shù)release( )來(lái)完成的,這個(gè)設(shè)備方法有時(shí)也被稱(chēng)為close( ),它的作用正好與open( )相反,通常要完成下列工作:

1.使用計(jì)數(shù)減1。

2.釋放在file->private_data中分配的內(nèi)存。

3.如果使用計(jì)算為0,則關(guān)閉設(shè)備。

設(shè)備的讀寫(xiě)操作

字符設(shè)備的讀寫(xiě)操作相對(duì)比較簡(jiǎn)單,直接使用函數(shù)read( )和write( )就可以了。但如果是塊設(shè)備的話,則需要調(diào)用函數(shù)block_read( )和block_write( )來(lái)進(jìn)行數(shù)據(jù)讀寫(xiě),這兩個(gè)函數(shù)將向設(shè)備請(qǐng)求表中增加讀寫(xiě)請(qǐng)求,以便Linux內(nèi)核可以對(duì)請(qǐng)求順序進(jìn)行優(yōu)化。由于是對(duì)內(nèi)存緩沖區(qū)而不是直接對(duì)設(shè)備進(jìn)行操作 的,因此能很大程度上加快讀寫(xiě)速度。如果內(nèi)存緩沖區(qū)中沒(méi)有所要讀入的數(shù)據(jù),或者需要執(zhí)行寫(xiě)操作將數(shù)據(jù)寫(xiě)入設(shè)備,那么就要執(zhí)行真正的數(shù)據(jù)傳輸,這是通過(guò)調(diào)用 數(shù)據(jù)結(jié)構(gòu)blk_dev_struct中的函數(shù)request_fn( )來(lái)完成的。

設(shè)備的控制操作

除了讀寫(xiě)操作外,應(yīng)用程序有時(shí)還需要對(duì)設(shè)備進(jìn)行控制,這可以通過(guò)設(shè)備驅(qū)動(dòng)程序中的函數(shù)ioctl( )來(lái)完成,ioctl 系統(tǒng)調(diào)用有下面的原型: int ioctl(int fd, unsigned long cmd, …),第一個(gè)參數(shù)是文件描述符,第二個(gè)參數(shù)是具體的命令,一般使用宏定義來(lái)確定,第三個(gè)參數(shù)一般是傳遞給驅(qū)動(dòng)中處理設(shè)備控制操作函數(shù)的參數(shù)。ioctl( )的用法與具體設(shè)備密切關(guān)聯(lián),因此需要根據(jù)設(shè)備的實(shí)際情況進(jìn)行具體分析。

設(shè)備的中斷和輪詢(xún)處理對(duì)于不支持中斷的硬件設(shè)備,讀寫(xiě)時(shí)需要輪流查詢(xún)?cè)O(shè)備狀態(tài),以便決定是否繼續(xù)進(jìn)行數(shù)據(jù)傳輸。如果設(shè)備支持中斷,則可以按中斷方式進(jìn)行操作。

5.基本框架

在用模塊方式實(shí)現(xiàn)PCI設(shè)備驅(qū)動(dòng)程序時(shí),通常至少要實(shí)現(xiàn)以下幾個(gè)部分:初始化設(shè)備模塊、設(shè)備打開(kāi)模塊、數(shù)據(jù)讀寫(xiě)和控制模塊、中斷處理模塊、設(shè)備釋放模塊、設(shè)備卸載模塊。下面給出一個(gè)典型的PCI設(shè)備驅(qū)動(dòng)程序的基本框架,從中不難體會(huì)到這幾個(gè)關(guān)鍵模塊是如何組織起來(lái)的。

/* 指明該驅(qū)動(dòng)程序適用于哪一些PCI設(shè)備 */static struct pci_device_id my_pci_tbl [] __initdata = {{PCI_VENDOR_ID, PCI_DEVICE_ID,PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},{0,}};/* 對(duì)特定PCI設(shè)備進(jìn)行描述的數(shù)據(jù)結(jié)構(gòu) */struct device_private {...}/* 中斷處理模塊 */static irqreturn_t device_interrupt(int irq, void *dev_id){/* ... */}/* 設(shè)備文件操作接口 */static struct file_operations device_fops = {owner: THIS_MODULE, /* demo_fops所屬的設(shè)備模塊 */read: device_read, /* 讀設(shè)備操作*/write: device_write, /* 寫(xiě)設(shè)備操作*/ioctl: device_ioctl, /* 控制設(shè)備操作*/mmap: device_mmap, /* 內(nèi)存重映射操作*/open: device_open, /* 打開(kāi)設(shè)備操作*/release: device_release /* 釋放設(shè)備操作*//* ... */};/* 設(shè)備模塊信息 */static struct pci_driver my_pci_driver = {name: DEVICE_MODULE_NAME, /* 設(shè)備模塊名稱(chēng) */id_table: device_pci_tbl, /* 能夠驅(qū)動(dòng)的設(shè)備列表 */probe: device_probe, /* 查找并初始化設(shè)備 */remove: device_remove /* 卸載設(shè)備模塊 *//* ... */};static int __init init_module (void){/* ... */}static void __exit cleanup_module (void){ pci_unregister_driver(&my_pci_driver);}module_init(init_module);/* 加載驅(qū)動(dòng)程序模塊入口 */module_exit(cleanup_module);/* 卸載驅(qū)動(dòng)程序模塊入口 */

上面這段代碼給出了一個(gè)典型的PCI設(shè)備驅(qū)動(dòng)程序的框架,是一種相對(duì)固定的模式。需要注意的是,同加載和卸載模塊相關(guān)的函數(shù)或數(shù)據(jù)結(jié)構(gòu)都要在前面加上 __init、__exit等標(biāo)志符,以使同普通函數(shù)區(qū)分開(kāi)來(lái)。構(gòu)造出這樣一個(gè)框架之后,接下去的工作就是如何完成框架內(nèi)的各個(gè)功能模塊了。 針對(duì)相應(yīng)設(shè)備定義描述該P(yáng)CI設(shè)備的數(shù)據(jù)結(jié)構(gòu):

struct device_private{ /*注冊(cè)字符驅(qū)動(dòng)和發(fā)現(xiàn)PCI設(shè)備的時(shí)候使用*/ struct pci_dev *my_pdev;// struct cdev my_cdev;// dev_t my_dev; atomic_t created; /* 用于獲取PCI設(shè)備配置空間的基本信息 */ unsigned long mmio_addr; unsigned long regs_len; int irq;//中斷號(hào) /*用于保存分配給PCI設(shè)備的內(nèi)存空間的信息*/ dma_addr_t rx_dma_addrp; dma_addr_t tx_dma_addrp; /*基本的同步手段*/ spinlock_t lock_send; spinlock_t lock_rev; /*保存內(nèi)存空間轉(zhuǎn)換后的地址信息*/ void __iomem *ioaddr; unsigned long virts_addr; int open_flag // 設(shè)備打開(kāi)標(biāo)記 .....};

初始化設(shè)備模塊:

static struct pci_driver my_pci_driver = { name: DRV_NAME, // 驅(qū)動(dòng)的名字,一般是一個(gè)宏定義 id_table: my_pci_tbl, //包含了相關(guān)物理PCI設(shè)備的基本信息,vendorID,deviceID等 probe: pci_probe, //用于發(fā)現(xiàn)PCI設(shè)備 remove: __devexit_p(pci_remove), //PCI設(shè)備的移除}; // my_pci_tbl 其實(shí)是一個(gè) struct pci_device 結(jié)構(gòu),該結(jié)構(gòu)可以有很多項(xiàng),每一項(xiàng)代表一個(gè)設(shè)備// 該結(jié)構(gòu)可以包含很多項(xiàng),每一項(xiàng)表明使用該結(jié)構(gòu)的驅(qū)動(dòng)支持的設(shè)備// 注意:需要以一個(gè)空的項(xiàng)結(jié)尾,也就是:{0,}static struct pci_device_id my_pci_tbl[] __initdata = { { vendor_id, device_id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { 0,}};static int __init init_module(void) { int result; printk(KERN_INFO "my_pci_driver built on %s, %s/n",__DATE__,__TIME__); result = pci_register_driver(&my_pci_driver ); //注冊(cè)設(shè)備驅(qū)動(dòng) if(result) return result; return 0;}

卸載設(shè)備模塊:

static void __devexit my_pci_remove(struct pci_dev *pci_dev){ struct device_private *private; private= (struct device_private*)pci_get_drvdata(pci_dev); printk("FCswitch->irq = %d/n",private->irq); // register_w32 是封裝的宏,便于直接操作 // #define register_w32 (reg, val32) iowrite32 ((val32), device_private->ioaddr + (reg)) // 這里的作用是關(guān)中斷,硬件復(fù)位 register_w32(IntrMask,0x00000001); register_w32(Reg_reset,0x00000001); // 移除動(dòng)態(tài)創(chuàng)建的設(shè)備號(hào)和設(shè)備 device_destroy(device_class, device->my_dev); class_destroy(device_class); cdev_del(&private->my_cdev); unregister_chrdev_region(priv->my_dev,1); //清理用于映射到用戶(hù)空間的內(nèi)存頁(yè)面 for(private->virts_addr = (unsigned long)private->rx_buf_virts;private->virts_addr < (unsigned long)private->rx_buf_virts + BUF_SIZE;private->virts_addr += PAGE_SIZE) { ClearPageReserved(virt_to_page(FCswitch->virts_addr)); } ... // 釋放分配的內(nèi)存空間 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);}// 總之模塊卸載函數(shù)的職責(zé)就是釋放一切分配過(guò)的資源,根據(jù)自己代碼的需要進(jìn)行具體的操作

PCI設(shè)備的探測(cè)(probe):

static int __devinit pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id){ unsigned long mmio_start; unsigned long mmio_end; unsigned long mmio_flags; unsigned long mmio_len; void __iomem *ioaddr1=NULL; struct device_private *private; int result; printk("probe function is running/n"); /* 啟動(dòng)PCI設(shè)備 */ if(pci_enable_device(pci_dev)) { printk(KERN_ERR "%s:cannot enable device/n", pci_name(pci_dev)); return -ENODEV; } printk( "enable device/n"); /* 在內(nèi)核空間中動(dòng)態(tài)申請(qǐng)內(nèi)存 */ if((private= kmalloc(sizeof(struct device_private), GFP_KERNEL)) == NULL) { printk(KERN_ERR "pci_demo: out of memory/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); printk("mmio_start is 0x%0x/n",(unsigned int)mmio_start); printk("mmio_len is 0x%0x/n",(unsigned int)mmio_len); if(!(mmio_flags & IORESOURCE_MEM)) { printk(KERN_ERR "cannot find proper PCI device base address, aborting./n"); result = -ENODEV; goto err_out; } /* 對(duì)PCI區(qū)進(jìn)行標(biāo)記 ,標(biāo)記該區(qū)域已經(jīng)分配出去*/ result = pci_request_regions(pci_dev, DEVICE_NAME); if(result) goto err_out; /* 設(shè)置成總線主DMA模式 */ pci_set_master(pci_dev); /*ioremap 重映射一個(gè)物理地址范圍到處理器的虛擬地址空間, 使它對(duì)內(nèi)核可用.*/ ioaddr1 = ioremap(mmio_start, mmio_len); if(ioaddr1 == NULL) { printk(KERN_ERR "%s:cannot remap mmio, aborting/n",pci_name(pci_dev)); result = -EIO; goto err_out; } printk("ioaddr1 = 0x%0x/n",(unsigned int)ioaddr1); private->ioaddr = ioaddr1; private->mmio_addr = mmio_start; private->regs_len = mmio_len; private->irq = pci_dev->irq; printk("irq is %d/n",pci_dev->irq); /* 初始化自旋鎖 */ spin_lock_init(&private->lock_send); spin_lock_init(&private->lock_rev); if(my_register_chrdev(private)) {//注:這里的注冊(cè)字符設(shè)備,類(lèi)似于前面的文章中介紹過(guò)的動(dòng)態(tài)創(chuàng)建設(shè)備號(hào)和動(dòng)態(tài)生成設(shè)備結(jié)點(diǎn) printk("chrdev register fail/n"); goto err_out; } //下面這兩個(gè)函數(shù)根據(jù)具體的硬件來(lái)處理,主要就是內(nèi)存分配、對(duì)硬件進(jìn)行初始化設(shè)置等 device_init_buf(xx_device);//這個(gè)函數(shù)主要進(jìn)行內(nèi)存分配,內(nèi)存映射,獲取中斷 device_hw_start(xx_device);//這個(gè)函數(shù)主要是往寄存器中寫(xiě)一些值,復(fù)位硬件,開(kāi)中斷,打開(kāi)DMA等 //把設(shè)備指針地址放入PCI設(shè)備中的設(shè)備指針中,便于后面調(diào)用pci_get_drvdata pci_set_drvdata(pci_dev, FCswitch); return 0;err_out: printk("error process/n"); resource_cleanup_dev(FCswitch); //如果出現(xiàn)任何問(wèn)題,釋放已經(jīng)分配了的資源 return result;}// probe函數(shù)的作用就是啟動(dòng)pci設(shè)備,讀取配置空間信息,進(jìn)行相應(yīng)的初始化

中斷處理: //中斷處理,主要就是讀取中斷寄存器,然后調(diào)用中斷處理函數(shù)來(lái)處理中斷的下半部分,一般通過(guò)tasklet或者workqueue來(lái)實(shí)現(xiàn) 注意:由于使用request_irq 獲得的中斷是共享中斷,因此在中斷處理函數(shù)的上半部需要區(qū)分是不是該設(shè)備發(fā)出的中斷,這就需要讀取中斷狀態(tài)寄存器的值來(lái)判斷,如果不是該設(shè)備發(fā)起的中斷則 返回 IRQ_NONE

static irqreturn_t device_interrupt(int irq, void *dev_id){ ...   if( READ(IntMask) == 0x00000001)   {      return IRQ_NONE;   }   WRITE(IntMask,0x00000001); tasklet_schedule(&my_tasklet); // 需要先申明tasklet 并關(guān)聯(lián)處理函數(shù) ... return IRQ_HANDLED;}// 聲明taskletstatic void my_tasklet_process(unsigned long unused);DECLARE_TASKLET(my_tasklet, my_tasklet_process, (unsigned long)&private);//第三個(gè)參數(shù)為傳遞給my_tasklet_process 函數(shù)的參數(shù)

設(shè)備驅(qū)動(dòng)的接口:

static struct file_operations device_fops = { owner: THIS_MODULE, open: device_open, //打開(kāi)設(shè)備 ioctl: device_ioctl, //設(shè)備控制操作 mmap: device_mmap, //內(nèi)存重映射操作 release: device_release, // 釋放設(shè)備};

打開(kāi)設(shè)備: open 方法提供給驅(qū)動(dòng)來(lái)做任何的初始化來(lái)準(zhǔn)備后續(xù)的操作. open 方法的原型是:

int (*open)(struct inode *inode, struct file *filp);

inode 參數(shù)有我們需要的信息,以它的 i_cdev 成員的形式, 里面包含我們之前建立的cdev 結(jié)構(gòu). 唯一的問(wèn)題是通常我們不想要 cdev 結(jié)構(gòu)本身, 我們需要的是包含 cdev 結(jié)構(gòu)的 device_private 結(jié)構(gòu).

static int device_open(struct inode *inode, struct file *filp){ struct device_private *private; private= container_of(inode->i_cdev, struct device_private, my_cdev); filp->private_data = private; private->open_flag++; try_module_get(THIS_MODULE); ... return 0;}

釋放設(shè)備: release 方法的角色是 open 的反面,設(shè)備方法應(yīng)當(dāng)進(jìn)行下面的任務(wù): ? 釋放 open 分配在 filp->private_data 中的任何東西 ? 在最后的 close 關(guān)閉設(shè)備

static int FCswitch_release(struct inode *inode,struct file *filp){ struct device_private *private= filp->private_data; private->open_flag--; module_put(THIS_MODULE); printk("pci device close success/n"); return 0;}

設(shè)備控制操作: PCI設(shè)備驅(qū)動(dòng)程序可以通過(guò)device_fops 結(jié)構(gòu)中的函數(shù)device_ioctl( ),向應(yīng)用程序提供對(duì)硬件進(jìn)行控制的接口。例如,通過(guò)它可以從I/O寄存器里讀取一個(gè)數(shù)據(jù),并傳送到用戶(hù)空間里。

static int device_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg){ int retval = 0; struct device_private *FCswitch = filp->private_data; switch (cmd) { case DMA_EN://DMA使能 device_w32(Dma_wr_en, arg); break; ... default: retval = -EINVAL; } return retval;}

內(nèi)存映射:

static int device_mmap(struct file *filp, struct vm_area_struct *vma){ int ret; struct device_private *private = filp->private_data; vma->vm_page_prot = PAGE_SHARED;//訪問(wèn)權(quán)限 vma->vm_pgoff = virt_to_phys(FCswitch->rx_buf_virts) >> PAGE_SHIFT;//偏移(頁(yè)幀號(hào)) ret = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, (unsigned long)(vma->vm_end-vma->vm_start), vma->vm_page_prot); if(ret!=0) return -EAGAIN; return 0;}

對(duì) remap_pfn_range()函數(shù)的說(shuō)明: remap_pfn_range()函數(shù)的原型:

int remap_pfn_range(struct vm_area_struct *vma, unsigned long virt_addr, unsigned long pfn, unsigned long size, pgprot_t prot);

該函數(shù)的功能是創(chuàng)建頁(yè)表。其中參數(shù)vma是內(nèi)核根據(jù)用戶(hù)的請(qǐng)求自己填寫(xiě)的,而參數(shù)addr表示內(nèi)存映射開(kāi)始處的虛擬地址,因此,該函數(shù)為addr~addr+size之間的虛擬地址構(gòu)造頁(yè)表。

另外,pfn(Page Fram Number)是虛擬地址應(yīng)該映射到的物理地址的頁(yè)面號(hào),實(shí)際上就是物理地址右移PAGE_SHIFT位。如果PAGE_SHIFT為4kb,則 PAGE_SHIFT為12,因?yàn)镻AGE_SHIFT等于1<< PAGE_SHIFT。 最后一個(gè)參數(shù)prot是新頁(yè)所要求的保護(hù)屬性。

在驅(qū)動(dòng)程序中,一般能使用remap_pfn_range()映射內(nèi)存中的保留頁(yè)(如X86系統(tǒng)中的640KB~1MB區(qū)域)和設(shè)備I/O內(nèi)存。因此,如 果想把kmalloc()申請(qǐng)的內(nèi)存映射到用戶(hù)空間,則可以通過(guò)SetPageReserved把相應(yīng)的內(nèi)存設(shè)置為保留后就可以。


上一篇:POJ 刷題順序

下一篇:leetcode1. Two Sum

發(fā)表評(píng)論 共有條評(píng)論
用戶(hù)名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 桃源县| 临武县| 晋宁县| 定南县| 古浪县| 尖扎县| 泰宁县| 祁阳县| 南漳县| 郎溪县| 泰州市| 内黄县| 泰兴市| 洛宁县| 五家渠市| 越西县| 莆田市| 绥阳县| 木兰县| 渝北区| 临汾市| 永定县| 肃宁县| 逊克县| 靖江市| 绥中县| 青冈县| 宜良县| 平邑县| 桐柏县| 南丰县| 呼伦贝尔市| 元氏县| 萝北县| 阳江市| 天镇县| 望都县| 富阳市| 托里县| 镇沅| 五家渠市|