Linux设备驱动模型和kobject

一、嵌入式系统常见系统框图

硬件拓撲描述Linux設備模型中四個重要概念中三個:Bus,Class和Device(第四個為Driver)。对应sysfs中的目录结构如下图:

二、设备、总线、驱动关系

在計算機中有這樣一類設備,它們通過各自的設備控制器,直接和CPU連接,CPU可以通過常規的尋址操作訪問它們(或者說訪問它們的控制器)。這種連接方式,並不屬於傳統意義上的總線連接。但設備模型應該具備普適性,因此Linux就虛構了一條Platform Bus,供這些設備掛靠。

SoC systemspii2cpciThe entity bus is used for peripheral connections, and the Linux kernel provides a virtual bus for the peripheral controller integrated in the SOC.platformUsed for the connection of these peripheral controllers,platformThe bus can also be used for peripherals without an entity bus;

Linux kernel会将所有的总线注册一次,包括platform bus/pci bus/i2c bus ......方便device和driver挂接:

retval = bus_register(&usb_bus_type);
return bus_register(&pci_bus_type);
retval = bus_register(&i2c_bus_type);
error =  bus_register(&platform_bus_type);
......

bus_register函数主要逻辑如下:

/**
 *	bus_register - register a bus with the system.
 *	@bus:	bus.
 *
 *	Once we have that, we registered the bus with the kobject
 *	infrastructure, then register the children subsystems it has:
 *	the devices and drivers that belong to the bus.
 *  這樣的結果就是在/sys/bus/下會多出一個pci的folder。之後就是kernel scan到
 *  devices或者drivers時,再加入/sys/bus/pci/devices/及/sys/bus/pci/drivers/
 */
int bus_register(struct bus_type * bus)
{
	int retval;

	BLOCKING_INIT_NOTIFIER_HEAD(&bus->bus_notifier);

	retval = kobject_set_name(&bus->subsys.kobj, "%s", bus->name);//bus->name是:"pci"
	if (retval)
		goto out;

	subsys_set_kset(bus, bus_subsys);
	retval = subsystem_register(&bus->subsys);					//1.在sysfs下面以总线名创建一个文件夹,以pci总线为例:创建pci文件夹
	if (retval)
		goto out;

	kobject_set_name(&bus->devices.kobj, "devices");			//2.在pci文件夹下面创建devices
	bus->devices.kobj.parent = &bus->subsys.kobj;
	retval = kset_register(&bus->devices);
	if (retval)
		goto bus_devices_fail;

	kobject_set_name(&bus->drivers.kobj, "drivers");			//3.在pci文件夹下面创建drivers
	bus->drivers.kobj.parent = &bus->subsys.kobj;
	bus->drivers.ktype = &ktype_driver;
	retval = kset_register(&bus->drivers);
	if (retval)
		goto bus_drivers_fail;

	klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put);
	klist_init(&bus->klist_drivers, NULL, NULL);

	bus->drivers_autoprobe = 1;
	retval = add_probe_files(bus);
	if (retval)
		goto bus_probe_files_fail;

	retval = bus_add_attrs(bus);
	if (retval)
		goto bus_attrs_fail;

	pr_debug("bus type '%s' registered\n", bus->name);
	return 0;

bus_attrs_fail:
	remove_probe_files(bus);
bus_probe_files_fail:
	kset_unregister(&bus->drivers);
bus_drivers_fail:
	kset_unregister(&bus->devices);
bus_devices_fail:
	subsystem_unregister(&bus->subsys);
out:
	return retval;
}

三、kobject知识

Kobject大多數的使用場景,是內嵌在大型的數據結構中(如Kset、device_driver等),因此這些大型的數據結構,也必須是動態分配、動態釋放的。那麼釋放的時機是什麼呢?是內嵌的Kobject釋放時。但是Kobject的釋放是由Kobject模塊自動完成的(在引用計數為0時),那麼怎麼一併釋放包含自己的大型數據結構呢(上述标号为1的箭头)

這時Ktype就派上用場了。我們知道,Ktype中的release回調函數負責釋放Kobject(甚至是包裹Kobject的數據結構)的內存空間,那麼Ktype及其內部函數,是由誰實現呢?是由上層數據結構所在的模塊!因為只有它,才清楚Kobject嵌在哪個數據結構中,並通過Kobject指針以及自身的數據結構類型,找到需要釋放的上層數據結構的指針,然後釋放它(上述标号为2的箭头)

講到這裡,就清晰多了。所以,每一個內嵌Kobject的數據結構,例如kset、device、device_driver等等,都要實現一個Ktype,並定義其中的回調函數。同理,sysfs相關的操作也一樣,必須經過ktype的中轉,因為sysfs看到的是Kobject,而真正的文件操作的主體,是內嵌Kobject的上層數據結構(上述标号为3的箭头)

四、kobject/kset/kobj_type数据结构之间的关系

 

 

Previous mentionkobject/ksetThe structure itself will not be used alone, usually in other structures, sincekobjcet/ksetCan organize into topology, then the structure containing them can also build this relationship, becausecontainer_ofYou can find the first address of the structure.

  • Structural A, B, C, D, and E can also construct topological relationships;
  • struct deviceandstruct device_driverThe structure is included in the structurestruct kobject,andstruct bus_typeThe structure is included in the structurestruct ksetStructure, this also adds to the device and driver mentioned in the previous article to the bus, and is responsible for match by the bus;

五、处理流程分析

 六、一个例子

 

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

 // Customize a structure containing the Struct Kobject sub-structure
struct test_kobj {
    int value;
    struct kobject kobj;
};

 / / Customize a property structure that contains the Struct Attribute structure
struct test_kobj_attribute {
    struct attribute attr;
    ssize_t (*show)(struct test_kobj *obj, struct test_kobj_attribute *attr, char *buf);
    ssize_t (*store)(struct test_kobj *obj, struct test_kobj_attribute *attr, const char *buf, size_t count);
};

 // Declare a global structure for testing
struct test_kobj *obj;

 // Used to initialize the function pointer in sysfs_ops
static ssize_t test_kobj_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
    struct test_kobj_attribute *test_kobj_attr;
    ssize_t ret = -EIO;

    test_kobj_attr = container_of(attr, struct test_kobj_attribute, attr);
    
         // Press to a specific implementation function
    if (test_kobj_attr->show)
        ret = test_kobj_attr->show(container_of(kobj, struct test_kobj, kobj), test_kobj_attr, buf);
    
    return ret;
}

 // Used to initialize the function pointer in sysfs_ops
static ssize_t test_kobj_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count)
{
    struct test_kobj_attribute *test_kobj_attr;
    ssize_t ret = -EIO;

    test_kobj_attr = container_of(attr, struct test_kobj_attribute, attr);
    
         // Press to a specific implementation function
    if (test_kobj_attr->store)
        ret = test_kobj_attr->store(container_of(kobj, struct test_kobj, kobj), test_kobj_attr, buf, count);
    
    return ret;
}

 // Used to initialize Kobj_ktype
const struct sysfs_ops test_kobj_sysfs_ops = {
    .show = test_kobj_attr_show,
    .store = test_kobj_attr_store,
};

 // Used to initialize Kobj_ktype, and finally used to release KOBJECT
void obj_release(struct kobject *kobj)
{
    struct test_kobj *obj = container_of(kobj, struct test_kobj, kobj);

    printk(KERN_INFO "test kobject release %s\n", kobject_name(&obj->kobj));
    
    kfree(obj);
}

 // Define kobj_ktype, used to specify the type of KOBJECT, when initialization
static struct kobj_type test_kobj_ktype = {
    .release = obj_release,
    .sysfs_ops = &test_kobj_sysfs_ops,
};

 // SHOW function specific implementation
ssize_t name_show(struct test_kobj *obj, struct test_kobj_attribute *attr, char *buffer)
{
    return sprintf(buffer, "%s\n", kobject_name(&obj->kobj));
}

 // SHOW function specific implementation
ssize_t value_show(struct test_kobj *obj, struct test_kobj_attribute *attr, char *buffer)
{
    return sprintf(buffer, "%d\n", obj->value);
}

 // Store function specific implementation
ssize_t value_store(struct test_kobj *obj, struct test_kobj_attribute *attr, const char *buffer, size_t size)
{
    sscanf(buffer, "%d", &obj->value);

    return size;
}

 / / Define attributes, and finally register into the SYSFS system
struct test_kobj_attribute name_attribute = __ATTR(name, 0664, name_show, NULL);
struct test_kobj_attribute value_attribute = __ATTR(value, 0664, value_show, value_store);
struct attribute *test_kobj_attrs[] = {
    &name_attribute.attr,
    &value_attribute.attr,
    NULL,
};

 // Define group
struct attribute_group test_kobj_group = {
    .name = "test_kobj_group",
    .attrs = test_kobj_attrs,
};

 // Module initialization function
static int __init test_kobj_init(void)
{
    int retval;
    printk(KERN_INFO "test_kobj_init\n");
    obj = kmalloc(sizeof(struct test_kobj), GFP_KERNEL);
    if (!obj) {
        return -ENOMEM;
    }
    
    obj->value = 1;
    memset(&obj->kobj, 0, sizeof(struct kobject));
         // Add into the SYSFS system
    kobject_init_and_add(&obj->kobj, &test_kobj_ktype, NULL, "test_kobj");

         / / Create a file under the SYS folder
    retval = sysfs_create_files(&obj->kobj, (const struct attribute **)test_kobj_attrs);
    if (retval) {
        kobject_put(&obj->kobj);
        return retval;
    }
    
         / / Create a group under the SYS folder
    retval = sysfs_create_group(&obj->kobj, &test_kobj_group);
    if (retval) {
        kobject_put(&obj->kobj);
        return retval;
    }
    
    return 0;
}

 // module cleaning function
static void __exit test_kobj_exit(void)
{
    printk(KERN_INFO "test_kobj_exit\n");

    kobject_del(&obj->kobj);
    kobject_put(&obj->kobj);
    
    return;
}

module_init(test_kobj_init);
module_exit(test_kobj_exit);

MODULE_AUTHOR("xxxxxx");
MODULE_LICENSE("GPL");

七、关于kobject的API分析————待完成

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

denglin12315

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

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

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

打赏作者

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

抵扣说明:

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

余额充值