![嵌入式驅動程序_第1頁](http://file4.renrendoc.com/view/7936161897f31de7e5a2952bc85faf86/7936161897f31de7e5a2952bc85faf861.gif)
![嵌入式驅動程序_第2頁](http://file4.renrendoc.com/view/7936161897f31de7e5a2952bc85faf86/7936161897f31de7e5a2952bc85faf862.gif)
![嵌入式驅動程序_第3頁](http://file4.renrendoc.com/view/7936161897f31de7e5a2952bc85faf86/7936161897f31de7e5a2952bc85faf863.gif)
![嵌入式驅動程序_第4頁](http://file4.renrendoc.com/view/7936161897f31de7e5a2952bc85faf86/7936161897f31de7e5a2952bc85faf864.gif)
![嵌入式驅動程序_第5頁](http://file4.renrendoc.com/view/7936161897f31de7e5a2952bc85faf86/7936161897f31de7e5a2952bc85faf865.gif)
版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
Linux下PCI設備驅動程序開發(fā)一、pci總線系統(tǒng)體系結構pci是外圍設備互連(PeripheralComponentInterconnect)的簡稱,作為一種通用的總線接口標準,它在目前的計算機系統(tǒng)中得到了非常廣泛的應用。pci提供了一組完整的總線接口規(guī)范,其目的是描述如何將計算機系統(tǒng)中的外圍設備以一種結構化和可控化的方式連接在一起,同時它還刻畫了外圍設備在連接時的電氣特性和行為規(guī)約,并且詳細定義了計算機系統(tǒng)中的各個不同部件之間應該如何正確地進行交互。無論是在基于Intel芯片的PC機中,或是在基于Alpha芯片的工作站上,pci毫無疑問都是目前使用最廣泛的一種總線接口標準。同舊式的ISA總線不同,pci將計算機系統(tǒng)中的總線子系統(tǒng)與存儲子系統(tǒng)完全地分開,CPU通過一塊稱為pci橋(Pci-Bridge)的設備來完成同總線子系統(tǒng)的交互,如圖1所示。圖1pci子系統(tǒng)的體系結構由于使用了更高的時鐘頻率,因此PCI總線能夠獲得比ISA總線更好的整體性能。PCI總線的時鐘頻率一般在25MHz到33MHz范圍內,有些甚至能夠達到66MHz或者133MHz,而在64位系統(tǒng)中則最高能達到266MHz。盡管目前pci設備大多采用32位數據總線,但PCI規(guī)范中已經給出了64位的擴展實現(xiàn),從而使PCI總線能夠更好地實現(xiàn)平臺無關性,現(xiàn)在pci總線已經能夠用于IA-32、Alpha、PowerPC.SPARC64和IA-64等體系結構中。PCI總線具有三個非常顯著的優(yōu)點,使得它能夠完成最終取代ISA總線這一歷史使命:■在計算機和外設間傳輸數據時具有更好的性能;■能夠盡量獨立于具體的平臺;■可以很方便地實現(xiàn)即插即用。圖2是一個典型的基于pci總線的計算機系統(tǒng)邏輯示意圖,系統(tǒng)的各個部分通過PCI總線和PCI-PCI橋連接在一起。從圖中不難看出,CPU和RAM需要通過pci橋連接到PCI總線。(即主PCI總線),而具有PCI接口的顯卡則可以直接連接到主PCI總線上。PCI-PCI橋是一個特殊的PCI設備,它負責將PCI總線0和PCI總線1(即從PCI主線)連接在一起,通常pci總線1稱為pci-pci橋的下游(downstream),而pci總線0則稱為pci-pci橋的上游(upstream)。圖中連接到從pci總線上的是SCSI卡和以太網卡。為了兼容舊的ISA總線標準,pci總線還可以通過pci-ISA橋來連接ISA總線,從而能夠支持以前的ISA設備。圖中ISA總線上連接著一個多功能I/O控制器,用于控制鍵盤、鼠標和軟驅。PCI系統(tǒng)示意圖在此我只對PCI總線系統(tǒng)體系結構作了概括性介紹,如果讀者想進一步了解,DavidARusling在TheLinuxKernel中對Linux的pci子系統(tǒng)有比較詳細的介紹。二、Linux驅動程序框架Linux將所有外部設備看成是一類特殊文件,稱之為''設備文件”,如果說系統(tǒng)調用是Linux內核和應用程序之間的接口,那么設備驅動程序則可以看成是Linux內核與外部設備之間的接口。設備驅動程序向應用程序屏蔽了硬件在實現(xiàn)上的細節(jié),使得應用程序可以像操作普通文件一樣來操作外部設備。字符設備和塊設備Linux抽象了對硬件的處理,所有的硬件設備都可以像普通文件一樣來看待:它們可以使用和操作文件相同的、標準的系統(tǒng)調用接口來完成打開、關閉、讀寫和I/O控制操作,而驅動程序的主要任務也就是要實現(xiàn)這些系統(tǒng)調用函數。Linux系統(tǒng)中的所有硬件設備都使用一個特殊的設備文件來表示,例如,系統(tǒng)中的第一個IDE硬盤使用/dev/hda表示。每個設備文件對應有兩個設備號:一個是主設備號,標識該設備的種類,也標識了該設備所使用的驅動程序;另一個是次設備號,標識使用同一設備驅動程序的不同硬件設備。設備文件的主設備號必須與設備驅動程序在登錄該設備時申請的主設備號一致,否則用戶進程將無法訪問到設備驅動程序。在Linux操作系統(tǒng)下有兩類主要的設備文件:一類是字符設備,另一類則是塊設備。字符設備是以字節(jié)為單位逐個進行I/O操作的設備,在對字符設備發(fā)出讀寫請求時,實際的硬件I/O緊接著就發(fā)生了,一般來說字符設備中的緩存是可有可無的,而且也不支持隨機訪問。塊設備則是利用一塊系統(tǒng)內存作為緩沖區(qū),當用戶進程對設備進行讀寫請求時,驅動程序先查看緩沖區(qū)中的內容,如果緩沖區(qū)中的數據能滿足用戶的要求就返回相應的數據,否則就調用相應的請求函數來進行實際的I/O操作。塊設備主要是針對磁盤等慢速設備設計的,其目的是避免耗費過多的CPU時間來等待操作的完成。一般說來,PCI卡通常都屬于字符設備。所有已經注冊(即已經加載了驅動程序)的硬件設備的主設備號可以從/proc/devices文件中得到。使用mknod命令可以創(chuàng)建指定類型的設備文件,同時為其分配相應的主設備號和次設備號。例如,下面的命令:[root@garyroot]#mknod/dev/lp0c60將建立一個主設備號為6,次設備號為0的字符設備文件/dev/lp0。當應用程序對某個設備文件進行系統(tǒng)調用時,Linux內核會根據該設備文件的設備類型和主設備號調用相應的驅動程序,并從用戶態(tài)進入到核心態(tài),再由驅動程序判斷該設備的次設備號,最終完成對相應硬件的操作。設備驅動程序接口Linux中的I/O子系統(tǒng)向內核中的其他部分提供了一個統(tǒng)一的標準設備接口,這是通過include/linux/fs.h中的數據結構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);};當應用程序對設備文件進行諸如open、close、read、write等操作時,Linux內核將通過file_operations結構訪問驅動程序提供的函數。例如,當應用程序對設備文件執(zhí)行讀操作時,內核將調用file_operations結構中的read函數。2.設備驅動程序模塊Linux下的設備驅動程序可以按照兩種方式進行編譯,一種是直接靜態(tài)編譯成內核的一部分,另一種則是編譯成可以動態(tài)加載的模塊。如果編譯進內核的話,會增加內核的大小,還要改動內核的源文件,而且不能動態(tài)地卸載,不利于調試,所有推薦使用模塊方式。從本質上來講,模塊也是內核的一部分,它不同于普通的應用程序,不能調用位于用戶態(tài)下的C或者C++庫函數,而只能調用Linux內核提供的函數,在/proc/ksyms中可以查看到內核提供的所有函數。在以模塊方式編寫驅動程序時,要實現(xiàn)兩個必不可少的函數init_module()和cleanup_module(),而且至少要包含<linux/krernel.h>和<linux/module.h>兩個頭文件。在用gcc編譯內核模塊時,需要加上-DMODULE-DKERNEL-DLINUX這幾個參數,編譯生成的模塊(一般為.o文件)可以使用命令insmod載入Linux內核,從而成為內核的一個組成部分,此時內核會調用模塊中的函數init_module()。當不需要該模塊時,可以使用rmmod命令進行卸載,此進內核會調用模塊中的函數cleanup_module()。任何時候都可以使用命令來lsmod查看目前已經加載的模塊以及正在使用該模塊的用戶數。設備驅動程序結構了解設備驅動程序的基本結構(或者稱為框架),對開發(fā)人員而言是非常重要的,Linux的設備驅動程序大致可以分為如下幾個部分:驅動程序的注冊與注銷、設備的打開與釋放、設備的讀寫操作、設備的控制操作、設備的中斷和輪詢處理。驅動程序的注冊與注銷向系統(tǒng)增加一個驅動程序意味著要賦予它一個主設備號,這可以通過在驅動程序的初始化過程中調用register_chrdev()或者register_blkdev()來完成。而在關閉字符設備或者塊設備時,則需要通過調用unregister_chrdev()或unregister_blkdev()從內核中注銷設備,同時釋放占用的主設備號。設備的打開與釋放打開設備是通過調用file_operations結構中的函數open()來完成的,它是驅動程序用來為今后的操作完成初始化準備工作的。在大部分驅動程序中,open()通常需要完成下列工作:■檢查設備相關錯誤,如設備尚未準備好等?!鋈绻堑谝淮未蜷_,則初始化硬件設備?!鲎R別次設備號,如果有必要則更新讀寫操作的當前位置指針f_ops?!龇峙浜吞顚懸旁趂ile->private_data里的數據結構?!鍪褂糜嫈翟?。釋放設備是通過調用file_operations結構中的函數release()來完成的,這個設備方法有時也被稱為close(),它的作用正好與open()相反,通常要完成下列工作:■使用計數減1?!鲠尫旁趂ile->private_data中分配的內存。■如果使用計算為0,則關閉設備。設備的讀寫操作字符設備的讀寫操作相對比較簡單,直接使用函數read()和write()就可以了。但如果是塊設備的話,則需要調用函數block_read()和block_write()來進行數據讀寫,這兩個函數將向設備請求表中增加讀寫請求,以便Linux內核可以對請求順序進行優(yōu)化。由于是對內存緩沖區(qū)而不是直接對設備進行操作的,因此能很大程度上加快讀寫速度。如果內存緩沖區(qū)中沒有所要讀入的數據,或者需要執(zhí)行寫操作將數據寫入設備,那么就要執(zhí)行真正的數據傳輸,這是通過調用數據結構blk_dev_struct中的函數request_fn()來完成的。設備的控制操作除了讀寫操作外,應用程序有時還需要對設備進行控制,這可以通過設備驅動程序中的函數ioctl()來完成。ioctl()的用法與具體設備密切關聯(lián),因此需要根據設備的實際情況進行具體分析。設備的中斷和輪詢處理對于不支持中斷的硬件設備,讀寫時需要輪流查詢設備狀態(tài),以便決定是否繼續(xù)進行數據傳輸。如果設備支持中斷,則可以按中斷方式進行操作。三、PCI驅動程序實現(xiàn)1.關鍵數據結構PCI設備上有三種地址空間:PCI的I/O空間、PCI的存儲空間和PCI的配置空間。CPU可以訪問PCI設備上的所有地址空間,其中I/O空間和存儲空間提供給設備驅動程序使用,而配置空間則由Linux內核中的PCI初始化代碼使用。內核在啟動時負責對所有PCI設備進行初始化,配置好所有的PCI設備,包括中斷號以及I/O基址,并在文件/proc/pci中列出所有找到的pci設備,以及這些設備的參數和屬性。Linux驅動程序通常使用結構(Struct)來表示一種設備,而結構體中的變量則代表某一具體設備,該變量存放了與該設備相關的所有信息。好的驅動程序都應該能驅動多個同種設備,每個設備之間用次設備號進行區(qū)分,如果采用結構數據來代表所有能由該驅動程序驅動的設備,那么就可以簡單地使用數組下標來表示次設備號。在PCI驅動程序中,下面幾個關鍵數據結構起著非常核心的作用:pci_driver這個數據結構在文件include/linux/pci.h里,這是Linux內核版本2.4之后為新型的pci設備驅動程序所添加的,其中最主要的是用于識別設備的id_table結構,以及用于檢測設備的函數probe()和卸載設備的函數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這個數據結構也在文件include/linux/pci.h里,它詳細描述了一個pci設備幾乎所有的硬件信息,包括廠商ID、設備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.基本框架在用模塊方式實現(xiàn)pci設備驅動程序時,通常至少要實現(xiàn)以下幾個部分:初始化設備模塊、設備打開模塊、數據讀寫和控制模塊、中斷處理模塊、設備釋放模塊、設備卸載模塊。下面給出一個典型的PCI設備驅動程序的基本框架,從中不難體會到這幾個關鍵模塊是如何組織起來的。/*指明該驅動程序適用于哪一些PCI設備*/staticstructpci_device_iddemo_pci_tbl[]__initdata={{PCI_VENDOR_ID_DEMO,PCI_DEVICE_ID_DEMO,PCI_ANY_ID,pci_ANY_ID,0,0,DEMO},{0,}};/*對特定PCI設備進行描述的數據結構*/structdemo_card{unsignedintmagic;/*使用鏈表保存所有同類的PCI設備*/structdemo_card*next;/*...*//*中斷處理模塊*/staticvoiddemo_interrupt(intirq,void*dev_id,structpt_regs*regs)/*...*/}/*設備文件操作接口*/staticstructfile_operationsdemo_fops={owner: THIS_MODULE,/*demo_fops所屬的設備模塊*/read: demo_read, /*讀設備操作*/write: demo_write,/*寫設備操作*/ioctl: demo_ioctl,/*控制設備操作*/mmap: demo_mmap,/*內存重映射操作*/open: demo_open,/*打開設備操作*/release:demo_release/*釋放設備操作*//*...*/};/*設備模塊信息*/staticstructpci_driverdemo_pci_driver={name: demo_MODULE_NAME,/*設備模塊名稱*/id_table:demo_pci_tbl,/*能夠驅動的設備列表*/probe: demo_probe,/*查找并初始化設備*/remove: demo_remove/*卸載設備模塊*//*...*/};staticintinitdemo_init_module(void)/*...*/}staticvoid—exitdemo_cleanup_module(void){pci_unregister_driver(&demo_pci_driver);}/*加載驅動程序模塊入口*/module_init(demo_init_module);/*卸載驅動程序模塊入口*/module_exit(demo_cleanup_module);上面這段代碼給出了一個典型的pci設備驅動程序的框架,是一種相對固定的模式。需要注意的是,同加載和卸載模塊相關的函數或數據結構都要在前面加上init、exit等標志符,以使同普通函數區(qū)分開來。構造出這樣一個框架之后,接下去的工作就是如何完成框架內的各個功能模塊了。3.初始化設備模塊在Linux系統(tǒng)下,想要完成對一個PCI設備的初始化,需要完成以下工作:檢查PCI總線是否被Linux內核支持;檢查設備是否插在總線插槽上,如果在的話則保存它所占用的插槽的位置等信息。讀出配置頭中的信息提供給驅動程序使用。當Linux內核啟動并完成對所有PCI設備進行掃描、登錄和分配資源等初始化操作的同時,會建立起系統(tǒng)中所有PCI設備的拓撲結構,此后當PCI驅動程序需要對設備進行初始化時,一般都會調用如下的代碼:staticint__initdemo_init_module(void){/*檢查系統(tǒng)是否支持PCI總線*/if(!pci_present())return-ENODEV;/*注冊硬件驅動程序*/if(!pci_register_driver(&demo_pci_driver)){pci_unregister_driver(&demo_pci_driver);return-ENODEV;/*...*/return0;}驅動程序首先調用函數pci_present()檢查pci總線是否已經被Linux內核支持,如果系統(tǒng)支持pci總線結構,這個函數的返回值為0,如果驅動程序在調用這個函數時得到了一個非0的返回值,那么驅動程序就必須得中止自己的任務了。在2.4以前的內核中,需要手工調用pci_find_device()函數來查找pci設備,但在2.4以后更好的辦法是調用pci_register_driver(涵數來注冊pci設備的驅動程序,此時需要提供一個pci_driver結構,在該結構中給出的probe探測例程將負責完成對硬件的檢測工作。staticintinitdemo_probe(structpci_dev*pci_dev,conststructpci_device_id*pci_id){structdemo_card*card;/*啟動pci設備*/if(pci_enable_device(pci_dev))return-EIO;/*設備DMA標識*/if(pci_set_dma_mask(pci_dev,DEMO_DMA_MASK)){return-ENODEV;}/*在內核空間中動態(tà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;/*設置成總線主DMA模式*/pci_set_master(pci_dev);/*申請I/O資源*/request_region(card->iobase,64,card_names[pci_id->driver_data]);return0;}打開設備模塊在這個模塊里主要實現(xiàn)申請中斷、檢查讀寫模式以及申請對設備的控制權等。在申請控制權的時候,非阻塞方式遇忙返回,否則進程主動接受調度,進入睡眠狀態(tài),等待其它進程釋放對設備的控制權。staticintdemo_open(structinode*inode,structfile*file){/*申請中斷,注冊中斷處理程序*/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){/*...*//*申請對設備的控制權*/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{/*等待調度,獲得控制權*/card->open_mode|=f_mode&(FMODE_READ|FMODE_WRITE);up(&card->open_sem);/*設備打開計數增1*/MOD_INC_USE_COUNT;/*...*/數據讀寫和控制信息模塊pci設備驅動程序可以通過demo_fops結構中的函數demo_ioctl(),向應用程序提供對硬件進行控制的接口。例如,通過它可以從I/O寄存器里讀取一個數據,并傳送到用戶空間里:staticintdemo_ioctl(structinode*inode,structfile*file,unsignedintcmd,unsignedlongarg)/*...*/switch(cmd){caseDEMORDATA:/*從I/O端口讀取4字節(jié)的數據*/val=inl(card->iobae+0x10);/*將讀取的數據傳輸到用戶空間*/return0;/*...*/事實上,在demo_fops里還可以實現(xiàn)諸如demo_read()、demo_mmap()等操作,Linux內核源碼中的driver目錄里提供了許多設備驅動程序的源代碼,找那里可以找到類似的例子。在對資源的訪問方式上,除了有1/0指令以外,還有對外設I/O內存的訪問。對這些內存的操作一方面可以通過把I/O內存重新映射后作為普通內
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 實驗室設備采購合同模板
- 展位合同書展位合作協(xié)議
- 土地入股合作協(xié)議書
- 湖北城市建設職業(yè)技術學院《計算機數值計算方法》2023-2024學年第二學期期末試卷
- 有子女離婚協(xié)議書
- 工程勞務分包服務合同
- 公司股東出資合作協(xié)議書
- 浙江警官職業(yè)學院《數學優(yōu)化》2023-2024學年第二學期期末試卷
- 桂林學院《常微分方程雙語》2023-2024學年第二學期期末試卷
- 合同條款部分
- 高三開學收心班會課件
- 蒸汽換算計算表
- 人教版高中數學必修1全冊導學案
- 四年級計算題大全(列豎式計算,可打印)
- 科技計劃項目申報培訓
- 591食堂不合格食品處置制度
- 國際金融課件(完整版)
- 導向標識系統(tǒng)設計(一)課件
- 220t鍋爐課程設計 李學玉
- 全英文劇本 《劇院魅影》
- 北京城的中軸線PPT通用課件
評論
0/150
提交評論