第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)課件_第1頁(yè)
第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)課件_第2頁(yè)
第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)課件_第3頁(yè)
第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)課件_第4頁(yè)
第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)課件_第5頁(yè)
已閱讀5頁(yè),還剩58頁(yè)未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡(jiǎn)介

第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)2023/6/9第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)本章的主要內(nèi)容11.1設(shè)備驅(qū)動(dòng)概述11.2字符設(shè)備驅(qū)動(dòng)編程11.3GPIO驅(qū)動(dòng)程序?qū)嵗?1.4塊設(shè)備驅(qū)動(dòng)編程11.5中斷編程11.6按鍵驅(qū)動(dòng)程序?qū)嵗?1.7實(shí)驗(yàn)內(nèi)容——test驅(qū)動(dòng)第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)11.1設(shè)備驅(qū)動(dòng)概述第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)設(shè)備驅(qū)動(dòng)簡(jiǎn)介及驅(qū)動(dòng)模塊操作系統(tǒng)是通過(guò)各種驅(qū)動(dòng)程序來(lái)駕馭硬件設(shè)備的,它為用戶屏蔽了各種各樣的設(shè)備,驅(qū)動(dòng)硬件是操作系統(tǒng)最基本的功能,并且提供統(tǒng)一的操作方式。設(shè)備驅(qū)動(dòng)程序是內(nèi)核的一部分,硬件驅(qū)動(dòng)程序是操作系統(tǒng)最基本的組成部分,在Linux內(nèi)核源程序中也占有60%以上。因此,熟悉驅(qū)動(dòng)的編寫(xiě)是很重要的。Linux內(nèi)核中采用可加載的模塊化設(shè)計(jì)(LKMs,LoadableKernelModules),一般情況下編譯的Linux內(nèi)核是支持可插入式模塊的,也就是將最基本的核心代碼編譯在內(nèi)核中,其他的代碼可以編譯到內(nèi)核中,或者編譯為內(nèi)核的模塊文件(在需要時(shí)動(dòng)態(tài)加載)。第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)內(nèi)核模塊的主要相關(guān)命令常見(jiàn)的驅(qū)動(dòng)程序是作為內(nèi)核模塊動(dòng)態(tài)加載的,比如聲卡驅(qū)動(dòng)和網(wǎng)卡驅(qū)動(dòng)等,而Linux最基礎(chǔ)的驅(qū)動(dòng),如CPU、PCI總線、TCP/IP協(xié)議、APM(高級(jí)電源管理)、VFS等驅(qū)動(dòng)程序則直接編譯在內(nèi)核文件中。有時(shí)也把內(nèi)核模塊叫做驅(qū)動(dòng)程序,只不過(guò)驅(qū)動(dòng)的內(nèi)容不一定是硬件罷了,比如ext3文件系統(tǒng)的驅(qū)動(dòng)。因此,加載驅(qū)動(dòng)就是加載內(nèi)核模塊。lsmod列出當(dāng)前系統(tǒng)中加載的模塊,其中左邊第一列是模塊名,第二列是該模塊大小,第三列則是使用該模塊的對(duì)象數(shù)目。rmmod是用于將當(dāng)前模塊卸載。insmod和modprobe是用于加載當(dāng)前模塊,但insmod不會(huì)自動(dòng)解決依存關(guān)系,即如果要加載的模塊引用了當(dāng)前內(nèi)核符號(hào)表中不存在的符號(hào),則無(wú)法加載,也不會(huì)去查在其他尚未加載的模塊中是否定義了該符號(hào);modprobe可以根據(jù)模塊間依存關(guān)系以及/etc/modules.conf文件中的內(nèi)容自動(dòng)加載其他有依賴關(guān)系的模塊。第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)設(shè)備分類(1)Linux系統(tǒng)的設(shè)備分為三類:字符設(shè)備、塊設(shè)備和網(wǎng)絡(luò)設(shè)備。 字符設(shè)備通常指像普通文件或字節(jié)流一樣,以字節(jié)為單位順序讀寫(xiě)的設(shè)備,如并口設(shè)備、虛擬控制臺(tái)等。字符設(shè)備可以通過(guò)設(shè)備文件節(jié)點(diǎn)訪問(wèn),它與普通文件之間的區(qū)別在于普通文件可以被隨機(jī)訪問(wèn)(可以前后移動(dòng)訪問(wèn)指針),而大多數(shù)字符設(shè)備只能提供順序訪問(wèn),因?yàn)閷?duì)它們的訪問(wèn)不會(huì)被系統(tǒng)所緩存。但也有例外,例如幀緩存(framebuffer)是一個(gè)可以被隨機(jī)訪問(wèn)的字符設(shè)備。 塊設(shè)備通常指一些需要以塊為單位隨機(jī)讀寫(xiě)的設(shè)備,如IDE硬盤、SCSI硬盤、光驅(qū)等。塊設(shè)備也是通過(guò)文件節(jié)點(diǎn)來(lái)訪問(wèn),它不僅可以提供隨機(jī)訪問(wèn),而且可以容納文件系統(tǒng)(例如硬盤、閃存等)。Linux可以使用戶態(tài)程序像訪問(wèn)字符設(shè)備一樣每次進(jìn)行任意字節(jié)的操作,只是在內(nèi)核態(tài)內(nèi)部中的管理方式和內(nèi)核提供的驅(qū)動(dòng)接口上不同。第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)設(shè)備分類(2)

網(wǎng)絡(luò)設(shè)備通常是指通過(guò)網(wǎng)絡(luò)能夠與其他主機(jī)進(jìn)行數(shù)據(jù)通信的設(shè)備,如網(wǎng)卡等。內(nèi)核和網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)程序之間的通信調(diào)用一套數(shù)據(jù)包處理函數(shù),它們完全不同于內(nèi)核和字符以及塊設(shè)備驅(qū)動(dòng)程序之間的通信(read(),write()等函數(shù))。Linux網(wǎng)絡(luò)設(shè)備不是面向流的設(shè)備,因此不會(huì)將網(wǎng)絡(luò)設(shè)備的名字(例如eth0)映射到文件系統(tǒng)中去。$ls–l/devcrw-rw1rootuucp4,6408-3022:58ttyS0/*串口設(shè)備,c表示字符設(shè)備*/brw-r1rootfloppy2,008-3022:58fd0 /*軟盤設(shè)備,b表示塊設(shè)備*/第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)設(shè)備號(hào)設(shè)備號(hào)是一個(gè)數(shù)字,它是設(shè)備的標(biāo)志。就如前面所述,一個(gè)設(shè)備文件(也就是設(shè)備節(jié)點(diǎn))可以通過(guò)mknod命令來(lái)創(chuàng)建,其中指定了主設(shè)備號(hào)和次設(shè)備號(hào)。主設(shè)備號(hào)表明設(shè)備的類型(例如串口設(shè)備、SCSI硬盤),與一個(gè)確定的驅(qū)動(dòng)程序?qū)?yīng);次設(shè)備號(hào)通常是用于標(biāo)明不同的屬性,例如不同的使用方法,不同的位置,不同的操作等,它標(biāo)志著某個(gè)具體的物理設(shè)備。高字節(jié)為主設(shè)備號(hào),底字節(jié)為次設(shè)備號(hào)。 例如,在系統(tǒng)中的塊設(shè)備IDE硬盤的主設(shè)備號(hào)是3,而多個(gè)IDE硬盤及其各個(gè)分區(qū)分別賦予次設(shè)備號(hào)1、2、3……$ls–l/devcrw-rw1rootuucp4,6408-3022:58ttyS0/*主設(shè)備號(hào)4,此設(shè)備號(hào)64*/第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)驅(qū)動(dòng)層次結(jié)構(gòu)第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)設(shè)備驅(qū)動(dòng)程序與外界的接口第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)設(shè)備驅(qū)動(dòng)程序的特點(diǎn)(1)(1)內(nèi)核代碼:設(shè)備驅(qū)動(dòng)程序是內(nèi)核的一部分,如果驅(qū)動(dòng)程序出錯(cuò),則可能導(dǎo)致系統(tǒng)崩潰。(2)內(nèi)核接口:設(shè)備驅(qū)動(dòng)程序必須為內(nèi)核或者其子系統(tǒng)提供一個(gè)標(biāo)準(zhǔn)接口。比如,一個(gè)終端驅(qū)動(dòng)程序必須為內(nèi)核提供一個(gè)文件I/O接口;一個(gè)SCSI設(shè)備驅(qū)動(dòng)程序應(yīng)該為SCSI子系統(tǒng)提供一個(gè)SCSI設(shè)備接口,同時(shí)SCSI子系統(tǒng)也必須為內(nèi)核提供文件的I/O接口及緩沖區(qū)。(3)內(nèi)核機(jī)制和服務(wù):設(shè)備驅(qū)動(dòng)程序使用一些標(biāo)準(zhǔn)的內(nèi)核服務(wù),如內(nèi)存分配等。第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)設(shè)備驅(qū)動(dòng)程序的特點(diǎn)(2)(4)可裝載:大多數(shù)的Linux操作系統(tǒng)設(shè)備驅(qū)動(dòng)程序都可以在需要時(shí)裝載進(jìn)內(nèi)核,在不需要時(shí)從內(nèi)核中卸載。(5)可設(shè)置:Linux操作系統(tǒng)設(shè)備驅(qū)動(dòng)程序可以集成為內(nèi)核的一部分,并可以根據(jù)需要把其中的某一部分集成到內(nèi)核中,這只需要在系統(tǒng)編譯時(shí)進(jìn)行相應(yīng)的設(shè)置即可。(6)動(dòng)態(tài)性:在系統(tǒng)啟動(dòng)且各個(gè)設(shè)備驅(qū)動(dòng)程序初始化后,驅(qū)動(dòng)程序?qū)⒕S護(hù)其控制的設(shè)備。如果該設(shè)備驅(qū)動(dòng)程序控制的設(shè)備不存在也不影響系統(tǒng)的運(yùn)行,那么此時(shí)的設(shè)備驅(qū)動(dòng)程序只是多占用了一點(diǎn)系統(tǒng)內(nèi)存罷了。第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)11.2字符設(shè)備驅(qū)動(dòng)編程第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)設(shè)備驅(qū)動(dòng)程序工作原理模塊在調(diào)用insmod命令時(shí)被加載,此時(shí)的入口點(diǎn)是init_module()函數(shù),通常在該函數(shù)中完成設(shè)備的注冊(cè)。同樣,模塊在調(diào)用rmmod命令時(shí)被卸載,此時(shí)的入口點(diǎn)是cleanup_module()函數(shù),在該函數(shù)中完成設(shè)備的卸載。在設(shè)備完成注冊(cè)加載之后,用戶的應(yīng)用程序就可以對(duì)該設(shè)備進(jìn)行一定的操作,如open()、read()、write()等,而驅(qū)動(dòng)程序就是用于實(shí)現(xiàn)這些操作,在用戶應(yīng)用程序調(diào)用相應(yīng)入口函數(shù)時(shí)執(zhí)行相關(guān)的操作。第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)重要數(shù)據(jù)結(jié)構(gòu)-file_operaions結(jié)構(gòu)structfile_operations{loff_t(*llseek)(structfile*,loff_t,int);ssize_t(*read)(structfile*filp,char*buff,size_tcount,loff_t*offp);ssize_t(*write)(structfile*filp,constchar*buff,size_tcount,loff_t*offp);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*);int(*fasync)(int,structfile*,int);int(*check_media_change)(kdev_tdev);int(*revalidate)(kdev_tdev);int(*lock)(structfile*,int,structfile_lock*);};第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)重要數(shù)據(jù)結(jié)構(gòu)-inode結(jié)構(gòu)structfile{mode_tf_mode;/*標(biāo)識(shí)文件是否可讀或可寫(xiě),F(xiàn)MODE_READ或FMODE_WRITE*/dev_tf_rdev;/*用于/dev/tty*/off_tf_pos;/*當(dāng)前文件位移*/unsignedshortf_flags;/*文件標(biāo)志,如O_RDONLY、O_NONBLOCK和O_SYNC*/unsignedshortf_count;/*打開(kāi)的文件數(shù)目*/unsignedshortf_reada;structinode*f_inode;/*指向inode的結(jié)構(gòu)指針*/structfile_operations*f_op;/*文件索引指針*/};第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)早期版本的字符設(shè)備注冊(cè)(1)早期版本的設(shè)備注冊(cè)使用函數(shù)register_chrdev(),調(diào)用該函數(shù)后就可以向系統(tǒng)申請(qǐng)主設(shè)備號(hào),如果register_chrdev()操作成功,設(shè)備名就會(huì)出現(xiàn)在/proc/devices文件里。在關(guān)閉設(shè)備時(shí),通常需要解除原先的設(shè)備注冊(cè),此時(shí)可使用函數(shù)unregister_chrdev(),此后該設(shè)備就會(huì)從/proc/devices里消失。其中主設(shè)備號(hào)和次設(shè)備號(hào)不能大于255。第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)早期版本的字符設(shè)備注冊(cè)(2)第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)設(shè)備號(hào)相關(guān)函數(shù)(1)在linux2.6的版本中,用dev_t類型來(lái)描述設(shè)備號(hào)(dev_t是32位數(shù)值類型,其中高12位表示主設(shè)備號(hào),低20位表示次設(shè)備號(hào))。用兩個(gè)宏MAJOR和MINOR分別獲得dev_t設(shè)備號(hào)的主設(shè)備號(hào)和次設(shè)備號(hào),而且用MKDEV宏來(lái)實(shí)現(xiàn)逆過(guò)程,即組合主設(shè)備號(hào)和次設(shè)備號(hào)而獲得dev_t類型設(shè)備號(hào)。分配設(shè)備號(hào)有靜態(tài)和動(dòng)態(tài)的兩種方法。靜態(tài)分配(register_chrdev_region()函數(shù))是指在事先知道設(shè)備主設(shè)備號(hào)的情況下,通過(guò)參數(shù)函數(shù)指定第一個(gè)設(shè)備號(hào)(它的次設(shè)備號(hào)通常為0)而向系統(tǒng)申請(qǐng)分配一定數(shù)目的設(shè)備號(hào)。動(dòng)態(tài)分配(alloc_chrdev_region())是指通過(guò)參數(shù)僅設(shè)置第一個(gè)次設(shè)備號(hào)(通常為0,事先不會(huì)知道主設(shè)備號(hào))和要分配的設(shè)備數(shù)目而系統(tǒng)動(dòng)態(tài)分配所需的設(shè)備號(hào)。通過(guò)unregister_chrdev_region()函數(shù)釋放已分配的(無(wú)論是靜態(tài)的還是動(dòng)態(tài)的)設(shè)備號(hào)。第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)設(shè)備號(hào)相關(guān)函數(shù)(2)第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)字符設(shè)備注冊(cè)(1)在Linux內(nèi)核中使用structcdev結(jié)構(gòu)來(lái)描述字符設(shè)備,我們?cè)隍?qū)動(dòng)程序中必須將已分配到的設(shè)備號(hào)以及設(shè)備操作接口(即為structfile_operations結(jié)構(gòu))賦予structcdev結(jié)構(gòu)變量。首先使用cdev_alloc()函數(shù)向系統(tǒng)申請(qǐng)分配structcdev結(jié)構(gòu),再用cdev_init()函數(shù)初始化已分配到的結(jié)構(gòu)并與file_operations結(jié)構(gòu)關(guān)聯(lián)起來(lái)。最后調(diào)用cdev_add()函數(shù)將設(shè)備號(hào)與structcdev結(jié)構(gòu)進(jìn)行關(guān)聯(lián)并向內(nèi)核正式報(bào)告新設(shè)備的注冊(cè),這樣新設(shè)備可以被用起來(lái)了!。如果要從系統(tǒng)中刪除一個(gè)設(shè)備,則要調(diào)用cdev_del()函數(shù)。第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)字符設(shè)備注冊(cè)(2)第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)打開(kāi)設(shè)備打開(kāi)設(shè)備的函數(shù)接口是open,根據(jù)設(shè)備的不同,open函數(shù)接口完成的功能也有所不同,但通常情況下在open函數(shù)接口中要完成如下工作。遞增計(jì)數(shù)器,檢查錯(cuò)誤。如果未初始化,則進(jìn)行初始化。識(shí)別次設(shè)備號(hào),如果必要,更新f_op指針。分配并填寫(xiě)被置于filp->private_data的數(shù)據(jù)結(jié)構(gòu)。其中遞增計(jì)數(shù)器是用于設(shè)備計(jì)數(shù)的。由于設(shè)備在使用時(shí)通常會(huì)打開(kāi)多次,也可以由不同的進(jìn)程所使用,所以若有一進(jìn)程想要?jiǎng)h除該設(shè)備,則必須保證其他設(shè)備沒(méi)有使用該設(shè)備。因此使用計(jì)數(shù)器就可以很好地完成這項(xiàng)功能。第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)釋放設(shè)備釋放設(shè)備的函數(shù)接口是release()。要注意釋放設(shè)備和關(guān)閉設(shè)備是完全不同的。當(dāng)一個(gè)進(jìn)程釋放設(shè)備時(shí),其他進(jìn)程還能繼續(xù)使用該設(shè)備,只是該進(jìn)程暫時(shí)停止對(duì)該設(shè)備的使用;而當(dāng)一個(gè)進(jìn)程關(guān)閉設(shè)備時(shí),其他進(jìn)程必須重新打開(kāi)此設(shè)備才能使用它。釋放設(shè)備時(shí)要完成的工作如下。遞減計(jì)數(shù)器MOD_DEC_USE_COUNT(最新版本已經(jīng)不再使用)。釋放打開(kāi)設(shè)備時(shí)系統(tǒng)所分配的內(nèi)存空間(包括filp->private_data指向的內(nèi)存空間)。在最后一次釋放設(shè)備操作時(shí)關(guān)閉設(shè)備。第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)讀寫(xiě)設(shè)備讀寫(xiě)設(shè)備的主要任務(wù)就是把內(nèi)核空間的數(shù)據(jù)復(fù)制到用戶空間,或者從用戶空間復(fù)制到內(nèi)核空間,也就是將內(nèi)核空間緩沖區(qū)里的數(shù)據(jù)復(fù)制到用戶空間的緩沖區(qū)中或者相反。第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)內(nèi)核空間和用戶空間的數(shù)據(jù)交換內(nèi)核空間地址和用戶空間地址是有很大區(qū)別的,其中一個(gè)區(qū)別是用戶空間的內(nèi)存是可以被換出的,因此可能會(huì)出現(xiàn)頁(yè)面失效等情況。所以不能使用諸如memcpy()之類的函數(shù)來(lái)完成這樣的操作。在這里要使用copy_to_user()或copy_from_user()等函數(shù),它們是用來(lái)實(shí)現(xiàn)用戶空間和內(nèi)核空間的數(shù)據(jù)交換的。第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)ioctl大部分設(shè)備除了讀寫(xiě)操作,還需要硬件配置和控制(例如,設(shè)置串口設(shè)備的波特率)等很多其他操作。在字符設(shè)備驅(qū)動(dòng)中ioctl函數(shù)接口給用戶提供對(duì)設(shè)備的非讀寫(xiě)操作機(jī)制。第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)獲取內(nèi)存(1)在應(yīng)用程序中獲取內(nèi)存通常使用函數(shù)malloc(),但在設(shè)備驅(qū)動(dòng)程序中動(dòng)態(tài)開(kāi)辟內(nèi)存可以以字節(jié)或頁(yè)面為單位。其中,以字節(jié)為單位分配內(nèi)存的函數(shù)有kmalloc(),注意的是,kmalloc()函數(shù)返回的是物理地址,而malloc()等返回的是線性虛擬地址,因此在驅(qū)動(dòng)程序中不能使用malloc()函數(shù)。與malloc()不同,kmalloc()申請(qǐng)空間有大小限制。長(zhǎng)度是2的整次方,并且不會(huì)對(duì)所獲取的內(nèi)存空間清零。以頁(yè)為單位分配內(nèi)存的函數(shù)如下所示:get_zeroed_page():獲得一個(gè)已清零頁(yè)面。get_free_page():獲得一個(gè)或幾個(gè)連續(xù)頁(yè)面。get_dma_pages():獲得用于DMA傳輸?shù)捻?yè)面。與之相對(duì)應(yīng)的釋放內(nèi)存用也有kfree()或free_page函數(shù)族。第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)獲取內(nèi)存(2)第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)獲取內(nèi)存(3)第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)獲取內(nèi)存(4)第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)打印信息在內(nèi)核空間要用函數(shù)printk()而不能用平常的函數(shù)printf()。printk()還可以定義打印消息的優(yōu)先級(jí)。第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)proc文件系統(tǒng)(1)/proc文件系統(tǒng)是一個(gè)偽文件系統(tǒng),它是一種內(nèi)核和內(nèi)核模塊用來(lái)向進(jìn)程發(fā)送信息的機(jī)制。這個(gè)偽文件系統(tǒng)讓用戶可以和內(nèi)核內(nèi)部數(shù)據(jù)結(jié)構(gòu)進(jìn)行交互,獲取有關(guān)系統(tǒng)和進(jìn)程的有用信息,在運(yùn)行時(shí)通過(guò)改變內(nèi)核參數(shù)來(lái)改變?cè)O(shè)置。與其他文件系統(tǒng)不同,/proc存在于內(nèi)存之中而不是在硬盤上。讀者可以通過(guò)“l(fā)s”查看/proc文件系統(tǒng)的內(nèi)容。

第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)proc文件系統(tǒng)(2)第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)11.3GPIO驅(qū)動(dòng)程序?qū)嵗?1章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)GPIO工作原理FS2410開(kāi)發(fā)板的S3C2410處理器具有117個(gè)多功能通用I/O(GPIO)端口管腳,包括GPIO8個(gè)端口組,分別為GPA(23個(gè)輸出端口)、GPB(11個(gè)輸入/輸出端口)、GPC(16個(gè)輸入/輸出端口)、GPD(16個(gè)輸入/輸出端口)、GPE(16個(gè)輸入/輸出端口)、GPF(8個(gè)輸入/輸出端口)、GPH(11個(gè)輸入/輸出端口)。根據(jù)各種系統(tǒng)設(shè)計(jì)的需求,通過(guò)軟件方法可以將這些端口配置成具有相應(yīng)功能(例如:外部中斷或數(shù)據(jù)總線)的端口。為了控制這些端口,S3C2410處理器為每個(gè)端口組分別提供幾種相應(yīng)的控制寄存器。其中最常用的有端口配置寄存器(GPACON~GPHCON)和端口數(shù)據(jù)寄存器(GPADAT~GPHDAT)。因?yàn)榇蟛糠諭/O管腳可以提供多種功能,通過(guò)配置寄存器(PnCON)設(shè)定每個(gè)管腳用于何種目的。數(shù)據(jù)寄存器的每位將對(duì)應(yīng)于某個(gè)管腳上的輸入或輸出。所以通過(guò)對(duì)數(shù)據(jù)寄存器(PnDAT)的位讀寫(xiě),可以進(jìn)行對(duì)每個(gè)端口的輸入或輸出。第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)LED和蜂鳴器驅(qū)動(dòng)電路可知使用S3C2410處理器的通用I/O口GPF4、GPF5、GPF6和GPF7分別直接驅(qū)動(dòng)LEDD12、D11、D10以及D9,而使用GPB0端口驅(qū)動(dòng)蜂鳴器。4個(gè)LED分別在對(duì)應(yīng)端口(GPF4~GPF7)為低電平時(shí)發(fā)亮,而蜂鳴器在GPB0為高電平時(shí)發(fā)聲。這5個(gè)端口的數(shù)據(jù)流方向均為輸出。第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)主要控制寄存器(1)第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)主要控制寄存器(2)為了驅(qū)動(dòng)LED和蜂鳴器,首先通過(guò)端口配置寄存器將5個(gè)相應(yīng)寄存器配置為輸出模式。然后通過(guò)對(duì)端口數(shù)據(jù)寄存器的寫(xiě)操作,實(shí)現(xiàn)對(duì)每個(gè)GPIO設(shè)備的控制(發(fā)亮或發(fā)聲)。在下一個(gè)小節(jié)中介紹的驅(qū)動(dòng)程序中,s3c2410_gpio_cfgpin()函數(shù)和s3c2410_gpio_pullup()函數(shù)將進(jìn)行對(duì)某個(gè)端口的配置,而s3c2410_gpio_setpin()函數(shù)實(shí)現(xiàn)向數(shù)據(jù)寄存器的某個(gè)端口的輸出。第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)GPIO驅(qū)動(dòng)程序閱讀并運(yùn)行11-3-2$makeclean;make/*驅(qū)動(dòng)程序的編譯*/$insmodgpio_drv.ko/*加載gpio驅(qū)動(dòng)*/$cat/proc/devices/*通過(guò)這個(gè)命令可以查到gpio設(shè)備的主設(shè)備號(hào)*/$mknod/dev/gpioc2520/*假設(shè)主設(shè)備號(hào)為252,創(chuàng)建設(shè)備文件節(jié)點(diǎn)*/$arm-linux-gcc–ogpio_testgpio_test.c$./gpio_test運(yùn)行結(jié)果為4個(gè)LED輪流閃爍,同時(shí)蜂鳴器以一定周期發(fā)出聲響。第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)11.4塊設(shè)備驅(qū)動(dòng)編程第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)塊設(shè)備驅(qū)動(dòng)塊設(shè)備通常指一些需要以塊(如512字節(jié))的方式寫(xiě)入的設(shè)備,如IDE硬盤、SCSI硬盤、光驅(qū)等。它的驅(qū)動(dòng)程序的編寫(xiě)過(guò)程與字符型設(shè)備驅(qū)動(dòng)程序的編寫(xiě)有很大的區(qū)別。塊設(shè)備驅(qū)動(dòng)編程接口相對(duì)復(fù)雜,不如字符設(shè)備明晰易用。塊設(shè)備驅(qū)動(dòng)程序?qū)φ麄€(gè)系統(tǒng)的性能影響較大,速度和效率是設(shè)計(jì)塊設(shè)備驅(qū)動(dòng)程要重點(diǎn)考慮的問(wèn)題。系統(tǒng)中使用緩沖區(qū)與訪問(wèn)請(qǐng)求的優(yōu)化管理(合并與重新排序)來(lái)提高系統(tǒng)性能。

第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)塊設(shè)備驅(qū)動(dòng)工作流程塊設(shè)備驅(qū)動(dòng)程序的編寫(xiě)流程同字符設(shè)備驅(qū)動(dòng)程序的編寫(xiě)流程很類似,也包括了注冊(cè)和使用兩部分。但與字符驅(qū)動(dòng)設(shè)備所不同的是,塊設(shè)備驅(qū)動(dòng)程序包括一個(gè)request請(qǐng)求隊(duì)列。它是當(dāng)內(nèi)核安排一次數(shù)據(jù)傳輸時(shí)在列表中的一個(gè)請(qǐng)求隊(duì)列,以最大化系統(tǒng)性能為原則進(jìn)行排序。第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)重要數(shù)據(jù)結(jié)構(gòu)(1)每個(gè)塊設(shè)備物理實(shí)體由一個(gè)gendisk結(jié)構(gòu)體來(lái)表示,每個(gè)gendisk可以支持多個(gè)分區(qū)。每個(gè)gendisk中包含了本物理實(shí)體的全部信息以及操作函數(shù)接口。整個(gè)塊設(shè)備的注冊(cè)過(guò)程是圍繞gendisk來(lái)展開(kāi)的。structgendisk{intmajor;/*主設(shè)備號(hào)*/intfirst_minor;/*第一個(gè)次設(shè)備號(hào)*/intminors;/*次設(shè)備號(hào)個(gè)數(shù),一個(gè)塊設(shè)備至少需要使用一個(gè)次設(shè)備號(hào),而且塊設(shè)備的每個(gè)分區(qū)都需要一個(gè)次設(shè)備號(hào),因此這個(gè)成員等于1,則表明該塊設(shè)備是不可被分區(qū)的,否則可以包含minors–1個(gè)分區(qū)。*/chardisk_name[32];/*塊設(shè)備名稱,在/proc/partions中顯示*/structhd_struct**part;/*分區(qū)表*/structblock_device_operations*fops;/*塊設(shè)備操作接口,與字符設(shè)備的file_operations結(jié)構(gòu)對(duì)應(yīng)*/structrequest_queue*queue;/*I/O請(qǐng)求隊(duì)列*/void*private_data;/*指向驅(qū)動(dòng)程序私有數(shù)據(jù)*/sector_tcapacity;/*塊設(shè)備可包含的扇區(qū)數(shù)*/……/*其他省略*/};第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)重要數(shù)據(jù)結(jié)構(gòu)(2)structblock_device_operations{int(*open)(structinode*,structfile*);int(*release)(structinode*,structfile*);int(*ioctl)(structinode*,structfile*,unsigned,unsignedlong);long(*unlocked_ioctl)(structfile*,unsigned,unsignedlong);long(*compat_ioctl)(structfile*,unsigned,unsignedlong);int(*direct_access)(structblock_device*,sector_t,unsignedlong*);int(*media_changed)(structgendisk*);int(*revalidate_disk)(structgendisk*);int(*getgeo)(structblock_device*,structhd_geometry*);structmodule*owner;};塊設(shè)備驅(qū)動(dòng)程序也包含一個(gè)在<linux/fs.h>中定義的block_device_operations結(jié)構(gòu)塊設(shè)備并不提供read()、write()等函數(shù)接口。對(duì)塊設(shè)備的讀寫(xiě)請(qǐng)求都是以異步方式發(fā)送到設(shè)備相關(guān)的request隊(duì)列之中。

第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)塊設(shè)備注冊(cè)和初始化(1)第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)塊設(shè)備注冊(cè)和初始化(2)(1)向內(nèi)核注冊(cè)使用register_blkdev()函數(shù)對(duì)設(shè)備進(jìn)行注冊(cè)。其中參數(shù)major為要注冊(cè)的塊設(shè)備的主設(shè)備號(hào),如果其值等于0,則系統(tǒng)動(dòng)態(tài)分配并返回主設(shè)備號(hào)。參數(shù)name為設(shè)備名,在/proc/devices中顯示。如果出錯(cuò),則該函數(shù)返回負(fù)值。與其對(duì)應(yīng)的塊設(shè)備的注銷函數(shù)為unregister_blkdev()。其參數(shù)必須與注冊(cè)函數(shù)中的參數(shù)相同。如果出錯(cuò)則返回負(fù)值。(2)申請(qǐng)并初始化請(qǐng)求隊(duì)列這一步要調(diào)用blk_init_queue()函數(shù)來(lái)申請(qǐng)并初始化請(qǐng)求隊(duì)列。其中參數(shù)rfn是請(qǐng)求隊(duì)列的處理函數(shù)指針,它負(fù)責(zé)執(zhí)行塊設(shè)備的讀、寫(xiě)請(qǐng)求。參數(shù)lock為自旋鎖,用于控制對(duì)所分配的隊(duì)列的訪問(wèn)。intregister_blkdev(unsignedintmajor,constchar*name);intunregister_blkdev(unsignedintmajor,constchar*name);structrequest_queue*blk_init_queue(request_fn_proc*rfn,spinlock_t*lock)第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)塊設(shè)備注冊(cè)和初始化(3)(3)初始化并注冊(cè)gendisk結(jié)構(gòu)首先使用alloc_disk()函數(shù)動(dòng)態(tài)分配gendisk結(jié)構(gòu),接下來(lái),對(duì)gendisk結(jié)構(gòu)的主設(shè)備號(hào)(major)、次設(shè)備號(hào)相關(guān)成員(first_minor和minors)、塊設(shè)備操作函數(shù)(fops)、請(qǐng)求隊(duì)列(queue)、可包含的扇區(qū)數(shù)(capacity)以及設(shè)備名稱(disk_name)等成員進(jìn)行初始化。在完成對(duì)gendisk的分配和初始化之后,調(diào)用add_disk()函數(shù)向系統(tǒng)注冊(cè)塊設(shè)備。在卸載gendisk結(jié)構(gòu)的時(shí)候,要調(diào)用del_gendisk()函數(shù)。第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)塊設(shè)備請(qǐng)求處理塊設(shè)備驅(qū)動(dòng)中一般要實(shí)現(xiàn)一個(gè)請(qǐng)求隊(duì)列處理函數(shù)來(lái)處理隊(duì)列中的請(qǐng)求。從塊設(shè)備的運(yùn)行流程,可知請(qǐng)求處理是塊設(shè)備的基本處理單位,也是最核心的部分。對(duì)塊設(shè)備的讀寫(xiě)操作被封裝到了每一個(gè)請(qǐng)求中。

第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)11.5中斷編程第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)中斷編程接口(1)實(shí)際上,有很多Linux的驅(qū)動(dòng)都是通過(guò)中斷的方式來(lái)進(jìn)行內(nèi)核和硬件的交互。中斷機(jī)制提供了硬件和軟件之間異步傳遞信息的方式。硬件設(shè)備在發(fā)生某個(gè)事件時(shí)通過(guò)中斷通知軟件進(jìn)行處理。中斷實(shí)現(xiàn)了硬件設(shè)備按需獲得處理器關(guān)注的機(jī)制,與查詢方式相比可以大大節(jié)省CPU資源的開(kāi)銷。申請(qǐng)中斷使用request_irq()調(diào)用,釋放中斷使用free_irq()調(diào)用。intrequest_irq(unsignedintirq,void(*handler)(intirq,void*dev_id,structpt_regs*regs),unsignedlongirqflags,constchar*devname,oid*dev_id);voidfree_irq(unsignedintirq,void*dev_id);第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)中斷編程接口(2)其中irq是要申請(qǐng)的硬件中斷號(hào)。在Intel平臺(tái),范圍是0~15。參數(shù)handler為將要向系統(tǒng)注冊(cè)的中斷處理函數(shù)。這是一個(gè)回調(diào)函數(shù),中斷發(fā)生時(shí),系統(tǒng)調(diào)用這個(gè)函數(shù),傳入的參數(shù)包括硬件中斷號(hào)、設(shè)備id以及寄存器值。設(shè)備id就是在調(diào)用request_irq()時(shí)傳遞給系統(tǒng)的參數(shù)dev_id。參數(shù)irqflags是中斷處理的一些屬性,其中比較重要的有SA_INTERRUPT。這個(gè)參數(shù)用于標(biāo)明中斷處理程序是快速處理程序(設(shè)置SA_INTERRUPT)還是慢速處理程序(不設(shè)置SA_INTERRUPT)??焖偬幚沓绦虮徽{(diào)用時(shí)屏蔽所有中斷。慢速處理程序只屏蔽正在處理的中斷。還有一個(gè)SA_SHIRQ屬性,設(shè)置了以后運(yùn)行多個(gè)設(shè)備共享中斷,在中斷處理程序中根據(jù)dev_id區(qū)分不同設(shè)備產(chǎn)生的中斷。參數(shù)devname為設(shè)備名,會(huì)在/dev/interrupts中顯示。參數(shù)dev_id在中斷共享時(shí)會(huì)用到。一般設(shè)置為這個(gè)設(shè)備的device結(jié)構(gòu)本身或者NULL。中斷處理程序可以用dev_id找到相應(yīng)的控制這個(gè)中斷的設(shè)備,或者用irq2dev_map()找到中斷對(duì)應(yīng)的設(shè)備。第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)11.6按鍵驅(qū)動(dòng)程序?qū)嵗?1章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)按鍵工作原理(1)LED和蜂鳴器是最簡(jiǎn)單的GPIO的應(yīng)用,都不需要任何外部輸入或控制。按鍵同樣使用GPIO接口,但按鍵本身需要外部的輸入,即在驅(qū)動(dòng)程序中要處理外部中斷。按鍵硬件驅(qū)動(dòng)原理圖如圖11-7所示。在圖11-7的4X4矩陣按鍵(K1~K16)電路中,使用4個(gè)輸入/輸出端口(EINT0、EINT2、EINT11和EINT19)和4個(gè)輸出端口(KSCAN0~KSCAN3)。

第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)按鍵工作原理(2)第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)按鍵電路的主要端口第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)按鍵驅(qū)動(dòng)程序原理(1)因?yàn)橥ǔV袛喽丝谑潜容^珍貴且有限的資源,所以在本電路設(shè)計(jì)中,16個(gè)按鍵復(fù)用了4個(gè)中斷線。那怎么樣才能及時(shí)而準(zhǔn)確地對(duì)矩陣按鍵進(jìn)行掃描呢?某個(gè)中斷的產(chǎn)生表示,與它所對(duì)應(yīng)的矩陣行的4個(gè)按鍵中,至少有一個(gè)按鍵被按住了。因此可以通過(guò)查看產(chǎn)生了哪個(gè)中斷,來(lái)確定在矩陣的哪一行中發(fā)生了按鍵操作(按住或釋放)。例如,如果產(chǎn)生了外部2號(hào)線中斷(EINT2變?yōu)榈碗娖剑瑒t表示K7、K8、K9和K15中至少有一個(gè)按鍵被按住了。這時(shí)候4個(gè)EINT端口應(yīng)該通過(guò)GPIO配置寄存器被設(shè)置為外部中斷端口,而且4個(gè)KSCAN端口的輸出必須為低電平。第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)按鍵驅(qū)動(dòng)程序原理(2)在確定按鍵操作所在行的位置之后,我們還得查看按鍵操作所在列的位置。此時(shí)要使用KSCAN端口組,同時(shí)將4個(gè)EINT端口配置為通用輸入端口(而不是中斷端口)。在4個(gè)KSCAN端口中,輪流將其中某一個(gè)端口的輸出置為低電平,其他3個(gè)端口的輸出置為高電平。這樣逐列進(jìn)行掃描,直到按鍵所在列的KSCAN端口輸出為低電平,此時(shí)按鍵操作所在行的EINT管腳的輸入端口的值會(huì)變成低電平。例如,在確認(rèn)產(chǎn)生了外部2號(hào)中斷之后,進(jìn)行逐列掃描。若發(fā)現(xiàn)在KSCAN1為低電平時(shí)(其他端口輸出均為高電平),GPF2(EINT2管腳的輸入端口)變?yōu)榈碗娖?,則可以斷定按鍵K8被按住了。實(shí)際的按鍵動(dòng)作會(huì)在短時(shí)間(幾毫秒至幾十毫秒)內(nèi)產(chǎn)生信號(hào)抖動(dòng)。例如,當(dāng)按鍵被按下時(shí),其動(dòng)作就像彈簧的若干次往復(fù)運(yùn)動(dòng),將產(chǎn)生幾個(gè)脈沖信號(hào)。一次按鍵操作將會(huì)產(chǎn)生若干次按鍵中斷,從而會(huì)產(chǎn)生抖動(dòng)現(xiàn)象。因此驅(qū)動(dòng)程序中必須要解決去除抖動(dòng)所產(chǎn)生的毛刺信號(hào)的問(wèn)題。第11章嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)按鍵驅(qū)動(dòng)程序閱讀并運(yùn)行示例11-6。$makeclean;make /*驅(qū)動(dòng)程序的編譯*/$insmodbutt_dev.ko/*加載b

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(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)論