1:APP打开的文件在内核中如何表示
1.1
int open(const char *pathname, int flags, mode_t mode);例如: fd = open(argv[1], O_RDWR);

1.2 打开字符设备节点时,内核中也有对应的 struct file
这个结构体中的结构体:struct file_operations *f_op,这是由驱动程序提供的。
2:如何编写驱动程序
1:确定主设备号,也可以让内核分配
2:定义自己的 file_operations 结构体
3:实现对应的 drv_open/drv_read/drv_write 等函数,填入 file_operations 结构体
4:把 file_operations 结构体告诉内核:register_chrdev
5:谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数
6:有入口函数就应该有出口函数:卸载驱动程序时,出口函数调用unregister_chrdev
7:其他完善:提供设备信息,自动创建设备节点:class_create, device_create
3:代码编写
3.1:主要代码分析
3.1.1:确定主设备号。
/* 1. 确定主设备号*/
static int major = 0;
//后续在入口函数中,使用下列代码:注册函数进行分配
major = register_chrdev(0, "hello", &hello_drv);
3.1.2:定义自己的 file_operations 结构体
/* 2. 定义自己的file_operations结构体 */
static struct file_operations hello_drv = {
.owner = THIS_MODULE, //必须存在
.open = hello_drv_open, //hello驱动程序的open函数,后续我们需要自己实现这个函数
.read = hello_drv_read, //hello驱动程序的read函数,后续我们需要自己实现这个函数
.write = hello_drv_write, //hello驱动程序的write函数,后续我们需要自己实现这个函数
.release = hello_drv_close, //hello驱动程序的clos函数,后续我们需要自己实现这个函数
};
3.1.3:实现对应的 drv_open/drv_read/drv_write 等函数,
注:我们现在编写的函数属于驱动函数,那么我们在串口掉用这些函数时,
串口那边,我们属于APP
而这些驱动属于 “内核”
我们串口调用read函数 len = read(fd, buf, 1024);时,我们是需要把kernel_buf的数据读到buf中。所以在这read代码中,我们使用 err = copy_to_user(buf, kernel_buf, MIN(1024, size));这行代码,将kernel_buf写入到buf中。
我们串口调用write函数 write(fd, argv[2], len);时,是把argv[2]的数据写入到kernel_buf中。所以在这write代码中,我们使用 err = copy_from_user(kernel_buf, buf, MIN(1024, size));这行代码,将buf也就是argv[2]写入到kernel_buf中。
open函数和close函数只是打印一下内核信息,不做数据处理。
/* 3. 实现对应的open/read/write等函数,填入file_operations结构体 */
static char kernel_buf[1024];
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;
}
3.1.4:把 file_operations 结构体告诉内核:register_chrdev
3.1.5. 谁来注册驱动程序啊?得有一个入口函数:
3.4和3.5可同时在入口函数中实现,安装驱动程序时,系统去调用这个入口函数,这是直接在入口函数中直接将结构体告诉内核
static int __init hello_init(void)
{
int err;
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
//3.4也就是这行代码。在入口函数中进行 注册驱动程序
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;
}
3.1.6:有入口函数就应该有出口函数:卸载驱动程序时,出口函数调用unregister_chrdev
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");
}
3.1.7:其他完善:提供设备信息,自动创建设备节点:class_create, device_create
/* 7. 其他完善:提供设备信息,自动创建设备节点 */
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
3.2:全部代码
3.2.1: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");
3.2.2 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;
}
3.3:测试
./hello_drv_test -w www.100ask.net // 把字符串“www.100ask.net”发给驱动程序./hello_drv_test -r // 把驱动中保存的字符串读回