struct file
应用程序每打开一个文件就会得到一个文件句柄,每一个文件在内核中对应了一个struct file结构体。struct file文件中包含了很多文件相关的信息和方法。
struct file_operations结构相关的方法由驱动程序提供。
struct file_operations
与设备驱动相关的结构体:struct file_operations
在fs.h文件中定义
每个设备驱动程序都要实现 struct file_operations
中的一些方法。
字符设备驱动编写
参考内核里面的字符设备驱动编写。
,如misc.c
这个经典的字符设备驱动。
编写字符设备驱动步骤:
1、定义驱动的file_operations结构体
static const struct file_operations hello_fops = {
.owner = THIS_MODULE,
.open = hello_drv_open,
.read = hello_drv_read,
.write = hello_drv_write,
.release = hello_drv_close,
};
2、实现驱动的drv_open/drv_close/drv_write/drv_read等函数,填充进驱动的file_operations结构体
static int hello_drv_open(struct inode *node, struct file *hello_file)
{
printk("%s %s line %d",__FILE__,__FUNCTION__,__LINE__);
return 0;
}
static ssize_t hello_drv_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{
printk("read buffer\r\n");
copy_to_user(data_buffer,buf,MIN(size,BUFFER_SIZE));
return MIN(size,BUFFER_SIZE);
}
static ssize_t hello_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
printk("write buffer\r\n");
copy_from_user(data_buffer,buf,MIN(size,BUFFER_SIZE));
return MIN(size,BUFFER_SIZE);
}
static int hello_drv_close(struct inode *node, struct file *file)
{
printk("%s %s line %d",__FILE__,__FUNCTION__,__LINE__);
return 0;
}
3、确定主设备号(或Linux自动分配),实现入口函数注册设备。安装驱动时,入口函数调用register_chrdev来注册驱动
/* 入口函数 */
static int __init hello_drv_init(void)
{
int err;
/* 注册设备,返回值为主设备号
* 参数1 : 主设备号,为0表示让Linux自动分配主设备号
* 参数2 : 设备节点的名字
* 参数3 : file_operatios结构体
*/
hello_major = register_chrdev(0,"hello",&hello_fops);
/* 让驱动自动创建类 */
hello_drv_class = class_create(THIS_MODULE, "hello_drv_class"); /* 创建一个类 */
err = PTR_ERR(hello_drv_class);
if (IS_ERR(hello_drv_class))
{
unregister_chrdev(hello_major,"hello"); /* 注销设备 */
return -1;
}
device_create(hello_drv_class, NULL, MKDEV(hello_major, 0), NULL, "hello");
return err;
}
4、实现出口函数。卸载驱动时,出口函数调用unregister_chrdev函数注销设备。
/* 出口函数 */
static void __exit hello_drv_exit(void)
{
device_destroy(hello_drv_class, MKDEV(hello_major, 0)); /* 注销设备节点 */
class_destroy(hello_drv_class); /* 销毁类 */
unregister_chrdev(hello_major, "hello"); /* 注销设备 */
}
5、完善出口、入口函数驱动,提供设备信息,使设备自动创建设备节点,相关函数:class_create函数, device_create 函数
- 入口函数:
入口函数注册驱动、创建类、创建设备驱动。
/* 让驱动自动创建类 */
hello_drv_class = class_create(THIS_MODULE, "hello_drv_class"); /* 创建一个类 */
err = PTR_ERR(hello_drv_class);
if (IS_ERR(hello_drv_class))
{
unregister_chrdev(hello_major,"hello"); /* 注销设备 */
return -1;
}
device_create(hello_drv_class, NULL, MKDEV(hello_major, 0), NULL, "hello"); //当加载驱动时,调用入口函数,就会自动调用device_create函数来创建设备节点
- 出口函数:
入口函数注册驱动、创建类、创建设备驱动。对应的出口函数销毁驱动设备节点、类、以及注销驱动。
device_destroy(hello_drv_class, MKDEV(hello_major, 0)); /* 注销设备节点 */
class_destroy(hello_drv_class); /* 销毁类 */
6、使用module_init()修饰入口函数Linux才知道加载驱动时调动哪个函数为入口函数;同样module_exit()修饰出口函数,Linux才知道卸载驱动时调动哪个函数为入口函数。
module_init(hello_drv_init);
module_exit(hello_drv_exit);
重要的点:
1、驱动程序
和应用程序
之间传递数据要使用 copy_from_user/copy_to_user
函数。应用程序工作在用户空间,驱动程序工作在内核空间。写数据时,数据方向用户空间传递给内核空间;读数据时数据方向内核空间到用户空间。
2、MODULE_LICENSE("GPL"); //注册协议,必须要有,否则出错
3、MKDEV(主设备号, 次设备号)
完整代码:
/**
* @file: xxx.c
* @author: AresXu
* @version: v1.00.00
* @date: 2019-11-xx
* @brief:
*/
#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>
#define BUFFER_SIZE 1024
#define MIN(a,b) (a < b ? a : b)
static int hello_major = 0; /* 存放设备主设备号 让内核自动分配 */
struct class * hello_drv_class;
static char data_buffer[BUFFER_SIZE];
static int hello_drv_open(struct inode *node, struct file *hello_file)
{
printk("%s %s line %d",__FILE__,__FUNCTION__,__LINE__);
return 0;
}
static ssize_t hello_drv_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{
printk("read buffer\r\n");
copy_to_user(data_buffer,buf,MIN(size,BUFFER_SIZE));
return MIN(size,BUFFER_SIZE);
}
static ssize_t hello_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
printk("write buffer\r\n");
copy_from_user(data_buffer,buf,MIN(size,BUFFER_SIZE));
return MIN(size,BUFFER_SIZE);
}
static int hello_drv_close(struct inode *node, struct file *file)
{
printk("%s %s line %d",__FILE__,__FUNCTION__,__LINE__);
return 0;
}
static const struct file_operations hello_fops = {
.owner = THIS_MODULE,
.open = hello_drv_open,
.read = hello_drv_read,
.write = hello_drv_write,
.release = hello_drv_close,
};
/* 入口函数 */
static int __init hello_drv_init(void)
{
int err;
hello_major = register_chrdev(0,"hello",&hello_fops); /* 注册设备,返回值为主设备号 */
/* 让驱动自动创建类 */
hello_drv_class = class_create(THIS_MODULE, "hello_drv_class"); /* 创建一个类 */
err = PTR_ERR(hello_drv_class);
if (IS_ERR(hello_drv_class))
{
unregister_chrdev(hello_major,"hello"); /* 注销设备 */
return -1;
}
device_create(hello_drv_class, NULL, MKDEV(hello_major, 0), NULL, "hello");
return err;
}
/* 出口函数 */
static void __exit hello_drv_exit(void)
{
device_destroy(hello_drv_class, MKDEV(hello_major, 0)); /* 注销设备节点 */
class_destroy(hello_drv_class); /* 销毁类 */
unregister_chrdev(hello_major, "hello"); /* 注销设备 */
}
module_init(hello_drv_init);
module_exit(hello_drv_exit);
MODULE_LICENSE("GPL"); //注册协议,必须要有,否则出错