diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/base/attribute_container.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/base/attribute_container.c')
-rw-r--r-- | drivers/base/attribute_container.c | 376 |
1 files changed, 376 insertions, 0 deletions
diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c new file mode 100644 index 000000000000..ec615d854be9 --- /dev/null +++ b/drivers/base/attribute_container.c | |||
@@ -0,0 +1,376 @@ | |||
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> | ||
21 | |||
22 | /* This is a private structure used to tie the classdev and the | ||
23 | * container .. it should never be visible outside this file */ | ||
24 | struct internal_container { | ||
25 | struct list_head node; | ||
26 | struct attribute_container *cont; | ||
27 | struct class_device classdev; | ||
28 | }; | ||
29 | |||
30 | /** | ||
31 | * attribute_container_classdev_to_container - given a classdev, return the container | ||
32 | * | ||
33 | * @classdev: the class device created by attribute_container_add_device. | ||
34 | * | ||
35 | * Returns the container associated with this classdev. | ||
36 | */ | ||
37 | struct attribute_container * | ||
38 | attribute_container_classdev_to_container(struct class_device *classdev) | ||
39 | { | ||
40 | struct internal_container *ic = | ||
41 | container_of(classdev, struct internal_container, classdev); | ||
42 | return ic->cont; | ||
43 | } | ||
44 | EXPORT_SYMBOL_GPL(attribute_container_classdev_to_container); | ||
45 | |||
46 | static struct list_head attribute_container_list; | ||
47 | |||
48 | static DECLARE_MUTEX(attribute_container_mutex); | ||
49 | |||
50 | /** | ||
51 | * attribute_container_register - register an attribute container | ||
52 | * | ||
53 | * @cont: The container to register. This must be allocated by the | ||
54 | * callee and should also be zeroed by it. | ||
55 | */ | ||
56 | int | ||
57 | attribute_container_register(struct attribute_container *cont) | ||
58 | { | ||
59 | INIT_LIST_HEAD(&cont->node); | ||
60 | INIT_LIST_HEAD(&cont->containers); | ||
61 | |||
62 | down(&attribute_container_mutex); | ||
63 | list_add_tail(&cont->node, &attribute_container_list); | ||
64 | up(&attribute_container_mutex); | ||
65 | |||
66 | return 0; | ||
67 | } | ||
68 | EXPORT_SYMBOL_GPL(attribute_container_register); | ||
69 | |||
70 | /** | ||
71 | * attribute_container_unregister - remove a container registration | ||
72 | * | ||
73 | * @cont: previously registered container to remove | ||
74 | */ | ||
75 | int | ||
76 | attribute_container_unregister(struct attribute_container *cont) | ||
77 | { | ||
78 | int retval = -EBUSY; | ||
79 | down(&attribute_container_mutex); | ||
80 | if (!list_empty(&cont->containers)) | ||
81 | goto out; | ||
82 | retval = 0; | ||
83 | list_del(&cont->node); | ||
84 | out: | ||
85 | up(&attribute_container_mutex); | ||
86 | return retval; | ||
87 | |||
88 | } | ||
89 | EXPORT_SYMBOL_GPL(attribute_container_unregister); | ||
90 | |||
91 | /* private function used as class release */ | ||
92 | static void attribute_container_release(struct class_device *classdev) | ||
93 | { | ||
94 | struct internal_container *ic | ||
95 | = container_of(classdev, struct internal_container, classdev); | ||
96 | struct device *dev = classdev->dev; | ||
97 | |||
98 | kfree(ic); | ||
99 | put_device(dev); | ||
100 | } | ||
101 | |||
102 | /** | ||
103 | * attribute_container_add_device - see if any container is interested in dev | ||
104 | * | ||
105 | * @dev: device to add attributes to | ||
106 | * @fn: function to trigger addition of class device. | ||
107 | * | ||
108 | * This function allocates storage for the class device(s) to be | ||
109 | * attached to dev (one for each matching attribute_container). If no | ||
110 | * fn is provided, the code will simply register the class device via | ||
111 | * class_device_add. If a function is provided, it is expected to add | ||
112 | * the class device at the appropriate time. One of the things that | ||
113 | * might be necessary is to allocate and initialise the classdev and | ||
114 | * then add it a later time. To do this, call this routine for | ||
115 | * allocation and initialisation and then use | ||
116 | * attribute_container_device_trigger() to call class_device_add() on | ||
117 | * it. Note: after this, the class device contains a reference to dev | ||
118 | * which is not relinquished until the release of the classdev. | ||
119 | */ | ||
120 | void | ||
121 | attribute_container_add_device(struct device *dev, | ||
122 | int (*fn)(struct attribute_container *, | ||
123 | struct device *, | ||
124 | struct class_device *)) | ||
125 | { | ||
126 | struct attribute_container *cont; | ||
127 | |||
128 | down(&attribute_container_mutex); | ||
129 | list_for_each_entry(cont, &attribute_container_list, node) { | ||
130 | struct internal_container *ic; | ||
131 | |||
132 | if (attribute_container_no_classdevs(cont)) | ||
133 | continue; | ||
134 | |||
135 | if (!cont->match(cont, dev)) | ||
136 | continue; | ||
137 | ic = kmalloc(sizeof(struct internal_container), GFP_KERNEL); | ||
138 | if (!ic) { | ||
139 | dev_printk(KERN_ERR, dev, "failed to allocate class container\n"); | ||
140 | continue; | ||
141 | } | ||
142 | memset(ic, 0, sizeof(struct internal_container)); | ||
143 | INIT_LIST_HEAD(&ic->node); | ||
144 | ic->cont = cont; | ||
145 | class_device_initialize(&ic->classdev); | ||
146 | ic->classdev.dev = get_device(dev); | ||
147 | ic->classdev.class = cont->class; | ||
148 | cont->class->release = attribute_container_release; | ||
149 | strcpy(ic->classdev.class_id, dev->bus_id); | ||
150 | if (fn) | ||
151 | fn(cont, dev, &ic->classdev); | ||
152 | else | ||
153 | attribute_container_add_class_device(&ic->classdev); | ||
154 | list_add_tail(&ic->node, &cont->containers); | ||
155 | } | ||
156 | up(&attribute_container_mutex); | ||
157 | } | ||
158 | |||
159 | /** | ||
160 | * attribute_container_remove_device - make device eligible for removal. | ||
161 | * | ||
162 | * @dev: The generic device | ||
163 | * @fn: A function to call to remove the device | ||
164 | * | ||
165 | * This routine triggers device removal. If fn is NULL, then it is | ||
166 | * simply done via class_device_unregister (note that if something | ||
167 | * still has a reference to the classdev, then the memory occupied | ||
168 | * will not be freed until the classdev is released). If you want a | ||
169 | * two phase release: remove from visibility and then delete the | ||
170 | * device, then you should use this routine with a fn that calls | ||
171 | * class_device_del() and then use | ||
172 | * attribute_container_device_trigger() to do the final put on the | ||
173 | * classdev. | ||
174 | */ | ||
175 | void | ||
176 | attribute_container_remove_device(struct device *dev, | ||
177 | void (*fn)(struct attribute_container *, | ||
178 | struct device *, | ||
179 | struct class_device *)) | ||
180 | { | ||
181 | struct attribute_container *cont; | ||
182 | |||
183 | down(&attribute_container_mutex); | ||
184 | list_for_each_entry(cont, &attribute_container_list, node) { | ||
185 | struct internal_container *ic, *tmp; | ||
186 | |||
187 | if (attribute_container_no_classdevs(cont)) | ||
188 | continue; | ||
189 | |||
190 | if (!cont->match(cont, dev)) | ||
191 | continue; | ||
192 | list_for_each_entry_safe(ic, tmp, &cont->containers, node) { | ||
193 | if (dev != ic->classdev.dev) | ||
194 | continue; | ||
195 | list_del(&ic->node); | ||
196 | if (fn) | ||
197 | fn(cont, dev, &ic->classdev); | ||
198 | else { | ||
199 | attribute_container_remove_attrs(&ic->classdev); | ||
200 | class_device_unregister(&ic->classdev); | ||
201 | } | ||
202 | } | ||
203 | } | ||
204 | up(&attribute_container_mutex); | ||
205 | } | ||
206 | EXPORT_SYMBOL_GPL(attribute_container_remove_device); | ||
207 | |||
208 | /** | ||
209 | * attribute_container_device_trigger - execute a trigger for each matching classdev | ||
210 | * | ||
211 | * @dev: The generic device to run the trigger for | ||
212 | * @fn the function to execute for each classdev. | ||
213 | * | ||
214 | * This funcion is for executing a trigger when you need to know both | ||
215 | * the container and the classdev. If you only care about the | ||
216 | * container, then use attribute_container_trigger() instead. | ||
217 | */ | ||
218 | void | ||
219 | attribute_container_device_trigger(struct device *dev, | ||
220 | int (*fn)(struct attribute_container *, | ||
221 | struct device *, | ||
222 | struct class_device *)) | ||
223 | { | ||
224 | struct attribute_container *cont; | ||
225 | |||
226 | down(&attribute_container_mutex); | ||
227 | list_for_each_entry(cont, &attribute_container_list, node) { | ||
228 | struct internal_container *ic, *tmp; | ||
229 | |||
230 | if (!cont->match(cont, dev)) | ||
231 | continue; | ||
232 | |||
233 | list_for_each_entry_safe(ic, tmp, &cont->containers, node) { | ||
234 | if (dev == ic->classdev.dev) | ||
235 | fn(cont, dev, &ic->classdev); | ||
236 | } | ||
237 | } | ||
238 | up(&attribute_container_mutex); | ||
239 | } | ||
240 | EXPORT_SYMBOL_GPL(attribute_container_device_trigger); | ||
241 | |||
242 | /** | ||
243 | * attribute_container_trigger - trigger a function for each matching container | ||
244 | * | ||
245 | * @dev: The generic device to activate the trigger for | ||
246 | * @fn: the function to trigger | ||
247 | * | ||
248 | * This routine triggers a function that only needs to know the | ||
249 | * matching containers (not the classdev) associated with a device. | ||
250 | * It is more lightweight than attribute_container_device_trigger, so | ||
251 | * should be used in preference unless the triggering function | ||
252 | * actually needs to know the classdev. | ||
253 | */ | ||
254 | void | ||
255 | attribute_container_trigger(struct device *dev, | ||
256 | int (*fn)(struct attribute_container *, | ||
257 | struct device *)) | ||
258 | { | ||
259 | struct attribute_container *cont; | ||
260 | |||
261 | down(&attribute_container_mutex); | ||
262 | list_for_each_entry(cont, &attribute_container_list, node) { | ||
263 | if (cont->match(cont, dev)) | ||
264 | fn(cont, dev); | ||
265 | } | ||
266 | up(&attribute_container_mutex); | ||
267 | } | ||
268 | EXPORT_SYMBOL_GPL(attribute_container_trigger); | ||
269 | |||
270 | /** | ||
271 | * attribute_container_add_attrs - add attributes | ||
272 | * | ||
273 | * @classdev: The class device | ||
274 | * | ||
275 | * This simply creates all the class device sysfs files from the | ||
276 | * attributes listed in the container | ||
277 | */ | ||
278 | int | ||
279 | attribute_container_add_attrs(struct class_device *classdev) | ||
280 | { | ||
281 | struct attribute_container *cont = | ||
282 | attribute_container_classdev_to_container(classdev); | ||
283 | struct class_device_attribute **attrs = cont->attrs; | ||
284 | int i, error; | ||
285 | |||
286 | if (!attrs) | ||
287 | return 0; | ||
288 | |||
289 | for (i = 0; attrs[i]; i++) { | ||
290 | error = class_device_create_file(classdev, attrs[i]); | ||
291 | if (error) | ||
292 | return error; | ||
293 | } | ||
294 | |||
295 | return 0; | ||
296 | } | ||
297 | EXPORT_SYMBOL_GPL(attribute_container_add_attrs); | ||
298 | |||
299 | /** | ||
300 | * attribute_container_add_class_device - same function as class_device_add | ||
301 | * | ||
302 | * @classdev: the class device to add | ||
303 | * | ||
304 | * This performs essentially the same function as class_device_add except for | ||
305 | * attribute containers, namely add the classdev to the system and then | ||
306 | * create the attribute files | ||
307 | */ | ||
308 | int | ||
309 | attribute_container_add_class_device(struct class_device *classdev) | ||
310 | { | ||
311 | int error = class_device_add(classdev); | ||
312 | if (error) | ||
313 | return error; | ||
314 | return attribute_container_add_attrs(classdev); | ||
315 | } | ||
316 | EXPORT_SYMBOL_GPL(attribute_container_add_class_device); | ||
317 | |||
318 | /** | ||
319 | * attribute_container_add_class_device_adapter - simple adapter for triggers | ||
320 | * | ||
321 | * This function is identical to attribute_container_add_class_device except | ||
322 | * that it is designed to be called from the triggers | ||
323 | */ | ||
324 | int | ||
325 | attribute_container_add_class_device_adapter(struct attribute_container *cont, | ||
326 | struct device *dev, | ||
327 | struct class_device *classdev) | ||
328 | { | ||
329 | return attribute_container_add_class_device(classdev); | ||
330 | } | ||
331 | EXPORT_SYMBOL_GPL(attribute_container_add_class_device_adapter); | ||
332 | |||
333 | /** | ||
334 | * attribute_container_remove_attrs - remove any attribute files | ||
335 | * | ||
336 | * @classdev: The class device to remove the files from | ||
337 | * | ||
338 | */ | ||
339 | void | ||
340 | attribute_container_remove_attrs(struct class_device *classdev) | ||
341 | { | ||
342 | struct attribute_container *cont = | ||
343 | attribute_container_classdev_to_container(classdev); | ||
344 | struct class_device_attribute **attrs = cont->attrs; | ||
345 | int i; | ||
346 | |||
347 | if (!attrs) | ||
348 | return; | ||
349 | |||
350 | for (i = 0; attrs[i]; i++) | ||
351 | class_device_remove_file(classdev, attrs[i]); | ||
352 | } | ||
353 | EXPORT_SYMBOL_GPL(attribute_container_remove_attrs); | ||
354 | |||
355 | /** | ||
356 | * attribute_container_class_device_del - equivalent of class_device_del | ||
357 | * | ||
358 | * @classdev: the class device | ||
359 | * | ||
360 | * This function simply removes all the attribute files and then calls | ||
361 | * class_device_del. | ||
362 | */ | ||
363 | void | ||
364 | attribute_container_class_device_del(struct class_device *classdev) | ||
365 | { | ||
366 | attribute_container_remove_attrs(classdev); | ||
367 | class_device_del(classdev); | ||
368 | } | ||
369 | EXPORT_SYMBOL_GPL(attribute_container_class_device_del); | ||
370 | |||
371 | int __init | ||
372 | attribute_container_init(void) | ||
373 | { | ||
374 | INIT_LIST_HEAD(&attribute_container_list); | ||
375 | return 0; | ||
376 | } | ||