浅析class是如何优先成为dev->parent->kobj父kobj的
来看一个实例:
setup_parent函数中只是修改dev->kobj.parent = kobj;即可视化parent,但是dev->parent是永远不会改变的,
所以setup_parent函数之后sleep_led对应的dev的kobj的parent变成了class的kobj,但是dev->parent是始终没有改变[luther.gliethttp],所以在device_add()=>device_add_class_symlinks()
=>sysfs_create_link(&dev->parent->kobj, &dev->kobj, class_name);这样就生成了
/sys/devices/platform/gpio_sleep_debug/leds:gpio_sleep_debug -> ../../../class/leds/gpio_sleep_debug符号链接[luther.gliethttp]
static struct led_classdev sleep_led = {
.name = "gpio_sleep_debug",
.flags = 0,
.brightness = 0,
.brightness_set = leds_brightness_set,
};
//platform_device对应的driver驱动probe
static int __init gpio_sleep_debug_probe(struct platform_device *pdev)
{
...
led_classdev_register(&pdev->dev, &sleep_led);//注册led
...
}
这里pdev就是struct platform_device *pdev;为已经添加到sysfs中的设备,
指定该sleep_led的父为&pdev->dev,但是并不能在/sys/devices/platform/gpio_sleep_debug目录下找到创建的这个目录,这是有原因的,我们继续往下看[luther.gliethttp].
arch/arm/mach-pxa/luther.c
static struct sleep_led_platform_data luther_sleep_led_info = {
.set_brightness = luther_sleep_led_brightness_set,
};
static void __init luther_init(void)
{
...
pxa_set_sleep_led_info(&luther_sleep_led_info);
...
}
MACHINE_START(LUTHER, "luther")
.phys_io
.boot_params = 0xa0000100, .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc, .map_io = pxa_map_io, .init_irq = pxa3xx_init_irq, .timer = &pxa_timer, .init_machine = luther_init, MACHINE_END
static int __init customize_machine(void) { /* customizes platform devices, or adds new ones */ if (init_machine) init_machine(); return 0; } arch_initcall(customize_machine);
struct platform_device pxa_devices_sleep_led = { .name = "gpio_sleep_debug", .id = -1, };
void __init pxa_set_sleep_led_info(struct sleep_led_platform_data *info) { pxa_register_device(&pxa_devices_sleep_led, info); //添加到/sys/devices/platform目录下,名字为"gpio_sleep_debug" } / # ls /sys/devices/platform/gpio_sleep_debug bus leds:gpio_sleep_debug subsystem driver modalias uevent enable power //是不是觉得比较奇怪,因为添加的sleep_led并没有出现在这个目录下面,怎么回事呢?来看看代码. static int __init leds_init(void) { leds_class = class_create(THIS_MODULE, "leds"); //创建/sys/class/leds目录 if (IS_ERR(leds_class)) return PTR_ERR(leds_class); return 0; } subsys_initcall(leds_init); ================================================= 明明指定了父kobj,但为什么没有出现在/sys/devices/platform/gpio_sleep_debug这个父kobj所在的目录呢?这得从device led_classdev_register led_cdev->dev = device_create(leds_class, parent, 0, "%s",led_cdev->name) =>device_create dev->devt = devt dev->class = class dev->parent = parent =>device_register =>device_add =>setup_parent(dev, parent) =>get_device_parent static struct kobject *get_device_parent(struct device *dev, struct device *parent) { /* class devices without a parent live in /sys/class// */ if (dev->class && (!parent || parent->class != dev->class)) //可以看到即便我们指定了parent,但是当 //dev存在class并且,parent的class和dev指定的class不等时,那么dev将由class //管理,所以这时dev的kobj的parent也就变成了class了,但是dev->parent是始终不会改变的,这也就是为什么 ///sys/devices/platform/gpio_sleep_debug这个指定的父kobj下没有发现sleep_led这个kobj的原因了, //那么sleep_led在哪里创建了呢,这不明摆着的事情了,它在这个自动调整到的class下面被创建了,这个class被自动调整为sleep_led的父kobj[luther.gliethttp] return &dev->class->subsys.kobj; /* all other devices keep their parent */ else if (parent) return &parent->kobj;
return NULL; } / # ls sys/class/leds/ gpio_sleep_debug 就是它了, 但是大家可能看到了 / # ls /sys/devices/platform/gpio_sleep_debug bus leds:gpio_sleep_debug subsystem driver modalias uevent enable power 既然没有在/sys/devices/platform/gpio_sleep_debug创建sleep_led,那么为什么 有一个"leds:gpio_sleep_debug"目录呢,而且还有一个莫名其妙的"leds:",我们来继续看看,它的由来[luther.gliethttp]. static void setup_parent(struct device *dev, struct device *parent) { struct kobject *kobj; kobj = get_device_parent(dev, parent); if (kobj) dev->kobj.parent = kobj;//只是修改dev->kobj.parent = kobj;即可视化parent,但是dev->parent是永远不会改变的 } int device_add(struct device *dev) { ... parent = get_device(dev->parent); setup_parent(dev, parent); error = kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev->bus_id); ... //虽然上面kobject_add中将dev->class->subsys.kobj作为了文件创建可视父kobj目录,但是真正的父kobj并没有改变,这里我们还是要将dev挂接到我们真实的parent下. if (parent) klist_add_tail(&dev->knode_parent, &parent->klist_children); ... } klist_children arch_initcall(parisc_init) parisc_init =>do_device_inventory =>system_map_inventory =>walk_central_bus
walk_native_bus和walk_lower_bus是一个嵌套调用
walk_central_bus =>walk_native_bus =>alloc_pa_dev =>create_parisc_device =>alloc_tree_node =>device_for_each_child int device_for_each_child(struct device *parent, void *data, int (*fn)(struct device *dev, void *data)) { struct klist_iter i; struct device *child; int error = 0;
klist_iter_init(&parent->klist_children, &i); while ((child = next_device(&i)) && !error) error = fn(child, data); klist_iter_exit(&i); return error; } =>create_tree_node struct parisc_device * create_tree_node(char id, struct device *parent) { struct parisc_device *dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return NULL;
dev->hw_path = id; dev->id.hw_type = HPHW_FAULTY;
dev->dev.parent = parent; setup_bus_id(dev);//生成设备名leds:gpio_sleep_debug
dev->dev.bus = &parisc_bus_type; dev->dma_mask = 0xffffffffUL; /* PARISC devices are 32-bit */
/* make the generic dma mask a pointer to the parisc one */ dev->dev.dma_mask = &dev->dma_mask; dev->dev.coherent_dma_mask = dev->dma_mask; if (device_register(&dev->dev)) {//注册到parent目录下 //即:/sys/devices/platform/gpio_sleep_debug/leds:gpio_sleep_debug kfree(dev); return NULL; }
return dev; } static void setup_bus_id(struct parisc_device *padev) { struct hardware_path path; char *output = padev->dev.bus_id; int i;
get_node_path(padev->dev.parent, &path);
for (i = 0; i < 6; i++) { if (path.bc[i] == -1) continue; output += sprintf(output, "%u:", (unsigned char) path.bc[i]); //生成设备名leds:gpio_sleep_debug } sprintf(output, "%u", (unsigned char) padev->hw_path); }
#define arch_initcall(fn) __define_initcall("3",fn,3) #define subsys_initcall(fn) __define_initcall("4",fn,4) #define device_initcall(fn) __define_initcall("6",fn,6) #define __initcall(fn) device_initcall(fn) #define module_init(x) __initcall(x); arch_initcall(customize_machine);//首先先注册设备,这时因为 //驱动该device的driver定义为module_init(gpio_sleep_debug_init); //所以只有到稍后才会调用gpio_sleep_debug_init尝试注册驱动的时候会因为platform的name匹配 //而调用gpio_sleep_debug_probe执行进一步的sleep_led的创建[luther.gliethttp] //会 arch_initcall(parisc_init);//它们2个熟先熟后,可能存在不可控性 subsys_initcall(leds_init);//明显这个leds_init创建leds_class = class_create(THIS_MODULE, "leds");的程序在它们2个后面执行的,所以parisc_init就肯定是分析不到leds 从创建的先后顺序来看,以上的创建最后将创建一个/sys/devices/platform/gpio_sleep_debug/leds:gpio_sleep_debug目录,但是ls -l发现/sys/devices/platform/gpio_sleep_debug/leds:gpio_sleep_debug是一个link,所以从这2方面来看,就不是使用这部份代码来创建/sys/devices/platform/gpio_sleep_debug/leds:gpio_sleep_debug的,那么到底在哪里呢? 搞来搞去,搞复杂了,原来就在 device_add =>device_add_class_symlinks =>
static int device_add_class_symlinks(struct device *dev) { int error;
if (!dev->class) return 0;
error = sysfs_create_link(&dev->kobj, &dev->class->subsys.kobj, "subsystem");//dev->kobj现在位于/sys/class/leds/gpio_sleep_debug //所以可以看到一个/sys/class/leds/gpio_sleep_debug/subsystem -> ../../leds的符号链接 if (error) goto out; #define CONFIG_SYSFS_DEPRECATED 1//auconconf.h #ifdef CONFIG_SYSFS_DEPRECATED /* stacked class devices need a symlink in the class directory */ if (dev->kobj.parent != &dev->class->subsys.kobj && device_is_not_partition(dev)) { error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj, dev->bus_id); if (error) goto out_subsys; }
if (dev->parent && device_is_not_partition(dev)) { struct device *parent = dev->parent; char *class_name;
/* * stacked class devices have the 'device' link * pointing to the bus device instead of the parent */ while (parent->class && !parent->bus && parent->parent) parent = parent->parent;//
error = sysfs_create_link(&dev->kobj, &parent->kobj, "device"); if (error) goto out_busid;
class_name = make_class_name(dev->class->name, &dev->kobj); //现在class_name为"leds:gpio_sleep_debug" if (class_name) error = sysfs_create_link(&dev->parent->kobj, &dev->kobj, class_name); //这样就生成了 ///sys/devices/platform/gpio_sleep_debug/leds:gpio_sleep_debug -> ../../../class/leds/gpio_sleep_debug符号链接了[luther.gliethttp] kfree(class_name); if (error) goto out_device; } return 0;
out_device: if (dev->parent && device_is_not_partition(dev)) sysfs_remove_link(&dev->kobj, "device"); out_busid: if (dev->kobj.parent != &dev->class->subsys.kobj && device_is_not_partition(dev)) sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id); #else /* link in the class directory pointing to the device */ error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj, dev->bus_id); if (error) goto out_subsys;
if (dev->parent && device_is_not_partition(dev)) { error = sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device"); if (error) goto out_busid; } return 0;
out_busid: sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id); #endif
out_subsys: sysfs_remove_link(&dev->kobj, "subsystem"); out: return error; } char *make_class_name(const char *name, struct kobject *kobj) { char *class_name; int size;
size = strlen(name) + strlen(kobject_name(kobj)) + 2;
class_name = kmalloc(size, GFP_KERNEL); if (!class_name) return NULL;
strcpy(class_name, name); strcat(class_name, ":");//生成leds:gpio_sleep_debug strcat(class_name, kobject_name(kobj)); return class_name; }
|