diff options
Diffstat (limited to 'drivers/base/core.c')
-rw-r--r-- | drivers/base/core.c | 230 |
1 files changed, 211 insertions, 19 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c index be6b5bc0677d..b224bb43ff63 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c | |||
@@ -3,6 +3,8 @@ | |||
3 | * | 3 | * |
4 | * Copyright (c) 2002-3 Patrick Mochel | 4 | * Copyright (c) 2002-3 Patrick Mochel |
5 | * Copyright (c) 2002-3 Open Source Development Labs | 5 | * Copyright (c) 2002-3 Open Source Development Labs |
6 | * Copyright (c) 2006 Greg Kroah-Hartman <gregkh@suse.de> | ||
7 | * Copyright (c) 2006 Novell, Inc. | ||
6 | * | 8 | * |
7 | * This file is released under the GPLv2 | 9 | * This file is released under the GPLv2 |
8 | * | 10 | * |
@@ -92,6 +94,8 @@ static void device_release(struct kobject * kobj) | |||
92 | 94 | ||
93 | if (dev->release) | 95 | if (dev->release) |
94 | dev->release(dev); | 96 | dev->release(dev); |
97 | else if (dev->class && dev->class->dev_release) | ||
98 | dev->class->dev_release(dev); | ||
95 | else { | 99 | else { |
96 | printk(KERN_ERR "Device '%s' does not have a release() function, " | 100 | printk(KERN_ERR "Device '%s' does not have a release() function, " |
97 | "it is broken and must be fixed.\n", | 101 | "it is broken and must be fixed.\n", |
@@ -149,17 +153,21 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp, | |||
149 | "MINOR=%u", MINOR(dev->devt)); | 153 | "MINOR=%u", MINOR(dev->devt)); |
150 | } | 154 | } |
151 | 155 | ||
152 | /* add bus name of physical device */ | 156 | /* add bus name (same as SUBSYSTEM, deprecated) */ |
153 | if (dev->bus) | 157 | if (dev->bus) |
154 | add_uevent_var(envp, num_envp, &i, | 158 | add_uevent_var(envp, num_envp, &i, |
155 | buffer, buffer_size, &length, | 159 | buffer, buffer_size, &length, |
156 | "PHYSDEVBUS=%s", dev->bus->name); | 160 | "PHYSDEVBUS=%s", dev->bus->name); |
157 | 161 | ||
158 | /* add driver name of physical device */ | 162 | /* add driver name (PHYSDEV* values are deprecated)*/ |
159 | if (dev->driver) | 163 | if (dev->driver) { |
164 | add_uevent_var(envp, num_envp, &i, | ||
165 | buffer, buffer_size, &length, | ||
166 | "DRIVER=%s", dev->driver->name); | ||
160 | add_uevent_var(envp, num_envp, &i, | 167 | add_uevent_var(envp, num_envp, &i, |
161 | buffer, buffer_size, &length, | 168 | buffer, buffer_size, &length, |
162 | "PHYSDEVDRIVER=%s", dev->driver->name); | 169 | "PHYSDEVDRIVER=%s", dev->driver->name); |
170 | } | ||
163 | 171 | ||
164 | /* terminate, set to next free slot, shrink available space */ | 172 | /* terminate, set to next free slot, shrink available space */ |
165 | envp[i] = NULL; | 173 | envp[i] = NULL; |
@@ -177,6 +185,15 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp, | |||
177 | } | 185 | } |
178 | } | 186 | } |
179 | 187 | ||
188 | if (dev->class && dev->class->dev_uevent) { | ||
189 | /* have the class specific function add its stuff */ | ||
190 | retval = dev->class->dev_uevent(dev, envp, num_envp, buffer, buffer_size); | ||
191 | if (retval) { | ||
192 | pr_debug("%s - dev_uevent() returned %d\n", | ||
193 | __FUNCTION__, retval); | ||
194 | } | ||
195 | } | ||
196 | |||
180 | return retval; | 197 | return retval; |
181 | } | 198 | } |
182 | 199 | ||
@@ -193,6 +210,72 @@ static ssize_t store_uevent(struct device *dev, struct device_attribute *attr, | |||
193 | return count; | 210 | return count; |
194 | } | 211 | } |
195 | 212 | ||
213 | static int device_add_groups(struct device *dev) | ||
214 | { | ||
215 | int i; | ||
216 | int error = 0; | ||
217 | |||
218 | if (dev->groups) { | ||
219 | for (i = 0; dev->groups[i]; i++) { | ||
220 | error = sysfs_create_group(&dev->kobj, dev->groups[i]); | ||
221 | if (error) { | ||
222 | while (--i >= 0) | ||
223 | sysfs_remove_group(&dev->kobj, dev->groups[i]); | ||
224 | goto out; | ||
225 | } | ||
226 | } | ||
227 | } | ||
228 | out: | ||
229 | return error; | ||
230 | } | ||
231 | |||
232 | static void device_remove_groups(struct device *dev) | ||
233 | { | ||
234 | int i; | ||
235 | if (dev->groups) { | ||
236 | for (i = 0; dev->groups[i]; i++) { | ||
237 | sysfs_remove_group(&dev->kobj, dev->groups[i]); | ||
238 | } | ||
239 | } | ||
240 | } | ||
241 | |||
242 | static int device_add_attrs(struct device *dev) | ||
243 | { | ||
244 | struct class *class = dev->class; | ||
245 | int error = 0; | ||
246 | int i; | ||
247 | |||
248 | if (!class) | ||
249 | return 0; | ||
250 | |||
251 | if (class->dev_attrs) { | ||
252 | for (i = 0; attr_name(class->dev_attrs[i]); i++) { | ||
253 | error = device_create_file(dev, &class->dev_attrs[i]); | ||
254 | if (error) | ||
255 | break; | ||
256 | } | ||
257 | } | ||
258 | if (error) | ||
259 | while (--i >= 0) | ||
260 | device_remove_file(dev, &class->dev_attrs[i]); | ||
261 | return error; | ||
262 | } | ||
263 | |||
264 | static void device_remove_attrs(struct device *dev) | ||
265 | { | ||
266 | struct class *class = dev->class; | ||
267 | int i; | ||
268 | |||
269 | if (!class) | ||
270 | return; | ||
271 | |||
272 | if (class->dev_attrs) { | ||
273 | for (i = 0; attr_name(class->dev_attrs[i]); i++) | ||
274 | device_remove_file(dev, &class->dev_attrs[i]); | ||
275 | } | ||
276 | } | ||
277 | |||
278 | |||
196 | static ssize_t show_dev(struct device *dev, struct device_attribute *attr, | 279 | static ssize_t show_dev(struct device *dev, struct device_attribute *attr, |
197 | char *buf) | 280 | char *buf) |
198 | { | 281 | { |
@@ -236,6 +319,32 @@ void device_remove_file(struct device * dev, struct device_attribute * attr) | |||
236 | } | 319 | } |
237 | } | 320 | } |
238 | 321 | ||
322 | /** | ||
323 | * device_create_bin_file - create sysfs binary attribute file for device. | ||
324 | * @dev: device. | ||
325 | * @attr: device binary attribute descriptor. | ||
326 | */ | ||
327 | int device_create_bin_file(struct device *dev, struct bin_attribute *attr) | ||
328 | { | ||
329 | int error = -EINVAL; | ||
330 | if (dev) | ||
331 | error = sysfs_create_bin_file(&dev->kobj, attr); | ||
332 | return error; | ||
333 | } | ||
334 | EXPORT_SYMBOL_GPL(device_create_bin_file); | ||
335 | |||
336 | /** | ||
337 | * device_remove_bin_file - remove sysfs binary attribute file | ||
338 | * @dev: device. | ||
339 | * @attr: device binary attribute descriptor. | ||
340 | */ | ||
341 | void device_remove_bin_file(struct device *dev, struct bin_attribute *attr) | ||
342 | { | ||
343 | if (dev) | ||
344 | sysfs_remove_bin_file(&dev->kobj, attr); | ||
345 | } | ||
346 | EXPORT_SYMBOL_GPL(device_remove_bin_file); | ||
347 | |||
239 | static void klist_children_get(struct klist_node *n) | 348 | static void klist_children_get(struct klist_node *n) |
240 | { | 349 | { |
241 | struct device *dev = container_of(n, struct device, knode_parent); | 350 | struct device *dev = container_of(n, struct device, knode_parent); |
@@ -289,12 +398,20 @@ int device_add(struct device *dev) | |||
289 | { | 398 | { |
290 | struct device *parent = NULL; | 399 | struct device *parent = NULL; |
291 | char *class_name = NULL; | 400 | char *class_name = NULL; |
401 | struct class_interface *class_intf; | ||
292 | int error = -EINVAL; | 402 | int error = -EINVAL; |
293 | 403 | ||
294 | dev = get_device(dev); | 404 | dev = get_device(dev); |
295 | if (!dev || !strlen(dev->bus_id)) | 405 | if (!dev || !strlen(dev->bus_id)) |
296 | goto Error; | 406 | goto Error; |
297 | 407 | ||
408 | /* if this is a class device, and has no parent, create one */ | ||
409 | if ((dev->class) && (dev->parent == NULL)) { | ||
410 | error = virtual_device_parent(dev); | ||
411 | if (error) | ||
412 | goto Error; | ||
413 | } | ||
414 | |||
298 | parent = get_device(dev->parent); | 415 | parent = get_device(dev->parent); |
299 | 416 | ||
300 | pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id); | 417 | pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id); |
@@ -307,6 +424,10 @@ int device_add(struct device *dev) | |||
307 | if ((error = kobject_add(&dev->kobj))) | 424 | if ((error = kobject_add(&dev->kobj))) |
308 | goto Error; | 425 | goto Error; |
309 | 426 | ||
427 | /* notify platform of device entry */ | ||
428 | if (platform_notify) | ||
429 | platform_notify(dev); | ||
430 | |||
310 | dev->uevent_attr.attr.name = "uevent"; | 431 | dev->uevent_attr.attr.name = "uevent"; |
311 | dev->uevent_attr.attr.mode = S_IWUSR; | 432 | dev->uevent_attr.attr.mode = S_IWUSR; |
312 | if (dev->driver) | 433 | if (dev->driver) |
@@ -340,12 +461,17 @@ int device_add(struct device *dev) | |||
340 | "subsystem"); | 461 | "subsystem"); |
341 | sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj, | 462 | sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj, |
342 | dev->bus_id); | 463 | dev->bus_id); |
343 | 464 | if (parent) { | |
344 | sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device"); | 465 | sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device"); |
345 | class_name = make_class_name(dev->class->name, &dev->kobj); | 466 | class_name = make_class_name(dev->class->name, &dev->kobj); |
346 | sysfs_create_link(&dev->parent->kobj, &dev->kobj, class_name); | 467 | sysfs_create_link(&dev->parent->kobj, &dev->kobj, class_name); |
468 | } | ||
347 | } | 469 | } |
348 | 470 | ||
471 | if ((error = device_add_attrs(dev))) | ||
472 | goto AttrsError; | ||
473 | if ((error = device_add_groups(dev))) | ||
474 | goto GroupError; | ||
349 | if ((error = device_pm_add(dev))) | 475 | if ((error = device_pm_add(dev))) |
350 | goto PMError; | 476 | goto PMError; |
351 | if ((error = bus_add_device(dev))) | 477 | if ((error = bus_add_device(dev))) |
@@ -356,15 +482,16 @@ int device_add(struct device *dev) | |||
356 | klist_add_tail(&dev->knode_parent, &parent->klist_children); | 482 | klist_add_tail(&dev->knode_parent, &parent->klist_children); |
357 | 483 | ||
358 | if (dev->class) { | 484 | if (dev->class) { |
359 | /* tie the class to the device */ | ||
360 | down(&dev->class->sem); | 485 | down(&dev->class->sem); |
486 | /* tie the class to the device */ | ||
361 | list_add_tail(&dev->node, &dev->class->devices); | 487 | list_add_tail(&dev->node, &dev->class->devices); |
488 | |||
489 | /* notify any interfaces that the device is here */ | ||
490 | list_for_each_entry(class_intf, &dev->class->interfaces, node) | ||
491 | if (class_intf->add_dev) | ||
492 | class_intf->add_dev(dev, class_intf); | ||
362 | up(&dev->class->sem); | 493 | up(&dev->class->sem); |
363 | } | 494 | } |
364 | |||
365 | /* notify platform of device entry */ | ||
366 | if (platform_notify) | ||
367 | platform_notify(dev); | ||
368 | Done: | 495 | Done: |
369 | kfree(class_name); | 496 | kfree(class_name); |
370 | put_device(dev); | 497 | put_device(dev); |
@@ -372,6 +499,10 @@ int device_add(struct device *dev) | |||
372 | BusError: | 499 | BusError: |
373 | device_pm_remove(dev); | 500 | device_pm_remove(dev); |
374 | PMError: | 501 | PMError: |
502 | device_remove_groups(dev); | ||
503 | GroupError: | ||
504 | device_remove_attrs(dev); | ||
505 | AttrsError: | ||
375 | if (dev->devt_attr) { | 506 | if (dev->devt_attr) { |
376 | device_remove_file(dev, dev->devt_attr); | 507 | device_remove_file(dev, dev->devt_attr); |
377 | kfree(dev->devt_attr); | 508 | kfree(dev->devt_attr); |
@@ -449,6 +580,7 @@ void device_del(struct device * dev) | |||
449 | { | 580 | { |
450 | struct device * parent = dev->parent; | 581 | struct device * parent = dev->parent; |
451 | char *class_name = NULL; | 582 | char *class_name = NULL; |
583 | struct class_interface *class_intf; | ||
452 | 584 | ||
453 | if (parent) | 585 | if (parent) |
454 | klist_del(&dev->knode_parent); | 586 | klist_del(&dev->knode_parent); |
@@ -458,14 +590,23 @@ void device_del(struct device * dev) | |||
458 | sysfs_remove_link(&dev->kobj, "subsystem"); | 590 | sysfs_remove_link(&dev->kobj, "subsystem"); |
459 | sysfs_remove_link(&dev->class->subsys.kset.kobj, dev->bus_id); | 591 | sysfs_remove_link(&dev->class->subsys.kset.kobj, dev->bus_id); |
460 | class_name = make_class_name(dev->class->name, &dev->kobj); | 592 | class_name = make_class_name(dev->class->name, &dev->kobj); |
461 | sysfs_remove_link(&dev->kobj, "device"); | 593 | if (parent) { |
462 | sysfs_remove_link(&dev->parent->kobj, class_name); | 594 | sysfs_remove_link(&dev->kobj, "device"); |
595 | sysfs_remove_link(&dev->parent->kobj, class_name); | ||
596 | } | ||
463 | kfree(class_name); | 597 | kfree(class_name); |
464 | down(&dev->class->sem); | 598 | down(&dev->class->sem); |
599 | /* notify any interfaces that the device is now gone */ | ||
600 | list_for_each_entry(class_intf, &dev->class->interfaces, node) | ||
601 | if (class_intf->remove_dev) | ||
602 | class_intf->remove_dev(dev, class_intf); | ||
603 | /* remove the device from the class list */ | ||
465 | list_del_init(&dev->node); | 604 | list_del_init(&dev->node); |
466 | up(&dev->class->sem); | 605 | up(&dev->class->sem); |
467 | } | 606 | } |
468 | device_remove_file(dev, &dev->uevent_attr); | 607 | device_remove_file(dev, &dev->uevent_attr); |
608 | device_remove_groups(dev); | ||
609 | device_remove_attrs(dev); | ||
469 | 610 | ||
470 | /* Notify the platform of the removal, in case they | 611 | /* Notify the platform of the removal, in case they |
471 | * need to do anything... | 612 | * need to do anything... |
@@ -579,7 +720,7 @@ static void device_create_release(struct device *dev) | |||
579 | * been created with a call to class_create(). | 720 | * been created with a call to class_create(). |
580 | */ | 721 | */ |
581 | struct device *device_create(struct class *class, struct device *parent, | 722 | struct device *device_create(struct class *class, struct device *parent, |
582 | dev_t devt, char *fmt, ...) | 723 | dev_t devt, const char *fmt, ...) |
583 | { | 724 | { |
584 | va_list args; | 725 | va_list args; |
585 | struct device *dev = NULL; | 726 | struct device *dev = NULL; |
@@ -587,10 +728,6 @@ struct device *device_create(struct class *class, struct device *parent, | |||
587 | 728 | ||
588 | if (class == NULL || IS_ERR(class)) | 729 | if (class == NULL || IS_ERR(class)) |
589 | goto error; | 730 | goto error; |
590 | if (parent == NULL) { | ||
591 | printk(KERN_WARNING "%s does not work yet for NULL parents\n", __FUNCTION__); | ||
592 | goto error; | ||
593 | } | ||
594 | 731 | ||
595 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); | 732 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); |
596 | if (!dev) { | 733 | if (!dev) { |
@@ -644,3 +781,58 @@ void device_destroy(struct class *class, dev_t devt) | |||
644 | device_unregister(dev); | 781 | device_unregister(dev); |
645 | } | 782 | } |
646 | EXPORT_SYMBOL_GPL(device_destroy); | 783 | EXPORT_SYMBOL_GPL(device_destroy); |
784 | |||
785 | /** | ||
786 | * device_rename - renames a device | ||
787 | * @dev: the pointer to the struct device to be renamed | ||
788 | * @new_name: the new name of the device | ||
789 | */ | ||
790 | int device_rename(struct device *dev, char *new_name) | ||
791 | { | ||
792 | char *old_class_name = NULL; | ||
793 | char *new_class_name = NULL; | ||
794 | char *old_symlink_name = NULL; | ||
795 | int error; | ||
796 | |||
797 | dev = get_device(dev); | ||
798 | if (!dev) | ||
799 | return -EINVAL; | ||
800 | |||
801 | pr_debug("DEVICE: renaming '%s' to '%s'\n", dev->bus_id, new_name); | ||
802 | |||
803 | if ((dev->class) && (dev->parent)) | ||
804 | old_class_name = make_class_name(dev->class->name, &dev->kobj); | ||
805 | |||
806 | if (dev->class) { | ||
807 | old_symlink_name = kmalloc(BUS_ID_SIZE, GFP_KERNEL); | ||
808 | if (!old_symlink_name) | ||
809 | return -ENOMEM; | ||
810 | strlcpy(old_symlink_name, dev->bus_id, BUS_ID_SIZE); | ||
811 | } | ||
812 | |||
813 | strlcpy(dev->bus_id, new_name, BUS_ID_SIZE); | ||
814 | |||
815 | error = kobject_rename(&dev->kobj, new_name); | ||
816 | |||
817 | if (old_class_name) { | ||
818 | new_class_name = make_class_name(dev->class->name, &dev->kobj); | ||
819 | if (new_class_name) { | ||
820 | sysfs_create_link(&dev->parent->kobj, &dev->kobj, | ||
821 | new_class_name); | ||
822 | sysfs_remove_link(&dev->parent->kobj, old_class_name); | ||
823 | } | ||
824 | } | ||
825 | if (dev->class) { | ||
826 | sysfs_remove_link(&dev->class->subsys.kset.kobj, | ||
827 | old_symlink_name); | ||
828 | sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj, | ||
829 | dev->bus_id); | ||
830 | } | ||
831 | put_device(dev); | ||
832 | |||
833 | kfree(old_class_name); | ||
834 | kfree(new_class_name); | ||
835 | kfree(old_symlink_name); | ||
836 | |||
837 | return error; | ||
838 | } | ||