09 - 设备模型和 sysfs 文件系统

---- 整理自 王利涛老师 课程
实验环境:宅学部落 www.zhaixue.cc

文章目录

0. 什么是设备模型

  • 设备模型核心数据结构:
    kobject、kset、uevent
    device、device_driver、bus、class
  • 设备模型的作用
    电源管理
    热插拔时间管理:hotplug
  • 设备模型的好处:
    • 代码复用:多个设备复用一个驱动
    • 资源的动态申请和释放
    • 简化驱动的编写
    • 热插拔机制:hotplug / uevent
    • 设备模型在内核驱动中的地位
    • 驱动的 OOP 思想:USB、platform、I2C、……

在这里插入图片描述

  • 该节课程主要内容:
    • 设备模型基础:kobject、kset、attribute、uevent
    • 文件系统编程接口:sysfs、注册、挂载、读写
    • 设备模型:device、device_driver、bus、class
    • 设备热插拔事件:hotplug / uevent
    • 如何编写总线型驱动
    • 如何从零实现一个 bus 子系统:hello bus
    • 如何往 bus 子系统注册设备、注册驱动
    • 如何实现驱动的复用性:match_table
    • 如何自动创建设备节点
    • 应用程序:mdev / udev 机制分析

1. 设备模型基础:kobject(kernel object)

  • 对应 sysfs 下的目录:每个 kobject 对应 /sys/ 目录下面的一个目录
  • 结构体定义:struct kobject,name 指定的就是这个目录的名字
struct kobject {
	const char		*name;
	struct list_head	entry;
	struct kobject		*parent;
	struct kset		*kset;
	struct kobj_type	*ktype;
	struct kernfs_node	*sd; /* sysfs directory entry */
	struct kref		kref;
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
	struct delayed_work	release;
#endif
	unsigned int state_initialized:1;
	unsigned int state_in_sysfs:1;
	unsigned int state_add_uevent_sent:1;
	unsigned int state_remove_uevent_sent:1;
	unsigned int uevent_suppress:1;
};
  • kobject 的作用:内核通常用 kobject 结构将各个对象连接起来组成一个分层的结构体系
  • kobject 的初始化和添加流程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 常用的相关 API 接口
struct kobject * __must_check kobject_create(void);
struct kobject * __must_check kobject_create_and_add(const char *name, struct kobject *parent);

void kobject_init(struct kobject *kobj, struct kobj_type *ktype);
int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...);
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...);

void kobject_del(struct kobject *kobj);

struct kobject *kobject_get(struct kobject *kobj);
void kobject_put(struct kobject *kobj);
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>

static struct kobject *kobj_hello;

static int kobject_hello_init(void)
{
    //kobj_hello = kobject_create_and_add("hello", NULL); // 创建/sys/hello
    kobj_hello = kobject_create_and_add("hello", kernel_kobj); // 创建/sys/kernel/hello
    if (!kobj_hello) {
        return -ENOMEM;
    }

    return 0;
}

static void kobject_hello_exit(void)
{
    kobject_put(kobj_hello);
}

module_init(kobject_hello_init);
module_exit(kobject_hello_exit);
MODULE_LICENSE("GPL");
ifneq ($(KERNELRELEASE),)
obj-m := hello.o
else
EXTRA_CFLAGS += -DDEBUG 
KDIR:=/home/code_folder/uboot_linux_rootfs/kernel/linux-5.10.4
ARCH_ARGS := ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
all:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules
clean:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules clean
endif

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2. 设备模型基础:attribute

  • 关键结构体:attribute、kobj_attribute
  • 初始化宏:__ATTR(_name, _mode, _show, _store)
  • 属性的读写方法:show、store

在这里插入图片描述
在这里插入图片描述

  • 编程示例:
    在 /sys 指定目录下创建文件
    通过 /sys 接口修改内核数据
  • attribute 相关编程接口
static inline int __must_check sysfs_create_file(struct kobject *kobj, const struct attribute *attr);
static inline void sysfs_remove_file(struct kobject *kobj, const struct attribute *attr);

在这里插入图片描述
也可以直接:
在这里插入图片描述
在这里插入图片描述

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>


unsigned long hello_value;

static ssize_t value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
    return sprintf(buf, "hello_value = %lu\n", hello_value);
}

static ssize_t value_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
    char tmp_buf[10] = {0};
    strncpy(tmp_buf, buf, count);
    hello_value = simple_strtoul(tmp_buf, NULL, 0);
    return count;
}

static struct kobj_attribute value_attribute = {
    .attr = {
        .name = "value",
        .mode = 0664,
    },
    .show  = value_show,
    .store = value_store, 
};


char hello_buf[100];

static ssize_t hello_buf_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
    strncpy(buf, hello_buf, strlen(hello_buf));
    return strlen(hello_buf);
}

static ssize_t hello_buf_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
    strncpy(hello_buf, buf, count);
    return count;
}

static struct kobj_attribute foo_attribute = __ATTR(buf, 0664, hello_buf_show, hello_buf_store);


static struct kobject *kobj_hello;

static int kobject_hello_init(void)
{
    int retval;

    kobj_hello = kobject_create_and_add("hello", NULL); // 创建目录 /sys/hello
    if (!kobj_hello) {
        return -ENOMEM;
    }

    retval = sysfs_create_file(kobj_hello, &foo_attribute.attr); // 创建文件 /sys/hello/buf
    if (retval) {
        printk(KERN_ALERT "%s: create sysfs file failed\n", __func__);
        kobject_put(kobj_hello);
        return -1;
    }
    retval = sysfs_create_file(kobj_hello, &value_attribute.attr); // 创建文件 /sys/hello/value
    if (retval) {
        printk(KERN_ALERT "%s: create sysfs file failed\n", __func__);
        kobject_put(kobj_hello);
        return -1;
    }

    return 0;
}

static void kobject_hello_exit(void)
{
    kobject_put(kobj_hello);
}

module_init(kobject_hello_init);
module_exit(kobject_hello_exit);
MODULE_LICENSE("GPL");
ifneq ($(KERNELRELEASE),)
obj-m := hello.o
else
EXTRA_CFLAGS += -DDEBUG 
KDIR:=/home/code_folder/uboot_linux_rootfs/kernel/linux-5.10.4
ARCH_ARGS := ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
all:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules
clean:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules clean
endif

在这里插入图片描述

  • 属性群组:attribute_group
  • 二进制属性:bin_attribute
  • 宏:__ATTRIBUTE_GROUPS(_name)
  • 宏:__BIN_ATTR(_name, _mode, _read, _write, _size)
  • 编程示例:
    在 /sys 指定目录下创建一组文件
    在 /sys 指定目录下创建二进制文件
  • attribute 相关编程接口
int __must_check sysfs_create_bin_file(struct kobject *kobj,
				       const struct bin_attribute *attr);
void sysfs_remove_bin_file(struct kobject *kobj,
			   const struct bin_attribute *attr);

int __must_check sysfs_create_group(struct kobject *kobj,
				    const struct attribute_group *grp);
void sysfs_remove_group(struct kobject *kobj,
			const struct attribute_group *grp);

在这里插入图片描述

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>

char hello_buf[100];

static ssize_t hello_buf_read(struct file *filp, struct kobject *kobj, 
                              struct bin_attribute *attr, char *buf, 
                              loff_t offset, size_t count)
{
    //memcpy(buf, hello_buf + offset, count);
    memcpy(buf, attr->private + offset, count);
    return count;
}

static ssize_t hello_buf_write(struct file *filp, struct kobject *kobj, 
                              struct bin_attribute *attr, char *buf, 
                              loff_t offset, size_t count)
{
    //memcpy(hello_buf + offset, buf, count);
    memcpy(attr->private + offset, buf, count); // <------
    return count;
}

static struct bin_attribute hello_bin_attribute = {
    .attr  = {
        .name = "hello_bin_attribute",
        .mode = 0644,
    },
    .read  = hello_buf_read,
    .write = hello_buf_write,
    .size  = 100,
    .private = hello_buf, // <------ 传入hello_buf地址
};


static struct kobject *kobj_hello;

static int kobject_hello_init(void)
{
    int retval;

    kobj_hello = kobject_create_and_add("hello", NULL);
    if (!kobj_hello)
        return -ENOMEM;

    retval = sysfs_create_bin_file(kobj_hello, &hello_bin_attribute); // <------
    if (retval) {
        printk(KERN_ALERT "%s: create sysfs file failed\n", __func__);
        return -1;
    }

    return 0;
}

void kobject_hello_exit(void)
{
    sysfs_remove_bin_file(kobj_hello, &hello_bin_attribute);
    kobject_put(kobj_hello);
}

module_init(kobject_hello_init);
module_exit(kobject_hello_exit);
MODULE_LICENSE("GPL");

在这里插入图片描述
在这里插入图片描述
等价的两种写法:
在这里插入图片描述
在这里插入图片描述

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>

unsigned long hello_value;

static ssize_t value_show(struct kobject *kobj, struct kobj_attribute *attr,
                          char *buf)
{
    return sprintf(buf, "hello_value = %lu\n", hello_value);
}

static ssize_t value_store(struct kobject *kobj, struct kobj_attribute *attr, 
                          const char *buf, size_t count)
{
    char tmp_buf[10] = {0};
    strncpy(tmp_buf, buf, count);
    hello_value = simple_strtoul(tmp_buf, NULL, 0);
    return count;
}

static struct kobj_attribute value_attribute = {
    .attr = {
        .name = "value",
        .mode = 0664,
    },
    .show  = value_show,
    .store = value_store, 
};


char hello_buf[100];

static ssize_t hello_buf_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
    {
    strncpy(buf, hello_buf, strlen(hello_buf));
    return strlen(hello_buf);
}

static ssize_t hello_buf_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
    strncpy(hello_buf, buf, count);
    return count;
}

static struct kobj_attribute foo_attribute = __ATTR(buf, 0664, hello_buf_show, hello_buf_store);



static struct attribute *hello_attrs[] = {
    &value_attribute.attr,
    &foo_attribute.attr,
    NULL,
};

#if 1
static struct attribute_group hello_group = {
    .attrs = hello_attrs,
};
#else
ATTRIBUTE_GROUPS(hello);
#endif

static struct kobject *kobj_hello;

static int kobject_hello_init(void)
{
    int retval;

    kobj_hello = kobject_create_and_add("hello", NULL);
    if (!kobj_hello)
        return -ENOMEM;

    retval = sysfs_create_group(kobj_hello, &hello_group); // <------ 创建多个文件
    if (retval) {
        printk(KERN_ALERT "%s: create sysfs file group failed\n", __func__);
        kobject_put(kobj_hello);
        return -1;
    }

    return 0;
}

void kobject_hello_exit(void)
{
    kobject_put(kobj_hello);
}

module_init(kobject_hello_init);
module_exit(kobject_hello_exit);
MODULE_LICENSE("GPL");

在这里插入图片描述

3. kobject 和 sysfs 的关联

  • sysfs 文件系统简介
  • sysfs 文件系统的注册
  • sysfs 文件系统的挂载
  • kobject 和 sysfs 如何建立关联

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

回到 do_new_mount:

在这里插入图片描述
在这里插入图片描述

再回到这里:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4. sysfs 目录创建流程

  • kobject 和 sysfs 的关联
  • sysfs 目录创建过程:kobject_create_and_add
  • 私有指针的用途

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
父目录为 NULL 时,“hello” 目录就挂在到 sysfs 的根目录:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5. sysfs 文件创建流程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 第 4 节和第 5 节流程走下来,我们就知道了 kobject、attribute 和 sysfs 的关联。

6. VFS inode 的生成过程

  • VFS 层面的 inode 是如何生成的?
    各个文件系统都有自己的 inode
    inode->priv = minix_inode
  • inode 的作用:
    包含了一系列操作集:i_fop、i_op
    定义了 VFS 和具体文件系统的各种借口
    打开文件时,file->f_op = inode->f_fop
  • vfs inode 与 kernfs_node 的关联

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
内核会调用 kernfs_get_inode 来为 kernfs_node 分配一个对应的 VFS inode:
在这里插入图片描述
在这里插入图片描述

7. sysfs 文件打开过程分析

  • 核心结构体之间的关联
  • file 指针和 inode 之间的关联
  • file、seq_file、kernfs_open_file 的关联
  • kernfs_open_file 和 kernfs_inode 的关联
  • 私有指针的用途

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
回到在上一小节中建立 vfs inode 和 kernfs_node 关联的地方:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

8. sysfs 的读写过程分析

根据前面分析的过程,直接到这里:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里回顾下之前的内容:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里再回到之前的内容:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
请添加图片描述

9. kobject 的生命周期

  • 为什么要引入生命周期? – 例如 USB,插上占用内存,拔掉不占用内存
  • 内嵌结构体:cdev、device
  • 结构体成员:对应属性
  • 计数接口:
void kobject_del (struct kobject *kobj);
struct kobject *kobject_get (struct kobject *kobj);
void kobject_put (struct kobject *kobj);
kobj_type->release  // 回调函数

在这里插入图片描述

  • kobject 引用计数
    当添加一个 kobject 时:自身和 parent 都加 1
    当移除一个 kobject 时:自身和 parent 都减 1
    在 kobject 下创建属性,引用计数不变

在这里插入图片描述

  • 实验:kref
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>

unsigned long hello_value;

static ssize_t value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
    return sprintf(buf, "hello_value = %lu\n", hello_value);
}

static ssize_t value_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
    char tmp_buf[10] = {0};
    strncpy(tmp_buf, buf, count);
    hello_value = simple_strtoul(tmp_buf, NULL, 0);
    return count;
}

static struct kobj_attribute value_attribute = {
    .attr = {
        .name = "value",
        .mode = 0664,
    },
    .show  = value_show,
    .store = value_store, 
};


char hello_buf[100];

static ssize_t hello_buf_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
    strncpy(buf, hello_buf, strlen(hello_buf));
    return strlen(hello_buf);
}

static ssize_t hello_buf_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
    strncpy(hello_buf, buf, count);
    return count;
}

static struct kobj_attribute foo_attribute = __ATTR(buf, 0664, hello_buf_show, hello_buf_store);


static struct kobject *kobj_hello;
static struct kobject *kobj_child;

static int kobject_hello_init(void)
{
    int retval;

    kobj_hello = kobject_create_and_add("hello", NULL);
    if (!kobj_hello)
        return -ENOMEM;
    printk("kobj_hello.refcount = %d\n", kobj_hello->kref.refcount.refs.counter);

	retval = sysfs_create_file(kobj_hello, &foo_attribute.attr);
	if (retval) {
		printk(KERN_ALERT "%s: create sysfs file failed\n", __func__);
	    kobject_put(kobj_hello);
        return -1;
    }
    printk("kobj_hello.refcount = %d\n", kobj_hello->kref.refcount.refs.counter);

    retval = sysfs_create_file(kobj_hello, &value_attribute.attr);
	if (retval) {
		printk(KERN_ALERT "%s: create sysfs file failed\n", __func__);
	    kobject_put(kobj_hello);
        return -1;
    }
    printk("kobj_hello.refcount = %d\n", kobj_hello->kref.refcount.refs.counter);

    // 创建 "child" kobject,并将其作为 "hello" kobject 的子对象
    kobj_child = kobject_create_and_add("child", kobj_hello);
    if (!kobj_child) {
        kobject_put(kobj_hello);
        return -ENOMEM;
     }
    printk("kobj_hello.refcount = %d, after child kobject created.\n", kobj_hello->kref.refcount.refs.counter);
    printk("kobj_child.refcount = %d\n", kobj_child->kref.refcount.refs.counter);
#if 0
    kobject_put(kobj_hello);
    kobject_put(kobj_hello);
    printk("hello.refcount = %d\n", kobj_hello->kref.refcount.refs.counter);
    printk("child.refcount = %d\n", kobj_child->kref.refcount.refs.counter);
#endif
    return 0;
}

void kobject_hello_exit(void)
{
    kobject_put(kobj_child);
    kobject_put(kobj_hello);
}

module_init(kobject_hello_init);
module_exit(kobject_hello_exit);
MODULE_LICENSE("GPL");

ifneq ($(KERNELRELEASE),)
obj-m := hello.o
else
EXTRA_CFLAGS += -DDEBUG 
KDIR:=/home/code_folder/uboot_linux_rootfs/kernel/linux-5.10.4
ARCH_ARGS := ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
all:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules
clean:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules clean
endif

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

10. 设备模型基础:kset

  • kset 的定义:一组 kobject,是 kobject 的容器;kset 本身也是一个内核对象,需要内嵌一个 kobject 对象
  • kset 的作用
  • kset 与 sysfs 之间的对应关系

在这里插入图片描述

  • kset 编程接口及示例:
    创建一个 kset 对象
    指定 kset 的 parent
    创建 kset 下的属性
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/slab.h>

unsigned long hello_value;

static ssize_t value_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
    return sprintf(buf, "hello_value = %lu\n", hello_value);
}

static ssize_t value_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count)
{
    char tmp_buf[10] = {0};
    strncpy(tmp_buf, buf, count);
    hello_value = simple_strtoul(tmp_buf, NULL, 0);
    return count;
}

struct attribute value_attr = {
    .name = "value",
    .mode = 0644,
};
struct sysfs_ops value_sysfs_ops = {
    .show  = value_show,
    .store = value_store, 
};
static struct attribute *value_attr_array[] = {
    &value_attr,
    NULL,
};

/******************************************************************/
char hello_buf[100];

static ssize_t hello_buf_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
    strncpy(buf, hello_buf, strlen(hello_buf));
    return strlen(hello_buf);
}

static ssize_t hello_buf_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count)
{
    strncpy(hello_buf, buf, count);
    return count;
}

static struct attribute buf_attr = {
    .name  = "buf",
    .mode  = 0644, 
};
struct sysfs_ops buf_sysfs_ops = {
    .show  = hello_buf_show,
    .store = hello_buf_store, 
};
static struct attribute *buf_attr_array[] = {
    &buf_attr,
    NULL,
};


/*------------------------------------------------------------------*/
void my_obj_release(struct kobject *kobj)
{
    printk("%s: kfree %s\n", __func__, kobj->name);
    kfree(kobj);
}
static struct kobject *kobj_hello;
static struct kset *kset_hello;
static struct kobject *kobj_value, *kobj_buf;
static struct kobj_type value_type, buf_type;

static int kobject_hello_init(void)
{
    int retval;

    kobj_hello = kobject_create_and_add("hello", NULL);
    if (!kobj_hello) {
        return -ENOMEM;
    }

    // 创建一个 kset,名为 kset_hello
    // kset_hello = kset_create_and_add("kset_hello", NULL, kobj_hello); // 指定父节点创建
    kset_hello = kset_create_and_add("kset_hello", NULL, NULL);
    if (!kset_hello) {
        kobject_put(kobj_hello);
        return -ENOMEM;
    }
    kobj_value = kzalloc(sizeof(struct kobject), GFP_KERNEL);
    kobj_value->kset = kset_hello; // 设置 kobject 所属的 kset
    value_type.release = my_obj_release;
    value_type.default_attrs = value_attr_array; // 绑定属性
    value_type.sysfs_ops = &value_sysfs_ops;

    kobj_buf = kzalloc(sizeof(struct kobject), GFP_KERNEL);
    kobj_buf->kset = kset_hello; // 设置 kobject 所属的 kset
    buf_type.release = my_obj_release;
    buf_type.default_attrs = buf_attr_array; // 绑定属性
    buf_type.sysfs_ops = &buf_sysfs_ops;

    // retval = kobject_init_and_add(kobj_value, &value_type, kobj_hello, "value");
    retval = kobject_init_and_add(kobj_value, &value_type, NULL, "value");
    retval = kobject_init_and_add(kobj_buf, &buf_type, NULL, "buf");

    return 0;
}

void kobject_hello_exit(void)
{
    kobject_del(kobj_value);
    kobject_put(kobj_value);
    kobject_put(kobj_buf);
    kobject_del(kobj_hello);
    kobject_put(kobj_hello);
    kset_unregister(kset_hello);
}

module_init(kobject_hello_init);
module_exit(kobject_hello_exit);
MODULE_LICENSE("GPL");

在这里插入图片描述
在这里插入图片描述

11. 热插拔事件:uevent

  • 什么是热插拔事件?
  • 什么是 uevent?
  • uevent 的主要功能是什么?
  • 如何监听 uevent?
  • 什么是 uevent_helper?
  • udev、mdev 是什么?
  • 知识点:
    一个 kobject 要有 kset,然后才能发送 uevent
    一个死循环程序,一直监听 uevent – udevadm monitor
    指定要运行程序 – uevent_helper
  • 热插拔(hotplug)uevent 编程实验
    编写一个内核模块,发送 uevent 事件
    在用户空间监听 uevent
    内核编译配置:uevent_helper
    执行用户空间的指定程序

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

11.1 x86 平台上的实验

11.1.1 实验 1

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/slab.h>

static struct kobject *kobj_hello;
static struct kset *kset_hello;
static struct kobject *kobj_value;
static struct kobj_type value_type;

static int kobject_hello_init(void)
{
    return 0;
}

void kobject_hello_exit(void)
{

}

module_init(kobject_hello_init);
module_exit(kobject_hello_exit);
MODULE_LICENSE("GPL");

obj-m = hello.o
all:
	make  -C /lib/modules/$(shell uname -r)/build/  M=$(PWD) modules
clean:
	make  -C /lib/modules/$(shell uname -r)/build/  M=$(PWD) clean

在这里插入图片描述

11.1.2 实验 2

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/slab.h>

void value_obj_release(struct kobject *kobj)
{
    printk("%s: %s released\n", __func__, kobj->name);
    kfree(kobj);
}

static struct kobject *kobj_hello;
static struct kset *kset_hello;
static struct kobject *kobj_value;
static struct kobj_type value_type;

static int kobject_hello_init(void)
{
    int retval;

    // 创建一个名为 kset_hello 的 kset 并将其添加到 sysfs 中
    kset_hello = kset_create_and_add("kset_hello", NULL, NULL);
    if (!kset_hello)
        return -ENOMEM;

    // 分配并初始化kobject kobj_value,将其挂载到 kset_hello 下
    kobj_value = kzalloc(sizeof(struct kobject), GFP_KERNEL);
    kobj_value->kset = kset_hello;
    value_type.release = value_obj_release;

    // 初始化 kobject 并将其添加到 sysfs 中,名称为 value
    retval = kobject_init_and_add(kobj_value, &value_type, NULL, "value");
    
    // 触发 uevent,通知用户空间 kobject 状态发生了变化
    kobject_uevent(kobj_value, KOBJ_CHANGE); // <== 这里KOBJ_CHANGE
    
    return 0;
}

void kobject_hello_exit(void)
{
    kobject_put(kobj_value);
    kset_unregister(kset_hello);
}

module_init(kobject_hello_init);
module_exit(kobject_hello_exit);
MODULE_LICENSE("GPL");

在这里插入图片描述

11.1.3 实验 3

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/slab.h>

static struct kobject *kobj_hello;
static struct kset *kset_hello;
static struct kobject *kobj_value;
static struct kobj_type value_type;

static int kobject_hello_init(void)
{
    kobj_hello = kobject_create_and_add("hello", NULL);
    if (!kobj_hello)
        return -ENOMEM;
    kobject_uevent(kobj_hello, KOBJ_CHANGE); // <== 监听不到

    return 0;
}

void kobject_hello_exit(void)
{
    kobject_put(kobj_hello);
}

module_init(kobject_hello_init);
module_exit(kobject_hello_exit);
MODULE_LICENSE("GPL");

在这里插入图片描述

11.1.4 实验 4:不使用一直监听的 udevadm monitor,使用 uevent_helper

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/slab.h>

void value_obj_release(struct kobject *kobj)
{
    printk("%s: %s released\n", __func__, kobj->name);
    kfree(kobj);
}

static struct kobject *kobj_hello;
static struct kset *kset_hello;
static struct kobject *kobj_value;
static struct kobj_type value_type;

static int kobject_hello_init(void)
{
	int retval;
#if 1
	kobj_hello = kobject_create_and_add("hello", NULL);
	if (!kobj_hello)
		return -ENOMEM;
    kobject_uevent(kobj_hello, KOBJ_CHANGE);
#endif

#if 1
    kset_hello = kset_create_and_add("kset_hello", NULL, NULL);
    if (!kset_hello)
        return -ENOMEM;

    // 分配并初始化kobject kobj_value,将其挂载到 kset_hello 下
    kobj_value = kzalloc(sizeof(struct kobject), GFP_KERNEL);
    kobj_value->kset = kset_hello;
    value_type.release = value_obj_release;

    // 初始化 kobject 并将其添加到 sysfs 中,名称为 value
    retval = kobject_init_and_add(kobj_value, &value_type, NULL, "value");
    
    // 触发 uevent,通知用户空间 kobject 状态发生了变化
    kobject_uevent(kobj_value, KOBJ_CHANGE); // <== 这里
#endif
    
    return 0;
}

void kobject_hello_exit(void)
{
#if 1
    kobject_put(kobj_hello);
#endif

#if 1
    kobject_put(kobj_value);
    kset_unregister(kset_hello);
#endif
}

module_init(kobject_hello_init);
module_exit(kobject_hello_exit);
MODULE_LICENSE("GPL");
#!/bin/sh

mknod /dev/hello0 c 253 0
mknod /dev/hello1 c 253 1
mknod /dev/hello2 c 253 2
mknod /dev/hello3 c 253 3
mknod /dev/hello4 c 253 4
mknod /dev/hello5 c 253 5

在这里插入图片描述

11.2 arm 平台上的实验

11.2.1 编译配置内核 uevent_helper

在这里插入图片描述

11.2.2 实验

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/slab.h>

void value_obj_release(struct kobject *kobj)
{
    printk("%s: %s released\n", __func__, kobj->name);
    kfree(kobj);
}

static struct kobject *kobj_hello;
static struct kset *kset_hello;
static struct kobject *kobj_value;
static struct kobj_type value_type;

static int kobject_hello_init(void)
{
    int retval;
#if 1
    kobj_hello = kobject_create_and_add("hello", NULL);
    if (!kobj_hello)
        return -ENOMEM;
    kobject_uevent(kobj_hello, KOBJ_CHANGE);
#endif

#if 1
    kset_hello = kset_create_and_add("kset_hello", NULL, NULL);
    if (!kset_hello)
        return -ENOMEM;

    // 分配并初始化kobject kobj_value,将其挂载到 kset_hello 下
    kobj_value = kzalloc(sizeof(struct kobject), GFP_KERNEL);
    kobj_value->kset = kset_hello;
    value_type.release = value_obj_release;

    // 初始化 kobject 并将其添加到 sysfs 中,名称为 value
    retval = kobject_init_and_add(kobj_value, &value_type, NULL, "value");
    
    // 触发 uevent,通知用户空间 kobject 状态发生了变化
    kobject_uevent(kobj_value, KOBJ_CHANGE); // <== 这里
#endif
    
    return 0;
}

void kobject_hello_exit(void)
{
#if 1
    kobject_put(kobj_hello);
#endif

#if 1
    kobject_put(kobj_value);
    kset_unregister(kset_hello);
#endif
}

module_init(kobject_hello_init);
module_exit(kobject_hello_exit);
MODULE_LICENSE("GPL");

ifneq ($(KERNELRELEASE),)
obj-m := hello.o
else
EXTRA_CFLAGS += -DDEBUG 
KDIR:=/home/code_folder/uboot_linux_rootfs/kernel/linux-5.10.4
ARCH_ARGS := ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
all:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules
clean:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules clean
endif

在这里插入图片描述

11.3 源码分析:uevent 热插拔事件处理流程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

11.4 编程实战

  • 发送自定义信息到用户空间
  • 编写用户解析工具,解析 uevent 信息
  • 自动创建设备文件
  • 自动删除设备文件
// netlink_monitor.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <linux/netlink.h>

const char *action = "";
const char *devpath = "";
const char *subsystem = "";
const char *firmware = "";
int major = 0;
int minor = 0;
const char *addr = "";
const char *devname = "";
const char *name = "";
	
int open_socket(void)
{
    int fd;

    struct sockaddr_nl socknl_addr = {
        .nl_family = AF_NETLINK,
        .nl_pad = 1,
        .nl_pid = getpid(),
        .nl_groups = 0xffffffff,
    };

    fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
    if (fd < 0) {
        printf("%s: create socket ep failed\n", __func__);
        return -1;
    }

    if (bind(fd, (struct sockaddr *) &socknl_addr, sizeof(socknl_addr)) < 0) {
        printf("%s: bind socket failed\n", __func__);
        close(fd);
        return -1;
    }

    return fd;
}

void parse_event(const char *msg)
{
    while (*msg) {
        printf("%s\n", msg);
        if (!strncmp(msg, "ACTION=", 7)) {
            msg += 7;
            action = msg;
        } else if (!strncmp(msg, "DEVPATH=", 8)) {
            msg += 8;
            devpath = msg;
        } else if (!strncmp(msg, "SUBSYSTEM=", 10)) {
            msg += 10;
            subsystem = msg;
        } else if (!strncmp(msg, "MAJOR=", 6)) {
            msg += 6;
            major = atoi(msg);
        } else if (!strncmp(msg, "MINOR=", 6)) {
            msg += 6;
            minor = atoi(msg);
        } else if (!strncmp(msg, "ADDR=", 5)) {
            msg += 5;
            addr = msg;
        } else if (!strncmp(msg, "NAME=", 5)) {
            msg += 5;
            name = msg;
        } else if (!strncmp(msg, "DEVNAME=", 8)) {
            msg += 8;
            devname = msg;
        }
        while(*msg++);
    }
    
    printf("---------------------------------------------------------\n");
}

void make_hello_node(const char *devname, mode_t mode, int major, int minor)
{
    char pathname[20];

    strcpy(pathname, "/dev/");
    strcpy(&pathname[5], devname);

    if (!strcmp(action, "add"))
        mknod(pathname, 0666, ((major<<20) | minor));

    if (!strcmp(action, "remove"))
        remove(pathname);
}

int main(void)
{
    int fd, len;
    char recv_msg[4096 + 2];
    
    fd = open_socket();
    do {
        while((len = recv(fd, recv_msg, 4096, 0)) > 0) {
            if(len == 4096)
                continue;
            recv_msg[len] = '\0';
            recv_msg[len + 1] = '\0';
            parse_event(recv_msg);
            make_hello_node(devname, 0666, major, minor);
        }
    } while(1);
}

// hello.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/slab.h>

static struct kobject *kobj_hello;
static struct kset *kset_hello;
static struct kobj_type hello_kobj_type;

static int hello_uevent(struct kset *kset, struct kobject *kobj, 
                        struct kobj_uevent_env *env)
{
    add_uevent_var(env, "ADDR=%s", "China");
    add_uevent_var(env, "NAME=%s", "Jiangsu");
    add_uevent_var(env, "DEVNAME=%s", "Huaian");

    return 0;
}

static const struct kset_uevent_ops hello_uevent_ops = {
    .uevent = hello_uevent,
    .filter = NULL,
    .name   = NULL,
};

void hello_kobj_release(struct kobject *kobj)
{
    kfree(kobj);
}

static int kobject_hello_init(void)
{
    int ret;
    
    kset_hello = kset_create_and_add("kset_hello", &hello_uevent_ops, NULL);
    
    kobj_hello = kzalloc(sizeof(struct kobject), GFP_KERNEL);
    kobj_hello->kset = kset_hello;

    hello_kobj_type.release = hello_kobj_release;

    ret = kobject_init_and_add(kobj_hello, &hello_kobj_type, NULL, "kobj_hello"); 
    if (ret) {
        kset_unregister(kset_hello);
        return ret;
    }

    kobject_uevent(kobj_hello, KOBJ_ADD);

    return 0;
}

void kobject_hello_exit(void)
{
    kobject_put(kobj_hello);
    kset_unregister(kset_hello);
}

module_init(kobject_hello_init);
module_exit(kobject_hello_exit);
MODULE_LICENSE("GPL");

在这里插入图片描述

12. 用 OOP 思想分析设备模型

12.1 设备模型中的 OOP 思想(Object-Oriented Programming,面向对象编程)

  • 俄罗斯套娃模式开启:
    kobject/kset ⇒ device/cdev ⇒ usb_device/net_device
    kobject_uevent/kobject_add ⇒ xx_register/xx_add
  • OOP 思想的 C 语言模拟实现:
    OOP:封装、继承、多态、抽象类、接口
    实现:函数指针、内嵌结构体、私有指针

12.2 设备模型的核心要素

  • bus:match、uevent、probe、suspend
  • device:kobject、parent、devt、init_nama
  • device_driver:name、probe、match_table
  • class:name
  • 设备模型的工作机制:
    • 注册一个总线:实现 match 方法
    • 往总线注册设备
    • 往总线注册驱动
    • 驱动的 probe

13. 设备模型:bus

13.1 结构体

在这里插入图片描述
在这里插入图片描述

13.2 如何注册一个总线

  • 注册接口:bus_register
  • 实现总线的 match 方法
  • 在 bus 下创建属性文件
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>

static int hello_bus_match(struct device *dev,  struct device_driver *driver)
{
    int match;
    // 比较设备名称和驱动名称,返回匹配结果
    match = !strncmp(dev_name(dev), driver->name, strlen(driver->name));
    return match;
}

// 定义一个新的总线类型
struct bus_type hello_bus_type = {
    .name	= "hello_bus",      // 总线名称
    .match 	= hello_bus_match,  // 匹配函数
};
EXPORT_SYMBOL_GPL(hello_bus_type);

// 显示总线名称的函数
static ssize_t hello_bus_show(struct bus_type *bus, char *buf)
{
	return sprintf(buf, "bus name: %s\n", hello_bus_type.name);
}

static struct bus_attribute hello_bus_attr = {
    .attr = {
        .name = "hello_bus_attr",
        .mode = 0644,
    },
    .show = hello_bus_show,
};

static int __init hello_bus_init(void)
{
    int ret;
    
    // 注册新的总线类型
    ret = bus_register(&hello_bus_type);
    if (ret)
        return ret;

    // 创建总线属性文件
    ret = bus_create_file(&hello_bus_type, &hello_bus_attr);
    if (ret) {
        bus_unregister(&hello_bus_type);
        return ret;
    }

    return 0;
}

static void __exit hello_bus_exit(void)
{
    bus_remove_file(&hello_bus_type, &hello_bus_attr);
    bus_unregister(&hello_bus_type);
}
module_init(hello_bus_init);
module_exit(hello_bus_exit);
MODULE_LICENSE("GPL");
ifneq ($(KERNELRELEASE),)
obj-m += bus.o
obj-m += device.o
obj-m += driver.o
else
EXTRA_CFLAGS += -DDEBUG 
KDIR:=/home/code_folder/uboot_linux_rootfs/kernel/linux-5.10.4
ARCH_ARGS := ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
all:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules
clean:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules clean
endif

在这里插入图片描述

13.3 bus 和 kobject/kset 的关联

bus:内嵌 kset

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

13.4 bus_register 注册流程分析

在这里插入图片描述
在这里插入图片描述

13.5 bus_create_file 过程分析

在这里插入图片描述
在这里插入图片描述

14. 设备模型:device

14.1 结构体:device 和 device_attribute

在这里插入图片描述
在这里插入图片描述

14.2 如何向 bus 上注册设备和创建设备属性文件

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>

static int hello_bus_match(struct device *dev,  struct device_driver *driver)
{
    int match;
    // 比较设备名称和驱动名称,返回匹配结果
    match = !strncmp(dev_name(dev), driver->name, strlen(driver->name));
    return match;
}

// 定义一个新的总线类型
struct bus_type hello_bus_type = {
    .name	= "hello_bus",      // 总线名称
    .match 	= hello_bus_match,  // 匹配函数
};
EXPORT_SYMBOL_GPL(hello_bus_type);

// 显示总线名称的函数
static ssize_t hello_bus_show(struct bus_type *bus, char *buf)
{
	return sprintf(buf, "bus name: %s\n", hello_bus_type.name);
}

static struct bus_attribute hello_bus_attr = {
    .attr = {
        .name = "hello_bus_attr",
        .mode = 0644,
    },
    .show = hello_bus_show,
};

static int __init hello_bus_init(void)
{
    int ret;
    
    // 注册新的总线类型
    ret = bus_register(&hello_bus_type);
    if (ret)
        return ret;

    // 创建总线属性文件
    ret = bus_create_file(&hello_bus_type, &hello_bus_attr);
    if (ret) {
        bus_unregister(&hello_bus_type);
        return ret;
    }

    return 0;
}

static void __exit hello_bus_exit(void)
{
    bus_remove_file(&hello_bus_type, &hello_bus_attr);
    bus_unregister(&hello_bus_type);
}
module_init(hello_bus_init);
module_exit(hello_bus_exit);
MODULE_LICENSE("GPL");
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>

extern struct bus_type hello_bus_type;

void hello_device_release (struct device *dev)
{
    printk("%s\n", __func__);
}

// 定义一个设备结构
struct device hello_device = {
    .init_name = "hello",       // 设备名称
    .bus   = &hello_bus_type,   // 所属总线类型
    .release = hello_device_release,
    .devt = ((251 << 20) | 0),  // 设备号
};


static ssize_t hello_device_show (struct device *dev, struct device_attribute *attr, char *buf)
{
    return sprintf(buf, "hello_device name: hello\n");
}

// 定义设备属性结构
static struct device_attribute hello_device_attr = {
    .attr = {
        .name = "hello_device_attr",
        .mode = 0444,
    },
    .show = hello_device_show,
};


static int __init hello_device_init(void)
{
    int ret;

    ret = device_register(&hello_device);
    if (ret)
        return ret;

    ret = device_create_file(&hello_device, &hello_device_attr);
    if (ret) {
        device_unregister(&hello_device);
        return ret;
    }

    return 0;
}

static void __exit hello_device_exit(void)
{
    device_remove_file(&hello_device, &hello_device_attr);
    device_unregister(&hello_device);
}

module_init(hello_device_init);
module_exit(hello_device_exit);
MODULE_LICENSE("GPL");

ifneq ($(KERNELRELEASE),)
obj-m += bus.o
obj-m += device.o
obj-m += driver.o
else
EXTRA_CFLAGS += -DDEBUG 
KDIR:=/home/code_folder/uboot_linux_rootfs/kernel/linux-5.10.4
ARCH_ARGS := ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
all:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules
clean:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules clean
endif

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

14.3 设备注册过程分析:device_register

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

14.4 创建设备属性文件:device_create_file

  • 默认属性文件:uevent、dev
  • 默认链接文件:subsystem
  • 自定义属性文件:hello_device_attr

在这里插入图片描述

回看之前 sysfs_create_file 部分的内容。

15. 设备模型:device_driver

15.1 结构体 device_driver 和 driver_attribute

在这里插入图片描述
在这里插入图片描述

15.2 如何向总线添加一个驱动和创建驱动属性文件

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>

static int hello_bus_match(struct device *dev,  struct device_driver *driver)
{
    int match;
    // 比较设备名称和驱动名称,返回匹配结果
    match = !strncmp(dev_name(dev), driver->name, strlen(driver->name));
    return match;
}

// 定义一个新的总线类型
struct bus_type hello_bus_type = {
    .name	= "hello_bus",      // 总线名称
    .match 	= hello_bus_match,  // 匹配函数
};
EXPORT_SYMBOL_GPL(hello_bus_type);

// 显示总线名称的函数
static ssize_t hello_bus_show(struct bus_type *bus, char *buf)
{
	return sprintf(buf, "bus name: %s\n", hello_bus_type.name);
}

static struct bus_attribute hello_bus_attr = {
    .attr = {
        .name = "hello_bus_attr",
        .mode = 0644,
    },
    .show = hello_bus_show,
};

static int __init hello_bus_init(void)
{
    int ret;
    
    // 注册新的总线类型
    ret = bus_register(&hello_bus_type);
    if (ret)
        return ret;

    // 创建总线属性文件
    ret = bus_create_file(&hello_bus_type, &hello_bus_attr);
    if (ret) {
        bus_unregister(&hello_bus_type);
        return ret;
    }

    return 0;
}

static void __exit hello_bus_exit(void)
{
    bus_remove_file(&hello_bus_type, &hello_bus_attr);
    bus_unregister(&hello_bus_type);
}
module_init(hello_bus_init);
module_exit(hello_bus_exit);
MODULE_LICENSE("GPL");
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>

extern struct bus_type hello_bus_type;

void hello_device_release (struct device *dev)
{
    printk("%s\n", __func__);
}

// 定义一个设备结构
struct device hello_device = {
    .init_name = "hello",       // 设备名称
    .bus   = &hello_bus_type,   // 所属总线类型
    .release = hello_device_release,
    .devt = ((251 << 20) | 0),  // 设备号
};


static ssize_t hello_device_show (struct device *dev, struct device_attribute *attr, char *buf)
{
    return sprintf(buf, "hello_device name: hello\n");
}

// 定义设备属性结构
static struct device_attribute hello_device_attr = {
    .attr = {
        .name = "hello_device_attr",
        .mode = 0444,
    },
    .show = hello_device_show,
};


static int __init hello_device_init(void)
{
    int ret;

    ret = device_register(&hello_device);
    if (ret)
        return ret;

    ret = device_create_file(&hello_device, &hello_device_attr);
    if (ret) {
        device_unregister(&hello_device);
        return ret;
    }

    return 0;
}

static void __exit hello_device_exit(void)
{
    device_remove_file(&hello_device, &hello_device_attr);
    device_unregister(&hello_device);
}

module_init(hello_device_init);
module_exit(hello_device_exit);
MODULE_LICENSE("GPL");

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/string.h>

extern struct bus_type hello_bus_type;

// 设备驱动的探测函数
static int hello_driver_probe(struct device *dev)
{
    printk("%s: probe and init hello_device\n", __func__);
    return 0;
}

// 设备驱动的移除函数
static int hello_driver_remove(struct device *dev)
{
    return 0;
}

// 定义设备驱动结构体
struct device_driver hello_driver = {
    .name	= "hello",          // 驱动名称
    .bus	= &hello_bus_type,  // 关联的总线类型
    .probe	= hello_driver_probe,
    .remove = hello_driver_remove,
};

// 展示驱动属性
static ssize_t hello_driver_show(struct device_driver *driver, char *buf)
{
    return sprintf(buf, "hello_driver name: %s\n", hello_driver.name);
}

// 定义驱动属性结构体
static struct driver_attribute hello_driver_attr = {
    .attr = {
        .name = "hello_driver_attr",  // 属性名称
        .mode = 0444,
    },
    .show = hello_driver_show,
};

static int __init hello_driver_init(void)
{
    int ret;

    ret = driver_register(&hello_driver);
    if (ret)
        return ret;

    ret = driver_create_file(&hello_driver, &hello_driver_attr);
    if (ret) {
        driver_unregister(&hello_driver);
        return ret;
    }

    return 0;
}

static void __exit hello_driver_exit(void)
{
    driver_remove_file(&hello_driver, &hello_driver_attr);
    driver_unregister(&hello_driver);
}

module_init(hello_driver_init);
module_exit(hello_driver_exit);
MODULE_LICENSE("GPL");

ifneq ($(KERNELRELEASE),)
obj-m += bus.o
obj-m += device.o
obj-m += driver.o
else
EXTRA_CFLAGS += -DDEBUG 
KDIR:=/home/code_folder/uboot_linux_rootfs/kernel/linux-5.10.4
ARCH_ARGS := ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
all:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules
clean:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules clean
endif

在这里插入图片描述
在这里插入图片描述

15.3 驱动注册过程分析:driver_register

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

16. bus probe 和 driver probe

由于上一节中:
在这里插入图片描述
在这里插入图片描述
因为这里的 if-else 判断是分开的,要么执行 bus 的 probe,要么执行 driver 的 probe,所以:
在这里插入图片描述
在这里插入图片描述

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>

static int hello_bus_match(struct device *dev,  struct device_driver *driver)
{
    int match;
    match = !strncmp(dev_name(dev), driver->name, strlen(driver->name));
    return match;
}

static int hello_bus_probe(struct device *dev)
{
    struct device_driver *drv = dev->driver; 
    
    printk("%s\n", __func__);
    if (drv->probe) {
        drv->probe(dev);
    }

    return 0;
}

struct bus_type hello_bus_type = {
    .name	= "hello_bus",
    .match 	= hello_bus_match, 
    .probe 	= hello_bus_probe, 
};
EXPORT_SYMBOL_GPL(hello_bus_type);


static void hello_bus_device_release(struct device *dev)
{
	printk("hello_bus release\n");
}

struct device hello_bus_device = {
    .init_name	= "hello_bus_device",
    .release	= hello_bus_device_release,
};
EXPORT_SYMBOL_GPL(hello_bus_device);


static ssize_t hello_bus_show(struct bus_type *bus, char *buf)
{
	return sprintf(buf, "bus name: %s\n", hello_bus_type.name);
}

static struct bus_attribute hello_bus_attr = {
    .attr = {
        .name = "hello_bus_attr",
        .mode = 0644,
    },
    .show = hello_bus_show,
};

static int __init hello_bus_init(void)
{
    int ret;

    ret = bus_register(&hello_bus_type);
    if (ret)
        return ret;

    ret = bus_create_file(&hello_bus_type, &hello_bus_attr);
    if (ret) {
        bus_unregister(&hello_bus_type);
        return ret;
    }

    ret = device_register(&hello_bus_device);
    if (ret) {
        bus_remove_file(&hello_bus_type, &hello_bus_attr);
        bus_unregister(&hello_bus_type);
        return ret;
    }

    printk("create hello_bus success\n");

    return 0;
}

static void __exit hello_bus_exit(void)
{
    device_unregister(&hello_bus_device);
    bus_remove_file(&hello_bus_type, &hello_bus_attr);
    bus_unregister(&hello_bus_type);
}

module_init(hello_bus_init);
module_exit(hello_bus_exit);
MODULE_LICENSE("GPL");

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>

extern struct bus_type hello_bus_type;
extern struct device hello_bus_device;

void hello_device_release (struct device *dev)
{
    printk("%s\n", __func__);
}

// 定义一个设备结构
struct device hello_device = {
    .parent = &hello_bus_device,
    .init_name = "hello",       // 设备名称
    .bus   = &hello_bus_type,   // 所属总线类型
    .release = hello_device_release,
    .devt = ((251 << 20) | 0),  // 设备号
};


static ssize_t hello_device_show (struct device *dev, struct device_attribute *attr, char *buf)
{
    return sprintf(buf, "hello_device name: hello\n");
}

// 定义设备属性结构
static struct device_attribute hello_device_attr = {
    .attr = {
        .name = "hello_device_attr",
        .mode = 0444,
    },
    .show = hello_device_show,
};

static int __init hello_device_init(void)
{
    int ret;

    ret = device_register(&hello_device);
    if (ret)
        return ret;

    ret = device_create_file(&hello_device, &hello_device_attr);
    if (ret) {
        device_unregister(&hello_device);
        return ret;
    }

    printk("create hello_device success\n");

    return 0;
}

static void __exit hello_device_exit(void)
{
    device_remove_file(&hello_device, &hello_device_attr);
    device_unregister(&hello_device);
}

module_init(hello_device_init);
module_exit(hello_device_exit);
MODULE_LICENSE("GPL");

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/string.h>

extern struct bus_type hello_bus_type;

// 设备驱动的探测函数
static int hello_driver_probe(struct device *dev)
{
    printk("%s: probe and init hello_device\n", __func__);
    return 0;
}

// 设备驱动的移除函数
static int hello_driver_remove(struct device *dev)
{
    printk("%s: driver remove\n", __func__);
    return 0;
}

// 定义设备驱动结构体
struct device_driver hello_driver = {
    .name	= "hello",          // 驱动名称
    .bus	= &hello_bus_type,  // 关联的总线类型
    .probe	= hello_driver_probe,
    .remove = hello_driver_remove,
};

// 展示驱动属性
static ssize_t hello_driver_show(struct device_driver *driver, char *buf)
{
    return sprintf(buf, "hello_driver name: %s\n", hello_driver.name);
}

// 定义驱动属性结构体
static struct driver_attribute hello_driver_attr = {
    .attr = {
        .name = "hello_driver_attr",  // 属性名称
        .mode = 0444,
    },
    .show = hello_driver_show,
};

static int __init hello_driver_init(void)
{
    int ret;

    ret = driver_register(&hello_driver);
    if (ret)
        return ret;

    ret = driver_create_file(&hello_driver, &hello_driver_attr);
    if (ret) {
        driver_unregister(&hello_driver);
        return ret;
    }

    printk("create and register hello_driver success\n");

    return 0;
}

static void __exit hello_driver_exit(void)
{
    driver_remove_file(&hello_driver, &hello_driver_attr);
    driver_unregister(&hello_driver);
}

module_init(hello_driver_init);
module_exit(hello_driver_exit);
MODULE_LICENSE("GPL");

ifneq ($(KERNELRELEASE),)
obj-m += bus.o
obj-m += device.o
obj-m += driver.o
else
EXTRA_CFLAGS += -DDEBUG 
KDIR:=/home/code_folder/uboot_linux_rootfs/kernel/linux-5.10.4
ARCH_ARGS := ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
all:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules
clean:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules clean
endif

17. 设备模型:class

  • 对某类设备的一个抽象,封装出一些标准的接口
  • 以 RTC 为例:RTC、file_operations,系统调用:ioctl、cmd、set_time、set_alarm,不同厂家接口不同
  • 抽象出一个 rtc_class,设置时间、设置闹钟

17.1 编程接口

  • class_create
  • class_create_file

17.2 编程实例

  • 如何去创建类?如何去创建设备?
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>

static int hello_bus_match(struct device *dev,  struct device_driver *driver)
{
    int match;

    match = !strncmp(dev_name(dev), driver->name, strlen(driver->name));
    
    return match;
}

struct bus_type hello_bus_type = {
    .name	= "hello_bus",
    .match 	= hello_bus_match, 
};
EXPORT_SYMBOL_GPL(hello_bus_type);

static ssize_t hello_bus_show(struct bus_type *bus, char *buf)
{
    return sprintf(buf, "bus name: %s\n", hello_bus_type.name);
}

static struct bus_attribute hello_bus_attr = {
    .attr = {
        .name = "hello_bus_attr",
        .mode = 0644,
    },
    .show = hello_bus_show,
};

static int __init hello_bus_init(void)
    {
    int ret;

    ret = bus_register(&hello_bus_type);
    if (ret)
        return ret;

    ret = bus_create_file(&hello_bus_type, &hello_bus_attr);
    if (ret) {
        bus_unregister(&hello_bus_type);
        return ret;
    }

    return 0;
}

static void __exit hello_bus_exit(void)
{
    bus_remove_file(&hello_bus_type, &hello_bus_attr);
    bus_unregister(&hello_bus_type);
}

module_init(hello_bus_init);
module_exit(hello_bus_exit);
MODULE_LICENSE("GPL");
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>

struct class *hello_class;
EXPORT_SYMBOL_GPL(hello_class);

static ssize_t hello_class_show(struct class *class, struct class_attribute *attr, char *buf)
{
    return sprintf(buf, "class name: %s\n", class->name);
}

// 定义类属性
static struct class_attribute hello_class_attr = {
    .attr = {
        .name = "hello_class_attr",
        .mode = 0444,
    },
    .show = hello_class_show,
};

static int __init hello_class_init(void)
{
    int ret;

    // 创建一个新的类
    hello_class = class_create(THIS_MODULE, "hello_class");
    ret = PTR_ERR(hello_class);
    if (IS_ERR(hello_class)) 
        return ret;

    // 创建类属性文件
    ret = class_create_file(hello_class, &hello_class_attr);
    if (ret) {
        class_destroy(hello_class);
        return ret;	
    }

    return 0;
}

static void __exit hello_class_exit(void)
{
    class_remove_file(hello_class, &hello_class_attr);
    class_destroy(hello_class);
}

module_init(hello_class_init);
module_exit(hello_class_exit);
MODULE_LICENSE("GPL");
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>

extern struct bus_type hello_bus_type;
extern struct class *hello_class;

void hello_device_release (struct device *dev)
{
    printk("%s\n", __func__);
}

struct device hello_device = {
    .init_name = "hello",
    .bus   = &hello_bus_type,
    .release = hello_device_release,
    .devt = ((251 << 20) | 0),
};

static ssize_t hello_device_show (struct device *dev, 
                               struct device_attribute *attr, char *buf)
{
    return sprintf(buf, "hello_device name: hello\n");
}


static struct device_attribute hello_device_attr = {
    .attr = {
        .name = "hello_device_attr",
        .mode = 0444,
    },
    .show = hello_device_show,
};

static int __init hello_device_init(void)
{
    int ret;

    ret = device_register(&hello_device);
    if (ret)
        return ret;
    
    ret = device_create_file(&hello_device, &hello_device_attr);
    if (ret) {
        device_unregister(&hello_device);
        return ret;
    }

    // 创建另一个新的设备实例
    device_create(hello_class, NULL, (251<<20 | 1), 0, "hello2");

    return 0;
}

static void __exit hello_device_exit(void)
{
    device_remove_file(&hello_device, &hello_device_attr);
    device_unregister(&hello_device);
    device_destroy(hello_class, 0);
}

module_init(hello_device_init);
module_exit(hello_device_exit);
MODULE_LICENSE("GPL");
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/string.h>

extern struct bus_type hello_bus_type;

static int hello_driver_probe(struct device *dev)
{
    printk("%s: probe and init hello_device\n", __func__);
    return 0;
}

static int hello_driver_remove(struct device *dev)
{
    printk("%s: driver remove\n", __func__);
    return 0;
}

struct device_driver hello_driver = {
    .name	= "hello",
    .bus	= &hello_bus_type,
    .probe	= hello_driver_probe,
    .remove = hello_driver_remove,
};


static ssize_t hello_driver_show(struct device_driver *driver, char *buf)
{
    return sprintf(buf, "hello_driver name: %s\n", hello_driver.name);
}

static struct driver_attribute hello_driver_attr = {
    .attr = {
        .name = "hello_driver_attr",
        .mode = 0444,
    },
    .show = hello_driver_show,
};

static int __init hello_driver_init(void)
{
    int ret;

    ret = driver_register(&hello_driver);
    if (ret)
        return ret;

    ret = driver_create_file(&hello_driver, &hello_driver_attr);
    if (ret) {
        driver_unregister(&hello_driver);
        return ret;
    }

    return 0;
}

static void __exit hello_driver_exit(void)
{
    driver_remove_file(&hello_driver, &hello_driver_attr);
    driver_unregister(&hello_driver);
}

module_init(hello_driver_init);
module_exit(hello_driver_exit);
MODULE_LICENSE("GPL");

在这里插入图片描述

ifneq ($(KERNELRELEASE),)
obj-m += bus.o
obj-m += device.o
obj-m += driver.o
obj-m += class.o
else
EXTRA_CFLAGS += -DDEBUG 
KDIR:=/home/code_folder/uboot_linux_rootfs/kernel/linux-5.10.4
ARCH_ARGS := ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
all:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules
clean:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules clean
endif

17.3 驱动案例分析:class 在内核驱动中的应用

在这里插入图片描述

  • class 接口的抽象:rtc_class_ops
    set_time
    set_alarm

在这里插入图片描述

  • rtc_device 的抽象
    cdev:注册一个字符设备驱动
    device:通过 bus 调用对应的 probe
    rtc_class_ops:通过该接口,回调具体 RTC 的 set_time 函数

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
回到 pl031_probe 函数:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

18. device 的二次抽象

18.1 内核中的 OOP 思想

在这里插入图片描述

18.2 进一步抽象

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>

static int hello_bus_match(struct device *dev,  struct device_driver *driver)
{
    int match;

    match = !strncmp(dev_name(dev), driver->name, strlen(driver->name));

    return match;
}

struct bus_type hello_bus_type = {
    .name	= "hello_bus",
    .match 	= hello_bus_match, 
};
EXPORT_SYMBOL_GPL(hello_bus_type);


static int __init hello_bus_init(void)
{
    int ret = 0;

    ret = bus_register(&hello_bus_type);

    return ret;
}

static void __exit hello_bus_exit(void)
{
	bus_unregister(&hello_bus_type);
}

module_init(hello_bus_init);
module_exit(hello_bus_exit);
MODULE_LICENSE("GPL");
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/slab.h>
#include "hello.h"

extern struct bus_type hello_bus_type;

static void hello_device_release (struct device *dev)
{
    struct hello_device *p = container_of(dev, struct hello_device, dev);
    printk("%s: %s device released\n", __func__, p->name);
    //kfree(p);
}

int hello_device_register(struct hello_device *hdev)
{
    int ret;
    
    hdev->dev.init_name  = hdev->name;
    hdev->dev.bus        = &hello_bus_type;
    hdev->dev.devt       = ((251 << 20) | hdev->id);
    hdev->dev.release    = &hello_device_release;

    ret = device_register(&hdev->dev);
    
    return ret;
}

void  hello_device_unregister(struct hello_device *hdev)
{
    device_unregister(&hdev->dev);
}

struct hello_device hello_dev = {
    .name = "hello",
    .id   = 1,
};

static int __init hello_device_init(void)
{
    return hello_device_register(&hello_dev);
}

static void __exit hello_device_exit(void)
{
    hello_device_unregister(&hello_dev);
}

module_init(hello_device_init);
module_exit(hello_device_exit);
MODULE_LICENSE("GPL");
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/string.h>
#include "hello.h"

extern struct bus_type hello_bus_type;

static int hello_driver_probe(struct device *dev)
{
    printk("%s: probe and init hello_device\n", __func__);
    return 0;
}

static int hello_driver_remove(struct device *dev)
{
    printk("%s: driver remove\n", __func__);
    return 0;
}

struct hello_driver {
    int (*probe)(struct hello_device *);
    int (*remove)(struct hello_device *);
    struct device_driver driver;
};

static struct hello_driver hello_drv = {
    .driver = {
        .probe = hello_driver_probe,
        .remove = hello_driver_remove,
        .name = "hello",
        .bus  = &hello_bus_type,
    },
};

int  hello_driver_register(struct hello_driver *drv)
{
    int ret = 0;
    
    ret = driver_register(&drv->driver);

    return ret;
}

void  hello_driver_unregister(struct hello_driver *drv)
{
    driver_unregister(&drv->driver);
}

static int __init hello_driver_init(void)
{
    return hello_driver_register(&hello_drv);
}

static void __exit hello_driver_exit(void)
{
    hello_driver_unregister(&hello_drv);
}

module_init(hello_driver_init);
module_exit(hello_driver_exit);
MODULE_LICENSE("GPL");
#ifndef __HELLO_H_
#define __HELLO_H_

struct hello_device {
    char *name;
    int id;
    struct device dev;   
};

#endif

ifneq ($(KERNELRELEASE),)
obj-m += bus.o
obj-m += device.o
obj-m += driver.o
else
EXTRA_CFLAGS += -DDEBUG 
KDIR:=/home/code_folder/uboot_linux_rootfs/kernel/linux-5.10.4
ARCH_ARGS := ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
all:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules
clean:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules clean
endif

在这里插入图片描述

19. 实现一个总线子系统

  • 如何实现一个子系统:hello bus
  • 接口的封装:
    • 往总线注册设备
    • 往总线注册驱动
    • 总线功能实现:match、probe、……
  • 头文件声明:接口进行声明
  • 编程示例
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include "hello.h"

static int hello_bus_match(struct device *dev,  struct device_driver *driver)
{
    int match;

    match = !strncmp(dev_name(dev), driver->name, strlen(driver->name));

    return match;
}

struct bus_type hello_bus_type = {
    .name	= "hello_bus",
    .match 	= hello_bus_match, 
};
EXPORT_SYMBOL_GPL(hello_bus_type);

static int __init hello_bus_init(void)
{
    int ret = 0;

    ret = bus_register(&hello_bus_type);

    return ret;
}

static void __exit hello_bus_exit(void)
{
    bus_unregister(&hello_bus_type);
}

module_init(hello_bus_init);
module_exit(hello_bus_exit);


static void hello_device_release(struct device *dev)
{
    struct hello_device *p = container_of(dev, struct hello_device, dev);
    printk("%s: %s device released\n", __func__, p->name);
    //kfree(p);
}

int hello_device_register(struct hello_device *hdev)
{
    int ret = 0;
    
    hdev->dev.init_name  = hdev->name;
    hdev->dev.bus        = &hello_bus_type;
    hdev->dev.devt       = ((251 << 20) | hdev->id);
    hdev->dev.release    = hello_device_release;

    ret = device_register(&hdev->dev);
    
    return ret;
}
EXPORT_SYMBOL_GPL(hello_device_register);

void hello_device_unregister(struct hello_device *hdev)
{
    device_unregister(&hdev->dev);
}
EXPORT_SYMBOL_GPL(hello_device_unregister);


int  hello_driver_register(struct hello_driver *drv)
{
    int ret = 0;

    drv->driver.name = drv->name;
    drv->driver.bus  = &hello_bus_type;
    drv->driver.probe = drv->probe;
    drv->driver.remove = drv->remove;

    ret = driver_register(&drv->driver);

    return ret;
}
EXPORT_SYMBOL_GPL(hello_driver_register);

void  hello_driver_unregister(struct hello_driver *drv)
{
	driver_unregister(&drv->driver);
}
EXPORT_SYMBOL_GPL(hello_driver_unregister);


MODULE_LICENSE("GPL");
#ifndef __HELLO_H_
#define __HELLO_H_
    
struct hello_device {
    char *name;
    int id;
    struct device dev;   
};

struct hello_driver {
    char *name;
    int (*probe)(struct device *);
    int (*remove)(struct device *);
    struct device_driver driver;
};

extern int  hello_device_register(struct hello_device *hdev);
extern void hello_device_unregister(struct hello_device *hdev);
extern int  hello_driver_register(struct hello_driver *drv);
extern void hello_driver_unregister(struct hello_driver *drv);

#endif

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/slab.h>
#include "hello.h"

struct hello_device hello_dev = {
    .name = "test",
    .id   = 1,
};

static int __init hello_device_init(void)
{
    return hello_device_register(&hello_dev);
}

static void __exit hello_device_exit(void)
{
    hello_device_unregister(&hello_dev);
}

module_init(hello_device_init);
module_exit(hello_device_exit);
MODULE_LICENSE("GPL");

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/string.h>
#include "hello.h"

static int hello_driver_probe(struct device *dev)
{
    printk("%s: probe and init hello_device: %s\n", __func__, dev_name(dev));
    return 0;
}

static int hello_driver_remove(struct device *dev)
{
    printk("%s: driver remove: %s\n", __func__, dev_name(dev));
    return 0;
}

static struct hello_driver hello_drv = {
    .name   = "test",
    .probe  = hello_driver_probe,
    .remove = hello_driver_remove,
};

static int __init hello_driver_init(void)
{
    return hello_driver_register(&hello_drv);
}

static void __exit hello_driver_exit(void)
{
    hello_driver_unregister(&hello_drv);
}

module_init(hello_driver_init);
module_exit(hello_driver_exit);
MODULE_LICENSE("GPL");

ifneq ($(KERNELRELEASE),)
obj-m += core.o 
obj-m += my_device.o my_driver.o
else
EXTRA_CFLAGS += -DDEBUG 
KDIR:=/home/code_folder/uboot_linux_rootfs/kernel/linux-5.10.4
ARCH_ARGS := ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
all:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules
clean:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules clean
endif

在这里插入图片描述

20. 驱动复用:match_table

  • 进一步完善子系统,增加功能
  • 驱动复用:多个设备共享同一个驱动
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include "hello.h"

static int hello_bus_match(struct device *dev,  struct device_driver *driver)
{
    int match;
    struct device_id *id;
    struct hello_driver *drv;

    match = !strncmp(dev_name(dev), driver->name, strlen(driver->name));
    if (match)
        return match;

    drv = container_of(driver, struct hello_driver, driver);
    id = drv->id_table;

    // 遍历驱动的 ID 表,根据设备的 init_name 进行匹配
    while (id->name[0]) {
        if (strcmp(id->name, dev->init_name) == 0) {
            return 1;
        }
        id++;
    }
    
    return 0;
}

struct bus_type hello_bus_type = {
    .name	= "hello_bus",
    .match 	= hello_bus_match, 
};
EXPORT_SYMBOL_GPL(hello_bus_type);

static int __init hello_bus_init(void)
{
    int ret = 0;

    ret = bus_register(&hello_bus_type);

    return ret;
}

static void __exit hello_bus_exit(void)
{
    bus_unregister(&hello_bus_type);
}

module_init(hello_bus_init);
module_exit(hello_bus_exit);


static void hello_device_release(struct device *dev)
{
    struct hello_device *p = container_of(dev, struct hello_device, dev);
    printk("%s: %s device released\n", __func__, p->name);
    //kfree(p);
}

int hello_device_register(struct hello_device *hdev)
{
    int ret = 0;

    hdev->dev.init_name  = hdev->name;
    hdev->dev.bus        = &hello_bus_type;
    hdev->dev.devt       = ((251 << 20) | hdev->id);
    hdev->dev.release    = hello_device_release;

    ret = device_register(&hdev->dev);
    
    return ret;
}
EXPORT_SYMBOL_GPL(hello_device_register);

void hello_device_unregister(struct hello_device *hdev)
{
    device_unregister(&hdev->dev);
}
EXPORT_SYMBOL_GPL(hello_device_unregister);


int  hello_driver_register(struct hello_driver *drv)
{
    int ret = 0;

    drv->driver.name = drv->name;
    drv->driver.bus  = &hello_bus_type;
    drv->driver.probe = drv->probe;
    drv->driver.remove = drv->remove;

    ret = driver_register(&drv->driver);

    return ret;
}
EXPORT_SYMBOL_GPL(hello_driver_register);

void  hello_driver_unregister(struct hello_driver *drv)
{
    driver_unregister(&drv->driver);
}
EXPORT_SYMBOL_GPL(hello_driver_unregister);


MODULE_LICENSE("GPL");
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/string.h>
#include "hello.h"

static int hello_driver_probe(struct device *dev)
{
    printk("%s: probe and init hello_device: %s\n", __func__, dev_name(dev));
    return 0;
}

static int hello_driver_remove(struct device *dev)
{
    printk("%s: driver remove: %s\n", __func__, dev_name(dev));
    return 0;
}

struct device_id compat_table[] = {
    { .name = "test1", .dev_id = 1, },
    { .name = "test2", .dev_id = 2, },
    { .name = "test3", .dev_id = 3, },
    {  },
};

static struct hello_driver hello_drv = {
    .name   = "test",
    .probe  = hello_driver_probe,
    .remove = hello_driver_remove,
    .id_table = compat_table,
};

static int __init hello_driver_init(void)
{
    return hello_driver_register(&hello_drv);
}

static void __exit hello_driver_exit(void)
{
    hello_driver_unregister(&hello_drv);
}

module_init(hello_driver_init);
module_exit(hello_driver_exit);
MODULE_LICENSE("GPL");

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/slab.h>
#include "hello.h"

struct hello_device hello_dev = {
    .name = "test",
    .id   = 1,
};

static int __init hello_device_init(void)
{
    return hello_device_register(&hello_dev);
}

static void __exit hello_device_exit(void)
{
    hello_device_unregister(&hello_dev);
}

module_init(hello_device_init);
module_exit(hello_device_exit);
MODULE_LICENSE("GPL");

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/slab.h>
#include "hello.h"

struct hello_device hello_dev1 = {
    .name = "test1",
    .id   = 2,
};

static int __init hello_device_init(void)
{
    return hello_device_register(&hello_dev1);
}

static void __exit hello_device_exit(void)
{
    hello_device_unregister(&hello_dev1);
}

module_init(hello_device_init);
module_exit(hello_device_exit);
MODULE_LICENSE("GPL");
#ifndef __HELLO_H_
#define __HELLO_H_
    
struct hello_device {
    char *name;
    int id;
    struct device dev;   
};

struct device_id {
    char name[30];
    int dev_id;
};

struct hello_driver {
    char *name;
    int (*probe)(struct device *);
    int (*remove)(struct device *);
    struct device_driver driver;
    struct device_id *id_table;
};

extern int  hello_device_register(struct hello_device *hdev);
extern void hello_device_unregister(struct hello_device *hdev);
extern int  hello_driver_register(struct hello_driver *drv);
extern void hello_driver_unregister(struct hello_driver *drv);

#endif

ifneq ($(KERNELRELEASE),)
obj-m += core.o 
obj-m += my_device.o my_device1.o my_driver.o
else
EXTRA_CFLAGS += -DDEBUG 
KDIR:=/home/code_folder/uboot_linux_rootfs/kernel/linux-5.10.4
ARCH_ARGS := ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
all:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules
clean:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules clean
endif

在这里插入图片描述

21. 设备的热插拔 hotplug 机制

  • device 热插拔实现机制:uevent
  • 自动创建设备节点
  • 应用程序:udev
  • 嵌入式应用程序:mdev
  • 实验:利用第 11.4 节 和第 20 节中的程序


在这里插入图片描述
在这里插入图片描述

22. 从字符驱动到总线驱动

22.1 将字符型驱动升级为 device_driver 总线型驱动

  • 支持用户接口:设置时间和闹钟
  • 中断线程化
  • 理解初始化流程、probe 流程
  • 将 RTC 注册到 hello bus 子系统
    • 将 RTC device 注册到 hello bus
    • 将 RTC driver 注册到 hello bus
  • 自动创建设备节点:mdev -d
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include "hello.h"

static int hello_bus_match(struct device *dev,  struct device_driver *driver)
{
    int match;
    struct device_id *id;
    struct hello_driver *drv;

    match = !strncmp(dev_name(dev), driver->name, strlen(driver->name));
    if (match)
        return match;

    drv = container_of(driver, struct hello_driver, driver);
    id = drv->id_table;

    // 遍历驱动的 ID 表,根据设备的 init_name 进行匹配
    while (id->name[0]) {
        if (strcmp(id->name, dev->init_name) == 0) {
            return 1;
        }
        id++;
    }
    
    return 0;
}

struct bus_type hello_bus_type = {
    .name	= "hello_bus",
    .match 	= hello_bus_match, 
};
EXPORT_SYMBOL_GPL(hello_bus_type);

static int __init hello_bus_init(void)
{
    int ret = 0;

    ret = bus_register(&hello_bus_type);

    return ret;
}

static void __exit hello_bus_exit(void)
{
    bus_unregister(&hello_bus_type);
}

module_init(hello_bus_init);
module_exit(hello_bus_exit);


static void hello_device_release(struct device *dev)
{
    struct hello_device *p = container_of(dev, struct hello_device, dev);
    printk("%s: %s device released\n", __func__, p->name);
    //kfree(p);
}

int hello_device_register(struct hello_device *hdev)
{
    int ret = 0;

    hdev->dev.init_name  = hdev->name;
    hdev->dev.bus        = &hello_bus_type;
    hdev->dev.devt       = ((251 << 20) | hdev->id);
    hdev->dev.release    = hello_device_release;

    ret = device_register(&hdev->dev);
    
    return ret;
}
EXPORT_SYMBOL_GPL(hello_device_register);

void hello_device_unregister(struct hello_device *hdev)
{
    device_unregister(&hdev->dev);
}
EXPORT_SYMBOL_GPL(hello_device_unregister);


int  hello_driver_register(struct hello_driver *drv)
{
    int ret = 0;

    drv->driver.name = drv->name;
    drv->driver.bus  = &hello_bus_type;
    drv->driver.probe = drv->probe;
    drv->driver.remove = drv->remove;

    ret = driver_register(&drv->driver);

    return ret;
}
EXPORT_SYMBOL_GPL(hello_driver_register);

void  hello_driver_unregister(struct hello_driver *drv)
{
    driver_unregister(&drv->driver);
}
EXPORT_SYMBOL_GPL(hello_driver_unregister);


MODULE_LICENSE("GPL");
#ifndef __HELLO_H_
#define __HELLO_H_
    
struct hello_device {
    char *name;
    int id;
    struct device dev;   
};

struct device_id {
    char name[30];
    int dev_id;
};

struct hello_driver {
    char *name;
    int (*probe)(struct device *);
    int (*remove)(struct device *);
    struct device_driver driver;
    struct device_id *id_table;
};

extern int  hello_device_register(struct hello_device *hdev);
extern void hello_device_unregister(struct hello_device *hdev);
extern int  hello_driver_register(struct hello_driver *drv);
extern void hello_driver_unregister(struct hello_driver *drv);

#endif

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/slab.h>
#include "hello.h"

struct hello_device rtc_dev = {
    .name = "rtc3",
    .id   = 3,
};

static int __init rtc_device_init(void)
{
    return hello_device_register(&rtc_dev);
}

static void __exit rtc_device_exit(void)
{
    hello_device_unregister(&rtc_dev);
}

module_init(rtc_device_init);
module_exit(rtc_device_exit);
MODULE_LICENSE("GPL");
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/workqueue.h>
#include "hello.h"

typedef volatile struct{
    unsigned long  RTCDR;    /* +0x00: data register */
    unsigned long  RTCMR;    /* +0x04: match register */
    unsigned long  RTCLR;    /* +0x08: load register */
    unsigned long  RTCCR;    /* +0x0C: control register */
    unsigned long  RTCIMSC;  /* +0x10: interrupt mask set and clear register*/
    unsigned long  RTCRIS;   /* +0x14: raw interrupt status register*/
    unsigned long  RTCMIS;   /* +0x18: masked interrupt status register */
    unsigned long  RTCICR;   /* +0x1C: interrupt clear register */
} rtc_reg_t;

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

#define RTC_BASE 0x10017000

volatile rtc_reg_t *regs = NULL;
static unsigned long current_time = 0;

void set_rtc_alarm(rtc_reg_t *regs)
{
    unsigned long tmp = 0;
    tmp = regs->RTCCR;    /* write enable */
    tmp = tmp & 0xFFFFFFFE;
    regs->RTCCR = tmp;

    tmp = regs->RTCDR;    /* get current time */
    current_time = tmp;
    regs->RTCMR = tmp + 1;/* set alarm time */

    regs->RTCICR = 1;     /* clear RTCINTR interrupt */ 
    regs->RTCIMSC = 1;    /* set the mask */

    tmp = regs->RTCCR;    /* write disable */
    tmp = tmp | 0x1;
    regs->RTCCR = tmp;
}

static irqreturn_t rtc_irq_thread(int irq, void *p) 
{
    struct rtc_time tm;
    tm.hour = (current_time % 86400) / 3600;
    tm.min  = (current_time % 3600) / 60;
    tm.sec  = current_time % 60;
    printk("%d:%d:%d\n", tm.hour, tm.min, tm.sec);

    return IRQ_HANDLED;
}

static irqreturn_t rtc_alarm_handler(int irq, void *dev_id)
{
    set_rtc_alarm(regs);
    return IRQ_WAKE_THREAD;
}


static int rtc_driver_probe(struct device *dev)
{
    printk("%s: probe and init hello_device: %s\n", __func__, dev_name(dev));
    set_rtc_alarm(regs);
    return 0;
}

static int rtc_driver_remove(struct device *dev)
{
    printk("%s: driver remove: %s\n", __func__, dev_name(dev));
    return 0;
}

struct device_id compat_table[] = {
    { .name = "rtc1", .dev_id = 1, },
    { .name = "rtc2", .dev_id = 2, },
    { .name = "rtc3", .dev_id = 3, },
    {  },
};

static struct hello_driver rtc_drv = {
    .name   = "rtc",
    .probe  = rtc_driver_probe,
    .remove = rtc_driver_remove,
    .id_table = compat_table,
};


static int __init rtc_driver_init(void)
{
    irqreturn_t ret = 0;

    regs = (rtc_reg_t *)ioremap(RTC_BASE, sizeof(rtc_reg_t)); 
    printk("rtc_init\n");

    ret = request_threaded_irq(39, rtc_alarm_handler, rtc_irq_thread, 0, "rtc0-test", NULL);
    if (ret == -1) {
        printk("request_irq failed!\n");
        return -1;
    }

    return hello_driver_register(&rtc_drv);
}

static void __exit rtc_driver_exit(void)
{
    free_irq(39,NULL);
    hello_driver_unregister(&rtc_drv);
}

module_init(rtc_driver_init);
module_exit(rtc_driver_exit);
MODULE_LICENSE("GPL");
ifneq ($(KERNELRELEASE),)
obj-m := core.o rtc_device.o rtc_driver.o
else
EXTRA_CFLAGS += -DDEBUG 
KDIR:=/home/code_folder/uboot_linux_rootfs/kernel/linux-5.10.4
ARCH_ARGS := ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
all:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules
clean:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules clean
endif

实验前需要重新编译内核,关掉内核默认使用的 rtc-pl031:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

22.2 使用 uevent 机制实时更新时间

  • 驱动中发送 uevent 到用户空间
  • 用户应用程序监听 uevent
  • 解析 uevent 信息,自动创建设备节点
  • 实时显示 RTC 系统时间
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include "hello.h"

static int hello_bus_match(struct device *dev,  struct device_driver *driver)
{
    struct device_id *id;
    struct hello_driver *drv;

    if (strcmp(dev_name(dev), driver->name) == 0)
        return 1;
    
    drv = container_of(driver, struct hello_driver, driver);
    id = drv->id_table;

    while (id->name[0]) {
        if (strcmp(id->name, dev_name(dev)) == 0) {
            return 1;
        }
        id++;
    }
    
    return 0;
}

struct bus_type hello_bus_type = {
    .name	= "hello_bus",
    .match 	= hello_bus_match, 
};
EXPORT_SYMBOL_GPL(hello_bus_type);

static int __init hello_bus_init(void)
{
    int ret = 0;

    ret = bus_register(&hello_bus_type);

    return ret;
}

static void __exit hello_bus_exit(void)
{
    bus_unregister(&hello_bus_type);
}

module_init(hello_bus_init);
module_exit(hello_bus_exit);


static void hello_device_release(struct device *dev)
{
    struct hello_device *p = container_of(dev, struct hello_device, dev);
    printk("%s: %s device released\n", __func__, p->name);
    //kfree(p);
}

int hello_device_uevent(struct device *dev, enum kobject_action action, char *env[])
{
    return kobject_uevent_env(&dev->kobj, action, env);
}
EXPORT_SYMBOL_GPL(hello_device_uevent);

int hello_device_register(struct hello_device *hdev)
{
    int ret = 0;

    hdev->dev.init_name  = hdev->name;
    hdev->dev.bus        = &hello_bus_type;
    hdev->dev.devt       = ((251 << 20) | hdev->id);
    hdev->dev.release    = hello_device_release;

    ret = device_register(&hdev->dev);
    
    return ret;
}
EXPORT_SYMBOL_GPL(hello_device_register);

void hello_device_unregister(struct hello_device *hdev)
{
    device_unregister(&hdev->dev);
}
EXPORT_SYMBOL_GPL(hello_device_unregister);


int  hello_driver_register(struct hello_driver *drv)
{
    int ret = 0;

    drv->driver.name = drv->name;
    drv->driver.bus  = &hello_bus_type;
    drv->driver.probe = drv->probe;
    drv->driver.remove = drv->remove;

    ret = driver_register(&drv->driver);

    return ret;
}
EXPORT_SYMBOL_GPL(hello_driver_register);

void  hello_driver_unregister(struct hello_driver *drv)
{
    driver_unregister(&drv->driver);
}
EXPORT_SYMBOL_GPL(hello_driver_unregister);


MODULE_LICENSE("GPL");
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/slab.h>
#include "hello.h"

struct hello_device rtc_dev = {
    .name = "rtc3",
    .id   = 3,
};

static int __init rtc_device_init(void)
{
    return hello_device_register(&rtc_dev);
}

static void __exit rtc_device_exit(void)
{
    hello_device_unregister(&rtc_dev);
}

module_init(rtc_device_init);
module_exit(rtc_device_exit);
MODULE_LICENSE("GPL");
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/workqueue.h>
#include "hello.h"

typedef volatile struct{
    unsigned long  RTCDR;    /* +0x00: data register */
    unsigned long  RTCMR;    /* +0x04: match register */
    unsigned long  RTCLR;    /* +0x08: load register */
    unsigned long  RTCCR;    /* +0x0C: control register */
    unsigned long  RTCIMSC;  /* +0x10: interrupt mask set and clear register*/
    unsigned long  RTCRIS;   /* +0x14: raw interrupt status register*/
    unsigned long  RTCMIS;   /* +0x18: masked interrupt status register */
    unsigned long  RTCICR;   /* +0x1C: interrupt clear register */
} rtc_reg_t;

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

#define RTC_BASE 0x10017000

volatile rtc_reg_t *regs = NULL;
static unsigned long current_time = 0;

struct device *devp;

void set_rtc_alarm(rtc_reg_t *regs)
{
    unsigned long tmp = 0;
    tmp = regs->RTCCR;    /* write enable */
    tmp = tmp & 0xFFFFFFFE;
    regs->RTCCR = tmp;

    tmp = regs->RTCDR;    /* get current time */
    current_time = tmp;
    regs->RTCMR = tmp + 10;/* set alarm time */

    regs->RTCICR = 1;     /* clear RTCINTR interrupt */ 
    regs->RTCIMSC = 1;    /* set the mask */

    tmp = regs->RTCCR;    /* write disable */
    tmp = tmp | 0x1;
    regs->RTCCR = tmp;
}

static irqreturn_t rtc_irq_thread(int irq, void *p) 
{
    char time[20];
    char name[20];
    char *envp[] = {
        time,
        name,
        NULL,
    };
    
    struct rtc_time tm;
    tm.hour = (current_time % 86400) / 3600;
    tm.min  = (current_time % 3600) / 60;
    tm.sec  = current_time % 60;
 // printk("%d:%d:%d\n", tm.hour, tm.min, tm.sec);
 
    sprintf(time, "TIME=%d:%d:%d", tm.hour, tm.min, tm.sec);
    sprintf(name, "NAME=%s", "test_name");
    hello_device_uevent(devp, KOBJ_CHANGE, envp);
    
    return IRQ_HANDLED;
}

static irqreturn_t rtc_alarm_handler(int irq, void *dev_id)
{
    set_rtc_alarm(regs);
    return IRQ_WAKE_THREAD;
}


static int rtc_driver_probe(struct device *dev)
{
    printk("%s: probe and init hello_device: %s\n", __func__, dev_name(dev));
    devp = dev;
    set_rtc_alarm(regs);
    return 0;
}

static int rtc_driver_remove(struct device *dev)
{
    printk("%s: driver remove: %s\n", __func__, dev_name(dev));
    devp = NULL;
    return 0;
}

struct device_id compat_table[] = {
    { .name = "rtc1", .dev_id = 1, },
    { .name = "rtc2", .dev_id = 2, },
    { .name = "rtc3", .dev_id = 3, },
    {  },
};

static struct hello_driver rtc_drv = {
    .name   = "rtc",
    .probe  = rtc_driver_probe,
    .remove = rtc_driver_remove,
    .id_table = compat_table,
};


static int __init rtc_driver_init(void)
{
    irqreturn_t ret = 0;

    regs = (rtc_reg_t *)ioremap(RTC_BASE, sizeof(rtc_reg_t)); 
    printk("rtc_init\n");

    ret = request_threaded_irq(39, rtc_alarm_handler, rtc_irq_thread, 0, "rtc0-test", NULL);
    if (ret == -1) {
        printk("request_irq failed!\n");
        return -1;
    }

    return hello_driver_register(&rtc_drv);
}

static void __exit rtc_driver_exit(void)
{
    free_irq(39,NULL);
    hello_driver_unregister(&rtc_drv);
}

module_init(rtc_driver_init);
module_exit(rtc_driver_exit);
MODULE_LICENSE("GPL");
#ifndef __HELLO_H_
#define __HELLO_H_
    
struct hello_device {
    char *name;
    int id;
    struct device dev;   
};

struct device_id {
    char name[30];
    int dev_id;
};

struct hello_driver {
    char *name;
    int (*probe)(struct device *);
    int (*remove)(struct device *);
    struct device_driver driver;
    struct device_id *id_table;
};

extern int  hello_device_register(struct hello_device *hdev);
extern void hello_device_unregister(struct hello_device *hdev);
extern int  hello_driver_register(struct hello_driver *drv);
extern void hello_driver_unregister(struct hello_driver *drv);
extern int   hello_device_uevent(struct device *hdev, enum kobject_action action, char *env[]);

#endif

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <linux/netlink.h>

const char *action = "";
const char *devpath = "";
const char *subsystem = "";
const char *bustype = "";
const char *addr = "";
const char *devname = "";
const char *name = "";
const char *time = "";
int major = 0;
int minor = 0;

int open_socket(void)
{
    int fd;

    struct sockaddr_nl socknl_addr = {
        .nl_family = AF_NETLINK,
        .nl_pad = 1,
        .nl_pid = getpid(),
        .nl_groups = 0xffffffff,
    };

    fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
    if (fd < 0) {
        printf("%s: create socket ep failed\n", __func__);
        return -1;
    }

    if (bind(fd, (struct sockaddr *) &socknl_addr, sizeof(socknl_addr)) < 0) {
        printf("%s: bind socket failed\n", __func__);
        close(fd);
        return -1;
    }

    return fd;
}

void parse_event(const char *msg)
{
    while (*msg) {
        printf("%s\n", msg);
        if (!strncmp(msg, "ACTION=", 7)) {
            msg += 7;
            action = msg;
        } else if (!strncmp(msg, "DEVPATH=", 8)) {
            msg += 8;
            devpath = msg;
        } else if (!strncmp(msg, "SUBSYSTEM=", 10)) {
            msg += 10;
            subsystem = msg;
        } else if (!strncmp(msg, "MAJOR=", 6)) {
            msg += 6;
            major = atoi(msg);
        } else if (!strncmp(msg, "MINORR=", 7)) {
            msg += 7;
            minor = atoi(msg);
        } else if (!strncmp(msg, "BUSTYPE=", 8)) {
            msg += 8;
            bustype = msg;
        } else if (!strncmp(msg, "TIME=", 5)) {
            msg += 5;
            time = msg;
        } else if (!strncmp(msg, "DEVNAME=", 8)) {
            msg += 8;
            devname = msg;
        } else if (!strncmp(msg, "NAME=", 5)) {
            msg += 5;
            name = msg;
        }
        while(*msg++);
    }
    
    printf("-------------------%s---------------------------\n", time);
}

void make_hello_mode(const char *devname, mode_t mode, int major, int minor)
{
    char pathname[20];

    strcpy(pathname, "/dev/");
    strcpy(&pathname[5], devname);

    if (!strcmp(action, "add"))
        mknod(pathname, 0666, ((major<<20) | minor));

    if (!strcmp(action, "remove"))
        remove(pathname);
}

int main(void)
{
    int skt_fd, len;
    char msg_buf[5000];
    
    skt_fd = open_socket();
    do {
        while((len = recv(skt_fd, msg_buf, 4096, 0)) > 0) {
            if(len == 4096)
                continue;
            msg_buf[len] = '\0';
            msg_buf[len + 1] = '\0';
            parse_event(msg_buf);
            make_hello_mode(devname, 0666, major, minor);
        }
    } while(1);
}
ifneq ($(KERNELRELEASE),)
obj-m := core.o rtc_device.o rtc_driver.o
else
EXTRA_CFLAGS += -DDEBUG 
KDIR:=/home/code_folder/uboot_linux_rootfs/kernel/linux-5.10.4
ARCH_ARGS := ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
all:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules
clean:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules clean
endif

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

uuxiang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值