drivers/base/Makefile | 2 drivers/base/base.h | 2 drivers/base/class.c | 3 drivers/base/subclass.c | 569 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/gregkh.c | 20 + include/linux/device.h | 66 +++++ 6 files changed, 659 insertions(+), 3 deletions(-) --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gregkh-2.6/drivers/base/subclass.c 2005-09-26 16:12:20.000000000 -0700 @@ -0,0 +1,569 @@ +/* + * subclass.c - subclass crap + * + * Portions based on drivers/base/class.c, which is: + * Copyright (c) 2002-3 Patrick Mochel + * Copyright (c) 2002-3 Open Source Development Labs + * Copyright (c) 2003-2004 Greg Kroah-Hartman + * Copyright (c) 2003-2004 IBM Corp. + * + * Copyright (c) 2005 Greg Kroah-Hartman + * Copyright (c) 2005 Novell, Inc. + * + * This file is released under the GPLv2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "base.h" + +#define to_subclass_attr(_attr) container_of(_attr, struct subclass_attribute, attr) +#define to_class(obj) container_of(obj, struct class, subsys.kset.kobj) + + +int subclass_device_create_file(struct subclass_device *dev, + const struct subclass_device_attribute *attr) +{ + int error = -EINVAL; + if (dev) + error = sysfs_create_file(&dev->kobj, &attr->attr); + return error; +} +EXPORT_SYMBOL_GPL(subclass_device_create_file); + +void subclass_device_remove_file(struct subclass_device *dev, + const struct subclass_device_attribute *attr) +{ + if (dev) + sysfs_remove_file(&dev->kobj, &attr->attr); +} +EXPORT_SYMBOL_GPL(subclass_device_remove_file); + +int subclass_device_create_bin_file(struct subclass_device *dev, + struct bin_attribute *attr) +{ + int error = -EINVAL; + if (dev) + error = sysfs_create_bin_file(&dev->kobj, attr); + return error; +} + +void subclass_device_remove_bin_file(struct subclass_device *dev, + struct bin_attribute *attr) +{ + if (dev) + sysfs_remove_bin_file(&dev->kobj, attr); +} + +static inline struct subclass_device *to_subclass_dev(struct kobject *obj) +{ + return container_of(obj, struct subclass_device, kobj); +} + +static inline struct subclass_device_attribute *to_subclass_dev_attr(struct attribute *_attr) +{ + return container_of(_attr, struct subclass_device_attribute, attr); +} + + +static ssize_t subclass_device_attr_show(struct kobject *kobj, struct attribute *attr, + char * buf) +{ + struct subclass_device_attribute *dev_attr = to_subclass_dev_attr(attr); + struct subclass_device *dev = to_subclass_dev(kobj); + ssize_t ret = 0; + + if (dev_attr->show) + ret = dev_attr->show(dev, dev_attr, buf); + return ret; +} + +static ssize_t subclass_device_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + struct subclass_device_attribute *dev_attr = to_subclass_dev_attr(attr); + struct subclass_device *dev = to_subclass_dev(kobj); + ssize_t ret = 0; + + if (dev_attr->store) + ret = dev_attr->store(dev, dev_attr, buf, count); + return ret; +} + +static struct sysfs_ops subclass_dev_sysfs_ops = { + .show = subclass_device_attr_show, + .store = subclass_device_attr_store, +}; + +static void subclass_dev_release(struct kobject *kobj) +{ + struct subclass_device *sd = to_subclass_dev(kobj); + struct class_device *class_dev = sd->parent; + + pr_debug("subclass device '%s': release.\n", kobject_name(&sd->kobj)); + + kfree(sd->devt_attr); + sd->devt_attr = NULL; + + if (class_dev->release) + class_dev->release(sd); + else { + printk(KERN_ERR "Class Device '%s' does not have a release() function, " + "it is broken and must be fixed.\n", + class_dev->class_id); + WARN_ON(1); + } +} + +static struct kobj_type ktype_subclass_device = { + .sysfs_ops = &subclass_dev_sysfs_ops, + .release = subclass_dev_release, +}; + +static int subclass_hotplug_filter(struct kset *kset, struct kobject *kobj) +{ + struct kobj_type *ktype = get_ktype(kobj); + + if (ktype == &ktype_subclass_device) { + struct subclass_device *dev = to_subclass_dev(kobj); + if (dev->parent) + return 1; + } + return 0; +} + +static const char *subclass_hotplug_name(struct kset *kset, struct kobject *kobj) +{ + struct subclass_device *dev = to_subclass_dev(kobj); + + return dev->parent->class->name; +} + +static int subclass_hotplug(struct kset *kset, struct kobject *kobj, char **envp, + int num_envp, char *buffer, int buffer_size) +{ + struct subclass_device *sd = to_subclass_dev(kobj); + int i = 0; + int length = 0; + int retval = 0; + + pr_debug("%s - name = %s\n", __FUNCTION__, kobject_name(&sd->kobj)); + + if (sd->dev) { + /* add physical device, backing this device */ + struct device *dev = sd->dev; + char *path = kobject_get_path(&dev->kobj, GFP_KERNEL); + + add_hotplug_env_var(envp, num_envp, &i, buffer, buffer_size, + &length, "PHYSDEVPATH=%s", path); + kfree(path); + + if (dev->bus) + add_hotplug_env_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "PHYSDEVBUS=%s", dev->bus->name); + + if (dev->driver) + add_hotplug_env_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "PHYSDEVDRIVER=%s", dev->driver->name); + } + + if (MAJOR(sd->devt)) { + add_hotplug_env_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "MAJOR=%u", MAJOR(sd->devt)); + + add_hotplug_env_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "MINOR=%u", MINOR(sd->devt)); + } + + /* terminate, set to next free slot, shrink available space */ + envp[i] = NULL; + envp = &envp[i]; + num_envp -= i; + buffer = &buffer[length]; + buffer_size -= length; + + if (sd->parent->class->hotplug) { + /* have the bus specific function add its stuff */ + retval = sd->parent->class->hotplug(sd->parent, envp, num_envp, + buffer, buffer_size); + if (retval) { + pr_debug ("%s - hotplug() returned %d\n", + __FUNCTION__, retval); + } + } + + return retval; +} + +static struct kset_hotplug_ops subclass_hotplug_ops = { + .filter = subclass_hotplug_filter, + .name = subclass_hotplug_name, + .hotplug = subclass_hotplug, +}; + +static decl_subsys(subclass_obj, &ktype_subclass_device, &subclass_hotplug_ops); + + +static int subclass_device_add_attrs(struct subclass_device *sd) +{ + int i; + int error = 0; + struct class *class = sd->parent->class; + + if (class->subclass_dev_attrs) { + for (i = 0; attr_name(class->subclass_dev_attrs[i]); i++) { + error = subclass_device_create_file(sd, + &class->subclass_dev_attrs[i]); + if (error) + goto error; + } + } +done: + return error; +error: + while (--i >= 0) + subclass_device_remove_file(sd, &class->subclass_dev_attrs[i]); + goto done; +} + +static void subclass_device_remove_attrs(struct subclass_device *sd) +{ + int i; + struct class *class = sd->parent->class; + + if (class->subclass_dev_attrs) { + for (i = 0; attr_name(class->subclass_dev_attrs[i]); i++) + subclass_device_remove_file(sd, &class->subclass_dev_attrs[i]); + } +} + +static ssize_t show_dev(struct subclass_device *sd, + struct subclass_device_attribute *attr, char *buf) +{ + return print_dev_t(buf, sd->devt); +} + +static ssize_t show_sample(struct subclass_device *sd, + struct subclass_device_attribute *attr, char *buf) +{ + return sprintf(buf, "#!/bin/sh\nmknod /dev/%s c %d %d\n", + kobject_name(&sd->kobj), + MAJOR(sd->devt), MINOR(sd->devt)); +} + +void subclass_device_initialize(struct subclass_device *sd) +{ + kobj_set_kset_s(sd, subclass_obj_subsys); + kobject_init(&sd->kobj); + INIT_LIST_HEAD(&sd->node); +} + +static char *make_class_name(struct subclass_device *sd) +{ + char *name; + int size; + + size = strlen(sd->parent->class->name) + + strlen(kobject_name(&sd->kobj)) + 2; + + name = kmalloc(size, GFP_KERNEL); + if (!name) + return ERR_PTR(-ENOMEM); + + strcpy(name, sd->parent->class->name); + strcat(name, ":"); + strcat(name, kobject_name(&sd->kobj)); + return name; +} + +int subclass_device_add(struct subclass_device *sd) +{ + struct class_device *parent = NULL; + struct class_interface * class_intf; + char *class_name = NULL; + int error; + + sd = subclass_device_get(sd); + if (!sd) + return -EINVAL; + + if (!strlen(sd->class_id)) { + error = -EINVAL; + goto register_done; + } + + parent = class_device_get(sd->parent); + if (!parent) { + error = -EINVAL; + goto register_done; + } + + pr_debug("SUBCLASS: registering subclass device: '%s'\n", sd->class_id); + + /* first, register with generic layer. */ + kobject_set_name(&sd->kobj, "%s", sd->class_id); + if (parent) + sd->kobj.parent = &parent->kobj; + + error = kobject_add(&sd->kobj); + if (error) + goto register_done; + + /* add the needed attributes to this device */ + if (MAJOR(sd->devt)) { + struct subclass_device_attribute *attr; + attr = kzalloc(sizeof(*attr), GFP_KERNEL); + if (!attr) { + error = -ENOMEM; + kobject_del(&sd->kobj); + goto register_done; + } + + attr->attr.name = "dev"; + attr->attr.mode = S_IRUGO; + attr->attr.owner = parent->class->owner; + attr->show = show_dev; + attr->store = NULL; + subclass_device_create_file(sd, attr); + sd->devt_attr = attr; + + attr = kzalloc(sizeof(*attr), GFP_KERNEL); + if (!attr) { + error = -ENOMEM; + kobject_del(&sd->kobj); + goto register_done; + } + attr->attr.name = "sample.sh"; + attr->attr.mode = S_IRUSR | S_IXUSR | S_IRUGO; + attr->attr.owner = parent->class->owner; + attr->show = show_sample; + attr->store = NULL; + subclass_device_create_file(sd, attr); + sd->sample_attr = attr; + + ndevfs_create(kobject_name(&sd->kobj), sd->devt, 1); + } + + subclass_device_add_attrs(sd); + if (sd->dev) { + class_name = make_class_name(sd); + sysfs_create_link(&sd->kobj, &sd->dev->kobj, "device"); + sysfs_create_link(&sd->dev->kobj, &sd->kobj, class_name); + } + + /* notify any interfaces this device is now here */ + if (parent) { + down(&parent->sem); + list_add_tail(&sd->node, &parent->subdevs); +// list_for_each_entry(class_intf, &parent->interfaces, node) +// if (class_intf->add) +// class_intf->add(class_dev); + up(&parent->sem); + } + + kobject_hotplug(&sd->kobj, KOBJ_ADD); + + register_done: + if (error && parent) + class_device_put(parent); + subclass_device_put(sd); + kfree(class_name); + return error; +} + +int subclass_device_register(struct subclass_device *sd) +{ + subclass_device_initialize(sd); + return subclass_device_add(sd); +} + +/** + * class_device_create - creates a class device and registers it with sysfs + * @cs: pointer to the struct class that this device should be registered to. + * @dev: the dev_t for the char device to be added. + * @device: a pointer to a struct device that is assiociated with this class device. + * @fmt: string for the class device's name + * + * This function can be used by char device classes. A struct + * class_device will be created in sysfs, registered to the specified + * class. A "dev" file will be created, showing the dev_t for the + * device. The pointer to the struct class_device will be returned from + * the call. Any further sysfs files that might be required can be + * created using this pointer. + * + * Note: the struct class passed to this function must have previously + * been created with a call to class_create(). + */ +struct subclass_device *subclass_device_create(struct class_device *class_dev, + dev_t devt, struct device *device, + char *fmt, ...) +{ + va_list args; + struct subclass_device *sd = NULL; + int retval = -ENODEV; + + if (class_dev == NULL || IS_ERR(class_dev)) + goto error; + + sd = kzalloc(sizeof(struct subclass_device), GFP_KERNEL); + if (!sd) { + retval = -ENOMEM; + goto error; + } + + sd->devt = devt; + sd->dev = device; + sd->parent = class_dev; + sd->kobj.parent = &class_dev->kobj; + + va_start(args, fmt); + vsnprintf(sd->class_id, BUS_ID_SIZE, fmt, args); + va_end(args); + retval = subclass_device_register(sd); + if (retval) + goto error; + + return sd; + +error: + kfree(sd); + return ERR_PTR(retval); +} +EXPORT_SYMBOL_GPL(subclass_device_create); + +void subclass_device_del(struct subclass_device *sd) +{ + struct class_device *parent = sd->parent; + struct class_interface * class_intf; + char *class_name = NULL; + + if (parent) { + down(&parent->sem); + list_del_init(&sd->node); +// list_for_each_entry(class_intf, &parent->interfaces, node) +// if (class_intf->remove) +// class_intf->remove(class_dev); + up(&parent->sem); + } + + if (sd->dev) { + class_name = make_class_name(sd); + sysfs_remove_link(&sd->kobj, "device"); + sysfs_remove_link(&sd->dev->kobj, class_name); + } + if (sd->devt_attr) { + subclass_device_remove_file(sd, sd->devt_attr); + subclass_device_remove_file(sd, sd->sample_attr); + ndevfs_remove(kobject_name(&sd->kobj)); + } + subclass_device_remove_attrs(sd); + + kobject_hotplug(&sd->kobj, KOBJ_REMOVE); + kobject_del(&sd->kobj); + + if (parent) + class_device_put(parent); + kfree(class_name); +} + +void subclass_device_unregister(struct subclass_device *sd) +{ + pr_debug("SUBCLASS: Unregistering subclass device. ID = '%s'\n", + kobject_name(&sd->kobj)); + subclass_device_del(sd); + subclass_device_put(sd); +} + +/** + * subclass_device_destroy - removes a class device that was created with class_device_create() + * @cls: the pointer to the struct class that this device was registered * with. + * @dev: the dev_t of the device that was previously registered. + * + * This call unregisters and cleans up a class device that was created with a + * call to class_device_create() + */ +void subclass_device_destroy(struct class_device *class_dev, dev_t devt) +{ + struct subclass_device *sd = NULL; + struct subclass_device *sd_tmp; + + down(&class_dev->sem); + list_for_each_entry(sd_tmp, &class_dev->subdevs, node) { + if (sd_tmp->devt == devt) { + sd = sd_tmp; + break; + } + } + up(&class_dev->sem); + + if (sd) + subclass_device_unregister(sd); +} +EXPORT_SYMBOL_GPL(subclass_device_destroy); + +int subclass_device_rename(struct subclass_device *sd, char *new_name) +{ + int error = 0; + char *old_class_name = NULL, *new_class_name = NULL; + + sd = subclass_device_get(sd); + if (!sd) + return -EINVAL; + + pr_debug("SUBCLASS: renaming '%s' to '%s'\n", sd->class_id, + new_name); + + if (sd->dev) + old_class_name = make_class_name(sd); + + strlcpy(sd->class_id, new_name, KOBJ_NAME_LEN); + + error = kobject_rename(&sd->kobj, new_name); + + if (sd->dev) { + new_class_name = make_class_name(sd); + sysfs_create_link(&sd->dev->kobj, &sd->kobj, new_class_name); + sysfs_remove_link(&sd->dev->kobj, old_class_name); + } + subclass_device_put(sd); + + kfree(old_class_name); + kfree(new_class_name); + + return error; +} + +struct subclass_device *subclass_device_get(struct subclass_device *sd) +{ + if (sd) + return to_subclass_dev(kobject_get(&sd->kobj)); + return NULL; +} +EXPORT_SYMBOL_GPL(subclass_device_get); + +void subclass_device_put(struct subclass_device *sd) +{ + kobject_put(&sd->kobj); +} +EXPORT_SYMBOL_GPL(subclass_device_put); + +void __init subclass_init(void) +{ + /* ick, this is ugly, the things we go through to keep from showing up + * in sysfs... */ + subsystem_init(&subclass_obj_subsys); + if (!subclass_obj_subsys.kset.subsys) + subclass_obj_subsys.kset.subsys = &subclass_obj_subsys; +} + + --- gregkh-2.6.orig/drivers/base/Makefile 2005-09-26 14:59:33.000000000 -0700 +++ gregkh-2.6/drivers/base/Makefile 2005-09-26 16:12:20.000000000 -0700 @@ -1,7 +1,7 @@ # Makefile for the Linux device tree obj-y := core.o sys.o bus.o dd.o \ - driver.o class.o platform.o \ + driver.o class.o subclass.o platform.o \ cpu.o firmware.o init.o map.o dmapool.o \ attribute_container.o transport_class.o obj-y += power/ --- gregkh-2.6.orig/include/linux/device.h 2005-09-26 14:59:33.000000000 -0700 +++ gregkh-2.6/include/linux/device.h 2005-09-26 16:12:20.000000000 -0700 @@ -45,6 +45,7 @@ struct device_driver; struct class; struct class_device; +struct subclass_device; struct bus_type { const char * name; @@ -163,6 +164,7 @@ struct class_attribute * class_attrs; struct class_device_attribute * class_dev_attrs; + struct subclass_device_attribute* subclass_dev_attrs; int (*hotplug)(struct class_device *dev, char **envp, int num_envp, char *buffer, int buffer_size); @@ -201,7 +203,10 @@ struct class_device_attribute *sample_attr; struct device * dev; /* not necessary, but nice to have */ void * class_data; /* class-specific data */ + struct list_head subdevs; + struct semaphore sem; /* locks the subdevs list */ + void (*release)(struct subclass_device *dev); char class_id[BUS_ID_SIZE]; /* unique to this class */ }; @@ -266,6 +271,67 @@ __attribute__((format(printf,4,5))); extern void class_device_destroy(struct class *cls, dev_t devt); +struct subclass_device { + struct list_head node; + + struct kobject kobj; + struct class_device *parent; /* required */ + struct device *dev; /* not necessary, but nice to have */ + dev_t devt; /* dev_t, creates the sysfs "dev" */ + struct subclass_device_attribute *devt_attr; + struct subclass_device_attribute *sample_attr; + char class_id[BUS_ID_SIZE]; /* unique to this class device */ + void *data; /* class-specific data */ +}; + +static inline void *subclass_get_devdata(struct subclass_device *dev) +{ + return dev->data; +} + +static inline void subclass_set_devdata(struct subclass_device *dev, void *data) +{ + dev->data = data; +} + +extern int subclass_device_register(struct subclass_device *); +extern void subclass_device_unregister(struct subclass_device *); +extern void subclass_device_initialize(struct subclass_device *); +extern int subclass_device_add(struct subclass_device *); +extern void subclass_device_del(struct subclass_device *); + +extern struct subclass_device *subclass_device_get(struct subclass_device *); +extern void subclass_device_put(struct subclass_device *); + +struct subclass_device_attribute { + struct attribute attr; + ssize_t (*show)(struct subclass_device *, + struct subclass_device_attribute *attr, char *buf); + ssize_t (*store)(struct subclass_device *, + struct subclass_device_attribute *attr, + const char *buf, size_t count); +}; + +#define SUBCLASS_DEVICE_ATTR(_name,_mode,_show,_store) \ +struct subclass_device_attribute class_device_attr_##_name = \ + __ATTR(_name,_mode,_show,_store) + +extern int subclass_device_create_file(struct subclass_device *, + const struct subclass_device_attribute *); +extern void subclass_device_remove_file(struct subclass_device *, + const struct subclass_device_attribute *); +extern int subclass_device_create_bin_file(struct subclass_device *, + struct bin_attribute *); +extern void subclass_device_remove_bin_file(struct subclass_device *, + struct bin_attribute *); + +extern struct subclass_device *subclass_device_create(struct class_device *class_dev, + dev_t devt, struct device *device, + char *fmt, ...) + __attribute__((format(printf,4,5))); +extern void subclass_device_destroy(struct class_device *class_dev, dev_t devt); + + struct device { struct klist klist_children; --- gregkh-2.6.orig/drivers/base/class.c 2005-09-26 14:59:33.000000000 -0700 +++ gregkh-2.6/drivers/base/class.c 2005-09-26 16:12:20.000000000 -0700 @@ -455,6 +455,8 @@ 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->subdevs); + init_MUTEX(&class_dev->sem); } static char *make_class_name(struct class_device *class_dev) @@ -797,6 +799,7 @@ subsystem_init(&class_obj_subsys); if (!class_obj_subsys.kset.subsys) class_obj_subsys.kset.subsys = &class_obj_subsys; + subclass_init(); return 0; } --- gregkh-2.6.orig/drivers/usb/gregkh.c 2005-09-26 14:59:33.000000000 -0700 +++ gregkh-2.6/drivers/usb/gregkh.c 2005-09-26 16:12:20.000000000 -0700 @@ -208,17 +208,35 @@ static struct class *greg_class; + +ssize_t sg_show(struct subclass_device *sd, struct subclass_device_attribute *attr, char *buf) +{ + return sprintf(buf, "this is device %s\n", kobject_name(&sd->kobj)); +} +static SUBCLASS_DEVICE_ATTR(name, 0444, sg_show, NULL); + + + static void greg_class_init(void) { + struct class_device *g2; + struct subclass_device *sg; + greg_class = class_create(THIS_MODULE, "gregkh"); class_device_create(greg_class, MKDEV(42, 0), NULL, "greg1"); - class_device_create(greg_class, MKDEV(42, 1), NULL, "greg2"); + g2 = class_device_create(greg_class, MKDEV(42, 1), NULL, "greg2"); class_device_create(greg_class, MKDEV(42, 2), NULL, "greg3"); printk("GREG: create a dupe name\n"); class_device_create(greg_class, MKDEV(42, 3), NULL, "greg1"); printk("GREG: dup name created\n"); + + subclass_device_create(g2, MKDEV(42,4), NULL, "greg2.4"); + subclass_device_create(g2, 0, NULL, "greg2.%s", "null"); + sg = subclass_device_create(g2, MKDEV(42,5), NULL, "greg%d.%d", 2, 5); + subclass_device_create_file(sg, &class_device_attr_name); + } --- gregkh-2.6.orig/drivers/base/base.h 2005-09-26 14:59:33.000000000 -0700 +++ gregkh-2.6/drivers/base/base.h 2005-09-26 16:12:20.000000000 -0700 @@ -18,4 +18,4 @@ return container_of(_attr, struct class_device_attribute, attr); } - +extern void subclass_init(void);