




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
第七章linux設備驅動程序開發(fā)7.1設備驅動概述設備驅動可以理解為操作系統(tǒng)的一部分,對于一個特定的硬件設備來說,其對應的設備驅動程序是不同的。比如網卡、聲卡、鍵盤、鼠標、顯卡等。對于操作系統(tǒng)來說,掛接的設備越多,所需要的設備驅動程序也越多。操作系統(tǒng)本身并沒有對種類繁多的硬件設備提供持久不變的“設備驅動”,也就是說操作系統(tǒng)在沒有設備驅動程序支持下是無法正常支配硬件行為的。這個時候就需要獨立開發(fā)一套適合自己產品的設備驅動。正是操作系統(tǒng)留下了擴展設備驅動的接口,才有了現在支持各種應用場合的硬件設備的蓬勃發(fā)展。對于嵌入式開發(fā),更沒有通用的驅動程序可以便用。因此,驅動程序開發(fā)是整個嵌入式系統(tǒng)設計過程中必不可少的一部分。7.1設備驅動概述設備驅動程序功能設備驅動程序是Linux內核的重要組成部分。像操作系統(tǒng)的其他部分一樣,驅動程序在一個高優(yōu)先級的環(huán)境下工作,如果發(fā)生錯誤則可能會引發(fā)嚴重的問題。設備驅動程序控制了操作系統(tǒng)和硬件設備之間的交互,完成以下功能:對設備初始化和釋放;對設備進行管理,包括實時參數設置,以及提供對設備的操作接口;
讀取應用程序傳送給設備文件的數據或者回送應用程序請求的數據;檢測和處理設備出現的錯誤。應用程序硬件層驅動程序文件系統(tǒng)整個設備管理子系統(tǒng)的結構如圖7.1所示。7.1設備驅動概述2.Linux設備驅動程序接口系統(tǒng)調用是操作系統(tǒng)內核與應用程序之間的接口,驅動程序則是操作系統(tǒng)內核與機器硬件的接口。設備驅動程序能夠直接訪問硬件的代碼,必須為應用程序提供系統(tǒng)調用。以便應用程序能訪問設備。在LINUX中,主要有三種設備即:字符設備.塊設備和網絡設備,與此相關主要有三類設備驅動程序,字符設備驅動程序,塊設備驅動程序和網絡設備驅動程序.他們的系統(tǒng)調用是一致的,采用統(tǒng)一的接口(在數據結構file_operations
中)。應用程序使用設備就像使用讀寫普通的文件一樣方便,使用相同的open(),close(),read(),write()等,真正做到了與設備無關。7.1.2設備類型
Linux中的設備可以分為三類:字符設備塊設備網絡設備一個運行的linux系統(tǒng),當前使用的設備可以通過文件/proc/devices查看。
驅動程序中涉及的幾個概念模塊的概念Linux可以以模塊的形式加載設備類型,通常來說一個模塊對應實現一個設備驅動,因此是可以分類的。一般一個設備驅動對應一類設備的模塊方式,這樣便于多個設備的協(xié)調工作也利于應用程序的開發(fā)和擴展。Linux的驅動開發(fā)調試有兩種方法:(1)直接編譯到內核,再運行新的內核來測試;(2)編譯為模塊的形式,單獨加載運行調試。通常情況下設備驅動的模塊動態(tài)加載更為普遍,開發(fā)人員不必在調試過程中頻繁啟動機器就能完成設備驅動的開發(fā)工作。模塊加載與卸載模塊方式調試:(1)編譯的模塊直接插入內核:用insmod工具;(2)從內核中卸載模塊:用rmmod。模塊用insmod命令加載,用rmmod命令來卸載,這兩個命令分別調用module_init()和module_exit()函數,還可以用lsmod命令來查看所有已加載的模塊的狀態(tài)。Linux中模塊可以用C語言編寫,用gcc命令編譯成模塊*.ko編寫HelloWorld模塊#include<linux/init.h>#include<linux/module.h>#include<linux/kernel.h>MODULE_LICENSE("DualBSD/GPL");staticint__init
hello_init(void){printk(KERN_ALERT"Hello,World!\n");return0;}staticvoid__exit
hello_exit(void){printk(KERN_ALERT"Goodbye,world!\n");}module_init(hello_init);module_exit(hello_exit);定義__init、__exit、module_init、
module_exit所必需的宏定義所有模塊相關的宏,比如MODULE_LICENSEmodule_init()、module_exit()為內核特殊宏,分別用來定義模塊被裝載和卸載時調用的函數定義printk()中相關的宏,比如KERN_ALERT編寫HelloWorld模塊#include<linux/init.h>#include<linux/module.h>#include<linux/kernel.h>MODULE_LICENSE("DualBSD/GPL");staticinthello_init(void){printk(KERN_ALERT"Hello,World!\n");return0;}staticvoidhello_exit(void){printk(KERN_ALERT"Goodbye,world!\n");}module_init(hello_init);module_exit(hello_exit);內核函數printk被定義在linux內核中,他類似于標準C函數printf().用MODULE_LICENSE宏來聲明該模塊的許可協(xié)議,聲明為BSD和GPL雙重協(xié)議許可編寫HelloWorld模塊MODULE_AUTHOR(“BENSON”);//可選的MODULE_DESCRIPTION("STUDY_MODULE");//可選的Printk函數printk(KERN_ALERT"Hello,World!\n");問:為什么不用printf()函數呢?答:在講交叉編譯工具鏈的時候,曾經講到在編譯內核的時候不能使用標準的C庫和其他函數庫的支持,所以不能使用printf()庫函數內核有自己的打印函數printk(),它通過自身的運行而不需要C庫的幫助.在使用insmod裝載之后,內核與內核公共函數和變量進行連接,從而可以使用printk()函數.其中KERN_ALERT宏是標記printk()打印出字符的優(yōu)先等級的,通常有八種消息級別,定義在include/linux/kernel.h的文件中.printk(日志級別“消息文本”);這里的日志級別通俗的說指的是對文本信息的一種輸出范圍上的指定。#defineKERN_EMERG"<0>"/*緊急事件消息,系統(tǒng)崩潰之前提示,表示系統(tǒng)不可用*/#defineKERN_ALERT"<1>"/*報告消息,表示必須立即采取措施*/#defineKERN_CRIT"<2>"/*臨界條件,通常涉及嚴重的硬件或軟件操作失敗*/#defineKERN_ERR"<3>"/*錯誤條件,驅動程序常用KERN_ERR來報告硬件的錯誤*/#defineKERN_WARNING"<4>"/*警告條件,對可能出現問題的情況進行警告*/#defineKERN_NOTICE"<5>"/*正常但又重要的條件,用于提醒。常用于與安全相關的消息*/#defineKERN_INFO"<6>"/*提示信息,如驅動程序啟動時,打印硬件信息*/#defineKERN_DEBUG"<7>"/*調試級別的消息*/
linux2.4編寫HelloWorld模塊的MakefileEXEC=helloOBJS=hello.oSRC=hello.cINCLUDECCLDMODCFLAGS=-O2-Wall-D__KERNEL__-DMODULE-I$(INCLUDE)
-march=armv4tLDFLAGS=-rall:$(EXEC)$(EXEC):$(OBJS) $(LD)$(LDFLAGS)-ohellohello.o%.o:%.c $(CC)$(MODCFLAGS)-mapcs-c$<-o$@clean: -rm-f$(EXEC)*.o*~core$@:表示完整的目標文件,包括擴展名$<:比目標文件更新的依賴文件$^:表示所有的依賴文件最后生成hello.o和hello用來指定編譯內核時所用的編譯選項Linux2.6編寫HelloWorld模塊的MakefileP230ifneq($(KERNELRELEASE),)obj-m:=hello.oelsePWD:=$(shellpwd)default:$(MAKE)-C$(KDIR)M=$(PWD)modulesEndif指定了內核源代碼的位置,其中保存有內核的頂層makefile文件
obj-m表示要由hello.c文件編譯得到hello.o,并作為模塊編譯obj-y則表示要連接進內核obj-x則目標不會被編譯。M=$(PWD)指明存放hello.c的路徑。M=$(PWD)指明存放hello.c的路徑。(1)先讓FS2410P教學平臺進入Linux環(huán)境,利用超級終端來顯示,Linux進入命令行的模式下。(2)輸入命令cd/tmp,進入tmp目錄,因為/tmp是在SDRAM中,可以放數據。(3)將hello.ko下載到/tmp目錄下。采用的rz命令來傳輸的,rz命令是通過Zmodem協(xié)議來傳輸的。先在教學平臺的下輸入rz命令(Linux環(huán)境下),接著點擊超級終端的“傳送”—>“發(fā)送文件”,在彈出的對話框中設置如下:然后點擊發(fā)送。數據傳輸完后,再回車,接著通過ls來查看/tmp目錄下是否有hello.ko文件。(4)hello.ko下載成功后,接下來我們要進行真正的加載和運行的工作了。改變hello.ko的屬性,命令如下:chmod755hello.ko(5)加載hello.ko模塊:insmod./hello.ko
這時我們就可以看到期待已久的Hello,world了。(6).卸載驅動模塊:rmmodhello.ko卸載時就可在屏幕上看到如下信息:
Goodbye,world
驅動程序中涉及的幾個概念設備驅動程序的設備號和入口點Linux系統(tǒng)通過設備號來區(qū)分不同設備。設備號由兩部分組成:主設備號和次設備號。主設備號標識設備對應的驅動程序。系統(tǒng)中不同的設備可以有相同的主設備號,主設備號相同的設備使用相同的驅動程序。次設備號用來區(qū)分具體驅動程序的實例。一個主設備號可能有多個設備與之對應,這多個設備正是在驅動程序內通過次設備號來進一步區(qū)分的。次設備號只能由設備驅動程序使用,內核的其他部分僅將它作為參數傳遞給驅動程序。在/proc/devices中列出了系統(tǒng)中處于活動狀態(tài)設備的主設備號,所謂的活動狀態(tài)是指與該設備對應的設備驅動已經被系統(tǒng)內核裝載。/dev/字符為c表示字符設備,為b表示塊設備兩個數字對應主設備號和次設備號對于現有Linux操作系統(tǒng),/dev目錄是必不可少的,這個目錄包含了所有Linux系統(tǒng)所知道的字符設備,塊設備和網絡設備/proc/devices
p233在/proc/devices中列出了系統(tǒng)中處于活動狀態(tài)設備的主設備號dev_t類型dev_t類型內核用dev_t類型來保存設備編號,dev_t是個32位的數,12位表示主設備號,20為表示次設備號。在實際使用中,是通過中定義的宏來轉換格式。獲得dev_t的主設備號和次設備號,應使用以下的宏,例如:dev_tdev;MAJOR(dev);/*主設備號*/MINOR(dev);/*次設備號*/如果要將主設備號、次設備號轉換成dev_t類型,則使用以下的宏,例如:Intmajor=4,minor=3;MKDEV(intmajor,intminor)設備入口點p234設備入口點設備入口點也可以理解為“設備文件句柄”,一個設備的入口點和磁盤上的普通文件系統(tǒng)一樣,可以刪除(rm),移動(mv)和復制(cp)等。我們可以在文件系統(tǒng)中使用mknod命令創(chuàng)建一個設備入口點。在文件系統(tǒng)中創(chuàng)建了設備入口點并沒有代表響應的設備驅動和硬件已經準備好,只是代表了和設備驅動通信的一部分。下面給一個創(chuàng)建字符設備入口點的實例:mknod/dev/testCharc
1000其中c代表字符設備,如果想創(chuàng)建塊設備則用b代替c。參數100代表該設備的主設備號,0代表該設備的次設備號。設備入口點創(chuàng)建設備的入口點之后,可以通過如下命令查看:ls–l/dev/testChar刪除設備入口點也非常簡單:rm/dev/testChar用戶空間和內核空間p220設備驅動運行在內核空間(在一些操作系統(tǒng)的書籍里面稱為“系統(tǒng)態(tài)”),而應用程序則運行在用戶空間(也可稱為“用戶態(tài)”)。設備驅動程序運行在內核空間比應用程序執(zhí)行的優(yōu)先級要高很多。內核態(tài)具有最高的運行級別,可以做任何事。應用程序則運行在最低級別的用戶態(tài),在這一級別處理器禁止對硬件的直接訪問和對內存的未授權訪問。內核空間和用戶空間分別引用不同的內存映射,也就是程序代碼使用不同的地址空間.Linux操作系統(tǒng)通過系統(tǒng)調用和硬件中斷完成從用戶空間到內核空間的控制轉移。字符設備p218字符設備:字符設備按照字符流的方式被有序訪問,像串口和鍵盤就都屬于字符設備。如果一個硬件設備是以字符流的方式被訪問的話,那就應該將它歸于字符設備;字符設備是linux最簡單的設備,可以像文件一樣訪問,區(qū)別主要在于:普通文件可以來/回讀/寫,而字符設備只能是順序讀/寫.我們可以對其調用打開、讀取、寫和關閉。初始化字符設備時,驅動程序向linux登記,并在字符設備向量中增加一個device_struct數據結構條目,這個設備的主設備標識符(tty的主設備號為4)用作這個向量表的索引系統(tǒng)調用系統(tǒng)調用字符設備是linux最簡單的設備,可以像文件一樣訪問.所以在用戶空間中,系統(tǒng)進程(應用程序)對設備文件的操作通過系統(tǒng)調用來完成,如open、read、write、close等。下面通過一個簡單的例子來了解一下系統(tǒng)調用的使用:一個簡單的應用程序#defineDEVICE_GPIOTEST“/dev/gpio”//設備入口點intmain(intargc,char*argv[]){intfd;intval=-1;fd=open(DEVICE_GPIOTEST,O_RDONLY);//打開設備if(fd<0)//fd為返回的設備文件描述字handle
{perror("cannotopendevice");exit(1);}while(1){printf("pleaseselectnumbertorunprogram\n");printf("1:ledon\n2:quit");一個簡單的應用程序scanf("%d",&val);if(val==1)ioctl(fd,1,10);/*主要用于對設備進行讀寫之外的其他控制*/elseif(val==2){close(fd);/*關閉設備*/}}return0;}設備驅動和文件系統(tǒng)的關系
●open——打開設備準備I/O操作。
open——打開設備準備I/O操作。其調用格式為:intopen(char*filename,intaccess);
該函數返回文件描述字handle,如果返回值小于0,表示打開設備文件失敗close-——close()函數的作用是關閉由open()函數打開的文件,其調用格式為:intclose(inthandle);該函數關閉文件描述字handle相連的文件。
read——從設備上讀數據。對于有緩沖區(qū)的I/O操作,一般是從緩沖區(qū)里讀數據。write——往設備上寫數據,對于有緩沖區(qū)的I/O操作,一般是把數據寫入緩沖區(qū)里。write()函數的調用格式為:intwrite(inthandle,void*buf,intcount);write()函數把count個字節(jié)從buf指向的緩沖區(qū)寫入與handle相連的文件中,返回值為實際寫入的字節(jié)數。7.1.1設備驅動和文件系統(tǒng)的關系ioctl——主要用于對設備進行讀寫之外的其他控制。用戶空間的ioctl函數的原型為:intioctl(inffd,intcmd,…)其中的…代表可變數目的參數表,實際中是一個可選參數,一般定義為:intioctl(inffd,intcmd,char*argp)其中fd為文件描述字handlecmd為命令字,不同的命令字對應不同的操作.7.1.1設備驅動和文件系統(tǒng)的關系
●open——打開設備準備I/O操作。
open——打開設備準備I/O操作。該函數返回文件描述字handle
其調用格式為:intopen(char*filename,intaccess);
close-——close()函數的作用是關閉由open()函數打開的文件,其調用格式為:intclose(inthandle);該函數關閉文件描述字handle相連的文件。
read——從設備上讀數據。對于有緩沖區(qū)的I/O操作,一般是從緩沖區(qū)里讀數據。write——往設備上寫數據,對于有緩沖區(qū)的I/O操作,一般是把數據寫入緩沖區(qū)里。write()函數的調用格式為:intwrite(inthandle,void*buf,intcount);write()函數把count個字節(jié)從buf指向的緩沖區(qū)寫入與handle相連的文件中,返回值為實際寫入的字節(jié)數。7.1.1設備驅動和文件系統(tǒng)的關系ioctl——主要用于對設備進行讀寫之外的其他控制。用戶空間的ioctl函數的原型為:intioctl(inffd,intcmd,…)其中的…代表可變數目的參數表,實際中是一個可選參數,一般定義為:intioctl(inffd,intcmd,char*argp)驅動程序中定義的ioctl方法原型為:int(*ioctl)(structinode*inode,structfile*file,unsignedintcmd,unsignedlongarg)inode和filp兩個指針對應應用程序傳遞的文件描述符fd、cmd不會被修改地傳遞給驅動程序,可選的參數arg則無論用戶應用程序使用的是指針還是其他類型值,都以unsignedlong的形式傳遞給驅動。7.1.3設備號
linux/uclinux內核還需要:主設備號標識設備對應的驅動程序。系統(tǒng)中不同的設備可以有相同的主設備號,主設備號相同的設備使用相同的驅動程序。次設備號用來區(qū)分具體驅動程序的實例。一個主設備號可能有多個設備與之對應,這多個設備正是在驅動程序內通過次設備號來進一步區(qū)分的。次設備號只能由設備驅動程序使用,內核的其他部分僅將它作為參數傳遞給驅動程序。字符型設備主設備號的添加和注銷
字符型設備主設備號的添加和注銷分別通過調用函數register_chrdev()和unregister_chrdev()實現,這兩個函數原型在<linux/fs.h>文件說明。externintregister_chrdev(unsignedintmajor,constchar*name,structfile_operations*fops);externintunregister_chrdev(unsignedintmajor,constchar*name);7.2設備驅動程序基礎
7.2.1設備驅動中關鍵數據結構在linux系統(tǒng)中,設備驅動程序所提供的這組入口點由一個文件操作結構進行說明,分別是:file_operations(文件操作)數據結構file數據結構inode數據結構它們定義于Linux/fs.h文件中。file_operations(文件操作)數據結構由于用戶進程是通過設備文件同硬件打交道的,所以對設備文件的操作不外乎一些系統(tǒng)調用,如open、read、write、close等。但是如何把系統(tǒng)調用和驅動程序關聯起來,這里需要一個非常關鍵的數據結構,既file_operations(文件操作)。它用來存儲驅動內核模塊提供的對設備進行的各種操作的函數指針。1.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);};p228在simple設備驅動程序中它的file_operations結構是如下初始化的:structfile_operationssimple_fops={.owner=THIS_MODULE,.llseek=simple_llseek,.read=simple_read,.write=simple_write,.ioctl=simple_ioctl,.open=simple_open,.release=simple_release,};對simple_fops變量進行初始化將對應操作的函數名作為指針傳遞給相應的成員在file_operations數據結構中,指出了設備驅動程序所提供的入口點位置,分別是:1)structmodule*owner該成員是file_operations結構中唯一一個不是聲明操作的成員;它是一個指向擁有這個結構的模塊的指針.這個成員用來在它的操作還在被使用時阻止模塊被卸載.幾乎所有時間中,它被簡單初始化為THIS_MODULE,一個在<linux/module.h>中定義的宏.2)loff_t(*llseek)(structfile*,loff_t,int);llseek方法用作改變文件中的當前讀/寫位置,并且新位置作為(正的)返回值.loff_t參數是一個"longoffset",并且就算在32位平臺上也至少64位寬.錯誤由一個負返回值指示.3)ssize_t(*read)(structfile*,char__user*,size_t,loff_t*);用來從設備中獲取數據.當為空時,導致read系統(tǒng)調用以-EINVAL(“Invalidargument”)失敗.一個非負返回值代表了成功讀取的字節(jié)數其中ssize_t為int或long型,和平臺相關,__user用來聲明為用戶態(tài)4)ssize_t(*aio_read)(structkiocb*,char__user*,size_t,loff_t);初始化一個異步讀--可能在函數返回前不結束的讀操作.如果這個方法是NULL,所有的操作會由read代替進行(同步地).5)ssize_t(*write)(structfile*,constchar__user*,size_t,loff_t*)發(fā)送數據給設備.如果NULL,-EINVAL返回給調用write系統(tǒng)調用的程序.如果非負,返回值代表成功寫的字節(jié)數.6)ssize_t(*aio_write)(structkiocb*,constchar__user*,size_t,loff_t*);初始化設備上的一個異步寫.7)int(*readdir)(structfile*,void*,filldir_t);對于設備文件這個成員應當為NULL;它用來讀取目錄,并且僅對文件系統(tǒng)有用.unsignedint(*poll)(structfile*,structpoll_table_struct*);poll方法用作查詢對一個或多個文件描述符的讀或寫是否會阻塞.poll方法應當返回一個位掩碼指示是否非阻塞的讀或寫是可能的,并且,可能地,提供給內核信息用來使調用進程睡眠直到I/O變?yōu)榭赡?如果一個驅動的poll方法為NULL,設備假定為不阻塞地可讀可寫.int(*ioctl)(structinode*,structfile*,unsignedint,unsignedlong);ioctl系統(tǒng)調用提供了發(fā)出設備特定命令的方法..如果設備不提供ioctl方法,系統(tǒng)調用將返回一個錯誤.int(*mmap)(structfile*,structvm_area_struct*);mmap用來請求將設備內存映射到進程的地址空間.如果這個方法是NULL,mmap系統(tǒng)調用返回-ENODEVint(*open)(structinode*,structfile*);該操作用來打開設備文件,也是對設備文件進行的第一個操作,如果這個操作為空,設備打開一直成功,但是你的驅動程序不會被調用int(*flush)(structfile*);flush操作在進程關閉它的設備文件描述符的拷貝時調用;它應當執(zhí)行(并且等待)設備的任何未完成的操作.當前,flush在很少驅動中使用.如果flush為NULL,內核簡單地忽略用戶應用程序的請求.int(*release)(structinode*,structfile*);在文件結構被釋放時引用這個操作.如同open,release可以為NULL.int(*fsync)(structfile*,structdentry*,int);這個方法是fsync系統(tǒng)調用的后端,用戶調用來刷新任何掛著的數據.如果這個指針是NULL,系統(tǒng)調用返回-EINVAL.int(*aio_fsync)(structkiocb*,int);這是fsync方法的異步版本int(*fasync)(int,structfile*,int);這個操作用來通知設備它的FASYNC標志的改變.異步通知是一個高級的主題,在第6章中描述.這個成員可以是NULL如果驅動不支持異步通知.int(*lock)(structfile*,int,structfile_lock*);lock方法用來實現文件加鎖;加鎖對常規(guī)文件是必不可少的特性,但是設備驅動幾乎從不實現它.ssize_t(*readv)(structfile*,conststructiovec*,unsignedlong,loff_t*);ssize_t(*writev)(structfile*,conststructiovec*,unsignedlong,loff_t*);該操作用來對一個包含多個內存區(qū)的單個讀或寫操作;這些系統(tǒng)調用允許它們這樣做而不必對數據進行額外拷貝.如果這些函數指針為NULL,read和write方法被調用(可能多于一次).ssize_t(*sendfile)(structfile*,loff_t*,size_t,read_actor_t,void*);這個方法實現sendfile系統(tǒng)調用的讀,使用最少的拷貝從一個文件描述符搬移數據到另一個.例如,它被一個需要發(fā)送文件內容到一個網絡連接的web服務器使用.設備驅動常常使sendfile為NULL.ssize_t(*sendpage)(structfile*,structpage*,int,size_t,loff_t*,int);sendpage是sendfile的另一半;它由內核調用來發(fā)送數據,一次一頁,到對應的文件.設備驅動實際上不實現sendpage.unsignedlong(*get_unmapped_area)(structfile*,unsignedlong,unsignedlong,unsignedlong,unsignedlong);這個方法的目的是在進程的地址空間找一個合適的位置來映射在底層設備上的內存段中.這個任務通常由內存管理代碼進行;這個方法存在為了使驅動能強制特殊設備可能有的任何的對齊請求.大部分驅動可以置這個方法為NULL.int(*check_flags)(int)這個方法允許模塊檢查傳遞給fnctl(F_SETFL...)調用的標志.int(*dir_notify)(structfile*,unsignedlong);2.
inode數據結構inode譯成中文就是索引節(jié)點。每個存儲設備或存儲設備的分區(qū)(存儲設備是硬盤、軟盤、U盤......)被格式化為文件系統(tǒng)后,應該有兩部份,一部份是inode,另一部份是Block,Block是用來存儲數據用的。而inode呢,就是用來存儲這些數據的信息,這些信息包括文件大小、屬主、歸屬的用戶組、讀寫權限等。inode為每個文件進行信息索引,所以就有了inode的數值。操作系統(tǒng)根據指令,能通過inode值最快的找到相對應的文件。做個比喻,比如一本書,存儲設備或分區(qū)就相當于這本書,Block相當于書中的每一頁,inode就相當于這本書前面的目錄,一本書有很多的內容,如果想查找某部份的內容,我們可以先查目錄,通過目錄能最快的找到我們想要看的內容。雖然不太恰當,但還是比較形象。當我們用ls查看某個目錄或文件時,如果加上-i參數,就可以看到inode節(jié)點了;比如比如hello.c文件的inode值為4446762.
inode數據結構p222文件系統(tǒng)處理的文件所需要的信息在inode(索引結點)數據結構中。Inode數據結構提供了關于特殊設備文件/dev/DriverName的信息,定義如下:
structinode稱做索引節(jié)點數據結構,inode(索引結點)數據結構定義如下:structinode{structhlist_nodei_hash;structlist_headi_list;structlist_headi_sb_list;structlist_headi_dentry;unsignedlongi_ino;atomic_ti_count;umode_ti_mode;…….}Inode結構包含了大量有關文件的信息,但通常情況下對設備驅動開發(fā)比較有用的成員有下面兩個dev_ti_rdev;//該成員包含了設備編號structcdev*i_cdev;//指向字符設備文件的指針說明:當inode指向一個字符設備文件時,該成員包含了指向structcdev結構的指針,其中cdev結構是字符設備結構體cdev結構在Linux2.6內核中一個字符設備用cdev結構來描述,其定義如下:structcdev{structkobjectkobj;
structmodule*owner;//所屬模塊conststructfile_operations*ops;//文件操作結構
structlist_headlist;
dev_tdev;//設備號unsignedintcount;};3.file數據結構設備驅動程序中,另一個非常重要的數據結構就是File結構,它不同于應用程序空間的FILE指針,FILE指針定義在C庫中因而不會出現在內核代碼中,而sturctfile只出現在內核代碼中,從不出現在用戶程序中內核用inode結構表示具體的文件,而file結構表示打開的文件描述符對于單個文件,可能會有許多個表示打開的文件描述符file結構,但是它們都指向了單個的inode結構,所以file結構和inode結構是不同的.3.file數據結構structfile{structlist_head f_list;//打開的文件形成一個列表structdentry *f_dentry;//指向相關目錄項的指針structvfsmount*f_vfsmnt;//執(zhí)行VFS掛載點的指針structfile_operations *f_op;//執(zhí)行文件操作的指針atomic_t f_count;//使用該結構的進程數unsignedint f_flags;//文件打開的標志,如讀寫等mode_t f_mode;//文件打開的模式3.file數據結構loff_t f_pos;//文件的當前位置unsignedlongf_reada,f_ramax,f_raend,f_ralen,f_rawin;/*預讀標志、要預讀的最多頁面數、上次預讀后的文件指針、預讀的字節(jié)數以及預讀的頁面數*/structfown_struct f_owner;//文件的所有者unsignedint f_uid,f_gid;//用戶的UID和GIDint f_error;//網絡寫操作錯誤碼unsignedlong f_version;//版本號void *private_data;//tty驅動程序使用structkiobuf *f_iobuf;long f_iobuf_lock;};7.2.2字符設備驅動開發(fā)p2287.2.2設備驅動程序的開發(fā)流程
(1)硬件接口設計或使用嵌入式處理器的生產商提供參考接口電路。(2)設計所要實現的文件操作,定義file_operations結構。(3)實現所需的文件操作調用,如read、write等。(2)實現初始化模塊函數__init,其中定義設備號。(3)實現初始化函數,其中實現驅動的注冊。(6)實現中斷服務,并用request_irq向內核注冊。(7)實現卸載模塊函數__exit,完成驅動的注銷,釋放設備號(7)編譯該驅動程序到內核中,或者用insmod命令加載模塊。(8)測試該設備,編寫應用程序,對驅動程序進行測試。定義file_operations結構p228在simple設備驅動程序中它的file_operations結構是如下初始化的:structfile_operationssimple_fops={.owner=THIS_MODULE,.llseek=simple_llseek,.read=simple_read,.write=simple_write,.ioctl=simple_ioctl,.open=simple_open,.release=simple_release,};對simple_fops變量進行初始化將對應操作的函數名作為指針傳遞給相應的成員7.2.2字符設備驅動開發(fā)p2287.2.2設備驅動程序的開發(fā)流程
(1)硬件接口設計或使用嵌入式處理器的生產商提供參考接口電路。(2)設計所要實現的文件操作,定義file_operations結構。(3)實現所需的文件操作調用,如open、read、write等。(2)實現初始化模塊函數__init,其中定義設備號。(3)實現初始化函數,其中實現驅動的注冊。(6)實現中斷服務,并用request_irq向內核注冊。(7)實現卸載模塊函數__exit,完成驅動的注銷,釋放設備號(7)編譯該驅動程序到內核中,或者用insmod命令加載模塊。(8)測試該設備,編寫應用程序,對驅動程序進行測試。實現所需的文件操作調用p228staticintdevice_open(structinode*inode,structfile*file)
{
printk(“simpledeviceisopen\n”);//打印一句話try_module_get(THIS_MODULE);……return0;
}
函數:try_module_get()--如果模塊已經插入內核,則遞增該模塊引用計數,每次加1;如果該模塊還沒有插入內核,則返回0表示出錯。
實現所需的文件操作調用staticintdevice_release(structinode*inode,structfile*file)
{
printk(“Simpledevice_releasecall\n");
module_put(THIS_MODULE);……return0;
}函數:module_put()--與try_module_get()相對應,該模塊引用計數減1;如果該模塊還沒有插入內核,則返回0表示出錯7.2.2字符設備驅動開發(fā)7.2.2設備驅動程序的開發(fā)流程
(1)硬件接口設計或使用嵌入式處理器的生產商提供參考接口電路。(2)設計所要實現的文件操作,定義file_operations結構。(3)實現所需的文件操作調用,如read、write等。(2)實現初始化模塊函數__init,其中定義設備號。(3)實現初始化函數,其中實現驅動的注冊。(6)實現中斷服務,并用request_irq向內核注冊。(7)實現卸載模塊函數__exit,完成驅動的注銷,釋放設備號(7)編譯該驅動程序到內核中,或者用insmod命令加載模塊。(8)測試該設備,編寫應用程序,對驅動程序進行測試。1)設備主設備號的添加和注銷p225設備驅動在加載時首先需要調用入口函數module_init(),該函數完成設備驅動的初始化工作,比如分配設備號,驅動程序的注冊,寄存器置位、結構體賦值等。1)設備主設備號的添加和注銷p226建立字符設備以前,必須分配設備號??梢允褂胷egister_chrdev_region函數完成,該函數原型如下:intregister_chrdev_region(dev_tfirst,unsignedintcount,char*name)First:是要分配的設備號范圍的起始值Count:是請求的連續(xù)設備號的數目Name:是與該編號范圍關聯的設備名稱,也就是在/proc/devices和sysfh中出現的名稱。如何能夠確定起始范圍?實際上,Linux提供了動態(tài)分配設備號的函數,通過這個函數,內核可以為我們分配合適的設備號。函數原型如下:intalloc_chrdev_region(dev_t*dev,unsignedintfirsetminor,unsignedintcount,char*name)該函數的返回值為<0,表示不能獲得設備號dev:用于輸出的參數,保存已分配范圍的第一個編號。Firstminor:是要使用的被請求的第一個次設備號,通常設置為0。Count:是請求的連續(xù)設備號的數目Name:是與該編號范圍關聯的設備名稱動態(tài)分配設備號例如:#defineCDRIVER_NAME“simple_chrev”dev_tsimple_dev;intresult,count=1,CDRIVER_MAJOR=0,CDRIVER_MINOR=0;result=alloc_chrdev_region(&simple_dev,CDRIVER_MINOR,count,CDRIVER_NAME)CDRIVER_MAJOR=MAJOR(simple_dev);If(result<0){printk(KERN_ERR“cannotgetmajor%d\n",CDRIVER_MAJOR);}將不需要的資源及時釋放是一個好的編程習慣。在不使用設備號時,也應該把它釋放,只需調用unregister_chrdev_region函數即可。函數原型如下:Voidunregister_chrdev_region(dev_tfirst,unsignedcount)例如:staticvoid__exitsimple_exit(void){unregister_chrdev_region(simple_dev,count)}7.2.2字符設備驅動開發(fā)7.2.2設備驅動程序的開發(fā)流程
(1)硬件接口設計或使用嵌入式處理器的生產商提供參考接口電路。(2)設計所要實現的文件操作,定義file_operations結構。(3)實現所需的文件操作調用,如read、write等。(2)實現初始化模塊函數__init,其中定義設備號。(3)實現初始化函數,其中實現設備驅動的注冊。(6)實現中斷服務,并用request_irq向內核注冊。(7)實現卸載模塊函數__exit,完成驅動的注銷,釋放設備號(7)編譯該驅動程序到內核中,或者用insmod命令加載模塊。(8)測試該設備,編寫應用程序,對驅動程序進行測試。linux2.4驅動的注冊和卸載在初始化中最重要的一個工作就是向內核注冊該設備.在linux2.4內核中,對于字符設備調用register_chrdev()完成注冊,對于塊設備需要調用register_blkdev()完成注冊。注
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 大班科學會唱歌的紙教學設計
- 4 我們的公共生活 第2課時維護公共利益 教學設計-2023-2024學年道德與法治五年級下冊統(tǒng)編版
- 3學會反思(第2課時)(教學設計)2023-2024學年統(tǒng)編版道德與法治六年級下冊
- 2023-2024學年高中英語 Unit 5 Music Reading for Writing 教學實錄 新人教版必修第二冊
- 11 葡萄溝 教學設計-2024-2025學年統(tǒng)編版語文二年級上冊
- 8 上課了第一課時(教學設計)2023-2024學年統(tǒng)編版道德與法治一年級上冊
- 2024年四年級英語上冊 Unit 5 I like those shoes Lesson 29教學實錄 人教精通版(三起)
- 2024年五年級英語上冊 Unit 8 where are you from教學實錄 陜旅版(三起)
- 27《設計小臺燈》教學設計-2024-2025學年青島版五四制小學科學四年級上冊
- 從城市到鄉(xiāng)村──《鄉(xiāng)下人家》教學回顧反思-教案教學設計
- 機械工程原理真題集
- 2025年甘肅甘南州國控資產投資管理集團有限公司面向社會招聘工作人員12人筆試參考題庫附帶答案詳解
- 2025年內蒙古北方職業(yè)技術學院單招職業(yè)傾向性測試題庫及答案一套
- 2025年安徽水利水電職業(yè)技術學院單招職業(yè)適應性測試題庫(含答案)
- 中國瓶裝水飲用水項目投資可行性研究報告
- 《心肌缺血心電圖》課件
- 2025年中國建筑股份有限公司招聘筆試參考題庫含答案解析
- 持續(xù)葡萄糖監(jiān)測臨床應用專家共識2024解讀
- 《胸部影像疾病診斷》課件
- DB33T 2157-2018 公共機構綠色數據中心建設與運行規(guī)范
- 健康促進機關創(chuàng)建培訓
評論
0/150
提交評論