lkml.org 
[lkml]   [2004]   [Aug]   [22]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    SubjectRe: Backlight and LCD module patches [2]
    On 08/13/04 18:27:40, Greg KH wrote:
    >
    > Care to provide a proposed implementation of this functionality?

    Here is an updated patch. The only difference between the previous one
    and this one was a broken continue; statement. I tried to continue the
    outer loop from inside an inner loop. Instead I just use a goto.

    #
    # Patch managed by http://www.holgerschurig.de/patcher.html
    #

    --- linux/include/linux/device.h~class_match
    +++ linux/include/linux/device.h
    @@ -147,6 +147,7 @@
    struct subsystem subsys;
    struct list_head children;
    struct list_head interfaces;
    + struct list_head matches;

    struct class_attribute * class_attrs;
    struct class_device_attribute * class_dev_attrs;
    @@ -187,6 +188,8 @@
    void * class_data; /* class-specific data */

    char class_id[BUS_ID_SIZE]; /* unique to this class */
    +
    + struct list_head matched_classes;
    };

    static inline void *
    @@ -240,6 +243,16 @@
    extern int class_interface_register(struct class_interface *);
    extern void class_interface_unregister(struct class_interface *);

    +struct class_match {
    + struct class* class1;
    + struct class* class2;
    +
    + int (*match)(struct class_device *dev1, struct class_device *dev2);
    +};
    +
    +extern int class_match_register(struct class_match *match);
    +extern void class_match_unregister(struct class_match *match);
    +
    /* interface for class simple stuff */
    extern struct class_simple *class_simple_create(struct module *owner, char *name);
    extern void class_simple_destroy(struct class_simple *cs);
    @@ -248,6 +261,7 @@
    extern int class_simple_set_hotplug(struct class_simple *,
    int (*hotplug)(struct class_device *dev, char **envp, int num_envp, char *buffer, int buffer_size));
    extern void class_simple_device_remove(dev_t dev);
    +extern void class_simple_set_match(struct class_simple *cs, struct class_match *match, int location);


    struct device {
    --- linux/drivers/base/class_simple.c~class_match
    +++ linux/drivers/base/class_simple.c
    @@ -214,3 +214,13 @@
    }
    }
    EXPORT_SYMBOL(class_simple_device_remove);
    +
    +extern void class_simple_set_match(struct class_simple *cs, struct class_match *match, int location)
    +{
    + if (location == 1) {
    + match->class1 = &cs->class;
    + } else if (location == 2) {
    + match->class2 = &cs->class;
    + }
    +}
    +EXPORT_SYMBOL(class_simple_set_match);
    --- linux/drivers/base/class.c~class_match
    +++ linux/drivers/base/class.c
    @@ -139,6 +139,7 @@

    INIT_LIST_HEAD(&cls->children);
    INIT_LIST_HEAD(&cls->interfaces);
    + INIT_LIST_HEAD(&cls->matches);
    error = kobject_set_name(&cls->subsys.kset.kobj, cls->name);
    if (error)
    return error;
    @@ -306,6 +307,134 @@

    static decl_subsys(class_obj, &ktype_class_device, &class_hotplug_ops);

    +static spinlock_t class_match_lock = SPIN_LOCK_UNLOCKED;
    +
    +struct class_match_node {
    + struct class_match *match;
    + struct class_device *dev;
    + struct list_head node;
    +};
    +/* Makes the calls to class_match->match functions and connects devices together */
    +static void class_match_check(struct class_device *dev)
    +{
    + struct class *parent = dev->class;
    + struct class_match_node *match_node, *other_match_node;
    +
    + struct class *other_class;
    + struct class_device *other_device;
    +
    + int ret;
    +
    + spin_lock(&class_match_lock);
    +
    + down_read(&parent->subsys.rwsem);
    + list_for_each_entry(match_node, &parent->matches, node) {
    + /* call the match() function and try and match devices together */
    + if (parent == match_node->match->class1) {
    + other_class = match_node->match->class2;
    + } else if (parent == match_node->match->class2) {
    + other_class = match_node->match->class1;
    + } else {
    + other_class = NULL;
    + }
    +
    + if (other_class) {
    + down_read(&other_class->subsys.rwsem);
    +
    + list_for_each_entry(other_device, &other_class->children, node) {
    + /* first check if this device is already matched... */
    + list_for_each_entry(other_match_node, &other_device->matched_classes, node) {
    + if (other_match_node->match == match_node->match) {
    + goto already_matched;
    + }
    + }
    +
    + /* now call the match function */
    + if (parent == match_node->match->class1) {
    + ret = match_node->match->match(dev, other_device);
    + } else {
    + ret = match_node->match->match(other_device, dev);
    + }
    + if (ret) {
    +
    + /* now add this to the matched_classes lists */
    + other_match_node = (struct class_match_node *) kmalloc(sizeof(struct class_match_node), GFP_KERNEL);
    + if (other_match_node) {
    + other_match_node->dev = other_device;
    + other_match_node->match = match_node->match;
    +
    + list_add_tail(&other_match_node->node, &dev->matched_classes);
    + sysfs_create_link(&dev->kobj, &other_device->kobj, other_class->name);
    + }
    +
    + other_match_node = (struct class_match_node *) kmalloc(sizeof(struct class_match_node), GFP_KERNEL);
    + if (other_match_node) {
    + other_match_node->dev = dev;
    + other_match_node->match = match_node->match;
    +
    + list_add_tail(&other_match_node->node, &other_device->matched_classes);
    + sysfs_create_link(&other_device->kobj, &dev->kobj, parent->name);
    + }
    +
    + break;
    + }
    +already_matched:;
    + }
    + up_read(&other_class->subsys.rwsem);
    + }
    + }
    + up_read(&parent->subsys.rwsem);
    +
    + spin_unlock(&class_match_lock);
    +}
    +
    +static void class_match_unlink(struct class_device *class_dev)
    +{
    + struct class * parent = class_dev->class;
    + struct class_match_node *match_node, *other_match_node, *temp_match_node;
    + struct class *other_class;
    +
    + spin_lock(&class_match_lock);
    +
    + down_read(&parent->subsys.rwsem);
    +
    + /* remove symlinks from devices matched to this device */
    + list_for_each_entry(match_node, &class_dev->matched_classes, node) {
    + if (parent == match_node->match->class1) {
    + other_class = match_node->match->class2;
    + } else if (parent == match_node->match->class2) {
    + other_class = match_node->match->class1;
    + } else {
    + /* should never happen */
    + other_class = NULL;
    + }
    + if (other_class) {
    + list_for_each_entry_safe(other_match_node, temp_match_node, &match_node->dev->matched_classes, node) {
    + if (match_node->match == other_match_node->match) {
    + list_del(&other_match_node->node);
    + kfree(other_match_node);
    + sysfs_remove_link(&match_node->dev->kobj, parent->name);
    + break;
    + }
    + }
    + }
    + }
    +
    + /* free entries in matched_classes */
    + list_for_each_entry_safe(match_node, temp_match_node, &class_dev->matched_classes, node) {
    + list_del(&match_node->node);
    + if (parent == match_node->match->class1) {
    + sysfs_remove_link(&class_dev->kobj, match_node->match->class2->name);
    + } else {
    + sysfs_remove_link(&class_dev->kobj, match_node->match->class1->name);
    + }
    + kfree(match_node);
    + }
    +
    + up_read(&parent->subsys.rwsem);
    +
    + spin_unlock(&class_match_lock);
    +}

    static int class_device_add_attrs(struct class_device * cd)
    {
    @@ -345,6 +474,7 @@
    kobj_set_kset_s(class_dev, class_obj_subsys);
    kobject_init(&class_dev->kobj);
    INIT_LIST_HEAD(&class_dev->node);
    + INIT_LIST_HEAD(&class_dev->matched_classes);
    }

    int class_device_add(struct class_device *class_dev)
    @@ -378,6 +508,7 @@
    if (class_intf->add)
    class_intf->add(class_dev);
    up_write(&parent->subsys.rwsem);
    + class_match_check(class_dev);
    }
    class_device_add_attrs(class_dev);
    class_device_dev_link(class_dev);
    @@ -408,6 +539,7 @@
    if (class_intf->remove)
    class_intf->remove(class_dev);
    up_write(&parent->subsys.rwsem);
    + class_match_unlink(class_dev);
    }

    class_device_dev_unlink(class_dev);
    @@ -505,7 +637,64 @@
    class_put(parent);
    }

    +int class_match_register(struct class_match *match)
    +{
    + struct class_match_node *match_node;
    +
    + if (!match)
    + return -ENODEV;
    + if (!match->class1 || !match->class2)
    + return -ENODEV;
    + if (!match->match || match->class1 == match->class2)
    + return -EINVAL;

    + spin_lock(&class_match_lock);
    +
    + match_node = (struct class_match_node *) kmalloc(sizeof(struct class_match_node), GFP_KERNEL);
    + if (match_node) {
    + match_node->match = match;
    + match_node->dev = NULL;
    + down_write(&match->class1->subsys.rwsem);
    + list_add_tail(&match_node->node, &match->class1->matches);
    + up_write(&match->class1->subsys.rwsem);
    + } else {
    + return -ENOMEM;
    + }
    +
    + match_node = (struct class_match_node *) kmalloc(sizeof(struct class_match_node), GFP_KERNEL);
    + if (match_node) {
    + match_node->match = match;
    + match_node->dev = NULL;
    + down_write(&match->class2->subsys.rwsem);
    + list_add_tail(&match_node->node, &match->class2->matches);
    + up_write(&match->class2->subsys.rwsem);
    + } else {
    + return -ENOMEM;
    + }
    +
    + spin_unlock(&class_match_lock);
    +
    + return 0;
    +}
    +
    +void class_match_unregister(struct class_match *match)
    +{
    + struct class_device *class_dev;
    +
    + if (!match)
    + return;
    + if (!match->class1 || !match->class2)
    + return;
    + if (!match->match || match->class1 == match->class2)
    + return;
    +
    + /* we only need to call class_match_unlink on one of the class devices */
    +
    + down_read(&match->class1->subsys.rwsem);
    + list_for_each_entry(class_dev, &match->class1->children, node)
    + class_match_unlink(class_dev);
    + up_read(&match->class1->subsys.rwsem);
    +}

    int __init classes_init(void)
    {
    @@ -542,3 +731,5 @@

    EXPORT_SYMBOL(class_interface_register);
    EXPORT_SYMBOL(class_interface_unregister);
    +EXPORT_SYMBOL(class_match_register);
    +EXPORT_SYMBOL(class_match_unregister);
    \
     
     \ /
      Last update: 2005-03-22 14:05    [W:0.053 / U:120.124 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site