设备模型(10)

一、简介

kobject 的主要职责:

引用计数管理:跟踪有多少地方在使用这个对象

sysfs 接口:在 /sys 中创建一个目录

热插拔事件:当对象添加/删除时通知用户空间

父子关系:构建设备树状结构

kset 是一组相关 kobject 的集合

二、创建kobject,kset

在/sys目录下创建my_kset目录,再在/sys/my_kest目录下创建my_kobj01,my_kobj02两个目录

在/sys/my_kest/my_kobj01,/sys/my_kest/my_kobj02下会有两个名为LHQ,DQ属性

cpp 复制代码
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/configfs.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
struct kset *test_struct_kset;
struct kobject *test_struct_kobj01;
struct kobject *test_struct_kobj02;

void my_release(struct kobject *kobj)
{
	printk("kfree %s\n",kobj->name);
	kfree(kobj);
}
/*********************************************************************/
ssize_t	my_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
	//cat时发生的函数,attr参数告诉我们是哪个属性文件被读取
	return sprintf(buf, "%s attribute from %s\n", attr->attr.name, kobj->name);
}
ssize_t	my_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
	//echo时发生的函数,buf是从用户空间传来的数据,count是数据长度
    // 使用 %.*s 格式打印指定长度的字符串
    printk("%s: attribute %s received: %.*s\n",kobj->name, attr->attr.name, (int)count, buf);
    return count;
}


struct kobj_attribute my_attr01 = __ATTR("LHQ",0644,my_show,my_store);
struct kobj_attribute my_attr02 = __ATTR("DQ",0644,my_show,my_store);

struct attribute *my_attribute[]={
	&my_attr01.attr,
	&my_attr02.attr,
	NULL,
};

/******************************************/
struct kobj_type test_struct_ktype={
	.release   = my_release, //释放函数
	.default_attrs=my_attribute,//属性
};

static int __init my_kset_init(void)
{
	int ret;
	
	//创建并添加一个名为my_kset的目录在sys/目录下
	test_struct_kset=kset_create_and_add("my_kset",NULL,NULL);
	if(!test_struct_kset)
	{
		printk("kset_create_and_add failed\n");
		return -1;
	}
	
	/****************************************************************************/
	//为kobj01分配内存
	test_struct_kobj01=kzalloc(sizeof(struct kobject), GFP_KERNEL);//分配内存地址
	if(!test_struct_kobj01)
	{
		printk("kzalloc test_struct_kobj01 failed\n");
		goto erro_kzalloc_kobj01;
	}
	//设置kobject属于哪个kset
	test_struct_kobj01->kset = test_struct_kset;   
    //创建并添加一个名为my_kobj01的目录在sys/my_kset/目录下
    //由于使用了&test_struct_ktype,所以会在sys/my_kset/my_kobj01存在属性名为LHQ,DQ两个属性
	//进入目录sys/my_kset/my_kobj01下,进行cat echo 操作,内核会告诉show,store函数在那个目录下
	ret= kobject_init_and_add(test_struct_kobj01,&test_struct_ktype,NULL,"%s","my_kobj01");
	if(ret)
	{
		printk("test_struct_kobj01 kobject_init_and_add failed\n");
		goto erro_kobject_init_and_add_kobj01;
	}
	/****************************************************************************/
	
	test_struct_kobj02=kzalloc(sizeof(struct kobject), GFP_KERNEL);//分配内存地址
	if(!test_struct_kobj02)
	{
		printk("kzalloc test_struct_kobj02 failed\n");
		goto erro_kzalloc_kobj02;
	}
	test_struct_kobj02->kset = test_struct_kset;                   //设置kobject属于哪个kset
	ret= kobject_init_and_add(test_struct_kobj02,&test_struct_ktype,NULL,"%s","my_kobj02");
	if(ret)
	{   printk("test_struct_kobj02 kobject_init_and_add failed\n");
		goto erro_kobject_init_and_add_kobj02;
	}
	
	return 0;
erro_kobject_init_and_add_kobj02:
		kfree(test_struct_kobj02);
erro_kzalloc_kobj02:
		kobject_put(test_struct_kobj01);
erro_kobject_init_and_add_kobj01:	
		kfree(test_struct_kobj01);
erro_kzalloc_kobj01:
		kset_unregister(test_struct_kset);
	
    return ret;

}
static void __exit my_kset_exit(void)
{
	kobject_put(test_struct_kobj01);
	kobject_put(test_struct_kobj02);
	kset_unregister(test_struct_kset);
}

module_init(my_kset_init);
module_exit(my_kset_exit);
MODULE_LICENSE("GPL");

三、创建总线

3.1 创建/sys/bus/my_bus_name/

cpp 复制代码
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/configfs.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/device.h>


//当设备注册时,内核遍历该总线上的所有驱动,为每个驱动调用 match(设备, 驱动)
//当驱动注册时,内核遍历该总线上的所有设备,为每个设备调用 match(设备, 驱动)
int my_bus_match(struct device *dev, struct device_driver *drv)
{
    //当内核中有新设备或新驱动注册时,总线会自动调用这个函数来判断这个驱动能否支持这个设备
    //当设备注册时:遍历所有已注册的驱动,找到能匹配的
    //当驱动注册时:遍历所有已注册的设备,找到能匹配的
	return (strcmp(dev_name(dev),drv->name)==0);
}

//当match成功时,内核调用 probe(设备) 来初始化绑定
int my_bus_probe(struct device *dev)
{
	//当 match 成功后,总线调用这个函数来初始化设备和驱动之间的绑定
	struct device_driver *drv=dev->driver;
	if(drv->probe)
		drv->probe(dev);//将将dev参数给到drv结构体中probe的函数
	return 0;
}

//定义struct bus_type结构体
struct bus_type my_bus_type={
	.name="my_bus",      //在/sys/bus下的名字
	.match=my_bus_match, 
	.probe=my_bus_probe,
};

static int __init bus_init(void)
{
	int ret;
    ret=bus_register(&my_bus_type);
	
	return 0;
}
static void __exit bus_exit(void)
{
    bus_unregister(&my_bus_type);
}

module_init(bus_init);
module_exit(bus_exit);
MODULE_LICENSE("GPL");

3.2 创建/sys/bus/my_bus_name/ 的属性

cpp 复制代码
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/configfs.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/device.h>

// 当设备注册时,内核遍历该总线上的所有驱动,为每个驱动调用 match(设备, 驱动)
// 当驱动注册时,内核遍历该总线上的所有设备,为每个设备调用 match(设备, 驱动)
int my_bus_match(struct device *dev, struct device_driver *drv)
{
    // 当内核中有新设备或新驱动注册时,总线会自动调用这个函数来判断这个驱动能否支持这个设备
    // 当设备注册时:遍历所有已注册的驱动,找到能匹配的
    // 当驱动注册时:遍历所有已注册的设备,找到能匹配的
    return (strcmp(dev_name(dev), drv->name) == 0);
}

// 当match成功时,内核调用 probe(设备) 来初始化绑定
int my_bus_probe(struct device *dev)
{
    // 当 match 成功后,总线调用这个函数来初始化设备和驱动之间的绑定
    struct device_driver *drv = dev->driver;
    int ret = 0;
    
    if (drv && drv->probe) {
        ret = drv->probe(dev);  // 将dev参数给到drv结构体中probe的函数
    }
    
    return ret;
}
/*********************************************************************/

// 总线属性的show函数
static ssize_t bus_attr_show(struct bus_type *bus, char *buf)
{
    return sprintf(buf, "This is bus attribute for bus: %s\n", bus->name);
}

// 总线属性的store函数
static ssize_t bus_attr_store(struct bus_type *bus, const char *buf, size_t count)
{
    printk("Bus %s: attribute received: %.*s\n", bus->name, (int)count, buf);
    return count;
}

// 使用BUS_ATTR宏定义总线属性,内核会将bus_attr与LHQ关联形成bus_attr_LHQ结构体
static BUS_ATTR(LHQ, 0644, bus_attr_show, bus_attr_store);

// 定义struct bus_type结构体
struct bus_type my_bus_type = {
    .name = "my_bus",      // 在/sys/bus下的名字
    .match = my_bus_match, 
    .probe = my_bus_probe,
};
EXPORT_SYMBOL_GPL(my_bus_type);//导出符号给设备/驱动使用

static int __init bus_init(void)
{
    int ret;
    
    printk("Registering my_bus\n");
    
    // 注册总线
    ret = bus_register(&my_bus_type);
    if (ret) {
        printk("Failed to register bus: %d\n", ret);
        return ret;
    }
    
    printk(KERN_INFO "Bus registered successfully\n");
    
    // 为总线创建属性文件
    ret = bus_create_file(&my_bus_type, &bus_attr_LHQ);
    if (ret) {
        printk("Failed to create bus attribute: %d\n", ret);
        bus_unregister(&my_bus_type);
        return ret;
    }
    
    printk("Bus attribute created successfully\n");
    
    return 0;
}

static void __exit bus_exit(void)
{
    printk("Unregistering bus\n");
    
    // 移除总线属性文件
    bus_remove_file(&my_bus_type, &bus_attr_LHQ);
    
    // 注销总线
    bus_unregister(&my_bus_type);
    
    printk("Bus unregistered\n");
}

module_init(bus_init);
module_exit(bus_exit);
MODULE_LICENSE("GPL");

四、在总线上注册设备

cpp 复制代码
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>

extern struct bus_type my_bus_type;//导出符
void my_bus_release(struct device *dev)
{
	printk("this is %s release\n",dev_name(dev));
}

struct device my_bus_device={
	.init_name= "my_bus_device", // /sys/bus/自定义总线名/devices/下的名字;也用于匹配
	.bus = &my_bus_type,
	.release=my_bus_release,
};

static int __init bus_device_init(void)
{
	int ret;
    ret = device_register(&my_bus_device);
	if (ret) {
        printk("Failed to register device: %d\n", ret);
        return ret;
    }
    return 0;
}

static void __exit bus_device_exit(void)
{
    device_unregister(&my_bus_device);
}

module_init(bus_device_init);
module_exit(bus_device_exit);
MODULE_LICENSE("GPL");

五、在总线上注册驱动

cpp 复制代码
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>

extern struct bus_type my_bus_type;//导出符

int my_bus_driver_probe(struct device *dev)
{
	printk("this is %s probe\n",dev_name(dev));
	return 0;
}
int my_bus_driver_remove(struct device *dev)
{
	printk("this is %s probe\n",dev_name(dev));
	return 0;
}

struct device_driver my_bus_driver={
	.name="my_bus_device", //用于匹配
	.bus = &my_bus_type, //挂载那个总线上
	.probe=my_bus_driver_probe,
	.remove=my_bus_driver_remove,
};

static int __init bus_driver_init(void)
{
	int ret;
    ret = driver_register(&my_bus_driver);
	if (ret) {
        printk("Failed to register driver: %d\n", ret);
        return ret;
    }
    return 0;
}

static void __exit bus_driver_exit(void)
{
    driver_unregister(&mmy_bus_driver);
}

module_init(bus_device_init);
module_exit(bus_device_exit);
MODULE_LICENSE("GPL");
相关推荐
123过去4 小时前
ike-scan使用教程
linux·测试工具
ywf12154 小时前
前端的dist包放到后端springboot项目下一起打包
前端·spring boot·后端
恋猫de小郭4 小时前
2026,Android Compose 终于支持 Hot Reload 了,但是收费
android·前端·flutter
hpoenixf10 小时前
2026 年前端面试问什么
前端·面试
还是大剑师兰特10 小时前
Vue3 中的 defineExpose 完全指南
前端·javascript·vue.js
疯狂吧小飞牛11 小时前
GPG基础指令
linux·服务器·网络
C++ 老炮儿的技术栈11 小时前
volatile使用场景
linux·服务器·c语言·开发语言·c++
泯泷11 小时前
阶段一:从 0 看懂 JSVMP 架构,先在脑子里搭出一台最小 JSVM
前端·javascript·架构
hjxu201611 小时前
【OpenClaw 龙虾养成笔记一】在远程服务器,使用Docker安装OpenClaw
服务器·笔记·docker
mengchanmian12 小时前
前端node常用配置
前端