驱动程序
#include <linux/types.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/ide.h> #include <linux/init.h> #include <linux/module.h> #define CHRDEVBASE_MAJOR 200 /* 主设备号 */ #define CHRDEVBASE_NAME "chrdevbase" /* 设备名 */ static char readbuf[100]; /* 读缓冲区 */ static char writebuf[100]; /* 写缓冲区 */ char kerneldata[] = {"kernel data!"}; /* * @description : 打开设备 * @param - inode : 传递给驱动的inode * @param - filp : 设备文件,file结构体有个叫做private_data的成员变量 * 一般在open的时候将private_data指向设备结构体。 * @return : 0 成功;其他 失败 */ static int chrdevbase_open(struct inode *inode, struct file *filp) { //printk("chrdevbase open!\r\n"); return 0; } /* * @description : 从设备读取数据 * @param - filp : 要打开的设备文件(文件描述符) * @param - buf : 返回给用户空间的数据缓冲区 * @param - cnt : 要读取的数据长度 * @param - offt : 相对于文件首地址的偏移 * @return : 读取的字节数,如果为负值,表示读取失败 */ static ssize_t chrdevbase_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) { int retvalue = 0; /* 向用户空间发送数据 */ memcpy(readbuf, kerneldata, sizeof(kerneldata)); retvalue = copy_to_user(buf, readbuf, cnt); if(retvalue == 0){ printk("kernel senddata ok!\r\n"); }else{ printk("kernel senddata failed!\r\n"); } //printk("chrdevbase read!\r\n"); return 0; } /* * @description : 向设备写数据 * @param - filp : 设备文件,表示打开的文件描述符 * @param - buf : 要写给设备写入的数据 * @param - cnt : 要写入的数据长度 * @param - offt : 相对于文件首地址的偏移 * @return : 写入的字节数,如果为负值,表示写入失败 */ static ssize_t chrdevbase_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) { int retvalue = 0; /* 接收用户空间传递给内核的数据并且打印出来 */ retvalue = copy_from_user(writebuf, buf, cnt); if(retvalue == 0){ printk("kernel recevdata:%s\r\n", writebuf); }else{ printk("kernel recevdata failed!\r\n"); } //printk("chrdevbase write!\r\n"); return 0; } /* * @description : 关闭/释放设备 * @param - filp : 要关闭的设备文件(文件描述符) * @return : 0 成功;其他 失败 */ static int chrdevbase_release(struct inode *inode, struct file *filp) { //printk("chrdevbase release!\r\n"); return 0; } /* * 设备操作函数结构体 */ static struct file_operations chrdevbase_fops = { .owner = THIS_MODULE, .open = chrdevbase_open, .read = chrdevbase_read, .write = chrdevbase_write, .release = chrdevbase_release, }; /* * @description : 驱动入口函数 * @param : 无 * @return : 0 成功;其他 失败 */ static int __init chrdevbase_init(void) { int retvalue = 0; /* 注册字符设备驱动 */ retvalue = register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME, &chrdevbase_fops); if(retvalue < 0){ printk("chrdevbase driver register failed\r\n"); } printk("chrdevbase init!\r\n"); return 0; } /* * @description : 驱动出口函数 * @param : 无 * @return : 无 */ static void __exit chrdevbase_exit(void) { /* 注销字符设备驱动 */ unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME); printk("chrdevbase exit!\r\n"); } /* * 将上面两个函数指定为驱动的入口和出口函数 */ module_init(chrdevbase_init); module_exit(chrdevbase_exit); /* * LICENSE和作者信息 */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("lk");
chrdevbase_open
函数,当应用程序调用 open 函数的时候此函数就会调用
chrdevbase_read
函数,应用程序调用 read 函数从设备中读取数据的时候此函 数会执行。参数 buf 是用户空间的内存,读取到的数据存储在 buf 中,参数 cnt 是要读取的字节 数,参数 offt 是相对于文件首地址的偏移。 kerneldata 里面保存着用户空间要读取的数据
kerneldata 数组中的数据拷贝到读缓冲区 readbuf 中,第 52 行通过函数 copy_to_user 将 readbuf 中的数据复制到参数 buf 中。因为内核空间不能直接操作用户空间的内存,因此需要借 助 copy_to_user 函数来完成内核空间的数据到用户空间的复制 。
chrdevbase_write
函数,应用程序调用 write 函数向设备写数据的时候此函数 就会执行。参数 buf 就是应用程序要写入设备的数据,也是用户空间的内存,参数 cnt 是要写入 的数据长度,参数 offt 是相对文件首地址的偏移。
chrdevbase_release
函数,应用程序调用 close 关闭设备文件的时候此函数会 执行,一般会在此函数里面执行一些释放操作。
驱动入口函数 chrdevbase_init
函数 register_chrdev
用来注册字符设备
驱动出口函数 chrdevbase_exit
函数 unregister_chrdev
用来注销字符设备。
通过 module_init
和 module_exit
这两个函数来指定驱动的入口和出口函数。
用户程序
#include "stdio.h" #include "unistd.h" #include "sys/types.h" #include "sys/stat.h" #include "fcntl.h" #include "stdlib.h" #include "string.h" static char usrdata[] = {"usr data!"}; int main(int argc, char *argv[]) { int fd, retvalue; char *filename; char readbuf[100], writebuf[100]; if(argc != 3){ printf("Error Usage!\r\n"); return -1; } filename = argv[1]; /* 打开驱动文件 */ fd = open(filename, O_RDWR); if(fd < 0){ printf("Can't open file %s\r\n", filename); return -1; } if(atoi(argv[2]) == 1){ /* 从驱动文件读取数据 */ retvalue = read(fd, readbuf, 50); if(retvalue < 0){ printf("read file %s failed!\r\n", filename); }else{ /* 读取成功,打印出读取成功的数据 */ printf("read data:%s\r\n",readbuf); } } if(atoi(argv[2]) == 2){ /* 向设备驱动写数据 */ memcpy(writebuf, usrdata, sizeof(usrdata)); retvalue = write(fd, writebuf, 50); if(retvalue < 0){ printf("write file %s failed!\r\n", filename); } else{ /* 读取成功,打印出读取成功的数据 */ printf("write data:%s\r\n",usrdata); } } /* 关闭设备 */ retvalue = close(fd); if(retvalue < 0){ printf("Can't close file %s\r\n", filename); return -1; } return 0; }
数组 usrdata 是测试 APP 要向 chrdevbase 设备写入的数据。
一共有三个参数“./chrdevbaseApp”、“/dev/chrdevbase”和“1”,这三个参数分别对应 argv[0]、argv[1]和 argv[2]。第一个参数表示运行 chrdevbaseAPP 这个软件,第二个参数表示测试APP要打开/dev/chrdevbase这个设备。第三个参数就是要执行的操作, 1表示从 chrdevbase 中读取数据, 2 表示向 chrdevbase 写数据。
调用 C 库中的 open 函数打开设备文件: /dev/chrdevbase。
运行测试
-
加载驱动模块
-
mount -t nfs -o nolock,nfsvers=3,vers=3 192.168.100.103:/home/lk/nfs /mnt/
-
insmod chrdevbase.ko
-
-
创建设备节点文件
-
mknod /dev/chrdevbase c 200 0
-
-
chrdevbase 设备操作测试
-
./chrdevbaseApp /dev/chrdevbase 1
read data:kernel data!
-
./chrdevbaseApp /dev/chrdevbase 2
kernel recevdata:usr data!
-