字符设备驱动初体验(hello驱动)
文章目錄
- 1 APP打開的文件在內(nèi)核中如何表示
- 2 打開字符設(shè)備節(jié)點(diǎn)時,內(nèi)核中也有對應(yīng)的 struct file
- 3 驅(qū)動程序編寫的步驟
- 4 驅(qū)動程序編寫
- 4.1 寫驅(qū)動程序
- 4.2 寫測試程序
- 4.3 測試
1 APP打開的文件在內(nèi)核中如何表示
APP 打開文件時,可以得到一個整數(shù),這個整數(shù)被稱為文件句柄。對于 APP 的每一個文件句柄,在內(nèi)核里面都有一個“struct file”與之對應(yīng)。
可以猜測,我們使用 open 打開文件時,傳入的 flags、mode 等參數(shù)會被記錄在內(nèi)核中對應(yīng)的 struct file 結(jié)構(gòu)體里(f_flags、f_mode):int open(const char *pathname, int flags, mode_t mode);
去讀寫文件時,文件的當(dāng)前偏移地址也會保存在 struct file 結(jié)構(gòu)體的 f_pos 成員里。
2 打開字符設(shè)備節(jié)點(diǎn)時,內(nèi)核中也有對應(yīng)的 struct file
注意這個結(jié)構(gòu)體中的結(jié)構(gòu)體:struct file_operations *f_op,這是由驅(qū)動程序提供的。
結(jié)構(gòu)體 struct file_operations 的定義如下:
3 驅(qū)動程序編寫的步驟
① 確定主設(shè)備號,也可以讓內(nèi)核分配。
② 定義自己的 file_operations 結(jié)構(gòu)體。
③ 實(shí)現(xiàn)對應(yīng)的 drv_open/drv_read/drv_write 等函數(shù),填入 file_operations 結(jié)構(gòu)體。
④ 把 file_operations 結(jié)構(gòu)體告訴內(nèi)核:register_chrdev。
⑤ 誰來注冊驅(qū)動程序啊?得有一個入口函數(shù):安裝驅(qū)動程序時,就會去調(diào)用這個入口函數(shù) 。
⑥ 有入口函數(shù)就應(yīng)該有出口函數(shù):卸載驅(qū)動程序時,出口函數(shù)調(diào)用 unregister_chrdev。
⑦ 其他完善:提供設(shè)備信息,自動創(chuàng)建設(shè)備節(jié)點(diǎn):class_create, device_create。
4 驅(qū)動程序編寫
4.1 寫驅(qū)動程序
參考 driver/char 中的程序,包含頭文件,寫框架,傳輸數(shù)據(jù):
A. 驅(qū)動中實(shí)現(xiàn) open, read, write, release,APP 調(diào)用這些函數(shù)時,都打印內(nèi)核信息
B. APP 調(diào)用 write 函數(shù)時,傳入的數(shù)據(jù)保存在驅(qū)動中
C. APP 調(diào)用 read 函數(shù)時,把驅(qū)動中保存的數(shù)據(jù)返回給 APP
hello_drv.c
#include <linux/module.h>#include <linux/fs.h> #include <linux/errno.h> #include <linux/miscdevice.h> #include <linux/kernel.h> #include <linux/major.h> #include <linux/mutex.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/stat.h> #include <linux/init.h> #include <linux/device.h> #include <linux/tty.h> #include <linux/kmod.h> #include <linux/gfp.h>/* 1. 確定主設(shè)備號 */ static int major = 0; static char kernel_buf[1024]; static struct class *hello_class;#define MIN(a, b) (a < b ? a : b)/* 3. 實(shí)現(xiàn)對應(yīng)的open/read/write等函數(shù),填入file_operations結(jié)構(gòu)體 */ static ssize_t hello_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset) {int err;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);err = copy_to_user(buf, kernel_buf, MIN(1024, size));return MIN(1024, size); }static ssize_t hello_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset) {int err;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);err = copy_from_user(kernel_buf, buf, MIN(1024, size));return MIN(1024, size); }static int hello_drv_open (struct inode *node, struct file *file) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return 0; }static int hello_drv_close (struct inode *node, struct file *file) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return 0; }/* 2. 定義自己的file_operations結(jié)構(gòu)體 */ static struct file_operations hello_drv = {.owner = THIS_MODULE,.open = hello_drv_open,.read = hello_drv_read,.write = hello_drv_write,.release = hello_drv_close, };/* 4. 把file_operations結(jié)構(gòu)體告訴內(nèi)核:注冊驅(qū)動程序 */ /* 5. 誰來注冊驅(qū)動程序啊?得有一個入口函數(shù):安裝驅(qū)動程序時,就會去調(diào)用這個入口函數(shù) */ static int __init hello_init(void) {int err;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);major = register_chrdev(0, "hello", &hello_drv); /* /dev/hello */hello_class = class_create(THIS_MODULE, "hello_class");err = PTR_ERR(hello_class);if (IS_ERR(hello_class)) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);unregister_chrdev(major, "hello");return -1;}device_create(hello_class, NULL, MKDEV(major, 0), NULL, "hello"); /* /dev/hello */return 0; }/* 6. 有入口函數(shù)就應(yīng)該有出口函數(shù):卸載驅(qū)動程序時,就會去調(diào)用這個出口函數(shù) */ static void __exit hello_exit(void) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);device_destroy(hello_class, MKDEV(major, 0));class_destroy(hello_class);unregister_chrdev(major, "hello"); }/* 7. 其他完善:提供設(shè)備信息,自動創(chuàng)建設(shè)備節(jié)點(diǎn) */module_init(hello_init); module_exit(hello_exit);MODULE_LICENSE("GPL");4.2 寫測試程序
測試程序要實(shí)現(xiàn)寫、讀功能:
A. ./hello_drv_test -w wiki.100ask.net // 把字符串“wiki.100ask.net”發(fā)給驅(qū)動程序
B. ./hello_drv_test -r // 把驅(qū)動中保存的字符串讀回來
hello_drv_test.c
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <string.h>/** ./hello_drv_test -w abc* ./hello_drv_test -r*/ int main(int argc, char **argv) {int fd;char buf[1024];int len;/* 1. 判斷參數(shù) */if (argc < 2) {printf("Usage: %s -w <string>\n", argv[0]);printf(" %s -r\n", argv[0]);return -1;}/* 2. 打開文件 */fd = open("/dev/hello", O_RDWR);if (fd == -1){printf("can not open file /dev/hello\n");return -1;}/* 3. 寫文件或讀文件 */if ((0 == strcmp(argv[1], "-w")) && (argc == 3)){len = strlen(argv[2]) + 1;len = len < 1024 ? len : 1024;write(fd, argv[2], len);}else{len = read(fd, buf, 1024); buf[1023] = '\0';printf("APP read : %s\n", buf);}close(fd);return 0; }4.3 測試
編寫驅(qū)動程序的 Makefile:
# 1. 使用不同的開發(fā)板內(nèi)核時, 一定要修改KERN_DIR # 2. KERN_DIR中的內(nèi)核要事先配置、編譯, 為了能編譯內(nèi)核, 要先設(shè)置下列環(huán)境變量: # 2.1 ARCH, 比如: export ARCH=arm64 # 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu- # 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin # 注意: 不同的開發(fā)板不同的編譯器上述3個環(huán)境變量不一定相同, # 請參考各開發(fā)板的高級用戶使用手冊KERN_DIR = /home/book/100ask_roc-rk3399-pc/linux-4.4all:make -C $(KERN_DIR) M=`pwd` modules $(CROSS_COMPILE)gcc -o hello_drv_test hello_drv_test.c clean:make -C $(KERN_DIR) M=`pwd` modules cleanrm -rf modules.orderrm -f hello_drv_testobj-m += hello_drv.o上機(jī)實(shí)驗(yàn):
參考資料:
總結(jié)
以上是生活随笔為你收集整理的字符设备驱动初体验(hello驱动)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 32位 win7旗舰版极速装机版怎么安装
- 下一篇: auto关键字