| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * class.c - basic device class management | 
|  | 3 | * | 
|  | 4 | * Copyright (c) 2002-3 Patrick Mochel | 
|  | 5 | * Copyright (c) 2002-3 Open Source Development Labs | 
|  | 6 | * Copyright (c) 2003-2004 Greg Kroah-Hartman | 
|  | 7 | * Copyright (c) 2003-2004 IBM Corp. | 
|  | 8 | * | 
|  | 9 | * This file is released under the GPLv2 | 
|  | 10 | * | 
|  | 11 | */ | 
|  | 12 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 13 | #include <linux/device.h> | 
|  | 14 | #include <linux/module.h> | 
|  | 15 | #include <linux/init.h> | 
|  | 16 | #include <linux/string.h> | 
|  | 17 | #include <linux/kdev_t.h> | 
| gregkh@suse.de | e9ba636 | 2005-03-15 11:54:21 -0800 | [diff] [blame] | 18 | #include <linux/err.h> | 
| Tim Schmielau | 4e57b68 | 2005-10-30 15:03:48 -0800 | [diff] [blame] | 19 | #include <linux/slab.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 20 | #include "base.h" | 
|  | 21 |  | 
| Greg Kroah-Hartman | c205ef4 | 2006-08-07 22:19:37 -0700 | [diff] [blame] | 22 | extern struct subsystem devices_subsys; | 
|  | 23 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 24 | #define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr) | 
|  | 25 | #define to_class(obj) container_of(obj, struct class, subsys.kset.kobj) | 
|  | 26 |  | 
|  | 27 | static ssize_t | 
|  | 28 | class_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) | 
|  | 29 | { | 
|  | 30 | struct class_attribute * class_attr = to_class_attr(attr); | 
|  | 31 | struct class * dc = to_class(kobj); | 
| Dmitry Torokhov | 4a0c20b | 2005-04-29 01:23:47 -0500 | [diff] [blame] | 32 | ssize_t ret = -EIO; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 33 |  | 
|  | 34 | if (class_attr->show) | 
|  | 35 | ret = class_attr->show(dc, buf); | 
|  | 36 | return ret; | 
|  | 37 | } | 
|  | 38 |  | 
|  | 39 | static ssize_t | 
|  | 40 | class_attr_store(struct kobject * kobj, struct attribute * attr, | 
|  | 41 | const char * buf, size_t count) | 
|  | 42 | { | 
|  | 43 | struct class_attribute * class_attr = to_class_attr(attr); | 
|  | 44 | struct class * dc = to_class(kobj); | 
| Dmitry Torokhov | 4a0c20b | 2005-04-29 01:23:47 -0500 | [diff] [blame] | 45 | ssize_t ret = -EIO; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 46 |  | 
|  | 47 | if (class_attr->store) | 
|  | 48 | ret = class_attr->store(dc, buf, count); | 
|  | 49 | return ret; | 
|  | 50 | } | 
|  | 51 |  | 
|  | 52 | static void class_release(struct kobject * kobj) | 
|  | 53 | { | 
|  | 54 | struct class *class = to_class(kobj); | 
|  | 55 |  | 
|  | 56 | pr_debug("class '%s': release.\n", class->name); | 
|  | 57 |  | 
|  | 58 | if (class->class_release) | 
|  | 59 | class->class_release(class); | 
|  | 60 | else | 
|  | 61 | pr_debug("class '%s' does not have a release() function, " | 
|  | 62 | "be careful\n", class->name); | 
|  | 63 | } | 
|  | 64 |  | 
|  | 65 | static struct sysfs_ops class_sysfs_ops = { | 
|  | 66 | .show	= class_attr_show, | 
|  | 67 | .store	= class_attr_store, | 
|  | 68 | }; | 
|  | 69 |  | 
|  | 70 | static struct kobj_type ktype_class = { | 
|  | 71 | .sysfs_ops	= &class_sysfs_ops, | 
|  | 72 | .release	= class_release, | 
|  | 73 | }; | 
|  | 74 |  | 
|  | 75 | /* Hotplug events for classes go to the class_obj subsys */ | 
|  | 76 | static decl_subsys(class, &ktype_class, NULL); | 
|  | 77 |  | 
|  | 78 |  | 
|  | 79 | int class_create_file(struct class * cls, const struct class_attribute * attr) | 
|  | 80 | { | 
|  | 81 | int error; | 
|  | 82 | if (cls) { | 
|  | 83 | error = sysfs_create_file(&cls->subsys.kset.kobj, &attr->attr); | 
|  | 84 | } else | 
|  | 85 | error = -EINVAL; | 
|  | 86 | return error; | 
|  | 87 | } | 
|  | 88 |  | 
|  | 89 | void class_remove_file(struct class * cls, const struct class_attribute * attr) | 
|  | 90 | { | 
|  | 91 | if (cls) | 
|  | 92 | sysfs_remove_file(&cls->subsys.kset.kobj, &attr->attr); | 
|  | 93 | } | 
|  | 94 |  | 
| Greg Kroah-Hartman | 1740757 | 2006-05-02 16:59:59 +0200 | [diff] [blame] | 95 | static struct class *class_get(struct class *cls) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 96 | { | 
|  | 97 | if (cls) | 
|  | 98 | return container_of(subsys_get(&cls->subsys), struct class, subsys); | 
|  | 99 | return NULL; | 
|  | 100 | } | 
|  | 101 |  | 
| Greg Kroah-Hartman | 1740757 | 2006-05-02 16:59:59 +0200 | [diff] [blame] | 102 | static void class_put(struct class * cls) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 103 | { | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 104 | if (cls) | 
|  | 105 | subsys_put(&cls->subsys); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 106 | } | 
|  | 107 |  | 
|  | 108 |  | 
|  | 109 | static int add_class_attrs(struct class * cls) | 
|  | 110 | { | 
|  | 111 | int i; | 
|  | 112 | int error = 0; | 
|  | 113 |  | 
|  | 114 | if (cls->class_attrs) { | 
|  | 115 | for (i = 0; attr_name(cls->class_attrs[i]); i++) { | 
|  | 116 | error = class_create_file(cls,&cls->class_attrs[i]); | 
|  | 117 | if (error) | 
|  | 118 | goto Err; | 
|  | 119 | } | 
|  | 120 | } | 
|  | 121 | Done: | 
|  | 122 | return error; | 
|  | 123 | Err: | 
|  | 124 | while (--i >= 0) | 
|  | 125 | class_remove_file(cls,&cls->class_attrs[i]); | 
|  | 126 | goto Done; | 
|  | 127 | } | 
|  | 128 |  | 
|  | 129 | static void remove_class_attrs(struct class * cls) | 
|  | 130 | { | 
|  | 131 | int i; | 
|  | 132 |  | 
|  | 133 | if (cls->class_attrs) { | 
|  | 134 | for (i = 0; attr_name(cls->class_attrs[i]); i++) | 
|  | 135 | class_remove_file(cls,&cls->class_attrs[i]); | 
|  | 136 | } | 
|  | 137 | } | 
|  | 138 |  | 
|  | 139 | int class_register(struct class * cls) | 
|  | 140 | { | 
|  | 141 | int error; | 
|  | 142 |  | 
|  | 143 | pr_debug("device class '%s': registering\n", cls->name); | 
|  | 144 |  | 
|  | 145 | INIT_LIST_HEAD(&cls->children); | 
| Greg Kroah-Hartman | 23681e4 | 2006-06-14 12:14:34 -0700 | [diff] [blame] | 146 | INIT_LIST_HEAD(&cls->devices); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 147 | INIT_LIST_HEAD(&cls->interfaces); | 
|  | 148 | init_MUTEX(&cls->sem); | 
|  | 149 | error = kobject_set_name(&cls->subsys.kset.kobj, "%s", cls->name); | 
|  | 150 | if (error) | 
|  | 151 | return error; | 
|  | 152 |  | 
|  | 153 | subsys_set_kset(cls, class_subsys); | 
|  | 154 |  | 
|  | 155 | error = subsystem_register(&cls->subsys); | 
|  | 156 | if (!error) { | 
|  | 157 | error = add_class_attrs(class_get(cls)); | 
|  | 158 | class_put(cls); | 
|  | 159 | } | 
|  | 160 | return error; | 
|  | 161 | } | 
|  | 162 |  | 
|  | 163 | void class_unregister(struct class * cls) | 
|  | 164 | { | 
|  | 165 | pr_debug("device class '%s': unregistering\n", cls->name); | 
| Mariusz Kozlowski | b92be9f | 2007-02-14 21:03:39 +0100 | [diff] [blame] | 166 | kobject_unregister(cls->virtual_dir); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 167 | remove_class_attrs(cls); | 
|  | 168 | subsystem_unregister(&cls->subsys); | 
|  | 169 | } | 
|  | 170 |  | 
| gregkh@suse.de | e9ba636 | 2005-03-15 11:54:21 -0800 | [diff] [blame] | 171 | static void class_create_release(struct class *cls) | 
|  | 172 | { | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 173 | pr_debug("%s called for %s\n", __FUNCTION__, cls->name); | 
| gregkh@suse.de | e9ba636 | 2005-03-15 11:54:21 -0800 | [diff] [blame] | 174 | kfree(cls); | 
|  | 175 | } | 
|  | 176 |  | 
|  | 177 | static void class_device_create_release(struct class_device *class_dev) | 
|  | 178 | { | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 179 | pr_debug("%s called for %s\n", __FUNCTION__, class_dev->class_id); | 
| gregkh@suse.de | e9ba636 | 2005-03-15 11:54:21 -0800 | [diff] [blame] | 180 | kfree(class_dev); | 
|  | 181 | } | 
|  | 182 |  | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 183 | /* needed to allow these devices to have parent class devices */ | 
| Kay Sievers | 312c004 | 2005-11-16 09:00:00 +0100 | [diff] [blame] | 184 | static int class_device_create_uevent(struct class_device *class_dev, | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 185 | char **envp, int num_envp, | 
|  | 186 | char *buffer, int buffer_size) | 
|  | 187 | { | 
|  | 188 | pr_debug("%s called for %s\n", __FUNCTION__, class_dev->class_id); | 
|  | 189 | return 0; | 
|  | 190 | } | 
|  | 191 |  | 
| gregkh@suse.de | 2fc6844 | 2005-03-23 10:02:56 -0800 | [diff] [blame] | 192 | /** | 
|  | 193 | * class_create - create a struct class structure | 
|  | 194 | * @owner: pointer to the module that is to "own" this struct class | 
|  | 195 | * @name: pointer to a string for the name of this class. | 
|  | 196 | * | 
|  | 197 | * This is used to create a struct class pointer that can then be used | 
|  | 198 | * in calls to class_device_create(). | 
|  | 199 | * | 
|  | 200 | * Note, the pointer created here is to be destroyed when finished by | 
|  | 201 | * making a call to class_destroy(). | 
|  | 202 | */ | 
| Miguel Ojeda Sandonis | ab7d737 | 2006-09-13 15:34:05 +0200 | [diff] [blame] | 203 | struct class *class_create(struct module *owner, const char *name) | 
| gregkh@suse.de | e9ba636 | 2005-03-15 11:54:21 -0800 | [diff] [blame] | 204 | { | 
|  | 205 | struct class *cls; | 
|  | 206 | int retval; | 
|  | 207 |  | 
| Jiri Slaby | 4aed064 | 2005-09-13 01:25:01 -0700 | [diff] [blame] | 208 | cls = kzalloc(sizeof(*cls), GFP_KERNEL); | 
| gregkh@suse.de | e9ba636 | 2005-03-15 11:54:21 -0800 | [diff] [blame] | 209 | if (!cls) { | 
|  | 210 | retval = -ENOMEM; | 
|  | 211 | goto error; | 
|  | 212 | } | 
| gregkh@suse.de | e9ba636 | 2005-03-15 11:54:21 -0800 | [diff] [blame] | 213 |  | 
|  | 214 | cls->name = name; | 
|  | 215 | cls->owner = owner; | 
|  | 216 | cls->class_release = class_create_release; | 
|  | 217 | cls->release = class_device_create_release; | 
|  | 218 |  | 
|  | 219 | retval = class_register(cls); | 
|  | 220 | if (retval) | 
|  | 221 | goto error; | 
|  | 222 |  | 
|  | 223 | return cls; | 
|  | 224 |  | 
|  | 225 | error: | 
|  | 226 | kfree(cls); | 
|  | 227 | return ERR_PTR(retval); | 
|  | 228 | } | 
|  | 229 |  | 
| gregkh@suse.de | 2fc6844 | 2005-03-23 10:02:56 -0800 | [diff] [blame] | 230 | /** | 
|  | 231 | * class_destroy - destroys a struct class structure | 
| Rolf Eike Beer | 92a0f86 | 2006-09-29 01:59:13 -0700 | [diff] [blame] | 232 | * @cls: pointer to the struct class that is to be destroyed | 
| gregkh@suse.de | 2fc6844 | 2005-03-23 10:02:56 -0800 | [diff] [blame] | 233 | * | 
|  | 234 | * Note, the pointer to be destroyed must have been created with a call | 
|  | 235 | * to class_create(). | 
|  | 236 | */ | 
| gregkh@suse.de | e9ba636 | 2005-03-15 11:54:21 -0800 | [diff] [blame] | 237 | void class_destroy(struct class *cls) | 
|  | 238 | { | 
|  | 239 | if ((cls == NULL) || (IS_ERR(cls))) | 
|  | 240 | return; | 
|  | 241 |  | 
|  | 242 | class_unregister(cls); | 
|  | 243 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 244 |  | 
|  | 245 | /* Class Device Stuff */ | 
|  | 246 |  | 
|  | 247 | int class_device_create_file(struct class_device * class_dev, | 
|  | 248 | const struct class_device_attribute * attr) | 
|  | 249 | { | 
|  | 250 | int error = -EINVAL; | 
|  | 251 | if (class_dev) | 
|  | 252 | error = sysfs_create_file(&class_dev->kobj, &attr->attr); | 
|  | 253 | return error; | 
|  | 254 | } | 
|  | 255 |  | 
|  | 256 | void class_device_remove_file(struct class_device * class_dev, | 
|  | 257 | const struct class_device_attribute * attr) | 
|  | 258 | { | 
|  | 259 | if (class_dev) | 
|  | 260 | sysfs_remove_file(&class_dev->kobj, &attr->attr); | 
|  | 261 | } | 
|  | 262 |  | 
|  | 263 | int class_device_create_bin_file(struct class_device *class_dev, | 
|  | 264 | struct bin_attribute *attr) | 
|  | 265 | { | 
|  | 266 | int error = -EINVAL; | 
|  | 267 | if (class_dev) | 
|  | 268 | error = sysfs_create_bin_file(&class_dev->kobj, attr); | 
|  | 269 | return error; | 
|  | 270 | } | 
|  | 271 |  | 
|  | 272 | void class_device_remove_bin_file(struct class_device *class_dev, | 
|  | 273 | struct bin_attribute *attr) | 
|  | 274 | { | 
|  | 275 | if (class_dev) | 
|  | 276 | sysfs_remove_bin_file(&class_dev->kobj, attr); | 
|  | 277 | } | 
|  | 278 |  | 
|  | 279 | static ssize_t | 
|  | 280 | class_device_attr_show(struct kobject * kobj, struct attribute * attr, | 
|  | 281 | char * buf) | 
|  | 282 | { | 
|  | 283 | struct class_device_attribute * class_dev_attr = to_class_dev_attr(attr); | 
|  | 284 | struct class_device * cd = to_class_dev(kobj); | 
|  | 285 | ssize_t ret = 0; | 
|  | 286 |  | 
|  | 287 | if (class_dev_attr->show) | 
|  | 288 | ret = class_dev_attr->show(cd, buf); | 
|  | 289 | return ret; | 
|  | 290 | } | 
|  | 291 |  | 
|  | 292 | static ssize_t | 
|  | 293 | class_device_attr_store(struct kobject * kobj, struct attribute * attr, | 
|  | 294 | const char * buf, size_t count) | 
|  | 295 | { | 
|  | 296 | struct class_device_attribute * class_dev_attr = to_class_dev_attr(attr); | 
|  | 297 | struct class_device * cd = to_class_dev(kobj); | 
|  | 298 | ssize_t ret = 0; | 
|  | 299 |  | 
|  | 300 | if (class_dev_attr->store) | 
|  | 301 | ret = class_dev_attr->store(cd, buf, count); | 
|  | 302 | return ret; | 
|  | 303 | } | 
|  | 304 |  | 
|  | 305 | static struct sysfs_ops class_dev_sysfs_ops = { | 
|  | 306 | .show	= class_device_attr_show, | 
|  | 307 | .store	= class_device_attr_store, | 
|  | 308 | }; | 
|  | 309 |  | 
|  | 310 | static void class_dev_release(struct kobject * kobj) | 
|  | 311 | { | 
|  | 312 | struct class_device *cd = to_class_dev(kobj); | 
|  | 313 | struct class * cls = cd->class; | 
|  | 314 |  | 
|  | 315 | pr_debug("device class '%s': release.\n", cd->class_id); | 
|  | 316 |  | 
| Jesper Juhl | fef6ec8 | 2005-08-17 22:06:34 +0200 | [diff] [blame] | 317 | kfree(cd->devt_attr); | 
|  | 318 | cd->devt_attr = NULL; | 
| Maneesh Soni | 208f3d6 | 2005-08-16 15:15:48 -0700 | [diff] [blame] | 319 |  | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 320 | if (cd->release) | 
|  | 321 | cd->release(cd); | 
|  | 322 | else if (cls->release) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 323 | cls->release(cd); | 
|  | 324 | else { | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 325 | printk(KERN_ERR "Class Device '%s' does not have a release() function, " | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 326 | "it is broken and must be fixed.\n", | 
|  | 327 | cd->class_id); | 
|  | 328 | WARN_ON(1); | 
|  | 329 | } | 
|  | 330 | } | 
|  | 331 |  | 
|  | 332 | static struct kobj_type ktype_class_device = { | 
|  | 333 | .sysfs_ops	= &class_dev_sysfs_ops, | 
|  | 334 | .release	= class_dev_release, | 
|  | 335 | }; | 
|  | 336 |  | 
| Kay Sievers | 312c004 | 2005-11-16 09:00:00 +0100 | [diff] [blame] | 337 | static int class_uevent_filter(struct kset *kset, struct kobject *kobj) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 338 | { | 
|  | 339 | struct kobj_type *ktype = get_ktype(kobj); | 
|  | 340 |  | 
|  | 341 | if (ktype == &ktype_class_device) { | 
|  | 342 | struct class_device *class_dev = to_class_dev(kobj); | 
|  | 343 | if (class_dev->class) | 
|  | 344 | return 1; | 
|  | 345 | } | 
|  | 346 | return 0; | 
|  | 347 | } | 
|  | 348 |  | 
| Kay Sievers | 312c004 | 2005-11-16 09:00:00 +0100 | [diff] [blame] | 349 | static const char *class_uevent_name(struct kset *kset, struct kobject *kobj) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 350 | { | 
|  | 351 | struct class_device *class_dev = to_class_dev(kobj); | 
|  | 352 |  | 
|  | 353 | return class_dev->class->name; | 
|  | 354 | } | 
|  | 355 |  | 
| Kay Sievers | 805fab4 | 2006-09-14 11:23:28 +0200 | [diff] [blame] | 356 | #ifdef CONFIG_SYSFS_DEPRECATED | 
|  | 357 | char *make_class_name(const char *name, struct kobject *kobj) | 
|  | 358 | { | 
|  | 359 | char *class_name; | 
|  | 360 | int size; | 
|  | 361 |  | 
|  | 362 | size = strlen(name) + strlen(kobject_name(kobj)) + 2; | 
|  | 363 |  | 
|  | 364 | class_name = kmalloc(size, GFP_KERNEL); | 
|  | 365 | if (!class_name) | 
| Cornelia Huck | cb360bb | 2006-11-27 10:35:05 +0100 | [diff] [blame] | 366 | return NULL; | 
| Kay Sievers | 805fab4 | 2006-09-14 11:23:28 +0200 | [diff] [blame] | 367 |  | 
|  | 368 | strcpy(class_name, name); | 
|  | 369 | strcat(class_name, ":"); | 
|  | 370 | strcat(class_name, kobject_name(kobj)); | 
|  | 371 | return class_name; | 
|  | 372 | } | 
|  | 373 |  | 
|  | 374 | static int deprecated_class_uevent(char **envp, int num_envp, int *cur_index, | 
|  | 375 | char *buffer, int buffer_size, | 
|  | 376 | int *cur_len, | 
|  | 377 | struct class_device *class_dev) | 
|  | 378 | { | 
|  | 379 | struct device *dev = class_dev->dev; | 
|  | 380 | char *path; | 
|  | 381 |  | 
|  | 382 | if (!dev) | 
|  | 383 | return 0; | 
|  | 384 |  | 
|  | 385 | /* add device, backing this class device (deprecated) */ | 
|  | 386 | path = kobject_get_path(&dev->kobj, GFP_KERNEL); | 
|  | 387 |  | 
|  | 388 | add_uevent_var(envp, num_envp, cur_index, buffer, buffer_size, | 
|  | 389 | cur_len, "PHYSDEVPATH=%s", path); | 
|  | 390 | kfree(path); | 
|  | 391 |  | 
|  | 392 | if (dev->bus) | 
|  | 393 | add_uevent_var(envp, num_envp, cur_index, | 
|  | 394 | buffer, buffer_size, cur_len, | 
|  | 395 | "PHYSDEVBUS=%s", dev->bus->name); | 
|  | 396 |  | 
|  | 397 | if (dev->driver) | 
|  | 398 | add_uevent_var(envp, num_envp, cur_index, | 
|  | 399 | buffer, buffer_size, cur_len, | 
|  | 400 | "PHYSDEVDRIVER=%s", dev->driver->name); | 
|  | 401 | return 0; | 
|  | 402 | } | 
|  | 403 |  | 
|  | 404 | static int make_deprecated_class_device_links(struct class_device *class_dev) | 
|  | 405 | { | 
|  | 406 | char *class_name; | 
|  | 407 | int error; | 
|  | 408 |  | 
|  | 409 | if (!class_dev->dev) | 
|  | 410 | return 0; | 
|  | 411 |  | 
|  | 412 | class_name = make_class_name(class_dev->class->name, &class_dev->kobj); | 
| Cornelia Huck | cb360bb | 2006-11-27 10:35:05 +0100 | [diff] [blame] | 413 | if (class_name) | 
|  | 414 | error = sysfs_create_link(&class_dev->dev->kobj, | 
|  | 415 | &class_dev->kobj, class_name); | 
|  | 416 | else | 
|  | 417 | error = -ENOMEM; | 
| Kay Sievers | 805fab4 | 2006-09-14 11:23:28 +0200 | [diff] [blame] | 418 | kfree(class_name); | 
|  | 419 | return error; | 
|  | 420 | } | 
|  | 421 |  | 
|  | 422 | static void remove_deprecated_class_device_links(struct class_device *class_dev) | 
|  | 423 | { | 
|  | 424 | char *class_name; | 
|  | 425 |  | 
|  | 426 | if (!class_dev->dev) | 
|  | 427 | return; | 
|  | 428 |  | 
|  | 429 | class_name = make_class_name(class_dev->class->name, &class_dev->kobj); | 
| Cornelia Huck | cb360bb | 2006-11-27 10:35:05 +0100 | [diff] [blame] | 430 | if (class_name) | 
|  | 431 | sysfs_remove_link(&class_dev->dev->kobj, class_name); | 
| Kay Sievers | 805fab4 | 2006-09-14 11:23:28 +0200 | [diff] [blame] | 432 | kfree(class_name); | 
|  | 433 | } | 
|  | 434 | #else | 
|  | 435 | static inline int deprecated_class_uevent(char **envp, int num_envp, | 
|  | 436 | int *cur_index, char *buffer, | 
|  | 437 | int buffer_size, int *cur_len, | 
|  | 438 | struct class_device *class_dev) | 
|  | 439 | { return 0; } | 
|  | 440 | static inline int make_deprecated_class_device_links(struct class_device *cd) | 
|  | 441 | { return 0; } | 
|  | 442 | static void remove_deprecated_class_device_links(struct class_device *cd) | 
|  | 443 | { } | 
|  | 444 | #endif | 
|  | 445 |  | 
| Kay Sievers | 312c004 | 2005-11-16 09:00:00 +0100 | [diff] [blame] | 446 | static int class_uevent(struct kset *kset, struct kobject *kobj, char **envp, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 447 | int num_envp, char *buffer, int buffer_size) | 
|  | 448 | { | 
|  | 449 | struct class_device *class_dev = to_class_dev(kobj); | 
|  | 450 | int i = 0; | 
|  | 451 | int length = 0; | 
|  | 452 | int retval = 0; | 
|  | 453 |  | 
|  | 454 | pr_debug("%s - name = %s\n", __FUNCTION__, class_dev->class_id); | 
|  | 455 |  | 
| Kay Sievers | 805fab4 | 2006-09-14 11:23:28 +0200 | [diff] [blame] | 456 | deprecated_class_uevent(envp, num_envp, &i, buffer, buffer_size, | 
|  | 457 | &length, class_dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 458 |  | 
|  | 459 | if (MAJOR(class_dev->devt)) { | 
| Kay Sievers | 312c004 | 2005-11-16 09:00:00 +0100 | [diff] [blame] | 460 | add_uevent_var(envp, num_envp, &i, | 
|  | 461 | buffer, buffer_size, &length, | 
|  | 462 | "MAJOR=%u", MAJOR(class_dev->devt)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 463 |  | 
| Kay Sievers | 312c004 | 2005-11-16 09:00:00 +0100 | [diff] [blame] | 464 | add_uevent_var(envp, num_envp, &i, | 
|  | 465 | buffer, buffer_size, &length, | 
|  | 466 | "MINOR=%u", MINOR(class_dev->devt)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 467 | } | 
|  | 468 |  | 
|  | 469 | /* terminate, set to next free slot, shrink available space */ | 
|  | 470 | envp[i] = NULL; | 
|  | 471 | envp = &envp[i]; | 
|  | 472 | num_envp -= i; | 
|  | 473 | buffer = &buffer[length]; | 
|  | 474 | buffer_size -= length; | 
|  | 475 |  | 
| Kay Sievers | 312c004 | 2005-11-16 09:00:00 +0100 | [diff] [blame] | 476 | if (class_dev->uevent) { | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 477 | /* have the class device specific function add its stuff */ | 
| Kay Sievers | 312c004 | 2005-11-16 09:00:00 +0100 | [diff] [blame] | 478 | retval = class_dev->uevent(class_dev, envp, num_envp, | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 479 | buffer, buffer_size); | 
|  | 480 | if (retval) | 
| Kay Sievers | 312c004 | 2005-11-16 09:00:00 +0100 | [diff] [blame] | 481 | pr_debug("class_dev->uevent() returned %d\n", retval); | 
|  | 482 | } else if (class_dev->class->uevent) { | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 483 | /* have the class specific function add its stuff */ | 
| Kay Sievers | 312c004 | 2005-11-16 09:00:00 +0100 | [diff] [blame] | 484 | retval = class_dev->class->uevent(class_dev, envp, num_envp, | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 485 | buffer, buffer_size); | 
|  | 486 | if (retval) | 
| Kay Sievers | 312c004 | 2005-11-16 09:00:00 +0100 | [diff] [blame] | 487 | pr_debug("class->uevent() returned %d\n", retval); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 488 | } | 
|  | 489 |  | 
|  | 490 | return retval; | 
|  | 491 | } | 
|  | 492 |  | 
| Kay Sievers | 312c004 | 2005-11-16 09:00:00 +0100 | [diff] [blame] | 493 | static struct kset_uevent_ops class_uevent_ops = { | 
|  | 494 | .filter =	class_uevent_filter, | 
|  | 495 | .name =		class_uevent_name, | 
|  | 496 | .uevent =	class_uevent, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 497 | }; | 
|  | 498 |  | 
| Kay Sievers | 312c004 | 2005-11-16 09:00:00 +0100 | [diff] [blame] | 499 | static decl_subsys(class_obj, &ktype_class_device, &class_uevent_ops); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 500 |  | 
|  | 501 |  | 
|  | 502 | static int class_device_add_attrs(struct class_device * cd) | 
|  | 503 | { | 
|  | 504 | int i; | 
|  | 505 | int error = 0; | 
|  | 506 | struct class * cls = cd->class; | 
|  | 507 |  | 
|  | 508 | if (cls->class_dev_attrs) { | 
|  | 509 | for (i = 0; attr_name(cls->class_dev_attrs[i]); i++) { | 
|  | 510 | error = class_device_create_file(cd, | 
|  | 511 | &cls->class_dev_attrs[i]); | 
|  | 512 | if (error) | 
|  | 513 | goto Err; | 
|  | 514 | } | 
|  | 515 | } | 
|  | 516 | Done: | 
|  | 517 | return error; | 
|  | 518 | Err: | 
|  | 519 | while (--i >= 0) | 
|  | 520 | class_device_remove_file(cd,&cls->class_dev_attrs[i]); | 
|  | 521 | goto Done; | 
|  | 522 | } | 
|  | 523 |  | 
|  | 524 | static void class_device_remove_attrs(struct class_device * cd) | 
|  | 525 | { | 
|  | 526 | int i; | 
|  | 527 | struct class * cls = cd->class; | 
|  | 528 |  | 
|  | 529 | if (cls->class_dev_attrs) { | 
|  | 530 | for (i = 0; attr_name(cls->class_dev_attrs[i]); i++) | 
|  | 531 | class_device_remove_file(cd,&cls->class_dev_attrs[i]); | 
|  | 532 | } | 
|  | 533 | } | 
|  | 534 |  | 
| Stephen Hemminger | 1498221 | 2006-05-06 17:55:11 -0700 | [diff] [blame] | 535 | static int class_device_add_groups(struct class_device * cd) | 
|  | 536 | { | 
|  | 537 | int i; | 
|  | 538 | int error = 0; | 
|  | 539 |  | 
|  | 540 | if (cd->groups) { | 
|  | 541 | for (i = 0; cd->groups[i]; i++) { | 
|  | 542 | error = sysfs_create_group(&cd->kobj, cd->groups[i]); | 
|  | 543 | if (error) { | 
|  | 544 | while (--i >= 0) | 
|  | 545 | sysfs_remove_group(&cd->kobj, cd->groups[i]); | 
|  | 546 | goto out; | 
|  | 547 | } | 
|  | 548 | } | 
|  | 549 | } | 
|  | 550 | out: | 
|  | 551 | return error; | 
|  | 552 | } | 
|  | 553 |  | 
|  | 554 | static void class_device_remove_groups(struct class_device * cd) | 
|  | 555 | { | 
|  | 556 | int i; | 
|  | 557 | if (cd->groups) { | 
|  | 558 | for (i = 0; cd->groups[i]; i++) { | 
|  | 559 | sysfs_remove_group(&cd->kobj, cd->groups[i]); | 
|  | 560 | } | 
|  | 561 | } | 
|  | 562 | } | 
|  | 563 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 564 | static ssize_t show_dev(struct class_device *class_dev, char *buf) | 
|  | 565 | { | 
|  | 566 | return print_dev_t(buf, class_dev->devt); | 
|  | 567 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 568 |  | 
| Kay Sievers | a7fd670 | 2005-10-01 14:49:43 +0200 | [diff] [blame] | 569 | static ssize_t store_uevent(struct class_device *class_dev, | 
|  | 570 | const char *buf, size_t count) | 
|  | 571 | { | 
| Kay Sievers | 312c004 | 2005-11-16 09:00:00 +0100 | [diff] [blame] | 572 | kobject_uevent(&class_dev->kobj, KOBJ_ADD); | 
| Kay Sievers | a7fd670 | 2005-10-01 14:49:43 +0200 | [diff] [blame] | 573 | return count; | 
|  | 574 | } | 
|  | 575 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 576 | void class_device_initialize(struct class_device *class_dev) | 
|  | 577 | { | 
|  | 578 | kobj_set_kset_s(class_dev, class_obj_subsys); | 
|  | 579 | kobject_init(&class_dev->kobj); | 
|  | 580 | INIT_LIST_HEAD(&class_dev->node); | 
|  | 581 | } | 
|  | 582 |  | 
|  | 583 | int class_device_add(struct class_device *class_dev) | 
|  | 584 | { | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 585 | struct class *parent_class = NULL; | 
|  | 586 | struct class_device *parent_class_dev = NULL; | 
|  | 587 | struct class_interface *class_intf; | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 588 | int error = -EINVAL; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 589 |  | 
|  | 590 | class_dev = class_device_get(class_dev); | 
|  | 591 | if (!class_dev) | 
|  | 592 | return -EINVAL; | 
|  | 593 |  | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 594 | if (!strlen(class_dev->class_id)) | 
| Stephen Hemminger | b7fe4a6 | 2006-04-26 09:53:14 -0700 | [diff] [blame] | 595 | goto out1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 596 |  | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 597 | parent_class = class_get(class_dev->class); | 
|  | 598 | if (!parent_class) | 
| Stephen Hemminger | b7fe4a6 | 2006-04-26 09:53:14 -0700 | [diff] [blame] | 599 | goto out1; | 
|  | 600 |  | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 601 | parent_class_dev = class_device_get(class_dev->parent); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 602 |  | 
|  | 603 | pr_debug("CLASS: registering class device: ID = '%s'\n", | 
|  | 604 | class_dev->class_id); | 
|  | 605 |  | 
|  | 606 | /* first, register with generic layer. */ | 
| Stephen Hemminger | b7fe4a6 | 2006-04-26 09:53:14 -0700 | [diff] [blame] | 607 | error = kobject_set_name(&class_dev->kobj, "%s", class_dev->class_id); | 
|  | 608 | if (error) | 
|  | 609 | goto out2; | 
|  | 610 |  | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 611 | if (parent_class_dev) | 
|  | 612 | class_dev->kobj.parent = &parent_class_dev->kobj; | 
|  | 613 | else | 
|  | 614 | class_dev->kobj.parent = &parent_class->subsys.kset.kobj; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 615 |  | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 616 | error = kobject_add(&class_dev->kobj); | 
|  | 617 | if (error) | 
| Stephen Hemminger | b7fe4a6 | 2006-04-26 09:53:14 -0700 | [diff] [blame] | 618 | goto out2; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 619 |  | 
| gregkh@suse.de | e9ba636 | 2005-03-15 11:54:21 -0800 | [diff] [blame] | 620 | /* add the needed attributes to this device */ | 
| Cornelia Huck | f0e1761 | 2006-09-22 11:37:00 +0200 | [diff] [blame] | 621 | error = sysfs_create_link(&class_dev->kobj, | 
|  | 622 | &parent_class->subsys.kset.kobj, "subsystem"); | 
|  | 623 | if (error) | 
|  | 624 | goto out3; | 
| Kay Sievers | a7fd670 | 2005-10-01 14:49:43 +0200 | [diff] [blame] | 625 | class_dev->uevent_attr.attr.name = "uevent"; | 
|  | 626 | class_dev->uevent_attr.attr.mode = S_IWUSR; | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 627 | class_dev->uevent_attr.attr.owner = parent_class->owner; | 
| Kay Sievers | a7fd670 | 2005-10-01 14:49:43 +0200 | [diff] [blame] | 628 | class_dev->uevent_attr.store = store_uevent; | 
| Stephen Hemminger | b7fe4a6 | 2006-04-26 09:53:14 -0700 | [diff] [blame] | 629 | error = class_device_create_file(class_dev, &class_dev->uevent_attr); | 
|  | 630 | if (error) | 
|  | 631 | goto out3; | 
| Kay Sievers | a7fd670 | 2005-10-01 14:49:43 +0200 | [diff] [blame] | 632 |  | 
| gregkh@suse.de | e9ba636 | 2005-03-15 11:54:21 -0800 | [diff] [blame] | 633 | if (MAJOR(class_dev->devt)) { | 
|  | 634 | struct class_device_attribute *attr; | 
| Jiri Slaby | 4aed064 | 2005-09-13 01:25:01 -0700 | [diff] [blame] | 635 | attr = kzalloc(sizeof(*attr), GFP_KERNEL); | 
| gregkh@suse.de | e9ba636 | 2005-03-15 11:54:21 -0800 | [diff] [blame] | 636 | if (!attr) { | 
|  | 637 | error = -ENOMEM; | 
| Stephen Hemminger | b7fe4a6 | 2006-04-26 09:53:14 -0700 | [diff] [blame] | 638 | goto out4; | 
| gregkh@suse.de | e9ba636 | 2005-03-15 11:54:21 -0800 | [diff] [blame] | 639 | } | 
| gregkh@suse.de | e9ba636 | 2005-03-15 11:54:21 -0800 | [diff] [blame] | 640 | attr->attr.name = "dev"; | 
|  | 641 | attr->attr.mode = S_IRUGO; | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 642 | attr->attr.owner = parent_class->owner; | 
| gregkh@suse.de | e9ba636 | 2005-03-15 11:54:21 -0800 | [diff] [blame] | 643 | attr->show = show_dev; | 
| Stephen Hemminger | b7fe4a6 | 2006-04-26 09:53:14 -0700 | [diff] [blame] | 644 | error = class_device_create_file(class_dev, attr); | 
|  | 645 | if (error) { | 
|  | 646 | kfree(attr); | 
|  | 647 | goto out4; | 
|  | 648 | } | 
|  | 649 |  | 
| gregkh@suse.de | e9ba636 | 2005-03-15 11:54:21 -0800 | [diff] [blame] | 650 | class_dev->devt_attr = attr; | 
|  | 651 | } | 
|  | 652 |  | 
| Stephen Hemminger | b7fe4a6 | 2006-04-26 09:53:14 -0700 | [diff] [blame] | 653 | error = class_device_add_attrs(class_dev); | 
|  | 654 | if (error) | 
|  | 655 | goto out5; | 
|  | 656 |  | 
| Dmitry Torokhov | 76d1ce0 | 2005-07-10 01:21:24 -0500 | [diff] [blame] | 657 | if (class_dev->dev) { | 
| Stephen Hemminger | b7fe4a6 | 2006-04-26 09:53:14 -0700 | [diff] [blame] | 658 | error = sysfs_create_link(&class_dev->kobj, | 
|  | 659 | &class_dev->dev->kobj, "device"); | 
|  | 660 | if (error) | 
|  | 661 | goto out6; | 
| Dmitry Torokhov | 76d1ce0 | 2005-07-10 01:21:24 -0500 | [diff] [blame] | 662 | } | 
| gregkh@suse.de | e9ba636 | 2005-03-15 11:54:21 -0800 | [diff] [blame] | 663 |  | 
| Stephen Hemminger | b7fe4a6 | 2006-04-26 09:53:14 -0700 | [diff] [blame] | 664 | error = class_device_add_groups(class_dev); | 
|  | 665 | if (error) | 
| Kay Sievers | 805fab4 | 2006-09-14 11:23:28 +0200 | [diff] [blame] | 666 | goto out7; | 
|  | 667 |  | 
|  | 668 | error = make_deprecated_class_device_links(class_dev); | 
|  | 669 | if (error) | 
| Stephen Hemminger | b7fe4a6 | 2006-04-26 09:53:14 -0700 | [diff] [blame] | 670 | goto out8; | 
| Stephen Hemminger | 1498221 | 2006-05-06 17:55:11 -0700 | [diff] [blame] | 671 |  | 
| Kay Sievers | 312c004 | 2005-11-16 09:00:00 +0100 | [diff] [blame] | 672 | kobject_uevent(&class_dev->kobj, KOBJ_ADD); | 
| Dmitry Torokhov | dbe9035 | 2005-09-15 02:01:37 -0500 | [diff] [blame] | 673 |  | 
| gregkh@suse.de | e9ba636 | 2005-03-15 11:54:21 -0800 | [diff] [blame] | 674 | /* notify any interfaces this device is now here */ | 
| Jayachandran C | a143889 | 2006-04-03 12:31:53 -0700 | [diff] [blame] | 675 | down(&parent_class->sem); | 
|  | 676 | list_add_tail(&class_dev->node, &parent_class->children); | 
|  | 677 | list_for_each_entry(class_intf, &parent_class->interfaces, node) { | 
|  | 678 | if (class_intf->add) | 
|  | 679 | class_intf->add(class_dev, class_intf); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 680 | } | 
| Jayachandran C | a143889 | 2006-04-03 12:31:53 -0700 | [diff] [blame] | 681 | up(&parent_class->sem); | 
| gregkh@suse.de | e9ba636 | 2005-03-15 11:54:21 -0800 | [diff] [blame] | 682 |  | 
| Stephen Hemminger | b7fe4a6 | 2006-04-26 09:53:14 -0700 | [diff] [blame] | 683 | goto out1; | 
|  | 684 |  | 
|  | 685 | out8: | 
| Kay Sievers | 805fab4 | 2006-09-14 11:23:28 +0200 | [diff] [blame] | 686 | class_device_remove_groups(class_dev); | 
| Stephen Hemminger | b7fe4a6 | 2006-04-26 09:53:14 -0700 | [diff] [blame] | 687 | out7: | 
|  | 688 | if (class_dev->dev) | 
|  | 689 | sysfs_remove_link(&class_dev->kobj, "device"); | 
|  | 690 | out6: | 
|  | 691 | class_device_remove_attrs(class_dev); | 
|  | 692 | out5: | 
|  | 693 | if (class_dev->devt_attr) | 
|  | 694 | class_device_remove_file(class_dev, class_dev->devt_attr); | 
|  | 695 | out4: | 
|  | 696 | class_device_remove_file(class_dev, &class_dev->uevent_attr); | 
|  | 697 | out3: | 
|  | 698 | kobject_del(&class_dev->kobj); | 
|  | 699 | out2: | 
|  | 700 | if(parent_class_dev) | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 701 | class_device_put(parent_class_dev); | 
| Stephen Hemminger | b7fe4a6 | 2006-04-26 09:53:14 -0700 | [diff] [blame] | 702 | class_put(parent_class); | 
|  | 703 | out1: | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 704 | class_device_put(class_dev); | 
|  | 705 | return error; | 
|  | 706 | } | 
|  | 707 |  | 
|  | 708 | int class_device_register(struct class_device *class_dev) | 
|  | 709 | { | 
|  | 710 | class_device_initialize(class_dev); | 
|  | 711 | return class_device_add(class_dev); | 
|  | 712 | } | 
|  | 713 |  | 
| gregkh@suse.de | 2fc6844 | 2005-03-23 10:02:56 -0800 | [diff] [blame] | 714 | /** | 
|  | 715 | * class_device_create - creates a class device and registers it with sysfs | 
| Rolf Eike Beer | 92a0f86 | 2006-09-29 01:59:13 -0700 | [diff] [blame] | 716 | * @cls: pointer to the struct class that this device should be registered to. | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 717 | * @parent: pointer to the parent struct class_device of this new device, if any. | 
| Rolf Eike Beer | 92a0f86 | 2006-09-29 01:59:13 -0700 | [diff] [blame] | 718 | * @devt: the dev_t for the char device to be added. | 
| gregkh@suse.de | 2fc6844 | 2005-03-23 10:02:56 -0800 | [diff] [blame] | 719 | * @device: a pointer to a struct device that is assiociated with this class device. | 
|  | 720 | * @fmt: string for the class device's name | 
|  | 721 | * | 
|  | 722 | * This function can be used by char device classes.  A struct | 
|  | 723 | * class_device will be created in sysfs, registered to the specified | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 724 | * class. | 
|  | 725 | * A "dev" file will be created, showing the dev_t for the device, if | 
|  | 726 | * the dev_t is not 0,0. | 
|  | 727 | * If a pointer to a parent struct class_device is passed in, the newly | 
|  | 728 | * created struct class_device will be a child of that device in sysfs. | 
|  | 729 | * The pointer to the struct class_device will be returned from the | 
|  | 730 | * call.  Any further sysfs files that might be required can be created | 
|  | 731 | * using this pointer. | 
| gregkh@suse.de | 2fc6844 | 2005-03-23 10:02:56 -0800 | [diff] [blame] | 732 | * | 
|  | 733 | * Note: the struct class passed to this function must have previously | 
|  | 734 | * been created with a call to class_create(). | 
|  | 735 | */ | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 736 | struct class_device *class_device_create(struct class *cls, | 
|  | 737 | struct class_device *parent, | 
|  | 738 | dev_t devt, | 
| Dmitry Torokhov | ddd5d35 | 2006-08-10 01:18:18 -0400 | [diff] [blame] | 739 | struct device *device, | 
|  | 740 | const char *fmt, ...) | 
| gregkh@suse.de | e9ba636 | 2005-03-15 11:54:21 -0800 | [diff] [blame] | 741 | { | 
|  | 742 | va_list args; | 
|  | 743 | struct class_device *class_dev = NULL; | 
|  | 744 | int retval = -ENODEV; | 
|  | 745 |  | 
|  | 746 | if (cls == NULL || IS_ERR(cls)) | 
|  | 747 | goto error; | 
|  | 748 |  | 
| Jiri Slaby | 4aed064 | 2005-09-13 01:25:01 -0700 | [diff] [blame] | 749 | class_dev = kzalloc(sizeof(*class_dev), GFP_KERNEL); | 
| gregkh@suse.de | e9ba636 | 2005-03-15 11:54:21 -0800 | [diff] [blame] | 750 | if (!class_dev) { | 
|  | 751 | retval = -ENOMEM; | 
|  | 752 | goto error; | 
|  | 753 | } | 
| gregkh@suse.de | e9ba636 | 2005-03-15 11:54:21 -0800 | [diff] [blame] | 754 |  | 
|  | 755 | class_dev->devt = devt; | 
|  | 756 | class_dev->dev = device; | 
|  | 757 | class_dev->class = cls; | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 758 | class_dev->parent = parent; | 
|  | 759 | class_dev->release = class_device_create_release; | 
| Kay Sievers | 312c004 | 2005-11-16 09:00:00 +0100 | [diff] [blame] | 760 | class_dev->uevent = class_device_create_uevent; | 
| gregkh@suse.de | e9ba636 | 2005-03-15 11:54:21 -0800 | [diff] [blame] | 761 |  | 
|  | 762 | va_start(args, fmt); | 
|  | 763 | vsnprintf(class_dev->class_id, BUS_ID_SIZE, fmt, args); | 
|  | 764 | va_end(args); | 
|  | 765 | retval = class_device_register(class_dev); | 
|  | 766 | if (retval) | 
|  | 767 | goto error; | 
|  | 768 |  | 
|  | 769 | return class_dev; | 
|  | 770 |  | 
|  | 771 | error: | 
|  | 772 | kfree(class_dev); | 
|  | 773 | return ERR_PTR(retval); | 
|  | 774 | } | 
|  | 775 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 776 | void class_device_del(struct class_device *class_dev) | 
|  | 777 | { | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 778 | struct class *parent_class = class_dev->class; | 
|  | 779 | struct class_device *parent_device = class_dev->parent; | 
|  | 780 | struct class_interface *class_intf; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 781 |  | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 782 | if (parent_class) { | 
|  | 783 | down(&parent_class->sem); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 784 | list_del_init(&class_dev->node); | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 785 | list_for_each_entry(class_intf, &parent_class->interfaces, node) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 786 | if (class_intf->remove) | 
| Dmitry Torokhov | d8539d8 | 2005-09-15 02:01:36 -0500 | [diff] [blame] | 787 | class_intf->remove(class_dev, class_intf); | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 788 | up(&parent_class->sem); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 789 | } | 
|  | 790 |  | 
| Dmitry Torokhov | 76d1ce0 | 2005-07-10 01:21:24 -0500 | [diff] [blame] | 791 | if (class_dev->dev) { | 
| Kay Sievers | 805fab4 | 2006-09-14 11:23:28 +0200 | [diff] [blame] | 792 | remove_deprecated_class_device_links(class_dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 793 | sysfs_remove_link(&class_dev->kobj, "device"); | 
| Dmitry Torokhov | 76d1ce0 | 2005-07-10 01:21:24 -0500 | [diff] [blame] | 794 | } | 
| Kay Sievers | b9d9c82 | 2006-06-15 15:31:56 +0200 | [diff] [blame] | 795 | sysfs_remove_link(&class_dev->kobj, "subsystem"); | 
| Kay Sievers | a7fd670 | 2005-10-01 14:49:43 +0200 | [diff] [blame] | 796 | class_device_remove_file(class_dev, &class_dev->uevent_attr); | 
| Maneesh Soni | 208f3d6 | 2005-08-16 15:15:48 -0700 | [diff] [blame] | 797 | if (class_dev->devt_attr) | 
| gregkh@suse.de | e9ba636 | 2005-03-15 11:54:21 -0800 | [diff] [blame] | 798 | class_device_remove_file(class_dev, class_dev->devt_attr); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 799 | class_device_remove_attrs(class_dev); | 
| Stephen Hemminger | 1498221 | 2006-05-06 17:55:11 -0700 | [diff] [blame] | 800 | class_device_remove_groups(class_dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 801 |  | 
| Kay Sievers | 312c004 | 2005-11-16 09:00:00 +0100 | [diff] [blame] | 802 | kobject_uevent(&class_dev->kobj, KOBJ_REMOVE); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 803 | kobject_del(&class_dev->kobj); | 
|  | 804 |  | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 805 | class_device_put(parent_device); | 
|  | 806 | class_put(parent_class); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 807 | } | 
|  | 808 |  | 
|  | 809 | void class_device_unregister(struct class_device *class_dev) | 
|  | 810 | { | 
|  | 811 | pr_debug("CLASS: Unregistering class device. ID = '%s'\n", | 
|  | 812 | class_dev->class_id); | 
|  | 813 | class_device_del(class_dev); | 
|  | 814 | class_device_put(class_dev); | 
|  | 815 | } | 
|  | 816 |  | 
| gregkh@suse.de | 2fc6844 | 2005-03-23 10:02:56 -0800 | [diff] [blame] | 817 | /** | 
|  | 818 | * class_device_destroy - removes a class device that was created with class_device_create() | 
|  | 819 | * @cls: the pointer to the struct class that this device was registered * with. | 
| Rolf Eike Beer | 92a0f86 | 2006-09-29 01:59:13 -0700 | [diff] [blame] | 820 | * @devt: the dev_t of the device that was previously registered. | 
| gregkh@suse.de | 2fc6844 | 2005-03-23 10:02:56 -0800 | [diff] [blame] | 821 | * | 
|  | 822 | * This call unregisters and cleans up a class device that was created with a | 
|  | 823 | * call to class_device_create() | 
|  | 824 | */ | 
| gregkh@suse.de | e9ba636 | 2005-03-15 11:54:21 -0800 | [diff] [blame] | 825 | void class_device_destroy(struct class *cls, dev_t devt) | 
|  | 826 | { | 
|  | 827 | struct class_device *class_dev = NULL; | 
|  | 828 | struct class_device *class_dev_tmp; | 
|  | 829 |  | 
|  | 830 | down(&cls->sem); | 
|  | 831 | list_for_each_entry(class_dev_tmp, &cls->children, node) { | 
|  | 832 | if (class_dev_tmp->devt == devt) { | 
|  | 833 | class_dev = class_dev_tmp; | 
|  | 834 | break; | 
|  | 835 | } | 
|  | 836 | } | 
|  | 837 | up(&cls->sem); | 
|  | 838 |  | 
|  | 839 | if (class_dev) | 
|  | 840 | class_device_unregister(class_dev); | 
|  | 841 | } | 
|  | 842 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 843 | struct class_device * class_device_get(struct class_device *class_dev) | 
|  | 844 | { | 
|  | 845 | if (class_dev) | 
|  | 846 | return to_class_dev(kobject_get(&class_dev->kobj)); | 
|  | 847 | return NULL; | 
|  | 848 | } | 
|  | 849 |  | 
|  | 850 | void class_device_put(struct class_device *class_dev) | 
|  | 851 | { | 
| Greg Kroah-Hartman | 51d172d | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 852 | if (class_dev) | 
|  | 853 | kobject_put(&class_dev->kobj); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 854 | } | 
|  | 855 |  | 
|  | 856 |  | 
|  | 857 | int class_interface_register(struct class_interface *class_intf) | 
|  | 858 | { | 
|  | 859 | struct class *parent; | 
|  | 860 | struct class_device *class_dev; | 
| Greg Kroah-Hartman | c47ed21 | 2006-09-13 15:34:05 +0200 | [diff] [blame] | 861 | struct device *dev; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 862 |  | 
|  | 863 | if (!class_intf || !class_intf->class) | 
|  | 864 | return -ENODEV; | 
|  | 865 |  | 
|  | 866 | parent = class_get(class_intf->class); | 
|  | 867 | if (!parent) | 
|  | 868 | return -EINVAL; | 
|  | 869 |  | 
|  | 870 | down(&parent->sem); | 
|  | 871 | list_add_tail(&class_intf->node, &parent->interfaces); | 
|  | 872 | if (class_intf->add) { | 
|  | 873 | list_for_each_entry(class_dev, &parent->children, node) | 
| Dmitry Torokhov | d8539d8 | 2005-09-15 02:01:36 -0500 | [diff] [blame] | 874 | class_intf->add(class_dev, class_intf); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 875 | } | 
| Greg Kroah-Hartman | c47ed21 | 2006-09-13 15:34:05 +0200 | [diff] [blame] | 876 | if (class_intf->add_dev) { | 
|  | 877 | list_for_each_entry(dev, &parent->devices, node) | 
|  | 878 | class_intf->add_dev(dev, class_intf); | 
|  | 879 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 880 | up(&parent->sem); | 
|  | 881 |  | 
|  | 882 | return 0; | 
|  | 883 | } | 
|  | 884 |  | 
|  | 885 | void class_interface_unregister(struct class_interface *class_intf) | 
|  | 886 | { | 
|  | 887 | struct class * parent = class_intf->class; | 
|  | 888 | struct class_device *class_dev; | 
| Greg Kroah-Hartman | c47ed21 | 2006-09-13 15:34:05 +0200 | [diff] [blame] | 889 | struct device *dev; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 890 |  | 
|  | 891 | if (!parent) | 
|  | 892 | return; | 
|  | 893 |  | 
|  | 894 | down(&parent->sem); | 
|  | 895 | list_del_init(&class_intf->node); | 
|  | 896 | if (class_intf->remove) { | 
|  | 897 | list_for_each_entry(class_dev, &parent->children, node) | 
| Dmitry Torokhov | d8539d8 | 2005-09-15 02:01:36 -0500 | [diff] [blame] | 898 | class_intf->remove(class_dev, class_intf); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 899 | } | 
| Greg Kroah-Hartman | c47ed21 | 2006-09-13 15:34:05 +0200 | [diff] [blame] | 900 | if (class_intf->remove_dev) { | 
|  | 901 | list_for_each_entry(dev, &parent->devices, node) | 
|  | 902 | class_intf->remove_dev(dev, class_intf); | 
|  | 903 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 904 | up(&parent->sem); | 
|  | 905 |  | 
|  | 906 | class_put(parent); | 
|  | 907 | } | 
|  | 908 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 909 | int __init classes_init(void) | 
|  | 910 | { | 
|  | 911 | int retval; | 
|  | 912 |  | 
|  | 913 | retval = subsystem_register(&class_subsys); | 
|  | 914 | if (retval) | 
|  | 915 | return retval; | 
|  | 916 |  | 
|  | 917 | /* ick, this is ugly, the things we go through to keep from showing up | 
|  | 918 | * in sysfs... */ | 
|  | 919 | subsystem_init(&class_obj_subsys); | 
|  | 920 | if (!class_obj_subsys.kset.subsys) | 
|  | 921 | class_obj_subsys.kset.subsys = &class_obj_subsys; | 
|  | 922 | return 0; | 
|  | 923 | } | 
|  | 924 |  | 
|  | 925 | EXPORT_SYMBOL_GPL(class_create_file); | 
|  | 926 | EXPORT_SYMBOL_GPL(class_remove_file); | 
|  | 927 | EXPORT_SYMBOL_GPL(class_register); | 
|  | 928 | EXPORT_SYMBOL_GPL(class_unregister); | 
| gregkh@suse.de | e9ba636 | 2005-03-15 11:54:21 -0800 | [diff] [blame] | 929 | EXPORT_SYMBOL_GPL(class_create); | 
|  | 930 | EXPORT_SYMBOL_GPL(class_destroy); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 931 |  | 
|  | 932 | EXPORT_SYMBOL_GPL(class_device_register); | 
|  | 933 | EXPORT_SYMBOL_GPL(class_device_unregister); | 
|  | 934 | EXPORT_SYMBOL_GPL(class_device_initialize); | 
|  | 935 | EXPORT_SYMBOL_GPL(class_device_add); | 
|  | 936 | EXPORT_SYMBOL_GPL(class_device_del); | 
|  | 937 | EXPORT_SYMBOL_GPL(class_device_get); | 
|  | 938 | EXPORT_SYMBOL_GPL(class_device_put); | 
| gregkh@suse.de | e9ba636 | 2005-03-15 11:54:21 -0800 | [diff] [blame] | 939 | EXPORT_SYMBOL_GPL(class_device_create); | 
|  | 940 | EXPORT_SYMBOL_GPL(class_device_destroy); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 941 | EXPORT_SYMBOL_GPL(class_device_create_file); | 
|  | 942 | EXPORT_SYMBOL_GPL(class_device_remove_file); | 
|  | 943 | EXPORT_SYMBOL_GPL(class_device_create_bin_file); | 
|  | 944 | EXPORT_SYMBOL_GPL(class_device_remove_bin_file); | 
|  | 945 |  | 
|  | 946 | EXPORT_SYMBOL_GPL(class_interface_register); | 
|  | 947 | EXPORT_SYMBOL_GPL(class_interface_unregister); |