设备模型(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");
相关推荐
xingzhemengyou12 小时前
Python 有哪些定时器
前端·python
木西2 小时前
Gemini 3 最新版!Node.js 代理调用教程
前端·node.js·gemini
Web极客码2 小时前
使用VPS主机进行数据分析的主要优势
linux·windows·vps主机
婷婷婷婷2 小时前
表格组件封装详解(含完整代码)
前端
晴虹2 小时前
lecen:一个更好的开源可视化系统搭建项目--页面设计器(表单设计器)--全低代码|所见即所得|利用可视化设计器构建你的应用系统-做一个懂你的人
前端·后端·低代码
小皮虾2 小时前
这应该是前端转后端最简单的办法了,不买服务器、不配 Nginx,也能写服务端接口,腾讯云云函数全栈实践
前端·javascript·全栈
码途进化论2 小时前
Vue3 防重复点击指令 - clickOnce
前端·javascript·vue.js
小二·2 小时前
从零手写俄罗斯方块(Tetris)——前端工程化实战与性能优化
前端·性能优化
九思x2 小时前
Linux 系统安装 JDK 17
linux·运维