Linux驱动开发(一):字符设备
目的:實(shí)現(xiàn)最簡(jiǎn)單的點(diǎn)燈操作。
Linux一切皆文件,應(yīng)用程序訪問某個(gè)物理設(shè)備(文件)時(shí),首先通過open, read, write等庫函數(shù)調(diào)用系統(tǒng)調(diào)用接口(System call interface),系統(tǒng)調(diào)用通過傳進(jìn)來的系統(tǒng)調(diào)用號(hào)操作虛擬文件系統(tǒng)(Virtual File System),VFS再根據(jù)目標(biāo)文件類型去找相應(yīng)的驅(qū)動(dòng)程序。
應(yīng)用程序和VFS之間的接口是系統(tǒng)調(diào)用,而VFS與文件系統(tǒng)以及設(shè)備文件之間的接口是file_iperations結(jié)構(gòu)體成員函數(shù)。
struct file_operations結(jié)構(gòu)體
Linux通過注冊(cè)+回調(diào)的方式將驅(qū)動(dòng)程序和系統(tǒng)調(diào)用聯(lián)系起來。file_operations 中包含對(duì)文件進(jìn)行打開,關(guān)閉,讀寫,控制等一些成員函數(shù)。
具體的成員函數(shù)參考:Linux 字符設(shè)備驅(qū)動(dòng)結(jié)構(gòu)(四)—— file_operations 結(jié)構(gòu)體知識(shí)解析
驅(qū)動(dòng)加載函數(shù)
/* 使用insmod命令安裝驅(qū)動(dòng)時(shí)會(huì)調(diào)用此函數(shù) */ int led_drv_init(void){int minor = 0;GPIOB = (GPIO *)ioremap(GPIOB_BASE, sizeof(GPIO)); // 地址映射major = register_chrdev(0, "led_drv", &led_drv_fops); // 注冊(cè)驅(qū)動(dòng), 0表示動(dòng)態(tài)分配主設(shè)備號(hào)leddrv_class = class_create(THIS_MODULE, "led_drv");// leddrv_class_dev = class_device_create(leddrv_class, NULL, MKDEV(major, 0), NULL, "led"); // 從3.0開始改為device_create()leddrv_dev[minor] = device_create(leddrv_class, NULL, MKDEV(major, 0), NULL, "leds");for(minor = 1; minor < 3; minor++){leddrv_dev[minor] = device_create(leddrv_class, NULL, MKDEV(major, minor), NULL, "led%d", minor);}led_init();printk("led_drv_init\n"); end:return 0; }module_init(led_drv_init);- ioremap將物理地址映射到內(nèi)核虛擬地址。
- register_chrdev注冊(cè)字符設(shè)備。
- 宏class_create用于動(dòng)態(tài)創(chuàng)建設(shè)備的邏輯類。---- linux驅(qū)動(dòng)的類class及其節(jié)點(diǎn)
- device_create用于動(dòng)態(tài)的創(chuàng)建邏輯設(shè)備。會(huì)在/sys/devices/virtual目錄下創(chuàng)建新的邏輯設(shè)備目錄,在/dev目錄下創(chuàng)建與邏輯類對(duì)應(yīng)的設(shè)備文件。例如上面的程序創(chuàng)建了/dev/leds,/dev/led1,/dev/led2。主設(shè)備號(hào)相同,次設(shè)備號(hào)分別為0,1,2。
- 最后通過宏module_init的修飾,將加載函數(shù)鏈接到.initcall段,方便內(nèi)核尋找。
驅(qū)動(dòng)卸載函數(shù)
void led_drv_exit(void){int minor;unregister_chrdev(major, "led_drv"); // 卸載驅(qū)動(dòng)for(minor = 0; minor < 3; minor++){device_unregister(leddrv_dev[minor]); // 與device_create()對(duì)應(yīng)}class_destroy(leddrv_class); // 與class_create()對(duì)應(yīng)iounmap(GPIOB); }module_exit(led_drv_exit);- unregister_chrdev刪除字符設(shè)備。
- device_unregister會(huì)刪除/sys/devices/virtual下對(duì)應(yīng)的設(shè)備目錄,以及/dev下對(duì)應(yīng)的設(shè)備文件。
- class_destroy刪除設(shè)備的邏輯類。
- module_exit修飾。
幾個(gè)內(nèi)核結(jié)構(gòu)體
- struct file代表一個(gè)已經(jīng)打開的文件,系統(tǒng)中的每個(gè)打開的文件在內(nèi)核空間都有一個(gè)關(guān)聯(lián)的 struct file。
- struct inode內(nèi)核使用inode結(jié)構(gòu)體在內(nèi)核內(nèi)部表示一個(gè)文件。
更多:Linux 字符設(shè)備驅(qū)動(dòng)結(jié)構(gòu)(三)—— file、inode結(jié)構(gòu)體及chardevs數(shù)組等相關(guān)知識(shí)解析
Makefile
KERN_DIR = ~/linux-3.4.yall:make -C $(KERN_DIR) M=`pwd` modules clean:make -C $(KERN_DIR) M=`pwd` modules cleanrm -rf modules.orderobj-m += first_drv.o- KERN_DIR是內(nèi)核目錄,編譯模塊前要先編譯內(nèi)核。
- make -C $(KERN_DIR) M=`pwd` modules ->參考文章
- obj-m表示生成獨(dú)立的.ko文件,obj-y表示該模塊編譯到zImage
- 多個(gè)源文件時(shí):obj-m表示生成的驅(qū)動(dòng)文件名,*-objs表示所有依賴,且所有源文件不能與生成的驅(qū)動(dòng)名同名,否則會(huì)報(bào)module license 'unspecified' taints kernel.錯(cuò)誤。->參考文章
工程文件
01_LED: first_drv
02_KEY: key_polling
總結(jié)
以上是生活随笔為你收集整理的Linux驱动开发(一):字符设备的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【C/C++】字节对齐 ALIGN宏
- 下一篇: linux E667 同步失败