Linux Kernel Driver 字符设备 之 自动创建节点

前言

之前的实验,都是 mknod 手动创建节点,确实比较麻烦。

如何自动创建节点呢?

如果是pc机,比较高级的用的如果是udev 那么比较简单,如果是嵌入式系统被才裁减了,用mdev 相对复杂一些。

之前,如果做过,mknod 创建过的,需要删除掉,rm -rf /dev/hello

 mkdev

1. 保证根文件系统支持mdev可执行程序

which is mdev 

如果没有,那就不支持了,然后 which is udevadm,如果有机制udev,那就不需要往下走了。

2. 保证文件系统的etc目录下有fstab文件,文件内容必须有:

proc  /proc        proc   defaults        0     0

说明:将procfs虚拟文件系统挂接到/proc目录

sysfs /sys         sysfs  defaults        0     0

说明:将sysfs虚拟文件系统挂接到/sys目录

tmpfs /dev         tmpfs  defaults        0     0

说明:将tmpfs虚拟文件系统挂接到/dev目录

注意:sysfs,tmpfs,procfs虚拟文件系统的内容都是存在于内存中!

3. 保证根文件系统的etc/init.d/rcS脚本中,必须有:

/bin/mount -a #为了解析fstab文件

echo /sbin/mdev > /proc/sys/kernel/hotplug #将来内核解析hotplug文件,执行mdev可执行程序,创建设备文件

4. 字符设备驱动只需调用以下四个函数,即可完成设备文件的最终创建

struct class *cls; //定义设备类指针

入口函数调用:

// 定义设备类

cls = class_create(THIS_MODULE, "dev_name");

// 创建设备文件(长苹果),dev表示设备号,myled表示设备文件

device_create(cls, NULL, dev, NULL, "dev_file_name");

出口函数调用:

// 删除设备文件

device_destroy(cls, dev);

//删除设备类

class_destroy(cls);

案例:基于之前的代码,添加自动创建设备文件的功能

#include 
#include 
#include        // struct file_operations
#include      // struct cdev
#include 
#include 


MODULE_DESCRIPTION("Frocheng: Driver for DEMO!");
MODULE_AUTHOR("Frodo Cheng");
MODULE_LICENSE("GPL");
MODULE_VERSION("V0.0.1");

static char* name = "hello";

static struct class *cls;

static int state;

// 应用程序调用关系:open->软中断->sys_open->hello_open
static int hello_open(struct inode *inode,
                        struct file *file)
{
    printk("===[frocheng]===[%s]===[%s]===[%d]===\n",__FILE__, __func__, __LINE__);
    return 0; // 执行成功返回0,失败返回负值
}

// 应用程序调用关系:close->软中断->sys_close->hello_close
static int hello_close(struct inode *inode,
                        struct file *file)
{
    printk("===[frocheng]===[%s]===[%s]===[%d]===\n",__FILE__, __func__, __LINE__);
    return 0; // 执行成功返回0,失败返回负值
}

// 应用read->软中断->sys_read->hello_read
static ssize_t hello_read(struct file *file,
                        char __user *buf,
                        size_t count,
                        loff_t *ppos)
{
    // 将内核缓冲区的数据拷贝到用户缓冲区
    copy_to_user(buf, &state, sizeof(state));
    return sizeof(state); // 返回实际读取的字节数
}

// 应用write->软中断->sys_write->hello_write
static ssize_t hello_write(struct file *file,
                        const char __user *buf,
                        size_t count,
                        loff_t *ppos)
{
    int m = 0;  // 内核区域的 对象
    copy_from_user(&m, buf, sizeof(m));
    // 模拟操作一下:
    // 如果写进来的是偶数,那么,内部状态 更新为 m * 2;
    // 如果写进来的是计数,那么,内部状态 更新为 m * 3;
    if ((m & 0x01) == 0x00)
    {
        state = m * 2;
    }
    else
    {
        state = m * 3;
    }
    
    return sizeof(m);
}



// 定义初始化LED的硬件操作对象
// open,release一旦加载内存中,静静等待着应用程序来调用
static struct file_operations hello_fops = {
    .owner = THIS_MODULE,
    .open = hello_open,     // 打开设备
    .release = hello_close,  // 关闭设备
    .write = hello_write,
    .read = hello_read
};

// 定义字符设备对象
static struct cdev hello_cdev;

// 定义设备号对象
static dev_t dev;

static int __init hello_init(void)
{
	int rc = -1;
    printk("===[frocheng]===[%s]===[%s]===[%d]===[Hello !]===\n",__FILE__, __func__, __LINE__);

    // 申请设备号
    rc = alloc_chrdev_region(&dev, 0, 1, name);
	if (rc != 0)
	{
    	printk("===[frocheng]===[%s]===[%s]===[alloc_chrdev_region error with %d]===\n",__FILE__, __func__, rc);
		// hello 驱动模块加载失败,因为并没有申请到字符神资源,驱动加载失败。
		return -1;
	}
	
    // 自动创建 device
    cls = class_create(THIS_MODULE, name);
    device_create(cls, NULL, dev, NULL, name);

    printk("===[frocheng]===[%s]===[%s]===[name=%s, major=%u, minor=%u]===\n",__FILE__, __func__, name, MAJOR(dev), MINOR(dev));
    
    // 初始化字符设备对象
    cdev_init(&hello_cdev, &hello_fops);

    // 注册字符设备对象
    cdev_add(&hello_cdev, dev, 1);
    
    return 0;
}

static void __exit hello_exit(void)
{
    // 删除设备文件
    device_destroy(cls, dev);
    // 删除设备类
    class_destroy(cls);

    // 卸载字符设备对象
    cdev_del(&hello_cdev);
    
    // 释放设备号
    unregister_chrdev_region(dev, 1);
    printk("===[frocheng]===[%s]===[%s]===[%d]===[Bye bye...]===\n",__FILE__, __func__, __LINE__);
}

module_init(hello_init);
module_exit(hello_exit);

注意: PC机器上,就不用走上面的流程了,一般PC即用的都是udev。

加载了模块之后,ls  /dev/hello,就有文件了,卸载了模块之后,ls /dev/hello ,就不存在了。

你可能感兴趣的:(Linux Kernel Driver 字符设备 之 自动创建节点)