| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * attribute_container.c - implementation of a simple container for classes | 
|  | 3 | * | 
|  | 4 | * Copyright (c) 2005 - James Bottomley <James.Bottomley@steeleye.com> | 
|  | 5 | * | 
|  | 6 | * This file is licensed under GPLv2 | 
|  | 7 | * | 
|  | 8 | * The basic idea here is to enable a device to be attached to an | 
|  | 9 | * aritrary numer of classes without having to allocate storage for them. | 
|  | 10 | * Instead, the contained classes select the devices they need to attach | 
|  | 11 | * to via a matching function. | 
|  | 12 | */ | 
|  | 13 |  | 
|  | 14 | #include <linux/attribute_container.h> | 
|  | 15 | #include <linux/init.h> | 
|  | 16 | #include <linux/device.h> | 
|  | 17 | #include <linux/kernel.h> | 
|  | 18 | #include <linux/slab.h> | 
|  | 19 | #include <linux/list.h> | 
|  | 20 | #include <linux/module.h> | 
| Michael S. Tsirkin | f8916c1 | 2007-06-10 22:39:12 +0300 | [diff] [blame] | 21 | #include <linux/mutex.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 22 |  | 
| Ben Dooks | a1bdc7a | 2005-10-13 17:54:41 +0100 | [diff] [blame] | 23 | #include "base.h" | 
|  | 24 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 25 | /* This is a private structure used to tie the classdev and the | 
|  | 26 | * container .. it should never be visible outside this file */ | 
|  | 27 | struct internal_container { | 
| James Bottomley | 53c165e | 2005-08-22 10:06:19 -0500 | [diff] [blame] | 28 | struct klist_node node; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 29 | struct attribute_container *cont; | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 30 | struct device classdev; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 31 | }; | 
|  | 32 |  | 
| Linus Torvalds | caf39e8 | 2005-09-07 18:44:33 -0700 | [diff] [blame] | 33 | static void internal_container_klist_get(struct klist_node *n) | 
|  | 34 | { | 
|  | 35 | struct internal_container *ic = | 
|  | 36 | container_of(n, struct internal_container, node); | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 37 | get_device(&ic->classdev); | 
| Linus Torvalds | caf39e8 | 2005-09-07 18:44:33 -0700 | [diff] [blame] | 38 | } | 
|  | 39 |  | 
|  | 40 | static void internal_container_klist_put(struct klist_node *n) | 
|  | 41 | { | 
|  | 42 | struct internal_container *ic = | 
|  | 43 | container_of(n, struct internal_container, node); | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 44 | put_device(&ic->classdev); | 
| Linus Torvalds | caf39e8 | 2005-09-07 18:44:33 -0700 | [diff] [blame] | 45 | } | 
|  | 46 |  | 
|  | 47 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 48 | /** | 
|  | 49 | * attribute_container_classdev_to_container - given a classdev, return the container | 
|  | 50 | * | 
|  | 51 | * @classdev: the class device created by attribute_container_add_device. | 
|  | 52 | * | 
|  | 53 | * Returns the container associated with this classdev. | 
|  | 54 | */ | 
|  | 55 | struct attribute_container * | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 56 | attribute_container_classdev_to_container(struct device *classdev) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 57 | { | 
|  | 58 | struct internal_container *ic = | 
|  | 59 | container_of(classdev, struct internal_container, classdev); | 
|  | 60 | return ic->cont; | 
|  | 61 | } | 
|  | 62 | EXPORT_SYMBOL_GPL(attribute_container_classdev_to_container); | 
|  | 63 |  | 
| Denis Cheng | db1118a | 2007-12-06 02:24:40 +0800 | [diff] [blame] | 64 | static LIST_HEAD(attribute_container_list); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 65 |  | 
| Matthias Kaehlcke | 61a2f59 | 2007-04-26 00:12:09 -0700 | [diff] [blame] | 66 | static DEFINE_MUTEX(attribute_container_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 67 |  | 
|  | 68 | /** | 
|  | 69 | * attribute_container_register - register an attribute container | 
|  | 70 | * | 
|  | 71 | * @cont: The container to register.  This must be allocated by the | 
|  | 72 | *        callee and should also be zeroed by it. | 
|  | 73 | */ | 
|  | 74 | int | 
|  | 75 | attribute_container_register(struct attribute_container *cont) | 
|  | 76 | { | 
|  | 77 | INIT_LIST_HEAD(&cont->node); | 
| Linus Torvalds | caf39e8 | 2005-09-07 18:44:33 -0700 | [diff] [blame] | 78 | klist_init(&cont->containers,internal_container_klist_get, | 
|  | 79 | internal_container_klist_put); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 80 |  | 
| Matthias Kaehlcke | 61a2f59 | 2007-04-26 00:12:09 -0700 | [diff] [blame] | 81 | mutex_lock(&attribute_container_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 82 | list_add_tail(&cont->node, &attribute_container_list); | 
| Matthias Kaehlcke | 61a2f59 | 2007-04-26 00:12:09 -0700 | [diff] [blame] | 83 | mutex_unlock(&attribute_container_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 84 |  | 
|  | 85 | return 0; | 
|  | 86 | } | 
|  | 87 | EXPORT_SYMBOL_GPL(attribute_container_register); | 
|  | 88 |  | 
|  | 89 | /** | 
|  | 90 | * attribute_container_unregister - remove a container registration | 
|  | 91 | * | 
|  | 92 | * @cont: previously registered container to remove | 
|  | 93 | */ | 
|  | 94 | int | 
|  | 95 | attribute_container_unregister(struct attribute_container *cont) | 
|  | 96 | { | 
|  | 97 | int retval = -EBUSY; | 
| Matthias Kaehlcke | 61a2f59 | 2007-04-26 00:12:09 -0700 | [diff] [blame] | 98 | mutex_lock(&attribute_container_mutex); | 
| James Bottomley | 53c165e | 2005-08-22 10:06:19 -0500 | [diff] [blame] | 99 | spin_lock(&cont->containers.k_lock); | 
|  | 100 | if (!list_empty(&cont->containers.k_list)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 101 | goto out; | 
|  | 102 | retval = 0; | 
|  | 103 | list_del(&cont->node); | 
|  | 104 | out: | 
| James Bottomley | 53c165e | 2005-08-22 10:06:19 -0500 | [diff] [blame] | 105 | spin_unlock(&cont->containers.k_lock); | 
| Matthias Kaehlcke | 61a2f59 | 2007-04-26 00:12:09 -0700 | [diff] [blame] | 106 | mutex_unlock(&attribute_container_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 107 | return retval; | 
|  | 108 |  | 
|  | 109 | } | 
|  | 110 | EXPORT_SYMBOL_GPL(attribute_container_unregister); | 
|  | 111 |  | 
|  | 112 | /* private function used as class release */ | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 113 | static void attribute_container_release(struct device *classdev) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 114 | { | 
|  | 115 | struct internal_container *ic | 
|  | 116 | = container_of(classdev, struct internal_container, classdev); | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 117 | struct device *dev = classdev->parent; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 118 |  | 
|  | 119 | kfree(ic); | 
|  | 120 | put_device(dev); | 
|  | 121 | } | 
|  | 122 |  | 
|  | 123 | /** | 
|  | 124 | * attribute_container_add_device - see if any container is interested in dev | 
|  | 125 | * | 
|  | 126 | * @dev: device to add attributes to | 
|  | 127 | * @fn:	 function to trigger addition of class device. | 
|  | 128 | * | 
|  | 129 | * This function allocates storage for the class device(s) to be | 
|  | 130 | * attached to dev (one for each matching attribute_container).  If no | 
|  | 131 | * fn is provided, the code will simply register the class device via | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 132 | * device_add.  If a function is provided, it is expected to add | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 133 | * the class device at the appropriate time.  One of the things that | 
|  | 134 | * might be necessary is to allocate and initialise the classdev and | 
|  | 135 | * then add it a later time.  To do this, call this routine for | 
|  | 136 | * allocation and initialisation and then use | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 137 | * attribute_container_device_trigger() to call device_add() on | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 138 | * it.  Note: after this, the class device contains a reference to dev | 
|  | 139 | * which is not relinquished until the release of the classdev. | 
|  | 140 | */ | 
|  | 141 | void | 
|  | 142 | attribute_container_add_device(struct device *dev, | 
|  | 143 | int (*fn)(struct attribute_container *, | 
|  | 144 | struct device *, | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 145 | struct device *)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 146 | { | 
|  | 147 | struct attribute_container *cont; | 
|  | 148 |  | 
| Matthias Kaehlcke | 61a2f59 | 2007-04-26 00:12:09 -0700 | [diff] [blame] | 149 | mutex_lock(&attribute_container_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 150 | list_for_each_entry(cont, &attribute_container_list, node) { | 
|  | 151 | struct internal_container *ic; | 
|  | 152 |  | 
|  | 153 | if (attribute_container_no_classdevs(cont)) | 
|  | 154 | continue; | 
|  | 155 |  | 
|  | 156 | if (!cont->match(cont, dev)) | 
|  | 157 | continue; | 
| Jiri Slaby | 4aed064 | 2005-09-13 01:25:01 -0700 | [diff] [blame] | 158 |  | 
|  | 159 | ic = kzalloc(sizeof(*ic), GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 160 | if (!ic) { | 
|  | 161 | dev_printk(KERN_ERR, dev, "failed to allocate class container\n"); | 
|  | 162 | continue; | 
|  | 163 | } | 
| Jiri Slaby | 4aed064 | 2005-09-13 01:25:01 -0700 | [diff] [blame] | 164 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 165 | ic->cont = cont; | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 166 | device_initialize(&ic->classdev); | 
|  | 167 | ic->classdev.parent = get_device(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 168 | ic->classdev.class = cont->class; | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 169 | cont->class->dev_release = attribute_container_release; | 
| Kay Sievers | 1e0b2cf | 2008-10-30 01:36:48 +0100 | [diff] [blame] | 170 | dev_set_name(&ic->classdev, dev_name(dev)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 171 | if (fn) | 
|  | 172 | fn(cont, dev, &ic->classdev); | 
|  | 173 | else | 
|  | 174 | attribute_container_add_class_device(&ic->classdev); | 
| James Bottomley | 53c165e | 2005-08-22 10:06:19 -0500 | [diff] [blame] | 175 | klist_add_tail(&ic->node, &cont->containers); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 176 | } | 
| Matthias Kaehlcke | 61a2f59 | 2007-04-26 00:12:09 -0700 | [diff] [blame] | 177 | mutex_unlock(&attribute_container_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 178 | } | 
|  | 179 |  | 
| James Bottomley | 53c165e | 2005-08-22 10:06:19 -0500 | [diff] [blame] | 180 | /* FIXME: can't break out of this unless klist_iter_exit is also | 
|  | 181 | * called before doing the break | 
|  | 182 | */ | 
|  | 183 | #define klist_for_each_entry(pos, head, member, iter) \ | 
|  | 184 | for (klist_iter_init(head, iter); (pos = ({ \ | 
|  | 185 | struct klist_node *n = klist_next(iter); \ | 
| Linus Torvalds | caf39e8 | 2005-09-07 18:44:33 -0700 | [diff] [blame] | 186 | n ? container_of(n, typeof(*pos), member) : \ | 
|  | 187 | ({ klist_iter_exit(iter) ; NULL; }); \ | 
| James Bottomley | 53c165e | 2005-08-22 10:06:19 -0500 | [diff] [blame] | 188 | }) ) != NULL; ) | 
|  | 189 |  | 
|  | 190 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 191 | /** | 
|  | 192 | * attribute_container_remove_device - make device eligible for removal. | 
|  | 193 | * | 
|  | 194 | * @dev:  The generic device | 
|  | 195 | * @fn:	  A function to call to remove the device | 
|  | 196 | * | 
|  | 197 | * This routine triggers device removal.  If fn is NULL, then it is | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 198 | * simply done via device_unregister (note that if something | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 199 | * still has a reference to the classdev, then the memory occupied | 
|  | 200 | * will not be freed until the classdev is released).  If you want a | 
|  | 201 | * two phase release: remove from visibility and then delete the | 
|  | 202 | * device, then you should use this routine with a fn that calls | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 203 | * device_del() and then use attribute_container_device_trigger() | 
|  | 204 | * to do the final put on the classdev. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 205 | */ | 
|  | 206 | void | 
|  | 207 | attribute_container_remove_device(struct device *dev, | 
|  | 208 | void (*fn)(struct attribute_container *, | 
|  | 209 | struct device *, | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 210 | struct device *)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 211 | { | 
|  | 212 | struct attribute_container *cont; | 
|  | 213 |  | 
| Matthias Kaehlcke | 61a2f59 | 2007-04-26 00:12:09 -0700 | [diff] [blame] | 214 | mutex_lock(&attribute_container_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 215 | list_for_each_entry(cont, &attribute_container_list, node) { | 
| James Bottomley | 53c165e | 2005-08-22 10:06:19 -0500 | [diff] [blame] | 216 | struct internal_container *ic; | 
|  | 217 | struct klist_iter iter; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 218 |  | 
|  | 219 | if (attribute_container_no_classdevs(cont)) | 
|  | 220 | continue; | 
|  | 221 |  | 
|  | 222 | if (!cont->match(cont, dev)) | 
|  | 223 | continue; | 
| James Bottomley | 53c165e | 2005-08-22 10:06:19 -0500 | [diff] [blame] | 224 |  | 
|  | 225 | klist_for_each_entry(ic, &cont->containers, node, &iter) { | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 226 | if (dev != ic->classdev.parent) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 227 | continue; | 
| Linus Torvalds | caf39e8 | 2005-09-07 18:44:33 -0700 | [diff] [blame] | 228 | klist_del(&ic->node); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 229 | if (fn) | 
|  | 230 | fn(cont, dev, &ic->classdev); | 
|  | 231 | else { | 
|  | 232 | attribute_container_remove_attrs(&ic->classdev); | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 233 | device_unregister(&ic->classdev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 234 | } | 
|  | 235 | } | 
|  | 236 | } | 
| Matthias Kaehlcke | 61a2f59 | 2007-04-26 00:12:09 -0700 | [diff] [blame] | 237 | mutex_unlock(&attribute_container_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 238 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 239 |  | 
|  | 240 | /** | 
|  | 241 | * attribute_container_device_trigger - execute a trigger for each matching classdev | 
|  | 242 | * | 
|  | 243 | * @dev:  The generic device to run the trigger for | 
|  | 244 | * @fn	  the function to execute for each classdev. | 
|  | 245 | * | 
|  | 246 | * This funcion is for executing a trigger when you need to know both | 
|  | 247 | * the container and the classdev.  If you only care about the | 
|  | 248 | * container, then use attribute_container_trigger() instead. | 
|  | 249 | */ | 
|  | 250 | void | 
|  | 251 | attribute_container_device_trigger(struct device *dev, | 
|  | 252 | int (*fn)(struct attribute_container *, | 
|  | 253 | struct device *, | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 254 | struct device *)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 255 | { | 
|  | 256 | struct attribute_container *cont; | 
|  | 257 |  | 
| Matthias Kaehlcke | 61a2f59 | 2007-04-26 00:12:09 -0700 | [diff] [blame] | 258 | mutex_lock(&attribute_container_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 259 | list_for_each_entry(cont, &attribute_container_list, node) { | 
| James Bottomley | 53c165e | 2005-08-22 10:06:19 -0500 | [diff] [blame] | 260 | struct internal_container *ic; | 
|  | 261 | struct klist_iter iter; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 262 |  | 
|  | 263 | if (!cont->match(cont, dev)) | 
|  | 264 | continue; | 
|  | 265 |  | 
| James Bottomley | ebd8bb7 | 2005-08-15 16:13:19 -0500 | [diff] [blame] | 266 | if (attribute_container_no_classdevs(cont)) { | 
|  | 267 | fn(cont, dev, NULL); | 
|  | 268 | continue; | 
|  | 269 | } | 
|  | 270 |  | 
| James Bottomley | 53c165e | 2005-08-22 10:06:19 -0500 | [diff] [blame] | 271 | klist_for_each_entry(ic, &cont->containers, node, &iter) { | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 272 | if (dev == ic->classdev.parent) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 273 | fn(cont, dev, &ic->classdev); | 
|  | 274 | } | 
|  | 275 | } | 
| Matthias Kaehlcke | 61a2f59 | 2007-04-26 00:12:09 -0700 | [diff] [blame] | 276 | mutex_unlock(&attribute_container_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 277 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 278 |  | 
|  | 279 | /** | 
|  | 280 | * attribute_container_trigger - trigger a function for each matching container | 
|  | 281 | * | 
|  | 282 | * @dev:  The generic device to activate the trigger for | 
|  | 283 | * @fn:	  the function to trigger | 
|  | 284 | * | 
|  | 285 | * This routine triggers a function that only needs to know the | 
|  | 286 | * matching containers (not the classdev) associated with a device. | 
|  | 287 | * It is more lightweight than attribute_container_device_trigger, so | 
|  | 288 | * should be used in preference unless the triggering function | 
|  | 289 | * actually needs to know the classdev. | 
|  | 290 | */ | 
|  | 291 | void | 
|  | 292 | attribute_container_trigger(struct device *dev, | 
|  | 293 | int (*fn)(struct attribute_container *, | 
|  | 294 | struct device *)) | 
|  | 295 | { | 
|  | 296 | struct attribute_container *cont; | 
|  | 297 |  | 
| Matthias Kaehlcke | 61a2f59 | 2007-04-26 00:12:09 -0700 | [diff] [blame] | 298 | mutex_lock(&attribute_container_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 299 | list_for_each_entry(cont, &attribute_container_list, node) { | 
|  | 300 | if (cont->match(cont, dev)) | 
|  | 301 | fn(cont, dev); | 
|  | 302 | } | 
| Matthias Kaehlcke | 61a2f59 | 2007-04-26 00:12:09 -0700 | [diff] [blame] | 303 | mutex_unlock(&attribute_container_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 304 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 305 |  | 
|  | 306 | /** | 
|  | 307 | * attribute_container_add_attrs - add attributes | 
|  | 308 | * | 
|  | 309 | * @classdev: The class device | 
|  | 310 | * | 
|  | 311 | * This simply creates all the class device sysfs files from the | 
|  | 312 | * attributes listed in the container | 
|  | 313 | */ | 
|  | 314 | int | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 315 | attribute_container_add_attrs(struct device *classdev) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 316 | { | 
|  | 317 | struct attribute_container *cont = | 
|  | 318 | attribute_container_classdev_to_container(classdev); | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 319 | struct device_attribute **attrs = cont->attrs; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 320 | int i, error; | 
|  | 321 |  | 
| James Bottomley | fd11097 | 2008-01-02 18:48:47 -0600 | [diff] [blame] | 322 | BUG_ON(attrs && cont->grp); | 
|  | 323 |  | 
|  | 324 | if (!attrs && !cont->grp) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 325 | return 0; | 
|  | 326 |  | 
| James Bottomley | fd11097 | 2008-01-02 18:48:47 -0600 | [diff] [blame] | 327 | if (cont->grp) | 
|  | 328 | return sysfs_create_group(&classdev->kobj, cont->grp); | 
|  | 329 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 330 | for (i = 0; attrs[i]; i++) { | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 331 | error = device_create_file(classdev, attrs[i]); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 332 | if (error) | 
|  | 333 | return error; | 
|  | 334 | } | 
|  | 335 |  | 
|  | 336 | return 0; | 
|  | 337 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 338 |  | 
|  | 339 | /** | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 340 | * attribute_container_add_class_device - same function as device_add | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 341 | * | 
|  | 342 | * @classdev:	the class device to add | 
|  | 343 | * | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 344 | * This performs essentially the same function as device_add except for | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 345 | * attribute containers, namely add the classdev to the system and then | 
|  | 346 | * create the attribute files | 
|  | 347 | */ | 
|  | 348 | int | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 349 | attribute_container_add_class_device(struct device *classdev) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 350 | { | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 351 | int error = device_add(classdev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 352 | if (error) | 
|  | 353 | return error; | 
|  | 354 | return attribute_container_add_attrs(classdev); | 
|  | 355 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 356 |  | 
|  | 357 | /** | 
|  | 358 | * attribute_container_add_class_device_adapter - simple adapter for triggers | 
|  | 359 | * | 
|  | 360 | * This function is identical to attribute_container_add_class_device except | 
|  | 361 | * that it is designed to be called from the triggers | 
|  | 362 | */ | 
|  | 363 | int | 
|  | 364 | attribute_container_add_class_device_adapter(struct attribute_container *cont, | 
|  | 365 | struct device *dev, | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 366 | struct device *classdev) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 367 | { | 
|  | 368 | return attribute_container_add_class_device(classdev); | 
|  | 369 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 370 |  | 
|  | 371 | /** | 
|  | 372 | * attribute_container_remove_attrs - remove any attribute files | 
|  | 373 | * | 
|  | 374 | * @classdev: The class device to remove the files from | 
|  | 375 | * | 
|  | 376 | */ | 
|  | 377 | void | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 378 | attribute_container_remove_attrs(struct device *classdev) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 379 | { | 
|  | 380 | struct attribute_container *cont = | 
|  | 381 | attribute_container_classdev_to_container(classdev); | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 382 | struct device_attribute **attrs = cont->attrs; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 383 | int i; | 
|  | 384 |  | 
| James Bottomley | fd11097 | 2008-01-02 18:48:47 -0600 | [diff] [blame] | 385 | if (!attrs && !cont->grp) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 386 | return; | 
|  | 387 |  | 
| James Bottomley | fd11097 | 2008-01-02 18:48:47 -0600 | [diff] [blame] | 388 | if (cont->grp) { | 
|  | 389 | sysfs_remove_group(&classdev->kobj, cont->grp); | 
|  | 390 | return ; | 
|  | 391 | } | 
|  | 392 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 393 | for (i = 0; attrs[i]; i++) | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 394 | device_remove_file(classdev, attrs[i]); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 395 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 396 |  | 
|  | 397 | /** | 
|  | 398 | * attribute_container_class_device_del - equivalent of class_device_del | 
|  | 399 | * | 
|  | 400 | * @classdev: the class device | 
|  | 401 | * | 
|  | 402 | * This function simply removes all the attribute files and then calls | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 403 | * device_del. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 404 | */ | 
|  | 405 | void | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 406 | attribute_container_class_device_del(struct device *classdev) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 407 | { | 
|  | 408 | attribute_container_remove_attrs(classdev); | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 409 | device_del(classdev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 410 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 411 |  | 
| James Bottomley | d0a7e57 | 2005-08-14 17:09:01 -0500 | [diff] [blame] | 412 | /** | 
|  | 413 | * attribute_container_find_class_device - find the corresponding class_device | 
|  | 414 | * | 
|  | 415 | * @cont:	the container | 
|  | 416 | * @dev:	the generic device | 
|  | 417 | * | 
|  | 418 | * Looks up the device in the container's list of class devices and returns | 
|  | 419 | * the corresponding class_device. | 
|  | 420 | */ | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 421 | struct device * | 
| James Bottomley | d0a7e57 | 2005-08-14 17:09:01 -0500 | [diff] [blame] | 422 | attribute_container_find_class_device(struct attribute_container *cont, | 
|  | 423 | struct device *dev) | 
|  | 424 | { | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 425 | struct device *cdev = NULL; | 
| James Bottomley | d0a7e57 | 2005-08-14 17:09:01 -0500 | [diff] [blame] | 426 | struct internal_container *ic; | 
| James Bottomley | 53c165e | 2005-08-22 10:06:19 -0500 | [diff] [blame] | 427 | struct klist_iter iter; | 
| James Bottomley | d0a7e57 | 2005-08-14 17:09:01 -0500 | [diff] [blame] | 428 |  | 
| James Bottomley | 53c165e | 2005-08-22 10:06:19 -0500 | [diff] [blame] | 429 | klist_for_each_entry(ic, &cont->containers, node, &iter) { | 
| Tony Jones | ee959b0 | 2008-02-22 00:13:36 +0100 | [diff] [blame] | 430 | if (ic->classdev.parent == dev) { | 
| James Bottomley | d0a7e57 | 2005-08-14 17:09:01 -0500 | [diff] [blame] | 431 | cdev = &ic->classdev; | 
| James Bottomley | 53c165e | 2005-08-22 10:06:19 -0500 | [diff] [blame] | 432 | /* FIXME: must exit iterator then break */ | 
|  | 433 | klist_iter_exit(&iter); | 
| James Bottomley | d0a7e57 | 2005-08-14 17:09:01 -0500 | [diff] [blame] | 434 | break; | 
|  | 435 | } | 
|  | 436 | } | 
| James Bottomley | d0a7e57 | 2005-08-14 17:09:01 -0500 | [diff] [blame] | 437 |  | 
|  | 438 | return cdev; | 
|  | 439 | } | 
|  | 440 | EXPORT_SYMBOL_GPL(attribute_container_find_class_device); |