驅動程序請使用第二章https://blog.csdn.net/chenhequanlalala/article/details/140034424
用戶app與驅動交互最常見的做法是insmod驅動后,生成一個設備節點,app通過open,read等系統調用去操作這個設備節點,這里先用mknode命令調試。
mknod 設備名 設備類型(b塊設備/c字符設備/p管道)?主設備號 次設備號
mknod /dev/hello c 240 0
?使用mknode后生成了/dev/hello節點,寫入數據到hello節點中,查看dmesg的輸出發現調用了驅動的open write release
echo 1 > /dev/hello
[ ?802.771723] /home/book/nfs_rootfs/drivers_projects/01_hello_drv/hello_drv.c hello_open 48
[ ?802.773196] /home/book/nfs_rootfs/drivers_projects/01_hello_drv/hello_drv.c hello_write 40
[ ?802.773285] /home/book/nfs_rootfs/drivers_projects/01_hello_drv/hello_drv.c hello_release 56
?
?這里寫一個C程序來讀寫這個設備節點
#include "linux/string.h"
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>/*
讀數據 ./hello_test xxx(設備節點名字)
寫數據 ./hello_test xxx(設備節點名字) string
*/int main(int argc, char **argv)
{int fd;int len;char buf[1024];if(argc < 2){printf("Usage :\n");printf("%s <dev> [str]\n", argv[0]);return -1;}//openfd = open(argv[1], O_RDWR);if(fd < 0){printf("open %s failed\n", argv[1]);return -1;}//readif(argc == 2){len = read(fd, buf, sizeof(buf));printf("%s\n", buf);}//writeelse if(argc == 3){len = write(fd, argv[2], strlen(argv[2]));}else{printf("Too many parameters\n");}close(fd);
}
分別調用?./hello_test /dev/hello 123 和?./hello_test /dev/hello 后,查看dmesg輸出顯示
[ 2770.434595] /home/book/nfs_rootfs/drivers_projects/01_hello_drv/hello_drv.c hello_open 48
[ 2770.434664] /home/book/nfs_rootfs/drivers_projects/01_hello_drv/hello_drv.c hello_write 40
[ 2770.434705] /home/book/nfs_rootfs/drivers_projects/01_hello_drv/hello_drv.c hello_release 56
[ 2772.388372] /home/book/nfs_rootfs/drivers_projects/01_hello_drv/hello_drv.c hello_open 48
[ 2772.388439] /home/book/nfs_rootfs/drivers_projects/01_hello_drv/hello_drv.c hello_read 32
[ 2772.389257] /home/book/nfs_rootfs/drivers_projects/01_hello_drv/hello_drv.c hello_release 56
?app的open write read release都一一對應上了。
這里的./hello_test /dev/hello其實沒有讀到數據,因為驅動程序中并沒有和app交互數據。
[root@100ask:/mnt/drivers_projects/01_hello_drv]# ./hello_test /dev/hello
read size = 1024 data =?
?
?app與驅動交換數據通過copy_from_user和copy_to_user來實現
copy_to_user(void __user *to, const void *from, unsigned long n);
copy_from_user(void *to, const void __user *from, unsigned long n);
在驅動程序中添加一個buf用來保存用戶的數據,用copy_from_user保存數據,用copy_to_user讀取數據。
#include "asm/uaccess.h"
#include "linux/scatterlist.h"
#include "linux/types.h"
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mman.h>
#include <linux/random.h>
#include <linux/init.h>
#include <linux/raw.h>
#include <linux/tty.h>
#include <linux/capability.h>
#include <linux/ptrace.h>
#include <linux/device.h>
#include <linux/highmem.h>
#include <linux/backing-dev.h>
#include <linux/shmem_fs.h>
#include <linux/splice.h>
#include <linux/pfn.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/uio.h>
#include <linux/module.h>#include <linux/uaccess.h>#define DEVICE_NAME "hello_device"
static int major;
#define hello_buf_size 100
static unsigned char hello_buf[hello_buf_size];//ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
//參數含義依次為 要讀取的文件指針 用戶保存數據的buf 讀取數據大小 文件內容的偏移量
static ssize_t hello_read (struct file * filp, char __user *buf, size_t size, loff_t *offset)
{unsigned long len = size > hello_buf_size ? hello_buf_size : size;copy_to_user(buf, hello_buf, len);printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return len;
}//ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
//參數含義依次為 要寫入的文件指針 用戶要寫入的數據buf 寫入數據大小 文件內容的偏移量
static ssize_t hello_write (struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{unsigned long len = size > hello_buf_size ? hello_buf_size : size;copy_from_user(hello_buf, buf, len);printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return len;
}//int (*open) (struct inode *, struct file *);
//參數含義依次為 文件索引節點 文件指針
static int hello_open (struct inode *node, struct file *filp)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}//int (*release) (struct inode *, struct file *);
//參數含義依次為 文件索引節點 文件指針
static int hello_release (struct inode *node, struct file *filp)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}/*構建file_operations結構體*/
static const struct file_operations hello_fops = {.owner = THIS_MODULE,.read = hello_read,.write = hello_write,.open = hello_open,.release = hello_release,
};/*init函數,實現register_chrdev*/
static int __init hello_init(void)
{//數含義依次為 主設備號,如果為0,內核會自動分配一個可用的。設備名,會在/proc/devices中顯示。 file_operations結構體//注冊成功就返回主設備號major = register_chrdev(0, DEVICE_NAME, &hello_fops);printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}/*exit函數unregister_chrdev*/
static void __exit hello_exit(void)
{//數含義依次為 主設備號 設備名unregister_chrdev(major, DEVICE_NAME);printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
}module_init(hello_init);
module_exit(hello_exit);
//遵循GPL協議
MODULE_LICENSE("GPL");
重新insmod驅動后,執行測試程序
[root@100ask:/mnt/drivers_projects/01_hello_drv]# ./hello_test /dev/hello 123456
[root@100ask:/mnt/drivers_projects/01_hello_drv]# ./hello_test /dev/hello ? ? ??
read size = 100 data = 123456
[root@100ask:/mnt/drivers_projects/01_hello_drv]# ./hello_test /dev/hello hello
[root@100ask:/mnt/drivers_projects/01_hello_drv]# ./hello_test /dev/hello?
read size = 100 data = hello6
?
?這里第二次輸出hello6,是因為copy_from_user只是寫入指定長度的數據,第一次寫入的123456只覆蓋了前5字節。