字符设备驱动初体验(hello驱动)
文章目錄
- 1 APP打開的文件在內核中如何表示
- 2 打開字符設備節點時,內核中也有對應的 struct file
- 3 驅動程序編寫的步驟
- 4 驅動程序編寫
- 4.1 寫驅動程序
- 4.2 寫測試程序
- 4.3 測試
1 APP打開的文件在內核中如何表示
APP 打開文件時,可以得到一個整數,這個整數被稱為文件句柄。對于 APP 的每一個文件句柄,在內核里面都有一個“struct file”與之對應。
可以猜測,我們使用 open 打開文件時,傳入的 flags、mode 等參數會被記錄在內核中對應的 struct file 結構體里(f_flags、f_mode):int open(const char *pathname, int flags, mode_t mode);
去讀寫文件時,文件的當前偏移地址也會保存在 struct file 結構體的 f_pos 成員里。
2 打開字符設備節點時,內核中也有對應的 struct file
注意這個結構體中的結構體:struct file_operations *f_op,這是由驅動程序提供的。
結構體 struct file_operations 的定義如下:
3 驅動程序編寫的步驟
① 確定主設備號,也可以讓內核分配。
② 定義自己的 file_operations 結構體。
③ 實現對應的 drv_open/drv_read/drv_write 等函數,填入 file_operations 結構體。
④ 把 file_operations 結構體告訴內核:register_chrdev。
⑤ 誰來注冊驅動程序啊?得有一個入口函數:安裝驅動程序時,就會去調用這個入口函數 。
⑥ 有入口函數就應該有出口函數:卸載驅動程序時,出口函數調用 unregister_chrdev。
⑦ 其他完善:提供設備信息,自動創建設備節點:class_create, device_create。
4 驅動程序編寫
4.1 寫驅動程序
參考 driver/char 中的程序,包含頭文件,寫框架,傳輸數據:
A. 驅動中實現 open, read, write, release,APP 調用這些函數時,都打印內核信息
B. APP 調用 write 函數時,傳入的數據保存在驅動中
C. APP 調用 read 函數時,把驅動中保存的數據返回給 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. 確定主設備號 */ static int major = 0; static char kernel_buf[1024]; static struct class *hello_class;#define MIN(a, b) (a < b ? a : b)/* 3. 實現對應的open/read/write等函數,填入file_operations結構體 */ 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結構體 */ 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結構體告訴內核:注冊驅動程序 */ /* 5. 誰來注冊驅動程序啊?得有一個入口函數:安裝驅動程序時,就會去調用這個入口函數 */ 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. 有入口函數就應該有出口函數:卸載驅動程序時,就會去調用這個出口函數 */ 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. 其他完善:提供設備信息,自動創建設備節點 */module_init(hello_init); module_exit(hello_exit);MODULE_LICENSE("GPL");4.2 寫測試程序
測試程序要實現寫、讀功能:
A. ./hello_drv_test -w wiki.100ask.net // 把字符串“wiki.100ask.net”發給驅動程序
B. ./hello_drv_test -r // 把驅動中保存的字符串讀回來
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. 判斷參數 */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 測試
編寫驅動程序的 Makefile:
# 1. 使用不同的開發板內核時, 一定要修改KERN_DIR # 2. KERN_DIR中的內核要事先配置、編譯, 為了能編譯內核, 要先設置下列環境變量: # 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 # 注意: 不同的開發板不同的編譯器上述3個環境變量不一定相同, # 請參考各開發板的高級用戶使用手冊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上機實驗:
參考資料:
總結
以上是生活随笔為你收集整理的字符设备驱动初体验(hello驱动)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 32位 win7旗舰版极速装机版怎么安装
- 下一篇: auto关键字