目的:實現最簡單的點燈操作。
Linux一切皆文件,應用程序訪問某個物理設備(文件)時,首先通過open, read, write
等庫函數調用系統調用接口(System call interface
),系統調用通過傳進來的系統調用號操作虛擬文件系統(Virtual File System
),VFS再根據目標文件類型去找相應的驅動程序。
應用程序和VFS之間的接口是系統調用,而VFS與文件系統以及設備文件之間的接口是file_iperations
結構體成員函數。
struct file_operations結構體
Linux通過註冊+回調的方式將驅動程序和系統調用聯繫起來。file_operations
中包含對文件進行打開,關閉,讀寫,控制等一些成員函數。
具體的成員函數參考:Linux 字符設備驅動結構(四)—— file_operations 結構體知識解析
static struct file_operations led_drv_fops = {
.owner = THIS_MODULE,
.open = led_drv_open,
.write = led_drv_write,
};
驅動加載函數
/* 使用insmod命令安裝驅動時會調用此函數 */
int led_drv_init(void){
int minor = 0;
GPIOB = (GPIO *)ioremap(GPIOB_BASE, sizeof(GPIO)); // 地址映射
major = register_chrdev(0, "led_drv", &led_drv_fops); // 註冊驅動, 0表示動態分配主設備號
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
將物理地址映射到內核虛擬地址。register_chrdev
註冊字符設備。- 宏
class_create
用於動態創建設備的邏輯類。---- linux驅動的類class及其節點 device_create
用於動態的創建邏輯設備。會在/dev
目錄下創建與邏輯類對應的設備文件。例如上面的程序創建了/dev/leds
,/dev/led1
,/dev/led2
。主設備號相同,次設備號分別爲0
,1
,2
。- 最後通過宏
module_init
的修飾,將加載函數鏈接到.initcall
段,方便內核尋找。
驅動卸載函數
void led_drv_exit(void){
int minor;
unregister_chrdev(major, "led_drv"); // 卸載驅動
for(minor = 0; minor < 3; minor++){
device_unregister(leddrv_dev[minor]); // 與device_create()對應
}
class_destroy(leddrv_class); // 與class_create()對應
iounmap(GPIOB);
}
module_exit(led_drv_exit);
unregister_chrdev
刪除字符設備。device_unregister
會刪除/sys/devices/virtual
下對應的設備目錄,以及/dev
下對應的設備文件。class_destroy
刪除設備的邏輯類。module_exit
修飾。
幾個內核結構體
struct file
代表一個已經打開的文件,系統中的每個打開的文件在內核空間都有一個關聯的 struct file。struct inode
內核使用inode結構體在內核內部表示一個文件。
更多:Linux 字符設備驅動結構(三)—— file、inode結構體及chardevs數組等相關知識解析
Makefile
KERN_DIR = ~/linux-3.4.y
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
obj-m += first_drv.o
KERN_DIR
是內核目錄,編譯模塊前要先編譯內核。make -C $(KERN_DIR) M=
pwdmodules
->解釋obj-m
表示生成獨立的.ko
文件,obj-y
表示該模塊編譯到zImage