這裡先簡單把如何寫一個驅動程式的基本程式碼列出來做說明,當然也可以從歐萊禮下載原始碼來做測試,如果官方網站掛掉還有這裡。
// 定義模組的初始函式(__init)與清理函式(__exit) #include <linux/init.h> // 定義許多 module_ 開頭的函式或 MODULE_ 開頭的巨集 #include <linux/module.h> #define DRIVER_AUTHOR "Linly" #define DRIVER_DESC "Hello World crap driver" #ifndef DRIVER_VERSION #define DRIVER_VERSION "v0.0.1" #endif /* * KERN_ALERT = <1>,代表訊息優先度,最高為 <0> * 用法同 printf,只是不支援浮點數 */ static int __init helloworld_init(void) { printk(KERN_ALERT "Hello, world\n"); return 0; } static void __exit helloworld_cleanup(void) { printk(KERN_ALERT "Goodbye, cruel world\n"); } module_init(helloworld_init); // for insmod calls module_exit(helloworld_cleanup); // for rmmod calls // 模組資訊,可從 modinfo helloworld.ko 查詢 MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); MODULE_VERSION(DRIVER_VERSION);■ 說明如下:
- 前兩行的標頭檔通常都會加入,作用如註解所述。
- module_init 是模組(驅動程式)載入會執行的;module_exit 是模組(驅動程式)移除會執行的。
- printk 是寫 kernel module 會使用的 API,因為一般開發應用程式的 API 都不能再撰寫驅動程式時使用,只能使用 kernel API。而優先度的部份共分為七種<0>到<7>,數字越小代表優先度越高,如果你希望訊息可以再你期望的地方印出來,就讓它優先度高一點吧,我通常使用<1>,而這些訊息可以透過「dmesg」看到。
- MODULE_LICENSE 一定要加上所規定的一些字串,有幾種選擇,擇一即可,如:"GPL"、"GPL v2"、"GPL and additional rights"、"Dual BSD/GPL"、"Dual MPL/GPL"、"Proprietary"。
- 載入模組為「sudo insmod xxxx.ko」;移除模組為「sudo rmmod xxxx.ko」,如果說要重新掛載已經存在的模組,需先移除,然後再載入。
KERNEL_VERSION := `uname -r` KERNEL_DIR := /lib/modules/$(KERNEL_VERSION)/build PWD := $(shell pwd) obj-m := helloworld.o #module-objs := helloworld.o all: echo "Building Hello World driver..." make -C $(KERNEL_DIR) M=$(PWD) modules clean: -rm -f *.o *.ko *.mod.c Module.symvers version.h
你會看到 KERNEL_DIR 是指派成 bra bra build,不過實際到那邊看,會發現 build 其實是一個 symbolic link,指向 /usr/src/$(KERNEL_VERSION),也就是 kernel source tree。編譯後會回到 pwd,並且在程式的目錄下,產生 .o 檔案,最後再根據 obj-m 的名稱,產生 .ko 檔(如根據 helloworld.o 產生 helloworld.ko)。然後「sudo insmod helloworld.ko」,就可以再 dmesg 看到訊息囉。