嵌入式驅(qū)動(dòng)程序_第1頁
嵌入式驅(qū)動(dòng)程序_第2頁
嵌入式驅(qū)動(dòng)程序_第3頁
嵌入式驅(qū)動(dòng)程序_第4頁
嵌入式驅(qū)動(dòng)程序_第5頁
已閱讀5頁,還剩12頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

Linux下PCI設(shè)備驅(qū)動(dòng)程序開發(fā)一、pci總線系統(tǒng)體系結(jié)構(gòu)pci是外圍設(shè)備互連(PeripheralComponentInterconnect)的簡(jiǎn)稱,作為一種通用的總線接口標(biāo)準(zhǔn),它在目前的計(jì)算機(jī)系統(tǒng)中得到了非常廣泛的應(yīng)用。pci提供了一組完整的總線接口規(guī)范,其目的是描述如何將計(jì)算機(jī)系統(tǒng)中的外圍設(shè)備以一種結(jié)構(gòu)化和可控化的方式連接在一起,同時(shí)它還刻畫了外圍設(shè)備在連接時(shí)的電氣特性和行為規(guī)約,并且詳細(xì)定義了計(jì)算機(jī)系統(tǒng)中的各個(gè)不同部件之間應(yīng)該如何正確地進(jìn)行交互。無論是在基于Intel芯片的PC機(jī)中,或是在基于Alpha芯片的工作站上,pci毫無疑問都是目前使用最廣泛的一種總線接口標(biāo)準(zhǔn)。同舊式的ISA總線不同,pci將計(jì)算機(jī)系統(tǒng)中的總線子系統(tǒng)與存儲(chǔ)子系統(tǒng)完全地分開,CPU通過一塊稱為pci橋(Pci-Bridge)的設(shè)備來完成同總線子系統(tǒng)的交互,如圖1所示。圖1pci子系統(tǒng)的體系結(jié)構(gòu)由于使用了更高的時(shí)鐘頻率,因此PCI總線能夠獲得比ISA總線更好的整體性能。PCI總線的時(shí)鐘頻率一般在25MHz到33MHz范圍內(nèi),有些甚至能夠達(dá)到66MHz或者133MHz,而在64位系統(tǒng)中則最高能達(dá)到266MHz。盡管目前pci設(shè)備大多采用32位數(shù)據(jù)總線,但PCI規(guī)范中已經(jīng)給出了64位的擴(kuò)展實(shí)現(xiàn),從而使PCI總線能夠更好地實(shí)現(xiàn)平臺(tái)無關(guān)性,現(xiàn)在pci總線已經(jīng)能夠用于IA-32、Alpha、PowerPC.SPARC64和IA-64等體系結(jié)構(gòu)中。PCI總線具有三個(gè)非常顯著的優(yōu)點(diǎn),使得它能夠完成最終取代ISA總線這一歷史使命:■在計(jì)算機(jī)和外設(shè)間傳輸數(shù)據(jù)時(shí)具有更好的性能;■能夠盡量獨(dú)立于具體的平臺(tái);■可以很方便地實(shí)現(xiàn)即插即用。圖2是一個(gè)典型的基于pci總線的計(jì)算機(jī)系統(tǒng)邏輯示意圖,系統(tǒng)的各個(gè)部分通過PCI總線和PCI-PCI橋連接在一起。從圖中不難看出,CPU和RAM需要通過pci橋連接到PCI總線。(即主PCI總線),而具有PCI接口的顯卡則可以直接連接到主PCI總線上。PCI-PCI橋是一個(gè)特殊的PCI設(shè)備,它負(fù)責(zé)將PCI總線0和PCI總線1(即從PCI主線)連接在一起,通常pci總線1稱為pci-pci橋的下游(downstream),而pci總線0則稱為pci-pci橋的上游(upstream)。圖中連接到從pci總線上的是SCSI卡和以太網(wǎng)卡。為了兼容舊的ISA總線標(biāo)準(zhǔn),pci總線還可以通過pci-ISA橋來連接ISA總線,從而能夠支持以前的ISA設(shè)備。圖中ISA總線上連接著一個(gè)多功能I/O控制器,用于控制鍵盤、鼠標(biāo)和軟驅(qū)。PCI系統(tǒng)示意圖在此我只對(duì)PCI總線系統(tǒng)體系結(jié)構(gòu)作了概括性介紹,如果讀者想進(jìn)一步了解,DavidARusling在TheLinuxKernel中對(duì)Linux的pci子系統(tǒng)有比較詳細(xì)的介紹。二、Linux驅(qū)動(dòng)程序框架Linux將所有外部設(shè)備看成是一類特殊文件,稱之為''設(shè)備文件”,如果說系統(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)用程序可以像操作普通文件一樣來操作外部設(shè)備。字符設(shè)備和塊設(shè)備Linux抽象了對(duì)硬件的處理,所有的硬件設(shè)備都可以像普通文件一樣來看待:它們可以使用和操作文件相同的、標(biāo)準(zhǔn)的系統(tǒng)調(diào)用接口來完成打開、關(guān)閉、讀寫和I/O控制操作,而驅(qū)動(dòng)程序的主要任務(wù)也就是要實(shí)現(xiàn)這些系統(tǒng)調(diào)用函數(shù)。Linux系統(tǒng)中的所有硬件設(shè)備都使用一個(gè)特殊的設(shè)備文件來表示,例如,系統(tǒng)中的第一個(gè)IDE硬盤使用/dev/hda表示。每個(gè)設(shè)備文件對(duì)應(yīng)有兩個(gè)設(shè)備號(hào):一個(gè)是主設(shè)備號(hào),標(biāo)識(shí)該設(shè)備的種類,也標(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)一致,否則用戶進(jìn)程將無法訪問到設(shè)備驅(qū)動(dòng)程序。在Linux操作系統(tǒng)下有兩類主要的設(shè)備文件:一類是字符設(shè)備,另一類則是塊設(shè)備。字符設(shè)備是以字節(jié)為單位逐個(gè)進(jìn)行I/O操作的設(shè)備,在對(duì)字符設(shè)備發(fā)出讀寫請(qǐng)求時(shí),實(shí)際的硬件I/O緊接著就發(fā)生了,一般來說字符設(shè)備中的緩存是可有可無的,而且也不支持隨機(jī)訪問。塊設(shè)備則是利用一塊系統(tǒng)內(nèi)存作為緩沖區(qū),當(dāng)用戶進(jìn)程對(duì)設(shè)備進(jìn)行讀寫請(qǐng)求時(shí),驅(qū)動(dòng)程序先查看緩沖區(qū)中的內(nèi)容,如果緩沖區(qū)中的數(shù)據(jù)能滿足用戶的要求就返回相應(yīng)的數(shù)據(jù),否則就調(diào)用相應(yīng)的請(qǐng)求函數(shù)來進(jìn)行實(shí)際的I/O操作。塊設(shè)備主要是針對(duì)磁盤等慢速設(shè)備設(shè)計(jì)的,其目的是避免耗費(fèi)過多的CPU時(shí)間來等待操作的完成。一般說來,PCI卡通常都屬于字符設(shè)備。所有已經(jīng)注冊(cè)(即已經(jīng)加載了驅(qū)動(dòng)程序)的硬件設(shè)備的主設(shè)備號(hào)可以從/proc/devices文件中得到。使用mknod命令可以創(chuàng)建指定類型的設(shè)備文件,同時(shí)為其分配相應(yīng)的主設(shè)備號(hào)和次設(shè)備號(hào)。例如,下面的命令:[root@garyroot]#mknod/dev/lp0c60將建立一個(gè)主設(shè)備號(hào)為6,次設(shè)備號(hào)為0的字符設(shè)備文件/dev/lp0。當(dāng)應(yīng)用程序?qū)δ硞€(gè)設(shè)備文件進(jìn)行系統(tǒng)調(diào)用時(shí),Linux內(nèi)核會(huì)根據(jù)該設(shè)備文件的設(shè)備類型和主設(shè)備號(hào)調(diào)用相應(yīng)的驅(qū)動(dòng)程序,并從用戶態(tài)進(jìn)入到核心態(tài),再由驅(qū)動(dòng)程序判斷該設(shè)備的次設(shè)備號(hào),最終完成對(duì)相應(yīng)硬件的操作。設(shè)備驅(qū)動(dòng)程序接口Linux中的I/O子系統(tǒng)向內(nèi)核中的其他部分提供了一個(gè)統(tǒng)一的標(biāo)準(zhǔn)設(shè)備接口,這是通過include/linux/fs.h中的數(shù)據(jù)結(jié)構(gòu)file_operations來完成的:structfile_operations{structmodule*owner;loff_t(*llseek)(structfile*,loff_t,int);ssize_t(*read)(structfile*,char*,size_t,loff_t*);ssize_t(*write)(structfile*,constchar*,size_t,loff_t*);int(*readdir)(structfile*,void*,filldir_t);unsignedint(*poll)(structfile*,structpoll_table_struct*);int(*ioctl)(structinode*,structfile*,unsignedint,unsignedlong);int(*mmap)(structfile*,structvm_area_struct*);int(*open)(structinode*,structfile*);int(*flush)(structfile*);int(*release)(structinode*,structfile*);int(*fsync)(structfile*,structdentry*,intdatasync);int(*fasync)(int,structfile*,int);int(*lock)(structfile*,int,structfile_lock*);ssize_t(*readv)(structfile*,conststructiovec*,unsignedlong,loff_t*);ssize_t(*writev)(structfile*,conststructiovec*,unsignedlong,loff_t*);ssize_t(*sendpage)(structfile*,structpage*,int,size_t,loff_t*,int);unsignedlong(*get_unmapped_area)(structfile*,unsignedlong,unsignedlong,unsignedlong,unsignedlong);};當(dāng)應(yīng)用程序?qū)υO(shè)備文件進(jìn)行諸如open、close、read、write等操作時(shí),Linux內(nèi)核將通過file_operations結(jié)構(gòu)訪問驅(qū)動(dòng)程序提供的函數(shù)。例如,當(dāng)應(yīng)用程序?qū)υO(shè)備文件執(zhí)行讀操作時(shí),內(nèi)核將調(diào)用file_operations結(jié)構(gòu)中的read函數(shù)。2.設(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ì)上來講,模塊也是內(nèi)核的一部分,它不同于普通的應(yīng)用程序,不能調(diào)用位于用戶態(tài)下的C或者C++庫函數(shù),而只能調(diào)用Linux內(nèi)核提供的函數(shù),在/proc/ksyms中可以查看到內(nèi)核提供的所有函數(shù)。在以模塊方式編寫驅(qū)動(dòng)程序時(shí),要實(shí)現(xiàn)兩個(gè)必不可少的函數(shù)init_module()和cleanup_module(),而且至少要包含<linux/krernel.h>和<linux/module.h>兩個(gè)頭文件。在用gcc編譯內(nèi)核模塊時(shí),需要加上-DMODULE-DKERNEL-DLINUX這幾個(gè)參數(shù),編譯生成的模塊(一般為.o文件)可以使用命令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í)候都可以使用命令來lsmod查看目前已經(jīng)加載的模塊以及正在使用該模塊的用戶數(shù)。設(shè)備驅(qū)動(dòng)程序結(jié)構(gòu)了解設(shè)備驅(qū)動(dòng)程序的基本結(jié)構(gòu)(或者稱為框架),對(duì)開發(fā)人員而言是非常重要的,Linux的設(shè)備驅(qū)動(dòng)程序大致可以分為如下幾個(gè)部分:驅(qū)動(dòng)程序的注冊(cè)與注銷、設(shè)備的打開與釋放、設(shè)備的讀寫操作、設(shè)備的控制操作、設(shè)備的中斷和輪詢處理。驅(qū)動(dòng)程序的注冊(cè)與注銷向系統(tǒng)增加一個(gè)驅(qū)動(dòng)程序意味著要賦予它一個(gè)主設(shè)備號(hào),這可以通過在驅(qū)動(dòng)程序的初始化過程中調(diào)用register_chrdev()或者register_blkdev()來完成。而在關(guān)閉字符設(shè)備或者塊設(shè)備時(shí),則需要通過調(diào)用unregister_chrdev()或unregister_blkdev()從內(nèi)核中注銷設(shè)備,同時(shí)釋放占用的主設(shè)備號(hào)。設(shè)備的打開與釋放打開設(shè)備是通過調(diào)用file_operations結(jié)構(gòu)中的函數(shù)open()來完成的,它是驅(qū)動(dòng)程序用來為今后的操作完成初始化準(zhǔn)備工作的。在大部分驅(qū)動(dòng)程序中,open()通常需要完成下列工作:■檢查設(shè)備相關(guān)錯(cuò)誤,如設(shè)備尚未準(zhǔn)備好等?!鋈绻堑谝淮未蜷_,則初始化硬件設(shè)備?!鲎R(shí)別次設(shè)備號(hào),如果有必要?jiǎng)t更新讀寫操作的當(dāng)前位置指針f_ops。■分配和填寫要放在file->private_data里的數(shù)據(jù)結(jié)構(gòu)?!鍪褂糜?jì)數(shù)增1。釋放設(shè)備是通過調(diào)用file_operations結(jié)構(gòu)中的函數(shù)release()來完成的,這個(gè)設(shè)備方法有時(shí)也被稱為close(),它的作用正好與open()相反,通常要完成下列工作:■使用計(jì)數(shù)減1?!鲠尫旁趂ile->private_data中分配的內(nèi)存?!鋈绻褂糜?jì)算為0,則關(guān)閉設(shè)備。設(shè)備的讀寫操作字符設(shè)備的讀寫操作相對(duì)比較簡(jiǎn)單,直接使用函數(shù)read()和write()就可以了。但如果是塊設(shè)備的話,則需要調(diào)用函數(shù)block_read()和block_write()來進(jìn)行數(shù)據(jù)讀寫,這兩個(gè)函數(shù)將向設(shè)備請(qǐng)求表中增加讀寫請(qǐng)求,以便Linux內(nèi)核可以對(duì)請(qǐng)求順序進(jìn)行優(yōu)化。由于是對(duì)內(nèi)存緩沖區(qū)而不是直接對(duì)設(shè)備進(jìn)行操作的,因此能很大程度上加快讀寫速度。如果內(nèi)存緩沖區(qū)中沒有所要讀入的數(shù)據(jù),或者需要執(zhí)行寫操作將數(shù)據(jù)寫入設(shè)備,那么就要執(zhí)行真正的數(shù)據(jù)傳輸,這是通過調(diào)用數(shù)據(jù)結(jié)構(gòu)blk_dev_struct中的函數(shù)request_fn()來完成的。設(shè)備的控制操作除了讀寫操作外,應(yīng)用程序有時(shí)還需要對(duì)設(shè)備進(jìn)行控制,這可以通過設(shè)備驅(qū)動(dòng)程序中的函數(shù)ioctl()來完成。ioctl()的用法與具體設(shè)備密切關(guān)聯(lián),因此需要根據(jù)設(shè)備的實(shí)際情況進(jìn)行具體分析。設(shè)備的中斷和輪詢處理對(duì)于不支持中斷的硬件設(shè)備,讀寫時(shí)需要輪流查詢?cè)O(shè)備狀態(tài),以便決定是否繼續(xù)進(jìn)行數(shù)據(jù)傳輸。如果設(shè)備支持中斷,則可以按中斷方式進(jìn)行操作。三、PCI驅(qū)動(dòng)程序?qū)崿F(xiàn)1.關(guān)鍵數(shù)據(jù)結(jié)構(gòu)PCI設(shè)備上有三種地址空間:PCI的I/O空間、PCI的存儲(chǔ)空間和PCI的配置空間。CPU可以訪問PCI設(shè)備上的所有地址空間,其中I/O空間和存儲(chǔ)空間提供給設(shè)備驅(qū)動(dòng)程序使用,而配置空間則由Linux內(nèi)核中的PCI初始化代碼使用。內(nèi)核在啟動(dòng)時(shí)負(fù)責(zé)對(duì)所有PCI設(shè)備進(jìn)行初始化,配置好所有的PCI設(shè)備,包括中斷號(hào)以及I/O基址,并在文件/proc/pci中列出所有找到的pci設(shè)備,以及這些設(shè)備的參數(shù)和屬性。Linux驅(qū)動(dòng)程序通常使用結(jié)構(gòu)(Struct)來表示一種設(shè)備,而結(jié)構(gòu)體中的變量則代表某一具體設(shè)備,該變量存放了與該設(shè)備相關(guān)的所有信息。好的驅(qū)動(dòng)程序都應(yīng)該能驅(qū)動(dòng)多個(gè)同種設(shè)備,每個(gè)設(shè)備之間用次設(shè)備號(hào)進(jìn)行區(qū)分,如果采用結(jié)構(gòu)數(shù)據(jù)來代表所有能由該驅(qū)動(dòng)程序驅(qū)動(dòng)的設(shè)備,那么就可以簡(jiǎn)單地使用數(shù)組下標(biāo)來表示次設(shè)備號(hào)。在PCI驅(qū)動(dòng)程序中,下面幾個(gè)關(guān)鍵數(shù)據(jù)結(jié)構(gòu)起著非常核心的作用:pci_driver這個(gè)數(shù)據(jù)結(jié)構(gòu)在文件include/linux/pci.h里,這是Linux內(nèi)核版本2.4之后為新型的pci設(shè)備驅(qū)動(dòng)程序所添加的,其中最主要的是用于識(shí)別設(shè)備的id_table結(jié)構(gòu),以及用于檢測(cè)設(shè)備的函數(shù)probe()和卸載設(shè)備的函數(shù)remove():structpci_driver{structlist_headnode;char*name;conststructpci_device_id*id_table;int(*probe)(structpci_dev*dev,conststructpci_device_id*id);void(*remove)(structpci_dev*dev);int(*save_state)(structpci_dev*dev,u32state);int(*suspend)(structpci_dev*dev,u32state);int(*resume)(structpci_dev*dev);int(*enable_wake)(structpci_dev*dev,u32state,intenable);};pci_dev這個(gè)數(shù)據(jù)結(jié)構(gòu)也在文件include/linux/pci.h里,它詳細(xì)描述了一個(gè)pci設(shè)備幾乎所有的硬件信息,包括廠商ID、設(shè)備ID、各種資源等:structpci_dev{structlist_headglobal_list;structlist_headbus_list;structpci_bus*bus;structpci_bus*subordinate;void*sysdata;structproc_dir_entry*procent;unsignedintdevfn;unsignedshortvendor;unsignedshortdevice;unsignedshortsubsystem_vendor;unsignedshortsubsystem_device;unsignedintclass;u8 hdr_type;u8 rom_base_reg;structpci_driver*driver;void*driver_data;u64dma_mask;u32 current_state;unsignedshortvendor_compatible[DEVICE_COUNT_COMPATIBLE];unsignedshortdevice_compatible[DEVICE_COUNT_COMPATIBLE];unsignedintirq;structresourceresource[DEVICE_COUNT_RESOURCE];structresourcedma_resource[DEVICE_COUNT_DMA];structresourceirq_resource[DEVICE_COUNT_IRQ];char name[80];char slot_name[8];intactive;intro;unsignedshortregs;int(*prepare)(structpci_dev*dev);int(*activate)(structpci_dev*dev);int(*deactivate)(structpci_dev*dev);};2.基本框架在用模塊方式實(shí)現(xiàn)pci設(shè)備驅(qū)動(dòng)程序時(shí),通常至少要實(shí)現(xiàn)以下幾個(gè)部分:初始化設(shè)備模塊、設(shè)備打開模塊、數(shù)據(jù)讀寫和控制模塊、中斷處理模塊、設(shè)備釋放模塊、設(shè)備卸載模塊。下面給出一個(gè)典型的PCI設(shè)備驅(qū)動(dòng)程序的基本框架,從中不難體會(huì)到這幾個(gè)關(guān)鍵模塊是如何組織起來的。/*指明該驅(qū)動(dòng)程序適用于哪一些PCI設(shè)備*/staticstructpci_device_iddemo_pci_tbl[]__initdata={{PCI_VENDOR_ID_DEMO,PCI_DEVICE_ID_DEMO,PCI_ANY_ID,pci_ANY_ID,0,0,DEMO},{0,}};/*對(duì)特定PCI設(shè)備進(jìn)行描述的數(shù)據(jù)結(jié)構(gòu)*/structdemo_card{unsignedintmagic;/*使用鏈表保存所有同類的PCI設(shè)備*/structdemo_card*next;/*...*//*中斷處理模塊*/staticvoiddemo_interrupt(intirq,void*dev_id,structpt_regs*regs)/*...*/}/*設(shè)備文件操作接口*/staticstructfile_operationsdemo_fops={owner: THIS_MODULE,/*demo_fops所屬的設(shè)備模塊*/read: demo_read, /*讀設(shè)備操作*/write: demo_write,/*寫設(shè)備操作*/ioctl: demo_ioctl,/*控制設(shè)備操作*/mmap: demo_mmap,/*內(nèi)存重映射操作*/open: demo_open,/*打開設(shè)備操作*/release:demo_release/*釋放設(shè)備操作*//*...*/};/*設(shè)備模塊信息*/staticstructpci_driverdemo_pci_driver={name: demo_MODULE_NAME,/*設(shè)備模塊名稱*/id_table:demo_pci_tbl,/*能夠驅(qū)動(dòng)的設(shè)備列表*/probe: demo_probe,/*查找并初始化設(shè)備*/remove: demo_remove/*卸載設(shè)備模塊*//*...*/};staticintinitdemo_init_module(void)/*...*/}staticvoid—exitdemo_cleanup_module(void){pci_unregister_driver(&demo_pci_driver);}/*加載驅(qū)動(dòng)程序模塊入口*/module_init(demo_init_module);/*卸載驅(qū)動(dòng)程序模塊入口*/module_exit(demo_cleanup_module);上面這段代碼給出了一個(gè)典型的pci設(shè)備驅(qū)動(dòng)程序的框架,是一種相對(duì)固定的模式。需要注意的是,同加載和卸載模塊相關(guān)的函數(shù)或數(shù)據(jù)結(jié)構(gòu)都要在前面加上init、exit等標(biāo)志符,以使同普通函數(shù)區(qū)分開來。構(gòu)造出這樣一個(gè)框架之后,接下去的工作就是如何完成框架內(nèi)的各個(gè)功能模塊了。3.初始化設(shè)備模塊在Linux系統(tǒng)下,想要完成對(duì)一個(gè)PCI設(shè)備的初始化,需要完成以下工作:檢查PCI總線是否被Linux內(nèi)核支持;檢查設(shè)備是否插在總線插槽上,如果在的話則保存它所占用的插槽的位置等信息。讀出配置頭中的信息提供給驅(qū)動(dòng)程序使用。當(dāng)Linux內(nèi)核啟動(dòng)并完成對(duì)所有PCI設(shè)備進(jìn)行掃描、登錄和分配資源等初始化操作的同時(shí),會(huì)建立起系統(tǒng)中所有PCI設(shè)備的拓?fù)浣Y(jié)構(gòu),此后當(dāng)PCI驅(qū)動(dòng)程序需要對(duì)設(shè)備進(jìn)行初始化時(shí),一般都會(huì)調(diào)用如下的代碼:staticint__initdemo_init_module(void){/*檢查系統(tǒng)是否支持PCI總線*/if(!pci_present())return-ENODEV;/*注冊(cè)硬件驅(qū)動(dòng)程序*/if(!pci_register_driver(&demo_pci_driver)){pci_unregister_driver(&demo_pci_driver);return-ENODEV;/*...*/return0;}驅(qū)動(dòng)程序首先調(diào)用函數(shù)pci_present()檢查pci總線是否已經(jīng)被Linux內(nèi)核支持,如果系統(tǒng)支持pci總線結(jié)構(gòu),這個(gè)函數(shù)的返回值為0,如果驅(qū)動(dòng)程序在調(diào)用這個(gè)函數(shù)時(shí)得到了一個(gè)非0的返回值,那么驅(qū)動(dòng)程序就必須得中止自己的任務(wù)了。在2.4以前的內(nèi)核中,需要手工調(diào)用pci_find_device()函數(shù)來查找pci設(shè)備,但在2.4以后更好的辦法是調(diào)用pci_register_driver(涵數(shù)來注冊(cè)pci設(shè)備的驅(qū)動(dòng)程序,此時(shí)需要提供一個(gè)pci_driver結(jié)構(gòu),在該結(jié)構(gòu)中給出的probe探測(cè)例程將負(fù)責(zé)完成對(duì)硬件的檢測(cè)工作。staticintinitdemo_probe(structpci_dev*pci_dev,conststructpci_device_id*pci_id){structdemo_card*card;/*啟動(dòng)pci設(shè)備*/if(pci_enable_device(pci_dev))return-EIO;/*設(shè)備DMA標(biāo)識(shí)*/if(pci_set_dma_mask(pci_dev,DEMO_DMA_MASK)){return-ENODEV;}/*在內(nèi)核空間中動(dòng)態(tài)申請(qǐng)內(nèi)存*/if((card=kmalloc(sizeof(structdemo_card),GFP_KERNEL))==NULL){printk(KERN_ERR"pci_demo:outofmemory\n");return-ENOMEM;}memset(card,0,sizeof(*card));/*讀取pci配置信息*/card->iobase=pci_resource_start(pci_dev,1);card->pci_dev=pci_dev;card->pci_id=pci_id->device;card->irq=pci_dev->irq;card->next=devs;card->magic=DEMO_CARD_MAGIC;/*設(shè)置成總線主DMA模式*/pci_set_master(pci_dev);/*申請(qǐng)I/O資源*/request_region(card->iobase,64,card_names[pci_id->driver_data]);return0;}打開設(shè)備模塊在這個(gè)模塊里主要實(shí)現(xiàn)申請(qǐng)中斷、檢查讀寫模式以及申請(qǐng)對(duì)設(shè)備的控制權(quán)等。在申請(qǐng)控制權(quán)的時(shí)候,非阻塞方式遇忙返回,否則進(jìn)程主動(dòng)接受調(diào)度,進(jìn)入睡眠狀態(tài),等待其它進(jìn)程釋放對(duì)設(shè)備的控制權(quán)。staticintdemo_open(structinode*inode,structfile*file){/*申請(qǐng)中斷,注冊(cè)中斷處理程序*/request_irq(card->irq,&demo_interrupt,SA_SHIRQ,card_names[pci_id->driver_data],card)){/*檢查讀寫模式*/if(file->f_mode&FMODE_READ){/*...*/if(file->f_mode&FMODE_WRITE){/*...*//*申請(qǐng)對(duì)設(shè)備的控制權(quán)*/down(&card->open_sem);while(card->open_mode&file->f_mode){if(file->f_flags&O_NONBLOCK){/*NONBLOCK模式,返回-EBUSY*/up(&card->open_sem);return-EBUSY;}else{/*等待調(diào)度,獲得控制權(quán)*/card->open_mode|=f_mode&(FMODE_READ|FMODE_WRITE);up(&card->open_sem);/*設(shè)備打開計(jì)數(shù)增1*/MOD_INC_USE_COUNT;/*...*/數(shù)據(jù)讀寫和控制信息模塊pci設(shè)備驅(qū)動(dòng)程序可以通過demo_fops結(jié)構(gòu)中的函數(shù)demo_ioctl(),向應(yīng)用程序提供對(duì)硬件進(jìn)行控制的接口。例如,通過它可以從I/O寄存器里讀取一個(gè)數(shù)據(jù),并傳送到用戶空間里:staticintdemo_ioctl(structinode*inode,structfile*file,unsignedintcmd,unsignedlongarg)/*...*/switch(cmd){caseDEMORDATA:/*從I/O端口讀取4字節(jié)的數(shù)據(jù)*/val=inl(card->iobae+0x10);/*將讀取的數(shù)據(jù)傳輸?shù)接脩艨臻g*/return0;/*...*/事實(shí)上,在demo_fops里還可以實(shí)現(xiàn)諸如demo_read()、demo_mmap()等操作,Linux內(nèi)核源碼中的driver目錄里提供了許多設(shè)備驅(qū)動(dòng)程序的源代碼,找那里可以找到類似的例子。在對(duì)資源的訪問方式上,除了有1/0指令以外,還有對(duì)外設(shè)I/O內(nèi)存的訪問。對(duì)這些內(nèi)存的操作一方面可以通過把I/O內(nèi)存重新映射后作為普通內(nèi)

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論