Chinaunix首页 | 论坛 | 博客
  • 博客访问: 15273220
  • 博文数量: 2005
  • 博客积分: 11986
  • 博客等级: 上将
  • 技术积分: 22535
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-17 13:56
文章分类

全部博文(2005)

文章存档

2014年(2)

2013年(2)

2012年(16)

2011年(66)

2010年(368)

2009年(743)

2008年(491)

2007年(317)

分类: LINUX

2008-10-22 12:22:51

浅析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;
}

/sys/class/leds/gpio_sleep_debug # ls -l
-rw-r--r--    1 0        0            4096 Oct 22 01:58 brightness
lrwxrwxrwx    1 0        0               0 Oct 22 01:58 device -> ../../../devices/platform/gpio_sleep_debug
drwxr-xr-x    2 0        0               0 Oct 22 01:58 power
lrwxrwxrwx    1 0        0               0 Oct 22 01:58 subsystem -> ../../leds
-rw-r--r--    1 0        0            4096 Oct 22 01:58 uevent
阅读(3025) | 评论(0) | 转发(3) |
给主人留下些什么吧!~~