簡單的字符设备驱动笔记

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");       //注册协议,必须要有,否则出错

猜你喜欢

转载自blog.csdn.net/qq_36413982/article/details/103898563