diff options
-rw-r--r-- | drivers/base/class.c | 1 | ||||
-rw-r--r-- | drivers/base/core.c | 162 | ||||
-rw-r--r-- | include/linux/device.h | 14 |
3 files changed, 176 insertions, 1 deletions
diff --git a/drivers/base/class.c b/drivers/base/class.c index 41a8e0934e3a..50e841a33af0 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c | |||
@@ -142,6 +142,7 @@ int class_register(struct class * cls) | |||
142 | pr_debug("device class '%s': registering\n", cls->name); | 142 | pr_debug("device class '%s': registering\n", cls->name); |
143 | 143 | ||
144 | INIT_LIST_HEAD(&cls->children); | 144 | INIT_LIST_HEAD(&cls->children); |
145 | INIT_LIST_HEAD(&cls->devices); | ||
145 | INIT_LIST_HEAD(&cls->interfaces); | 146 | INIT_LIST_HEAD(&cls->interfaces); |
146 | init_MUTEX(&cls->sem); | 147 | init_MUTEX(&cls->sem); |
147 | error = kobject_set_name(&cls->subsys.kset.kobj, "%s", cls->name); | 148 | error = kobject_set_name(&cls->subsys.kset.kobj, "%s", cls->name); |
diff --git a/drivers/base/core.c b/drivers/base/core.c index d5e15a03584e..252cf403f891 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
16 | #include <linux/slab.h> | 16 | #include <linux/slab.h> |
17 | #include <linux/string.h> | 17 | #include <linux/string.h> |
18 | #include <linux/kdev_t.h> | ||
18 | 19 | ||
19 | #include <asm/semaphore.h> | 20 | #include <asm/semaphore.h> |
20 | 21 | ||
@@ -98,6 +99,8 @@ static int dev_uevent_filter(struct kset *kset, struct kobject *kobj) | |||
98 | struct device *dev = to_dev(kobj); | 99 | struct device *dev = to_dev(kobj); |
99 | if (dev->bus) | 100 | if (dev->bus) |
100 | return 1; | 101 | return 1; |
102 | if (dev->class) | ||
103 | return 1; | ||
101 | } | 104 | } |
102 | return 0; | 105 | return 0; |
103 | } | 106 | } |
@@ -106,7 +109,11 @@ static const char *dev_uevent_name(struct kset *kset, struct kobject *kobj) | |||
106 | { | 109 | { |
107 | struct device *dev = to_dev(kobj); | 110 | struct device *dev = to_dev(kobj); |
108 | 111 | ||
109 | return dev->bus->name; | 112 | if (dev->bus) |
113 | return dev->bus->name; | ||
114 | if (dev->class) | ||
115 | return dev->class->name; | ||
116 | return NULL; | ||
110 | } | 117 | } |
111 | 118 | ||
112 | static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp, | 119 | static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp, |
@@ -117,6 +124,16 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp, | |||
117 | int length = 0; | 124 | int length = 0; |
118 | int retval = 0; | 125 | int retval = 0; |
119 | 126 | ||
127 | /* add the major/minor if present */ | ||
128 | if (MAJOR(dev->devt)) { | ||
129 | add_uevent_var(envp, num_envp, &i, | ||
130 | buffer, buffer_size, &length, | ||
131 | "MAJOR=%u", MAJOR(dev->devt)); | ||
132 | add_uevent_var(envp, num_envp, &i, | ||
133 | buffer, buffer_size, &length, | ||
134 | "MINOR=%u", MINOR(dev->devt)); | ||
135 | } | ||
136 | |||
120 | /* add bus name of physical device */ | 137 | /* add bus name of physical device */ |
121 | if (dev->bus) | 138 | if (dev->bus) |
122 | add_uevent_var(envp, num_envp, &i, | 139 | add_uevent_var(envp, num_envp, &i, |
@@ -161,6 +178,12 @@ static ssize_t store_uevent(struct device *dev, struct device_attribute *attr, | |||
161 | return count; | 178 | return count; |
162 | } | 179 | } |
163 | 180 | ||
181 | static ssize_t show_dev(struct device *dev, struct device_attribute *attr, | ||
182 | char *buf) | ||
183 | { | ||
184 | return print_dev_t(buf, dev->devt); | ||
185 | } | ||
186 | |||
164 | /* | 187 | /* |
165 | * devices_subsys - structure to be registered with kobject core. | 188 | * devices_subsys - structure to be registered with kobject core. |
166 | */ | 189 | */ |
@@ -231,6 +254,7 @@ void device_initialize(struct device *dev) | |||
231 | klist_init(&dev->klist_children, klist_children_get, | 254 | klist_init(&dev->klist_children, klist_children_get, |
232 | klist_children_put); | 255 | klist_children_put); |
233 | INIT_LIST_HEAD(&dev->dma_pools); | 256 | INIT_LIST_HEAD(&dev->dma_pools); |
257 | INIT_LIST_HEAD(&dev->node); | ||
234 | init_MUTEX(&dev->sem); | 258 | init_MUTEX(&dev->sem); |
235 | device_init_wakeup(dev, 0); | 259 | device_init_wakeup(dev, 0); |
236 | } | 260 | } |
@@ -274,6 +298,31 @@ int device_add(struct device *dev) | |||
274 | dev->uevent_attr.store = store_uevent; | 298 | dev->uevent_attr.store = store_uevent; |
275 | device_create_file(dev, &dev->uevent_attr); | 299 | device_create_file(dev, &dev->uevent_attr); |
276 | 300 | ||
301 | if (MAJOR(dev->devt)) { | ||
302 | struct device_attribute *attr; | ||
303 | attr = kzalloc(sizeof(*attr), GFP_KERNEL); | ||
304 | if (!attr) { | ||
305 | error = -ENOMEM; | ||
306 | goto PMError; | ||
307 | } | ||
308 | attr->attr.name = "dev"; | ||
309 | attr->attr.mode = S_IRUGO; | ||
310 | if (dev->driver) | ||
311 | attr->attr.owner = dev->driver->owner; | ||
312 | attr->show = show_dev; | ||
313 | error = device_create_file(dev, attr); | ||
314 | if (error) { | ||
315 | kfree(attr); | ||
316 | goto attrError; | ||
317 | } | ||
318 | |||
319 | dev->devt_attr = attr; | ||
320 | } | ||
321 | |||
322 | if (dev->class) | ||
323 | sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj, | ||
324 | dev->bus_id); | ||
325 | |||
277 | if ((error = device_pm_add(dev))) | 326 | if ((error = device_pm_add(dev))) |
278 | goto PMError; | 327 | goto PMError; |
279 | if ((error = bus_add_device(dev))) | 328 | if ((error = bus_add_device(dev))) |
@@ -292,6 +341,11 @@ int device_add(struct device *dev) | |||
292 | BusError: | 341 | BusError: |
293 | device_pm_remove(dev); | 342 | device_pm_remove(dev); |
294 | PMError: | 343 | PMError: |
344 | if (dev->devt_attr) { | ||
345 | device_remove_file(dev, dev->devt_attr); | ||
346 | kfree(dev->devt_attr); | ||
347 | } | ||
348 | attrError: | ||
295 | kobject_uevent(&dev->kobj, KOBJ_REMOVE); | 349 | kobject_uevent(&dev->kobj, KOBJ_REMOVE); |
296 | kobject_del(&dev->kobj); | 350 | kobject_del(&dev->kobj); |
297 | Error: | 351 | Error: |
@@ -366,6 +420,10 @@ void device_del(struct device * dev) | |||
366 | 420 | ||
367 | if (parent) | 421 | if (parent) |
368 | klist_del(&dev->knode_parent); | 422 | klist_del(&dev->knode_parent); |
423 | if (dev->devt_attr) | ||
424 | device_remove_file(dev, dev->devt_attr); | ||
425 | if (dev->class) | ||
426 | sysfs_remove_link(&dev->class->subsys.kset.kobj, dev->bus_id); | ||
369 | device_remove_file(dev, &dev->uevent_attr); | 427 | device_remove_file(dev, &dev->uevent_attr); |
370 | 428 | ||
371 | /* Notify the platform of the removal, in case they | 429 | /* Notify the platform of the removal, in case they |
@@ -450,3 +508,105 @@ EXPORT_SYMBOL_GPL(put_device); | |||
450 | 508 | ||
451 | EXPORT_SYMBOL_GPL(device_create_file); | 509 | EXPORT_SYMBOL_GPL(device_create_file); |
452 | EXPORT_SYMBOL_GPL(device_remove_file); | 510 | EXPORT_SYMBOL_GPL(device_remove_file); |
511 | |||
512 | |||
513 | static void device_create_release(struct device *dev) | ||
514 | { | ||
515 | pr_debug("%s called for %s\n", __FUNCTION__, dev->bus_id); | ||
516 | kfree(dev); | ||
517 | } | ||
518 | |||
519 | /** | ||
520 | * device_create - creates a device and registers it with sysfs | ||
521 | * @cs: pointer to the struct class that this device should be registered to. | ||
522 | * @parent: pointer to the parent struct device of this new device, if any. | ||
523 | * @dev: the dev_t for the char device to be added. | ||
524 | * @fmt: string for the class device's name | ||
525 | * | ||
526 | * This function can be used by char device classes. A struct | ||
527 | * device will be created in sysfs, registered to the specified | ||
528 | * class. | ||
529 | * A "dev" file will be created, showing the dev_t for the device, if | ||
530 | * the dev_t is not 0,0. | ||
531 | * If a pointer to a parent struct device is passed in, the newly | ||
532 | * created struct device will be a child of that device in sysfs. The | ||
533 | * pointer to the struct device will be returned from the call. Any | ||
534 | * further sysfs files that might be required can be created using this | ||
535 | * pointer. | ||
536 | * | ||
537 | * Note: the struct class passed to this function must have previously | ||
538 | * been created with a call to class_create(). | ||
539 | */ | ||
540 | struct device *device_create(struct class *class, struct device *parent, | ||
541 | dev_t devt, char *fmt, ...) | ||
542 | { | ||
543 | va_list args; | ||
544 | struct device *dev = NULL; | ||
545 | int retval = -ENODEV; | ||
546 | |||
547 | if (class == NULL || IS_ERR(class)) | ||
548 | goto error; | ||
549 | if (parent == NULL) { | ||
550 | printk(KERN_WARNING "%s does not work yet for NULL parents\n", __FUNCTION__); | ||
551 | goto error; | ||
552 | } | ||
553 | |||
554 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); | ||
555 | if (!dev) { | ||
556 | retval = -ENOMEM; | ||
557 | goto error; | ||
558 | } | ||
559 | |||
560 | dev->devt = devt; | ||
561 | dev->class = class; | ||
562 | dev->parent = parent; | ||
563 | dev->release = device_create_release; | ||
564 | |||
565 | va_start(args, fmt); | ||
566 | vsnprintf(dev->bus_id, BUS_ID_SIZE, fmt, args); | ||
567 | va_end(args); | ||
568 | retval = device_register(dev); | ||
569 | if (retval) | ||
570 | goto error; | ||
571 | |||
572 | /* tie the class to the device */ | ||
573 | down(&class->sem); | ||
574 | list_add_tail(&dev->node, &class->devices); | ||
575 | up(&class->sem); | ||
576 | |||
577 | return dev; | ||
578 | |||
579 | error: | ||
580 | kfree(dev); | ||
581 | return ERR_PTR(retval); | ||
582 | } | ||
583 | EXPORT_SYMBOL_GPL(device_create); | ||
584 | |||
585 | /** | ||
586 | * device_destroy - removes a device that was created with device_create() | ||
587 | * @class: the pointer to the struct class that this device was registered * with. | ||
588 | * @dev: the dev_t of the device that was previously registered. | ||
589 | * | ||
590 | * This call unregisters and cleans up a class device that was created with a | ||
591 | * call to class_device_create() | ||
592 | */ | ||
593 | void device_destroy(struct class *class, dev_t devt) | ||
594 | { | ||
595 | struct device *dev = NULL; | ||
596 | struct device *dev_tmp; | ||
597 | |||
598 | down(&class->sem); | ||
599 | list_for_each_entry(dev_tmp, &class->devices, node) { | ||
600 | if (dev_tmp->devt == devt) { | ||
601 | dev = dev_tmp; | ||
602 | break; | ||
603 | } | ||
604 | } | ||
605 | up(&class->sem); | ||
606 | |||
607 | if (dev) { | ||
608 | list_del_init(&dev->node); | ||
609 | device_unregister(dev); | ||
610 | } | ||
611 | } | ||
612 | EXPORT_SYMBOL_GPL(device_destroy); | ||
diff --git a/include/linux/device.h b/include/linux/device.h index ade10dd6b779..b473f4278910 100644 --- a/include/linux/device.h +++ b/include/linux/device.h | |||
@@ -142,6 +142,7 @@ struct class { | |||
142 | 142 | ||
143 | struct subsystem subsys; | 143 | struct subsystem subsys; |
144 | struct list_head children; | 144 | struct list_head children; |
145 | struct list_head devices; | ||
145 | struct list_head interfaces; | 146 | struct list_head interfaces; |
146 | struct semaphore sem; /* locks both the children and interfaces lists */ | 147 | struct semaphore sem; /* locks both the children and interfaces lists */ |
147 | 148 | ||
@@ -305,6 +306,7 @@ struct device { | |||
305 | struct kobject kobj; | 306 | struct kobject kobj; |
306 | char bus_id[BUS_ID_SIZE]; /* position on parent bus */ | 307 | char bus_id[BUS_ID_SIZE]; /* position on parent bus */ |
307 | struct device_attribute uevent_attr; | 308 | struct device_attribute uevent_attr; |
309 | struct device_attribute *devt_attr; | ||
308 | 310 | ||
309 | struct semaphore sem; /* semaphore to synchronize calls to | 311 | struct semaphore sem; /* semaphore to synchronize calls to |
310 | * its driver. | 312 | * its driver. |
@@ -332,6 +334,11 @@ struct device { | |||
332 | struct dma_coherent_mem *dma_mem; /* internal for coherent mem | 334 | struct dma_coherent_mem *dma_mem; /* internal for coherent mem |
333 | override */ | 335 | override */ |
334 | 336 | ||
337 | /* class_device migration path */ | ||
338 | struct list_head node; | ||
339 | struct class *class; /* optional*/ | ||
340 | dev_t devt; /* dev_t, creates the sysfs "dev" */ | ||
341 | |||
335 | void (*release)(struct device * dev); | 342 | void (*release)(struct device * dev); |
336 | }; | 343 | }; |
337 | 344 | ||
@@ -373,6 +380,13 @@ extern int device_attach(struct device * dev); | |||
373 | extern void driver_attach(struct device_driver * drv); | 380 | extern void driver_attach(struct device_driver * drv); |
374 | extern void device_reprobe(struct device *dev); | 381 | extern void device_reprobe(struct device *dev); |
375 | 382 | ||
383 | /* | ||
384 | * Easy functions for dynamically creating devices on the fly | ||
385 | */ | ||
386 | extern struct device *device_create(struct class *cls, struct device *parent, | ||
387 | dev_t devt, char *fmt, ...) | ||
388 | __attribute__((format(printf,4,5))); | ||
389 | extern void device_destroy(struct class *cls, dev_t devt); | ||
376 | 390 | ||
377 | /* | 391 | /* |
378 | * Platform "fixup" functions - allow the platform to have their say | 392 | * Platform "fixup" functions - allow the platform to have their say |