參考資訊:
https://linux-kernel-labs.github.io/refs/heads/master/labs/device_drivers.html
User Application可以透過檔案操作方式跟驅動程式溝通,在Linux Kernel中,將檔案分成字元驅動程式(Char)和區塊驅動程式(Block)兩大類,主要區別在於傳輸方式,可以把字元驅動程式想像成每次傳輸都是以1個Byte為單位,而區塊驅動程式則是多個Bytes為單位,如:512 Bytes,User Application可以透過open()、read()、write()、ioctl()、close()跟字元驅動程式溝通,那User Application怎麼知道路徑在哪呢?答案是Symbolic Link(/dev/xxx),那Symbolic Link又如何連結到字元驅動程式呢?答案是Major、Minor號碼,Major、Minor號碼是驅動程式的裝置索引,字元驅動程式在被系統載入時,會註冊Major和Minor號碼,使用者可以透過mknod工具,去建立一個Symbolic Link(名稱可以由使用者隨意取)並且綁定到字元驅動程式的這個Major、Minor號碼,完成連接的管道
字元驅動程式建立步驟:
1. alloc_chrdev_region() 2. cdev_init() 3. cdev_add() 4. cdev_del() 5. unregister_chrdev_region()
alloc_chrdev_region()可以取得空閒的裝置號碼,如果想要自訂Major號碼,可以使用register_chrdev_region()
main.c
#include <linux/cdev.h> #include <linux/init.h> #include <linux/device.h> #include <linux/module.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("Steward Fu"); MODULE_DESCRIPTION("Linux Driver"); static int base = 0; static struct cdev mycdev; static int myopen(struct inode *inode, struct file *file) { printk("%s\n", __func__); return 0; } static int myclose(struct inode *inode, struct file *file) { printk("%s\n", __func__); return 0; } static const struct file_operations myfops = { .owner = THIS_MODULE, .open = myopen, .release = myclose, }; int ldd_init(void) { alloc_chrdev_region(&base, 0, 1, "myfile"); cdev_init(&mycdev, &myfops); cdev_add(&mycdev, base, 1); printk("major:%d, minor:%d\n", MAJOR(base), MINOR(base)); return 0; } void ldd_exit(void) { cdev_del(&mycdev); unregister_chrdev_region(base, 1); } module_init(ldd_init); module_exit(ldd_exit);
ldd_init: 建立字元驅動程式
myopen: 對應User Application的open()
myclose: 對應User Application的close()
ldd_exit: 刪除字元驅動程式
安裝驅動
# insmod /boot/main.ko major:243, minor:0
P.S. Linux驅動程式使用Major、Minor號碼當作裝置索引
make node
# mknod /dev/myfile c 243 0 # ls -al /dev/myfile crw-r--r-- 1 root root 243, 0 Jan 1 00:00 /dev/myfile
P.S. 建立Symbolic符號,供User Application開啟使用
開啟裝置
# echo "" > /dev/myfile myopen sh: write error: Invalid argument myclose