一、简介
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");