版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
Linux驅(qū)動程序設(shè)計(jì)什么是驅(qū)動?驅(qū)動程序--DeviceDriver是指揮硬件工作的軟件。它是應(yīng)用程序與硬件之間的一個(gè)中層軟件層,為應(yīng)用程序屏蔽硬件的細(xì)節(jié)。操作系統(tǒng)只能通過驅(qū)動程序,才能控制硬件設(shè)備的工作,假如某設(shè)備的驅(qū)動程序未能正確安裝,便不能正常工作。簡單地說,驅(qū)動程序提供了硬件到操作系統(tǒng)的一個(gè)接口以及協(xié)調(diào)二者之間的關(guān)系。
主流的操作系統(tǒng)類型微內(nèi)核體系結(jié)構(gòu)
內(nèi)核只負(fù)責(zé)進(jìn)程管理、內(nèi)存管理、中斷處理等。文件系統(tǒng)、網(wǎng)絡(luò)協(xié)議等其他部分運(yùn)行與用戶空間??蓴U(kuò)展性好,不同層次之間消息傳遞比較麻煩。宏內(nèi)核體系結(jié)構(gòu)
內(nèi)核是一個(gè)大程序,包括了操作系統(tǒng)所有的部分,系統(tǒng)的的速度、性能好,但是可擴(kuò)展性和維護(hù)性相對較弱。采用模塊機(jī)制
內(nèi)核做的可很小,在內(nèi)核中設(shè)計(jì)一些模塊接口,可以動態(tài)的加載模塊,內(nèi)核管理所有模塊的運(yùn)行,系統(tǒng)功能的擴(kuò)展性留給模塊去完成。Linux內(nèi)核模塊模塊全稱:動態(tài)可加載內(nèi)核模塊(LoadableKernelModule,LKM)模塊(module)在內(nèi)核空間運(yùn)行。模塊實(shí)際上是一種目標(biāo)對象文件,沒有鏈接,不能獨(dú)立運(yùn)行,但是其代碼可以在運(yùn)行時(shí)鏈接到系統(tǒng)中作為內(nèi)核的一部分運(yùn)行或從內(nèi)核中取下,從而可以動態(tài)擴(kuò)充內(nèi)核的功能。模塊通常由一組函數(shù)和數(shù)據(jù)結(jié)構(gòu)組成。Linux的驅(qū)動設(shè)備程序大多采用模塊模式實(shí)現(xiàn)。采用模塊機(jī)制的優(yōu)點(diǎn)操作系統(tǒng)內(nèi)核更加緊湊靈活系統(tǒng)如果需要新的功能,只要編譯成相應(yīng)的模塊,然后插入操作系統(tǒng)即可。模塊一旦鏈接到內(nèi)核,就會與內(nèi)核中原有的代碼完全等價(jià)。驅(qū)動程序的安裝與卸載方法一:直接修改系統(tǒng)核心的源代碼,把設(shè)備驅(qū)動程序加進(jìn)操作系統(tǒng)核里。即和操作系統(tǒng)內(nèi)核一起編譯成為映像文件燒寫到flash中。(系統(tǒng)自帶的驅(qū)動)方法二:把設(shè)備驅(qū)動程序作為可加載的模塊,由系統(tǒng)管理員動態(tài)地加載它,使之成為核心的一部分,適合在調(diào)試階段使用。(用戶安裝的驅(qū)動)應(yīng)用程序與內(nèi)核模塊的區(qū)別應(yīng)用程序驅(qū)動程序運(yùn)行空間用戶空間內(nèi)核空間運(yùn)行入口main()函數(shù)module_init()指定出口無module_exit()編譯gcc-c,ldmakefile運(yùn)行直接運(yùn)行Insmod安裝,應(yīng)用程序或其他模塊調(diào)用設(shè)備驅(qū)動程序框架和使用操作系統(tǒng)與設(shè)備驅(qū)動之間有式進(jìn)行相互操作的標(biāo)準(zhǔn)接口:驅(qū)動設(shè)備的加載與卸載:如何把驅(qū)動加入到操作系統(tǒng)。(與模塊相關(guān)的命令)驅(qū)動程序與系統(tǒng)的接口:使得操作系統(tǒng)知道如何對硬件進(jìn)行操作。(有哪些必須的函數(shù))驅(qū)動程序與設(shè)備的接口:這部分描述了驅(qū)動程序如何與設(shè)備進(jìn)行交互,
這與具體設(shè)備密切相關(guān)。(具體的函數(shù)怎么寫)內(nèi)核模塊命令介紹insmod:向正在運(yùn)行的操作系統(tǒng)加載模塊。lsmod:顯示當(dāng)前加載的內(nèi)核模塊。rmmod:從當(dāng)前運(yùn)行的內(nèi)核中卸載內(nèi)核模塊。depmod:處理可加載內(nèi)核模塊的依賴關(guān)系。modprobe:利用depmod創(chuàng)建的依賴文件來自動加載相關(guān)內(nèi)核模塊。modinfo:獲取模塊信息。Linux設(shè)備驅(qū)動程序入口框架模塊加載函數(shù)init_module()/module_init():當(dāng)通過insmod或modporbe命令加載模塊時(shí),會被內(nèi)核自動執(zhí)行,主要完成模塊的初始化工作。模塊卸載函數(shù)cleanup_module()/module_exit():當(dāng)通過rmmod命令卸載模塊時(shí)自動執(zhí)行,主要完成模塊的卸載工作。模塊許可證聲明MODULE_LICENSE():描述內(nèi)核的許可權(quán)限,否則加載模塊會提示內(nèi)核被污染。DaulBSD、GPL、GPLv2等模塊參數(shù)module_param():(可選)模塊導(dǎo)出符號EXPORT_SYMBOL_GPL():(可選)模塊作者聲明MODULE_AUTHOR()等:(可選)#include<linux/init.h>#include<linux/module.h>MODULE_LICENSE("GPLv2");intinit_module(void){printk(KERN_ALERT"helloworld!\n");return0;}voidcleanup_module(void){printk(KERN_ALERT"goodbyeworld!\n");}hello.c最簡單的模塊(一)相關(guān)的頭文件許可證聲明#include<linux/init.h>#include<linux/module.h>MODULE_LICENSE("GPLv2");intinit_module(void){printk(KERN_INFO"helloworld!\n");return0;}voidcleanup_module(void){printk(KERN_ALERT"goodbyeworld!\n");}最簡單的模塊(一)模塊加載函數(shù),insmod時(shí)運(yùn)行內(nèi)核中的輸出函數(shù),與printf類似printk(KERN_INFO"helloworld!\n");printk("<級別>helloworld!\n");內(nèi)核中使用的打印信息,相當(dāng)于printf一般級別中可以使用0-7表示打印信息的等級,其中0級最高。#include<linux/module.h>MODULE_LICENSE("GPLv2");intinit_module(void){printk(KERN_INFO"helloworld!\n");return0;}voidcleanup_module(void){printk(KERN_INFO"goodbyeworld!\n");}hello.c最簡單的模塊(一)模塊卸載函數(shù),rmmod時(shí)運(yùn)行pc端Makefileobj-m=hello.oKERNELDIR:=/lib/modules/$(shelluname-r)/buildPWD:=$(shellpwd)modules: $(MAKE)-C$(KERNELDIR)M=$(PWD)modulesclean: rm-rf*.o~core.depend*.ko*.mod.c*.*.cmd rm–rf.tmp_versions*.order*.symvers要編譯的結(jié)果,名字和驅(qū)動源程序相同正在運(yùn)行的操作系統(tǒng)內(nèi)核源碼目錄。也就是編譯模塊需要的環(huán)境指定源文件的位置獲得當(dāng)前Linux版本號開發(fā)板端的Makefileobj-m=hello.oKERNELDIR:=/root/yizhi/boot-kernel-source/iTop4412_Kernel_3.0PWD:=$(shellpwd)modules: $(MAKE)-C$(KERNELDIR)M=$(PWD)modulesclean: rm-rf*.o~core.depend*.ko*.mod.c*.*.cmd rm-rf.tmp_versions*.order*.symvers開發(fā)板上的操作系統(tǒng)內(nèi)核源碼目錄。ubuntu系統(tǒng)屏蔽了內(nèi)核信息的輸出,方法:打開另一個(gè)終端,里面直接輸入whiletruedodmesg–csleep1done已經(jīng)把這些命令寫成了一個(gè)文件,只要直接運(yùn)行該shell文件即可。cd/home/xitee/drivers./kernel-show-info.sh這個(gè)終端就會每1秒查看當(dāng)前系統(tǒng)的日志并清空,此時(shí)可以在另一個(gè)終端運(yùn)行驅(qū)動,同時(shí)在這個(gè)終端看到輸出。在ubuntu系統(tǒng)中如何隨時(shí)內(nèi)核的打印信息make--編譯成目標(biāo)文件(*.o或者*.ko)。insmodhello.ko--加載hello.ko驅(qū)動模塊lsmod--查看模塊是否加載rmmodhello--卸載hello模式lsmod--查看模塊是否還存在內(nèi)核操作步驟最簡單的模塊(二)#include<linux/init.h>#include<linux/module.h>MODULE_LICENSE(“DualBSP/GPL”);static__init
inthello_init(void)//自己定義的初始化函數(shù){
printk(“<0>helloworld!Iamcoming!\n”);return0;}
static
__exitvoid
hello_exit(void)//自己定義的卸載函數(shù){printk(“<0>seeyou!\n”);return;}module_init(hello_init);module_exit(hello_exit);hello.cinsmod時(shí)調(diào)用,利用宏module_init說明入口rmmod時(shí)調(diào)用,利用宏module_exit說明入口module_init和module_exit宏定義所在頭文件static
int_
_inithello_init(void)static
void_
_exithello_exit(void)static聲明,因?yàn)檫@種函數(shù)在特定文件之外沒有其它意義__init標(biāo)記,該函數(shù)只在初始化期間使用。模塊裝載后,將該函數(shù)占用的內(nèi)存空間釋放,性能優(yōu)化__exit標(biāo)記
該代碼僅用于模塊卸載,性能優(yōu)化。模塊參數(shù)module_param()的使用#include<linux/init.h>#include<linux/module.h>MODULE_LICENSE(“GPLv2”);staticintnum=10;staticchar*name=“xit”;module_param(num,int,S_IRUGO);module_param(name,charp,S_IRUGO);static__init
inthello_init(void{printk(KERN_INFO”helloworld!Iamcoming!\n”);printk(KERN_INFO“num=:%d\n",num);printk(KERN_INFO“name=:%s\n",name);return0;}static
__exitvoid
hello_exit(void){printk(KERN_INFO“seeyou!\n”);return;}module_init(hello_init);module_exit(hello_exit);操做步驟make--編譯成目標(biāo)文件(*.o或者*.ko)。insmodhello.ko--不帶參數(shù)加載hello.ko驅(qū)動模塊rmmodhello--卸載hello模式insmodhello.konum=20name=‘xiamen’--帶參數(shù)加載驅(qū)動rmmodhello--卸載hello模式模塊導(dǎo)出符號EXPORT_SYMBOL()的使用#include<linux/init.h>#include<linux/module.h>MODULE_LICENSE(“GPLv2”);static__init
inthello_init(void){printk(KERN_INFO”helloworld!Iamcoming!\n”);return0;}static
__exitvoid
hello_exit(void){printk(KERN_INFO“seeyou!\n”);return;}voidfunc_for_test(void){printk(KERN_INFO“justfortest!\n”);return;}EXPORT_SYMBOL(func_for_test);module_init(hello_init);module_exit(hello_exit);其他模塊引用導(dǎo)出的符號#include<linux/init.h>#include<linux/module.h>externvoidfunc_for_test(void);MODULE_LICENSE(“GPLv2”);static__init
inttest_init(void{printk(KERN_INFO”thisistestdriver!\n”);func_for_test();return0;}static
__exitvoid
test_exit(void){printk(KERN_INFO“seeyou!\n”);return;}module_init(test_init);module_exit(test_exit);testdriver.c操做步驟1、hello_driver.c和對應(yīng)的Makefile放在hello_driver目錄下,編譯并運(yùn)行makeinsmodhello_driver.ko--加載hello.ko驅(qū)動模塊2、test_driver.c和對應(yīng)的Makefile放在test_driver目錄下,編譯并運(yùn)行makeinsmodtestdriver.ko--加載testdriver.ko驅(qū)動模塊3、rmmodtestdriver--卸載testdriver模式4、rmmodhello--卸載hello模式注意:如執(zhí)行步驟2出現(xiàn)“Unknownsymbolfunc_for_test(err-22)”錯(cuò)誤,把hello_driver目錄下的Module.symvers放test_driver目錄下,重新執(zhí)行步驟2即可,func_for_test的符號信息就會鏈接進(jìn)去。Linux驅(qū)動程序的實(shí)現(xiàn)機(jī)制Linux系統(tǒng)中每一類設(shè)備都有一個(gè)驅(qū)動程序,設(shè)備驅(qū)動程序存放在內(nèi)核中,不同的應(yīng)用可以共享這些代碼。在Linux系統(tǒng)中,每一個(gè)設(shè)備體現(xiàn)為/dev目錄下的一個(gè)文件一個(gè)驅(qū)動程序就是一個(gè)函數(shù)和數(shù)據(jù)結(jié)構(gòu)的集合,是一組特殊的接口,封裝了對設(shè)備的各種控制,應(yīng)用程序通過訪問對應(yīng)的設(shè)備文件來調(diào)用相關(guān)的處理函數(shù)。驅(qū)動的類型在Linux中,驅(qū)動分為三種類型:字符設(shè)備(chardevice):每次進(jìn)行輸入輸出操作時(shí)只需要傳送1個(gè)字符,如鼠標(biāo)、鍵盤、串口等。塊設(shè)備(blockdevice)
:傳送的數(shù)據(jù)量大,而且使用隨機(jī)訪問的方式傳輸數(shù)據(jù),數(shù)據(jù)總是具有固定大小的塊,保存在系統(tǒng)的緩沖塊中。如硬盤,光盤。網(wǎng)絡(luò)設(shè)備(netdevice)
:通過SOCKET訪問網(wǎng)絡(luò)設(shè)備。Linux設(shè)備文件設(shè)備文件都保存在/dev目錄下,分為字符設(shè)備文件和塊設(shè)備。文件每個(gè)設(shè)備文件都有主設(shè)備號和次設(shè)備號。系統(tǒng)通過主、次設(shè)備號找到具體的硬件。c:字符設(shè)備b:塊設(shè)備主設(shè)備號次設(shè)備號Linux設(shè)備號29在內(nèi)核中,設(shè)備號是一個(gè)數(shù)字,是設(shè)備的標(biāo)志,由主設(shè)備號和次設(shè)備號組成。主設(shè)備號表明某一類設(shè)備,主設(shè)備號相同的設(shè)備使用相同的驅(qū)動程序;(12位)次設(shè)備號用來標(biāo)識具體設(shè)備的實(shí)例。(20位)例如,系統(tǒng)中塊設(shè)備IDE硬盤的主設(shè)備號是3,而多個(gè)IDE硬盤及其各個(gè)分區(qū)分別賦予次設(shè)備號1、2、……設(shè)備文件與設(shè)備號30Linux內(nèi)核中通過主、次設(shè)備號識別一個(gè)設(shè)備,但是從應(yīng)用程序程序員的角度,設(shè)備號用起來很麻煩。Linux應(yīng)用程序通過設(shè)備文件來管理具體硬件。主、次設(shè)備號主:250次:0創(chuàng)建設(shè)備文件
設(shè)備文件的創(chuàng)建和普通文件不同,必須使用特殊的命令mknod來把設(shè)備映射為特別文件。其它程序需要使用這個(gè)設(shè)備的時(shí)候,對此特別文件進(jìn)行操作。
用法:
mknod文件名
設(shè)備類型
主設(shè)備號
次設(shè)備號
例如:mknod/dev/ttyS0c464mknod/dev/democ2540
mknod/dev/democ2540fd=open(“/dev/demo”,O_RDWR);read(fd,buf,100);ioctl(fd,1,2);close(fd);驅(qū)動開發(fā)流程33
在驅(qū)動加載時(shí)必須完成設(shè)備的注冊,并完成設(shè)備功能的登記。在驅(qū)動卸載時(shí)將設(shè)備加載。舊的設(shè)備注冊函數(shù)--register_chrdev和設(shè)備函卸載數(shù)--unregister_chrdev已經(jīng)很少用。moudle_init()moudle_exit()structcdev{structkobjectkobj;//內(nèi)嵌的kobject對象structmodule*owner;//所屬模塊structfile_operations*ops;//文件操作結(jié)構(gòu)體
dev_tdev;//設(shè)備號
unsignedintcount;
}cdev結(jié)構(gòu)體在Linux內(nèi)核中,使用cdev結(jié)構(gòu)體來描述一個(gè)字符設(shè)備MAJOR(dev_tdev)MINOR(dev_tdev)MKDEV(intmajor,intminor)cdev_alloc()--動態(tài)申請一塊cdev類型的內(nèi)存;
也可不用該函數(shù)而直接定義一個(gè)cdev的變量cdev_init()--初始化cdev成員cdev_add()--向系統(tǒng)添加一個(gè)cdev,完成字符設(shè)備的注冊cdev_del()--從系統(tǒng)中刪除一個(gè)cdev,完成字符設(shè)備的注銷對cdev結(jié)構(gòu)體的操作函數(shù)structcdev{structkobjectkobj;//內(nèi)嵌的kobject對象structmodule*owner;//所屬模塊structfile_operations*ops;//文件操作結(jié)構(gòu)體
dev_tdev;//設(shè)備號
unsignedintcount;
}申請?jiān)O(shè)備號在調(diào)用cdev_add向系統(tǒng)添加一個(gè)cdev前要先申請?jiān)O(shè)備號,可采用兩種方式:已知設(shè)備號:系統(tǒng)自動分配設(shè)備號:釋放設(shè)備號當(dāng)調(diào)用cdev_del()完成字符設(shè)備的注銷后,也要調(diào)用函數(shù)釋放設(shè)備號向系統(tǒng)申請和釋放設(shè)備號intregister_chrdev_region(dev_tfrom,unsignedcount,constchar*name)intalloc_chrdev_region(dev_t*dev,unsignedbaseminor,unsignedcount,constchar*name)voidunregister_chrdev_region(dev_tfrom,unsignedcount);該結(jié)構(gòu)體規(guī)定該設(shè)備可以有什么操作,對應(yīng)的功能是用戶空間中的對設(shè)備文件的文件操作。file_operations結(jié)構(gòu)體structfile_operations{structmodule*owner;loff_t(*llseek)(structfile*,loff_t,int);ssize_t(*read)(structfile*,char__user*,size_t,loff_t*);ssize_t(*write)(structfile*,constchar__user*,size_t,loff_t*);long(*unlocked_ioctl)(structfile*,unsignedint,unsignedlong);int(*open)(structinode*,structfile*);int(*release)(structinode*,structfile*);…}lseek()read()write()ioctl()open()close()對設(shè)備文件的操作
設(shè)備文件也是一個(gè)文件,因此,從應(yīng)用程序看來,應(yīng)用程序可以想使用普通文件一樣使用硬件設(shè)備。常用的文件操作有:intopen(constchar*filename,intflags);
如fd=open(“/dev/demo”,O_RDWR);intclose(intfd);
如:close(fd);intread(intfd,void*buf,size_tcount);intwrite(intfd,void*buf,size_tcount);intioctl(intfd,unsignedintcmd,…);…#include<unistd.h>#include<sys/ioctl.h>#include<stdio.h>#include<stdlib.h>#include<fcntl.h>intmain(){ intfd; intresult=0;charbuf[10]; fd=open("/dev/demo",O_RDWR); if(fd<0){ printf("####DEMOdeviceopenfail####\n"); return(-1); } result=write(fd,buf,5); printf("write%dbytes\n",result); result=read(fd,buf,4); printf("read%dbytes\n",result);ioctl(fd,1,NULL) close(fd);}測試代碼gcc
–o
test
test.cfile結(jié)構(gòu)structfile{ structpath f_path;//文件路徑 unsignedint f_flags; fmode_t f_mode;//文件的訪問模式
loff_t f_pos;//文件光標(biāo)的當(dāng)前位置…}驅(qū)動程序與設(shè)備的接口操作含義read從設(shè)備中讀取數(shù)據(jù)。write向字符設(shè)備中寫入數(shù)據(jù)。unlocked_ioctl控制設(shè)備,除讀寫操作外的其他控制命令。open打開設(shè)備并初始化設(shè)備。release關(guān)閉設(shè)備并釋放資源。llseek重新定位讀寫位置。mmap將設(shè)備內(nèi)存映射到進(jìn)程地址空間,通常只用于塊設(shè)備。flush清除內(nèi)容,一般只用于網(wǎng)絡(luò)文件系統(tǒng)中。fsync實(shí)現(xiàn)內(nèi)存與設(shè)備的同步,如將內(nèi)存數(shù)據(jù)寫入硬盤。fasync實(shí)現(xiàn)內(nèi)存與設(shè)備之間的異步通訊。lock文件鎖定,用于文件共享時(shí)的互斥訪問。內(nèi)核空間和用戶空間之間的數(shù)據(jù)復(fù)制在用戶空間不能直接訪問內(nèi)核空間的內(nèi)存,因此借助下面兩個(gè)函數(shù):把數(shù)據(jù)從用戶空間拷貝到內(nèi)核空間,一般用在write函數(shù)把數(shù)據(jù)從內(nèi)核空間拷貝到用戶空間,一般用在read函數(shù)unsignedlongcopy_from_user(void*to,constvoid__user*from,unsignedlongcount);unsignedlongcopy_to_user(void__user*to,constvoid*from,unsignedlongcount);編寫驅(qū)動,該驅(qū)動具有以下功能:在內(nèi)核中有1k的存儲空間。用戶可以向該驅(qū)動寫入數(shù)據(jù)用戶可以從驅(qū)動讀數(shù)據(jù)用戶可以定位從哪個(gè)位置開始讀數(shù)據(jù)用戶可以將全部數(shù)據(jù)清0用戶可以獲取數(shù)據(jù)的個(gè)數(shù)用戶可以將內(nèi)核中的數(shù)據(jù)倒序….編寫應(yīng)用程序測試該驅(qū)動。虛擬字符設(shè)備驅(qū)動虛擬字符設(shè)備驅(qū)動框架#include<linux/init.h>#include<linux/module.h>#include<linux/fs.h>#include<linux/cdev.h>#include<linux/uaccess.h>MODULE_LICENSE("GPLv2");#defineDEVICE_MAJOR0#defineDEVICE_MINOR0#defineDEVICE_NUM1#defineIO_CMD_CLEAR0#defineIO_CMD_11staticintmajor=DEVICE_MAJOR;//主設(shè)備號,次設(shè)備號staticintminor=DEVICE_MINOR;structcdevvirtual_dev_cdev;module_param(major,int,S_IRUGO);//主設(shè)備號可以通過輸入改變VirtuleCharDev.c默認(rèn)設(shè)備號為0定義ioctl的輸入命令定義系統(tǒng)的設(shè)備變量/****調(diào)用open()對應(yīng)的函數(shù)***************/staticintvirtual_dev_open(structinode*inode,structfile*file){ printk(KERN_INFO"deviceopensucess!\n"); return0;}/****調(diào)用close()對應(yīng)的函數(shù)***************/staticintvirtual_dev_release(structinode*inode,structfile*filp){ printk("devicerelease\n"); return0;}/*********write對應(yīng)的函數(shù)****/staticssize_tvirtual_dev_write(structfile*file,constchar__user*buf,size_tcount,loff_t*f_ops){ printk(KERN_INFO"userwritedatatodriver\n"); return2;}/*********read()對應(yīng)的函數(shù)****/staticssize_tvirtual_dev_read(structfile*file,char__user*buf,size_tcount,loff_t*f_ops){ printk(KERN_INFO"userreaddatafromdriver\n"); return1;}/******lseek()對應(yīng)的函數(shù)**/staticloff_tvirtual_dev_seek(structfile*file,loff_toffset,intorig){ printk(KERN_INFO"virtual_dev_seek\n"); return0;}/******ioctl()對應(yīng)的函數(shù)**/staticlongvirtual_dev_ioctl(structfile*file,unsignedintcmd,unsignedlongarg){ printk("<4>ioctlruning\n"); switch(cmd){ caseIO_CMD_CLEAR:printk("<4>runingcommand0\n");break; caseIO_CMD_1:printk("<4>runingcommand1\n");break; default:printk("<4>errorcmdnumber\n");break; } return0;}staticstructfile_operationsvirtual_dev_fops={
.owner=THIS_MODULE,
.llseek=virtual_dev_seek,
.write=virtual_dev_write,
.read=virtual_dev_read,
.unlocked_ioctl=virtual_dev_ioctl,
.open=virtual_dev_open,
.release=virtual_dev_release};/******模塊初始化函數(shù)**/staticint__initvirtual_dev_init(void){intret=0;dev_tdevno;printk(KERN_INFO"virtual_dev_init!\n");if(major>0){ printk(KERN_INFO"staticadev_regionmajoris%d!\n",major); devno=MKDEV(major,minor); ret=register_chrdev_region(devno,DEVICE_NUM,"char_test");}else{/*動態(tài)注冊設(shè)備號*/
ret=alloc_chrdev_region(&devno,minor,DEVICE_NUM,"char_test");
major=MAJOR(devno);/*獲得主設(shè)備號*/ printk(KERN_INFO"dynamicadev_regionmajoris%d!\n",major);}
如果已經(jīng)有主設(shè)備號,直接向系統(tǒng)申請系統(tǒng)分配一個(gè)尚未使用的設(shè)備號if(ret<0){ printk(KERN_INFO"register_chrdev%dfailed!\n",major);}else{
cdev_init(&virtual_dev_cdev,&virtual_dev_fops); virtual_dev_cdev.owner=THIS_MODULE; ret=cdev_add(&virtual_dev_cdev,devno,DEVICE_NUM); if(ret<0) { printk(KERN_INFO"cdev_add%disfailed!\n",ret);
unregister_chrdev_region(devno,DEVICE_NUM); }}returnret;}設(shè)備號申請成功,則向系統(tǒng)注冊設(shè)備/******模塊卸載函數(shù)**/staticvoid__exitvirtual_dev_exit(void){ dev_tdevno=MKDEV(major,minor);
cdev_del(&virtual_dev_cdev);
unregister_chrdev_region(devno,DEVICE_NUM); printk(KERN_INFO"virtual_dev_exit!\n");return;}module_init(virtual_dev_init);module_exit(virtual_dev_exit);系統(tǒng)注銷設(shè)備系統(tǒng)注銷設(shè)備號操作步驟:makeinsmodVirtuleCharDev.komknod/dev/virtdevc2500#include<stdio.h>#include<stdlib.h>#include<fcntl.h>#include<unistd.h>#include<sys/ioctl.h>staticchar*devfile="/dev/virtdev";intMAX_LEN=32;intmain(){ intfd,i,num=0; charbuf[256]; for(i=0;i<MAX_LEN;i++){ buf[i]=i; } 測試程序gcc
–o
test_virtdev
test_virtdev.ctest_virtdev.c
fd=open(devfile,O_RDWR); if(fd<0){ printf("####virtdevdeviceopenfail####\n"); return(-1); }
num=write(fd,buf,MAX_LEN); printf("write%dbytesdatato/dev/virtdev,returnnum=%d\n",MAX_LEN,num);
num=lseek(fd,4,SEEK_SET); printf("seek4bytesfromcur,returnnum=%d\n",num);
num=read(fd,buf,MAX_LEN);printf("Read%dbytesdatafrom/dev/virtdev,returnnum=%d\n",MAX_LEN,num);
ioctl(fd,1,NULL);
ioctl(fd,4,NULL);
close(fd); return0;}測試open測試write測試lseek測試read測試ioctl,命令1和4測試close操作步驟./test_virtdev驅(qū)動內(nèi)核消息測試程序消息rmmodVirtuleCharDev
編寫驅(qū)動,該驅(qū)動具有以下功能:在內(nèi)核中有1k的存儲空間。用戶可以向該驅(qū)動寫入數(shù)據(jù)用戶可以從驅(qū)動讀數(shù)據(jù)用戶可以定位從哪個(gè)位置開始讀數(shù)據(jù)用戶可以將全部數(shù)據(jù)清0用戶可以獲取數(shù)據(jù)的個(gè)數(shù)用戶可以將內(nèi)核中的數(shù)據(jù)倒序….編寫應(yīng)用程序測試該驅(qū)動。虛擬字符設(shè)備驅(qū)動虛擬字符設(shè)備驅(qū)動功能實(shí)現(xiàn)staticintmajor=DEVICE_MAJOR;//主設(shè)備號,次設(shè)備號staticintminor=DEVICE_MINOR;structcdevvirtual_dev_cdev;//定義1024的內(nèi)存區(qū)#defineBUFFER_SIZE1024staticcharMemBuff[BUFFER_SIZE];//主設(shè)備號可以通過輸入改變module_param(major,int,S_IRUGO);虛擬字符設(shè)備驅(qū)動功能實(shí)現(xiàn)staticssize_tvirtual_dev_write(structfile*file,constchar__user*buf,size_tcount,loff_t*f_ops){ inticount=count; unsignedlongcur_pos=*f_ops; printk(KERN_INFO"userwritedatatodriver\n"); if(cur_pos+icount>BUFFER_SIZE)
icount=BUFFER_SIZE-cur_pos;
copy_from_user(&MemBuff[cur_pos],buf,icount); *f_ops=cur_pos+icount; returnicount;num=write(fd,buf,MAX_LEN);虛擬字符設(shè)備驅(qū)動功能實(shí)現(xiàn)staticssize_tvirtual_dev_read(structfile*file,char__user*buf,size_tcount,loff_t*f_ops){ inticount=count; unsignedlongcur_pos=*f_ops; printk(KERN_INFO"userreaddatafromdriver\n"); if(cur_pos+icount>BUFFER_SIZE) icount=BUFFER_SIZE-cur_pos;
copy_to_user(buf,&MemBuff[cur_pos],icount); *f_ops=cur_pos+icount; returnicount;num=read(fd,buf,MAX_LEN);虛擬字符設(shè)備驅(qū)動功能實(shí)現(xiàn)staticloff_tvirtual_dev_seek(structfile*file,loff_toffset,intorig){ intret=0; printk(KERN_INFO"virtual_dev_seek\n"); switch(orig){ case0: if(offset<0||offset>BUFFER_SIZE){ret=-1;} else{
file->f_pos=offset; ret=offset; } break;
num=lseek(fd,4,SEEK_SET);虛擬字符設(shè)備驅(qū)動功能實(shí)現(xiàn) case1: if(file->f_pos+offset<0||file->f_pos+offset>BUFFER_SIZE) {ret=-1;} else{
file->f_pos=file->f_pos+offset; ret=file->f_pos; } break;
default: printk(KERN_INFO“wrongposition\n"); ret=-1;break; } returnret;}num=lseek(fd,4,SEEK_CUR);虛擬字符設(shè)備驅(qū)動功能實(shí)現(xiàn)staticlongvirtual_dev_ioctl(structfile*file,unsignedintcmd,unsignedlongarg){ switch(cmd){ caseIO_CMD_CLEAR: printk("<4>runingcommand1\n");
memset(MemBuff,0,BUFFER_SIZE); break; caseIO_CMD_2:printk("<4>runingcommand2\n");break; default: printk("<4>errorcmdnumber\n");break; } return0;}num=ioctl(fd,1,NULL);操作步驟makeinsmodVirtuleCharDev.ko./test_virtdev觀察驅(qū)動內(nèi)核消息和測試程序消息,檢驗(yàn)驅(qū)動功能的正確性rmmodVirtuleCharDev
關(guān)于IOCTL命令cmd的命名法staticlongvirtual_dev_ioctl(structfile*file,unsignedintcmd,unsignedlongarg){ switch(cmd){ caseIO_CMD_CLEAR: printk("<4>runingcommand1\n"); memset(MemBuff,0,BUFFER_SIZE); break; caseIO_CMD_2:printk("<4>runingcommand2\n");break; default: printk("<4>errorcmdnumber\n");break; } return0;}關(guān)于IOCTL命令的命名法#include<stdio.h>#include<stdlib.h>#include<fcntl.h>#include<unistd.h>#include<sys/ioctl.h>staticchar*devfile="/dev/virtdev";intmain(){ intfd,i,num; fd=open(devfile,O_RDWR); if(fd<0){ printf("####virtdevdeviceopenfail####\n"); return(-1); }i=ioctl(fd,1,NULL); i=ioctl(fd,2,NULL); close(fd); return0;}
命令2的調(diào)試信息沒有打印出來!每個(gè)cmd被分為了4個(gè)段,每一段都有各自的意義,隨意定cmd的問題:1、有一些cmd被系統(tǒng)過濾2、若有兩個(gè)不同的設(shè)備,但它們的ioctl的cmd相同,如果不小心打開錯(cuò)設(shè)備,并且調(diào)用ioctl,容易導(dǎo)致系統(tǒng)錯(cuò)誤。序數(shù):用這個(gè)數(shù)來給自己的命令編號,占8bit(_IOC_NRBITS),用戶自己使用,可以從1開始排序幻數(shù):說是個(gè)0~0xff的數(shù),占8bit(_IOC_TYPEBITS)。這個(gè)數(shù)是用來區(qū)分不同的驅(qū)動的,ioctl-number.txt中有說明那些幻數(shù)被使用過數(shù)據(jù)傳輸方向:占2bit(_IOC_DIRBITS)。如果涉及到要傳參,內(nèi)核要求描述一下傳輸?shù)姆较?,傳輸?shù)姆较蚴且詰?yīng)用層角度來描述。1)_IOC_NONE:值為0,無數(shù)據(jù)傳輸。2)_IOC_READ:值為1,從設(shè)備驅(qū)動讀取數(shù)據(jù)。3)_IOC_WRITE:值為2,往設(shè)備驅(qū)動寫入數(shù)據(jù)。4)_IOC_READ|_IOC_WRITE:雙向數(shù)據(jù)傳輸。數(shù)據(jù)大?。号c體系結(jié)構(gòu)相關(guān),ARM下占14bit(_IOC_SIZEBITS),如果數(shù)據(jù)是int,內(nèi)核給這個(gè)賦的值就是sizeof(int)。內(nèi)核用于生成cmd的命令:_IO(type,nr)//沒有參數(shù)的命令_IOR(type,nr,size)//該命令是從驅(qū)動讀取數(shù)據(jù)_IOW(type,nr,size)//該命令是從驅(qū)動寫入數(shù)據(jù)_IOWR(type,nr,size)//雙向數(shù)據(jù)傳輸內(nèi)核用于拆分cmd的命令:_IOC_DIR(cmd)//從命令中提取方向_IOC_TYPE(cmd)//從命令中提取幻數(shù)_IOC_NR(cmd)//從命令中提取序數(shù)_IOC_SIZE(cmd)//從命令中提取數(shù)據(jù)大小//VirtualCharDev.h#include<linux/ioctl.h>//內(nèi)核空間#defineIOC_MAGIC'x‘/*定義設(shè)備類型*/#defineIO_CMD_CLEAR_IO(IOC_MAGIC,0)#defineIO_CMD_2_IO(IOC_MAGIC,1)#defineIOC_MAXNR2//test_demo.h#include<sys/ioctl.h>//用戶空間#defineIOC_MAGIC'x‘/*定義設(shè)備類型*/#defineIO_CMD_CLEAR_IO(IOC_MAGIC,0)#defineIO_CMD_2_IO(IOC_MAGIC,1)#defineIOC_MAXNR2驅(qū)動程序VirtualCharDev.c修改#include"VirtualCharDev.h“//#defineIO_CMD_CLEAR1//#defineIO_CMD_22staticlongvirtual_dev_ioctl(structfile*file,unsignedintcmd,unsignedlongarg){ switch(cmd){ caseIO_CMD_CLEAR: printk("<4>runingcommand1\n"); memset(MemBuff,0,BUFFER_SIZE); break; caseIO_CMD_2:printk("<4>runingcommand2\n");break; default: printk("<4>errorcmdnumber\n");break; } return0;}修改測試程序#include<stdio.h>#include<stdlib.h>#include<fcntl.h>#include<unistd.h>#include“test_demo.h”staticchar*devfile="/dev/virtdev";intmain(){ intfd,i,num; fd=open(devfile,O_RDWR); if(fd<0){ printf("####virtdevdeviceopenfail####\n"); return(-1); }i=ioctl(fd,IO_CMD_CLEAR,NULL); i=ioctl(fd,IO_CMD_2,NULL); close(fd); return0;}
自動生成設(shè)備節(jié)點(diǎn)#include<stdio.h>#include<stdlib.h>#include<fcntl.h>#include<unistd.h>#include“test_demo.h”staticchar*devfile="/dev/virtdev";intmain(){ intfd,i,num; fd=open(devfile,O_RDWR); if(fd<0){ printf("####virtdevdeviceopenfail####\n"); return(-1); }i=ioctl(fd,IO_CMD_CLEAR,NULL); i=ioctl(fd,IO_CMD_2,NULL); close(fd); return0;}
修改驅(qū)動程序#include<linux/init.h>#include<linux/module.h>#include<linux/fs.h>#include<linux/cdev.h>#include<linux/uaccess.h>#include<linux/device.h>MODULE_LICENSE("GPLv2");#defineDEVICE_MAJOR0#defineDEVICE_MINOR0#defineDEVICE_NUM1#defineDEVICE_NAME“virtdev"staticstructclass*virt_class;staticintmajor=DEVICE_MAJOR;//主設(shè)備號,次設(shè)備號staticintminor=DEVICE_MINOR;structcdevvirtual_dev_cdev;module_param(major,int,S_IRUGO);//主設(shè)備號可以通過輸入改變VirtuleCharDev.c定義設(shè)備名稱/******模塊初始化函數(shù)**/staticint__initvirtual_dev_init(void){… cdev_init(&virtual_dev_cdev,&virtual_dev_fops); virtual_dev_cdev.owner=THIS_MODULE; ret=cdev_add(&virtual_dev_cdev,devno,DEVICE_NUM); if(ret<0) { printk(KERN_INFO"cdev_add%disfailed!\n",ret); unregister_chrdev_region(devno,DEVICE_NUM); }
else{
virt_class=class_create(THIS_MODULE,DEVICE_NAME);
device_create(virt_class,NULL,devno,NULL,DEVICE_NAME); }…} 向內(nèi)核申請?jiān)O(shè)備號,并登記了字符設(shè)備化后,創(chuàng)建設(shè)備文件節(jié)點(diǎn)修改驅(qū)動程序/******模塊卸載函數(shù)**/staticvoid__exitvirtual_dev_exit(void){ dev_tdevno=MKDEV(major,minor);
device_destroy(virt_class,devno);
class_unregister(virt_class);
class_destroy(virt_class);
cdev_del(&virtual_dev_cdev);
unregister_chrdev_region(devno,DEVICE_NUM); printk(KERN_INFO"virtual_dev_exit!\n");return;});系統(tǒng)注銷設(shè)備系統(tǒng)注銷設(shè)備號刪除設(shè)備節(jié)點(diǎn)修改驅(qū)動程序驅(qū)動任務(wù):1、輸入虛擬字符設(shè)備驅(qū)動和測試程序并運(yùn)行,分析驅(qū)動代碼與測試代碼之間的關(guān)系。2、在ioctl增加命令:輸出自己的學(xué)號和姓名。3、在ioctl增加命令:將曾經(jīng)寫入的數(shù)據(jù)倒序。4、在測試程序中調(diào)動對應(yīng)的ioctl進(jìn)行測試。作業(yè)截圖:ioctl的代碼、測試代碼、運(yùn)行結(jié)果編寫LED驅(qū)動以及測試程序開發(fā)板的LED燈電路
GPIO配置參數(shù)宏定義GPIO宏定義文件都是由原廠提供,是已經(jīng)做好的,屬于BSP板級開發(fā)包。在源碼目錄中arch/arm/mach-exynos/include/mach/gpio-exynos4.h所有的GPIO都已經(jīng)定義Led的宏定義:EXYNOS4_GPL2(0)EXYNOS4_GPK1(1)配置函數(shù)在源碼目錄中使用命令arch/arm/plat-samsung/include/plat/gpio-cfg.h”一般來說帶有s3cxxx的函數(shù)就是三星平臺能夠通用的函數(shù)。s3c_gpio_cfgpin(unsignedintpin,unsignedintto);參數(shù)pin,管腳參數(shù)to,配置參數(shù)。GPIO的功能配置S3C_GPIO_INPUTS3C_GPIO_OUTPUTS3C_GPIO_SFN()s3c_gpio_cfgpin(EXYNOS4_GPL2(0),S3C_GPIO_OUTPUT);s3c_gpio_cfgpin(EXYNOS4_GPK1(1),S3C_GPIO_OUTPUT);將GPIO配置為輸出模式之后,還需要給GPIO賦值,一般就是高電平和低電平兩種,高電平為1和低電平為0函數(shù)所需要的頭文件:linux/gpio.hGPIO賦值voidgpio_set_value(unsignedgpio,intvalue)intgpio_get_value(unsignedgpio)gpio_set_value(EXYNOS4_GPL2(0),1)gpio_set_value(EXYNOS4_GPK1(1),0)編寫簡單應(yīng)用調(diào)用LED管腳,并測試#include<linux/kernel.h>#include<linux/init.h>#include<linux/module.h>#include<linux/fs.h>#include<linux/cdev.h>#include<linux/uaccess.h>#include<linux/gpio.h>#include<plat/gpio-cfg.h>#include<mach/gpio.h>#include<mach/gpio-exynos4.h>#include<linux/device.h>#include"leds-ioctl.h"MODULE_LICENSE("GPLv2");#defineDEVICE_MAJOR0#defineDEVICE_MINOR0#defineDEVICE_NUM1#defineDEVICE_NAME“l(fā)ed_ctl“//定義設(shè)備節(jié)點(diǎn)名稱leddev.c//定義LED的管腳#defineLED1EXYNOS4_GPL2(0)#defineLED2EXYNOS4_GPK1(1)//主設(shè)備號,次設(shè)備號staticintmajor=DEVICE_MAJOR;staticintminor=DEVICE_MINOR;structcdevleds_dev_cdev;staticstructclass*leds_class;//主設(shè)備號可以通過輸入改變module_param(major,int,S_IRUGO);/*************************************************************/staticintleds_dev_open(structinode*inode,structfile*file){
s3c_gpio_cfgpin(LED1,S3C_GPIO_OUTPUT);
s3c_gpio_cfgpin(LED2,S3C_GPIO_OUTPUT);
gpio_set_value(LED1,1);
gpio_set_value(LED2,1); printk(KERN_INFO"ledsdeviceopensucess!\n"); return0;}/**************************************************************/staticintleds_dev_release(structinode*inode,structfile*filp){ printk("ledsdevicerelease\n"); return0;}設(shè)置GPIO為輸出初始化LED為亮staticlongleds_dev_ioctl(structfile*fi
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 專業(yè)廚房設(shè)備購銷協(xié)議2024版B版
- 2024版河南省事業(yè)編制人員勞動協(xié)議樣式版B版
- 二零二五年度大巴車租賃與城市慶典活動策劃合同3篇
- 二零二五年度酒吧股份投資及風(fēng)險(xiǎn)控制合同3篇
- 二零二五年度科技園區(qū)場地租賃詳細(xì)協(xié)議2篇
- 2024版短期勞務(wù)合同范例
- 濰坊護(hù)理職業(yè)學(xué)院《材料分析測試與表征》2023-2024學(xué)年第一學(xué)期期末試卷
- 太原學(xué)院《橋梁工程(一)》2023-2024學(xué)年第一學(xué)期期末試卷
- 2024年食堂管理員與廚師合同3篇
- 二零二五年建筑工程施工企業(yè)工程結(jié)算與審計(jì)合同2篇
- 上海某建筑基礎(chǔ)及上部結(jié)構(gòu)加固工程施工方案磚木結(jié)構(gòu) 磚混結(jié)構(gòu)
- 精神病醫(yī)院財(cái)務(wù)后勤總務(wù)管理制度
- 停車場施工施工組織設(shè)計(jì)方案
- GB/T 37238-2018篡改(污損)文件鑒定技術(shù)規(guī)范
- 普通高中地理課程標(biāo)準(zhǔn)簡介(湘教版)
- 河道治理工程監(jiān)理通知單、回復(fù)單范本
- 超分子化學(xué)簡介課件
- 高二下學(xué)期英語閱讀提升練習(xí)(一)
- 易制爆化學(xué)品合法用途說明
- 【PPT】壓力性損傷預(yù)防敷料選擇和剪裁技巧
- 大氣喜慶迎新元旦晚會PPT背景
評論
0/150
提交評論