|
@@ -16,6 +16,7 @@
|
|
|
#include <linux/init.h>
|
|
|
#include <linux/string.h>
|
|
|
#include <linux/kdev_t.h>
|
|
|
+#include <linux/err.h>
|
|
|
#include "base.h"
|
|
|
|
|
|
#define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr)
|
|
@@ -162,6 +163,51 @@ void class_unregister(struct class * cls)
|
|
|
subsystem_unregister(&cls->subsys);
|
|
|
}
|
|
|
|
|
|
+static void class_create_release(struct class *cls)
|
|
|
+{
|
|
|
+ kfree(cls);
|
|
|
+}
|
|
|
+
|
|
|
+static void class_device_create_release(struct class_device *class_dev)
|
|
|
+{
|
|
|
+ kfree(class_dev);
|
|
|
+}
|
|
|
+
|
|
|
+struct class *class_create(struct module *owner, char *name)
|
|
|
+{
|
|
|
+ struct class *cls;
|
|
|
+ int retval;
|
|
|
+
|
|
|
+ cls = kmalloc(sizeof(struct class), GFP_KERNEL);
|
|
|
+ if (!cls) {
|
|
|
+ retval = -ENOMEM;
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ memset(cls, 0x00, sizeof(struct class));
|
|
|
+
|
|
|
+ cls->name = name;
|
|
|
+ cls->owner = owner;
|
|
|
+ cls->class_release = class_create_release;
|
|
|
+ cls->release = class_device_create_release;
|
|
|
+
|
|
|
+ retval = class_register(cls);
|
|
|
+ if (retval)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ return cls;
|
|
|
+
|
|
|
+error:
|
|
|
+ kfree(cls);
|
|
|
+ return ERR_PTR(retval);
|
|
|
+}
|
|
|
+
|
|
|
+void class_destroy(struct class *cls)
|
|
|
+{
|
|
|
+ if ((cls == NULL) || (IS_ERR(cls)))
|
|
|
+ return;
|
|
|
+
|
|
|
+ class_unregister(cls);
|
|
|
+}
|
|
|
|
|
|
/* Class Device Stuff */
|
|
|
|
|
@@ -375,7 +421,6 @@ static ssize_t show_dev(struct class_device *class_dev, char *buf)
|
|
|
{
|
|
|
return print_dev_t(buf, class_dev->devt);
|
|
|
}
|
|
|
-static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL);
|
|
|
|
|
|
void class_device_initialize(struct class_device *class_dev)
|
|
|
{
|
|
@@ -412,7 +457,31 @@ int class_device_add(struct class_device *class_dev)
|
|
|
if ((error = kobject_add(&class_dev->kobj)))
|
|
|
goto register_done;
|
|
|
|
|
|
- /* now take care of our own registration */
|
|
|
+ /* add the needed attributes to this device */
|
|
|
+ if (MAJOR(class_dev->devt)) {
|
|
|
+ struct class_device_attribute *attr;
|
|
|
+ attr = kmalloc(sizeof(*attr), GFP_KERNEL);
|
|
|
+ if (!attr) {
|
|
|
+ error = -ENOMEM;
|
|
|
+ kobject_del(&class_dev->kobj);
|
|
|
+ goto register_done;
|
|
|
+ }
|
|
|
+ memset(attr, sizeof(*attr), 0x00);
|
|
|
+ attr->attr.name = "dev";
|
|
|
+ attr->attr.mode = S_IRUGO;
|
|
|
+ attr->attr.owner = parent->owner;
|
|
|
+ attr->show = show_dev;
|
|
|
+ attr->store = NULL;
|
|
|
+ class_device_create_file(class_dev, attr);
|
|
|
+ class_dev->devt_attr = attr;
|
|
|
+ }
|
|
|
+
|
|
|
+ class_device_add_attrs(class_dev);
|
|
|
+ if (class_dev->dev)
|
|
|
+ sysfs_create_link(&class_dev->kobj,
|
|
|
+ &class_dev->dev->kobj, "device");
|
|
|
+
|
|
|
+ /* notify any interfaces this device is now here */
|
|
|
if (parent) {
|
|
|
down(&parent->sem);
|
|
|
list_add_tail(&class_dev->node, &parent->children);
|
|
@@ -421,16 +490,8 @@ int class_device_add(struct class_device *class_dev)
|
|
|
class_intf->add(class_dev);
|
|
|
up(&parent->sem);
|
|
|
}
|
|
|
-
|
|
|
- if (MAJOR(class_dev->devt))
|
|
|
- class_device_create_file(class_dev, &class_device_attr_dev);
|
|
|
-
|
|
|
- class_device_add_attrs(class_dev);
|
|
|
- if (class_dev->dev)
|
|
|
- sysfs_create_link(&class_dev->kobj,
|
|
|
- &class_dev->dev->kobj, "device");
|
|
|
-
|
|
|
kobject_hotplug(&class_dev->kobj, KOBJ_ADD);
|
|
|
+
|
|
|
register_done:
|
|
|
if (error && parent)
|
|
|
class_put(parent);
|
|
@@ -444,6 +505,41 @@ int class_device_register(struct class_device *class_dev)
|
|
|
return class_device_add(class_dev);
|
|
|
}
|
|
|
|
|
|
+struct class_device *class_device_create(struct class *cls, dev_t devt,
|
|
|
+ struct device *device, char *fmt, ...)
|
|
|
+{
|
|
|
+ va_list args;
|
|
|
+ struct class_device *class_dev = NULL;
|
|
|
+ int retval = -ENODEV;
|
|
|
+
|
|
|
+ if (cls == NULL || IS_ERR(cls))
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ class_dev = kmalloc(sizeof(struct class_device), GFP_KERNEL);
|
|
|
+ if (!class_dev) {
|
|
|
+ retval = -ENOMEM;
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ memset(class_dev, 0x00, sizeof(struct class_device));
|
|
|
+
|
|
|
+ class_dev->devt = devt;
|
|
|
+ class_dev->dev = device;
|
|
|
+ class_dev->class = cls;
|
|
|
+
|
|
|
+ va_start(args, fmt);
|
|
|
+ vsnprintf(class_dev->class_id, BUS_ID_SIZE, fmt, args);
|
|
|
+ va_end(args);
|
|
|
+ retval = class_device_register(class_dev);
|
|
|
+ if (retval)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ return class_dev;
|
|
|
+
|
|
|
+error:
|
|
|
+ kfree(class_dev);
|
|
|
+ return ERR_PTR(retval);
|
|
|
+}
|
|
|
+
|
|
|
void class_device_del(struct class_device *class_dev)
|
|
|
{
|
|
|
struct class * parent = class_dev->class;
|
|
@@ -460,6 +556,11 @@ void class_device_del(struct class_device *class_dev)
|
|
|
|
|
|
if (class_dev->dev)
|
|
|
sysfs_remove_link(&class_dev->kobj, "device");
|
|
|
+ if (class_dev->devt_attr) {
|
|
|
+ class_device_remove_file(class_dev, class_dev->devt_attr);
|
|
|
+ kfree(class_dev->devt_attr);
|
|
|
+ class_dev->devt_attr = NULL;
|
|
|
+ }
|
|
|
class_device_remove_attrs(class_dev);
|
|
|
|
|
|
kobject_hotplug(&class_dev->kobj, KOBJ_REMOVE);
|
|
@@ -477,6 +578,24 @@ void class_device_unregister(struct class_device *class_dev)
|
|
|
class_device_put(class_dev);
|
|
|
}
|
|
|
|
|
|
+void class_device_destroy(struct class *cls, dev_t devt)
|
|
|
+{
|
|
|
+ struct class_device *class_dev = NULL;
|
|
|
+ struct class_device *class_dev_tmp;
|
|
|
+
|
|
|
+ down(&cls->sem);
|
|
|
+ list_for_each_entry(class_dev_tmp, &cls->children, node) {
|
|
|
+ if (class_dev_tmp->devt == devt) {
|
|
|
+ class_dev = class_dev_tmp;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ up(&cls->sem);
|
|
|
+
|
|
|
+ if (class_dev)
|
|
|
+ class_device_unregister(class_dev);
|
|
|
+}
|
|
|
+
|
|
|
int class_device_rename(struct class_device *class_dev, char *new_name)
|
|
|
{
|
|
|
int error = 0;
|
|
@@ -576,6 +695,8 @@ EXPORT_SYMBOL_GPL(class_register);
|
|
|
EXPORT_SYMBOL_GPL(class_unregister);
|
|
|
EXPORT_SYMBOL_GPL(class_get);
|
|
|
EXPORT_SYMBOL_GPL(class_put);
|
|
|
+EXPORT_SYMBOL_GPL(class_create);
|
|
|
+EXPORT_SYMBOL_GPL(class_destroy);
|
|
|
|
|
|
EXPORT_SYMBOL_GPL(class_device_register);
|
|
|
EXPORT_SYMBOL_GPL(class_device_unregister);
|
|
@@ -584,6 +705,8 @@ EXPORT_SYMBOL_GPL(class_device_add);
|
|
|
EXPORT_SYMBOL_GPL(class_device_del);
|
|
|
EXPORT_SYMBOL_GPL(class_device_get);
|
|
|
EXPORT_SYMBOL_GPL(class_device_put);
|
|
|
+EXPORT_SYMBOL_GPL(class_device_create);
|
|
|
+EXPORT_SYMBOL_GPL(class_device_destroy);
|
|
|
EXPORT_SYMBOL_GPL(class_device_create_file);
|
|
|
EXPORT_SYMBOL_GPL(class_device_remove_file);
|
|
|
EXPORT_SYMBOL_GPL(class_device_create_bin_file);
|