]>
Commit | Line | Data |
---|---|---|
2a41e607 RK |
1 | /* |
2 | * Componentized device handling. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 as | |
6 | * published by the Free Software Foundation. | |
7 | * | |
8 | * This is work in progress. We gather up the component devices into a list, | |
9 | * and bind them when instructed. At the moment, we're specific to the DRM | |
10 | * subsystem, and only handles one master device, but this doesn't have to be | |
11 | * the case. | |
12 | */ | |
13 | #include <linux/component.h> | |
14 | #include <linux/device.h> | |
15 | #include <linux/kref.h> | |
16 | #include <linux/list.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/mutex.h> | |
19 | #include <linux/slab.h> | |
20 | ||
ffc30b74 RK |
21 | struct component; |
22 | ||
ce657b1c RK |
23 | struct component_match_array { |
24 | void *data; | |
25 | int (*compare)(struct device *, void *); | |
26 | void (*release)(struct device *, void *); | |
27 | struct component *component; | |
28 | bool duplicate; | |
29 | }; | |
30 | ||
6955b582 RK |
31 | struct component_match { |
32 | size_t alloc; | |
33 | size_t num; | |
ce657b1c | 34 | struct component_match_array *compare; |
6955b582 RK |
35 | }; |
36 | ||
2a41e607 RK |
37 | struct master { |
38 | struct list_head node; | |
2a41e607 RK |
39 | bool bound; |
40 | ||
41 | const struct component_master_ops *ops; | |
42 | struct device *dev; | |
6955b582 | 43 | struct component_match *match; |
2a41e607 RK |
44 | }; |
45 | ||
46 | struct component { | |
47 | struct list_head node; | |
2a41e607 RK |
48 | struct master *master; |
49 | bool bound; | |
50 | ||
51 | const struct component_ops *ops; | |
52 | struct device *dev; | |
53 | }; | |
54 | ||
55 | static DEFINE_MUTEX(component_mutex); | |
56 | static LIST_HEAD(component_list); | |
57 | static LIST_HEAD(masters); | |
58 | ||
59 | static struct master *__master_find(struct device *dev, | |
60 | const struct component_master_ops *ops) | |
61 | { | |
62 | struct master *m; | |
63 | ||
64 | list_for_each_entry(m, &masters, node) | |
65 | if (m->dev == dev && (!ops || m->ops == ops)) | |
66 | return m; | |
67 | ||
68 | return NULL; | |
69 | } | |
70 | ||
ffc30b74 | 71 | static struct component *find_component(struct master *master, |
2a41e607 RK |
72 | int (*compare)(struct device *, void *), void *compare_data) |
73 | { | |
74 | struct component *c; | |
2a41e607 RK |
75 | |
76 | list_for_each_entry(c, &component_list, node) { | |
fcbcebce | 77 | if (c->master && c->master != master) |
2a41e607 RK |
78 | continue; |
79 | ||
ffc30b74 RK |
80 | if (compare(c->dev, compare_data)) |
81 | return c; | |
2a41e607 RK |
82 | } |
83 | ||
ffc30b74 | 84 | return NULL; |
2a41e607 | 85 | } |
2a41e607 | 86 | |
6955b582 RK |
87 | static int find_components(struct master *master) |
88 | { | |
89 | struct component_match *match = master->match; | |
90 | size_t i; | |
91 | int ret = 0; | |
92 | ||
6955b582 RK |
93 | /* |
94 | * Scan the array of match functions and attach | |
95 | * any components which are found to this master. | |
96 | */ | |
97 | for (i = 0; i < match->num; i++) { | |
ce657b1c | 98 | struct component_match_array *mc = &match->compare[i]; |
ffc30b74 RK |
99 | struct component *c; |
100 | ||
101 | dev_dbg(master->dev, "Looking for component %zu\n", i); | |
102 | ||
103 | if (match->compare[i].component) | |
104 | continue; | |
105 | ||
ce657b1c | 106 | c = find_component(master, mc->compare, mc->data); |
ffc30b74 RK |
107 | if (!c) { |
108 | ret = -ENXIO; | |
6955b582 | 109 | break; |
ffc30b74 RK |
110 | } |
111 | ||
112 | dev_dbg(master->dev, "found component %s, duplicate %u\n", dev_name(c->dev), !!c->master); | |
113 | ||
114 | /* Attach this component to the master */ | |
115 | match->compare[i].duplicate = !!c->master; | |
116 | match->compare[i].component = c; | |
117 | c->master = master; | |
6955b582 RK |
118 | } |
119 | return ret; | |
120 | } | |
121 | ||
ffc30b74 RK |
122 | /* Detach component from associated master */ |
123 | static void remove_component(struct master *master, struct component *c) | |
2a41e607 | 124 | { |
ffc30b74 | 125 | size_t i; |
2a41e607 | 126 | |
ffc30b74 RK |
127 | /* Detach the component from this master. */ |
128 | for (i = 0; i < master->match->num; i++) | |
129 | if (master->match->compare[i].component == c) | |
130 | master->match->compare[i].component = NULL; | |
2a41e607 RK |
131 | } |
132 | ||
133 | /* | |
134 | * Try to bring up a master. If component is NULL, we're interested in | |
135 | * this master, otherwise it's a component which must be present to try | |
136 | * and bring up the master. | |
137 | * | |
138 | * Returns 1 for successful bringup, 0 if not ready, or -ve errno. | |
139 | */ | |
140 | static int try_to_bring_up_master(struct master *master, | |
141 | struct component *component) | |
142 | { | |
c334940e RK |
143 | int ret; |
144 | ||
ffc30b74 RK |
145 | dev_dbg(master->dev, "trying to bring up master\n"); |
146 | ||
6955b582 | 147 | if (find_components(master)) { |
ffc30b74 RK |
148 | dev_dbg(master->dev, "master has incomplete components\n"); |
149 | return 0; | |
c334940e | 150 | } |
2a41e607 | 151 | |
c334940e | 152 | if (component && component->master != master) { |
ffc30b74 RK |
153 | dev_dbg(master->dev, "master is not for this component (%s)\n", |
154 | dev_name(component->dev)); | |
155 | return 0; | |
c334940e | 156 | } |
2a41e607 | 157 | |
ffc30b74 RK |
158 | if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) |
159 | return -ENOMEM; | |
2a41e607 | 160 | |
c334940e RK |
161 | /* Found all components */ |
162 | ret = master->ops->bind(master->dev); | |
163 | if (ret < 0) { | |
164 | devres_release_group(master->dev, NULL); | |
165 | dev_info(master->dev, "master bind failed: %d\n", ret); | |
ffc30b74 | 166 | return ret; |
c334940e | 167 | } |
9e1ccb4a | 168 | |
c334940e RK |
169 | master->bound = true; |
170 | return 1; | |
2a41e607 RK |
171 | } |
172 | ||
173 | static int try_to_bring_up_masters(struct component *component) | |
174 | { | |
175 | struct master *m; | |
176 | int ret = 0; | |
177 | ||
178 | list_for_each_entry(m, &masters, node) { | |
29f1c7fd RK |
179 | if (!m->bound) { |
180 | ret = try_to_bring_up_master(m, component); | |
181 | if (ret != 0) | |
182 | break; | |
183 | } | |
2a41e607 RK |
184 | } |
185 | ||
186 | return ret; | |
187 | } | |
188 | ||
189 | static void take_down_master(struct master *master) | |
190 | { | |
191 | if (master->bound) { | |
192 | master->ops->unbind(master->dev); | |
9e1ccb4a | 193 | devres_release_group(master->dev, NULL); |
2a41e607 RK |
194 | master->bound = false; |
195 | } | |
2a41e607 RK |
196 | } |
197 | ||
ce657b1c RK |
198 | static void component_match_release(struct device *master, |
199 | struct component_match *match) | |
6955b582 | 200 | { |
ce657b1c RK |
201 | unsigned int i; |
202 | ||
203 | for (i = 0; i < match->num; i++) { | |
204 | struct component_match_array *mc = &match->compare[i]; | |
205 | ||
206 | if (mc->release) | |
207 | mc->release(master, mc->data); | |
208 | } | |
6955b582 RK |
209 | } |
210 | ||
ce657b1c RK |
211 | static void devm_component_match_release(struct device *dev, void *res) |
212 | { | |
213 | component_match_release(dev, res); | |
214 | } | |
215 | ||
216 | static int component_match_realloc(struct device *dev, | |
6955b582 RK |
217 | struct component_match *match, size_t num) |
218 | { | |
ce657b1c | 219 | struct component_match_array *new; |
6955b582 | 220 | |
ce657b1c RK |
221 | if (match->alloc == num) |
222 | return 0; | |
6955b582 | 223 | |
ce657b1c | 224 | new = devm_kmalloc_array(dev, num, sizeof(*new), GFP_KERNEL); |
6955b582 | 225 | if (!new) |
ce657b1c | 226 | return -ENOMEM; |
6955b582 | 227 | |
ce657b1c RK |
228 | if (match->compare) { |
229 | memcpy(new, match->compare, sizeof(*new) * | |
230 | min(match->num, num)); | |
231 | devm_kfree(dev, match->compare); | |
6955b582 | 232 | } |
ce657b1c RK |
233 | match->compare = new; |
234 | match->alloc = num; | |
6955b582 | 235 | |
ce657b1c | 236 | return 0; |
6955b582 RK |
237 | } |
238 | ||
239 | /* | |
ce657b1c | 240 | * Add a component to be matched, with a release function. |
6955b582 RK |
241 | * |
242 | * The match array is first created or extended if necessary. | |
243 | */ | |
ce657b1c RK |
244 | void component_match_add_release(struct device *master, |
245 | struct component_match **matchptr, | |
246 | void (*release)(struct device *, void *), | |
6955b582 RK |
247 | int (*compare)(struct device *, void *), void *compare_data) |
248 | { | |
249 | struct component_match *match = *matchptr; | |
250 | ||
251 | if (IS_ERR(match)) | |
252 | return; | |
253 | ||
ce657b1c RK |
254 | if (!match) { |
255 | match = devres_alloc(devm_component_match_release, | |
256 | sizeof(*match), GFP_KERNEL); | |
257 | if (!match) { | |
258 | *matchptr = ERR_PTR(-ENOMEM); | |
259 | return; | |
260 | } | |
6955b582 | 261 | |
ce657b1c | 262 | devres_add(master, match); |
6955b582 RK |
263 | |
264 | *matchptr = match; | |
ce657b1c | 265 | } |
6955b582 | 266 | |
ce657b1c RK |
267 | if (match->num == match->alloc) { |
268 | size_t new_size = match ? match->alloc + 16 : 15; | |
269 | int ret; | |
270 | ||
271 | ret = component_match_realloc(master, match, new_size); | |
272 | if (ret) { | |
273 | *matchptr = ERR_PTR(ret); | |
6955b582 | 274 | return; |
ce657b1c | 275 | } |
6955b582 RK |
276 | } |
277 | ||
ce657b1c RK |
278 | match->compare[match->num].compare = compare; |
279 | match->compare[match->num].release = release; | |
6955b582 | 280 | match->compare[match->num].data = compare_data; |
ffc30b74 | 281 | match->compare[match->num].component = NULL; |
6955b582 RK |
282 | match->num++; |
283 | } | |
ce657b1c | 284 | EXPORT_SYMBOL(component_match_add_release); |
6955b582 RK |
285 | |
286 | int component_master_add_with_match(struct device *dev, | |
287 | const struct component_master_ops *ops, | |
288 | struct component_match *match) | |
2a41e607 RK |
289 | { |
290 | struct master *master; | |
291 | int ret; | |
292 | ||
fae9e2e0 | 293 | /* Reallocate the match array for its true size */ |
ce657b1c RK |
294 | ret = component_match_realloc(dev, match, match->num); |
295 | if (ret) | |
296 | return ret; | |
6955b582 | 297 | |
2a41e607 RK |
298 | master = kzalloc(sizeof(*master), GFP_KERNEL); |
299 | if (!master) | |
300 | return -ENOMEM; | |
301 | ||
302 | master->dev = dev; | |
303 | master->ops = ops; | |
6955b582 | 304 | master->match = match; |
2a41e607 RK |
305 | |
306 | /* Add to the list of available masters. */ | |
307 | mutex_lock(&component_mutex); | |
308 | list_add(&master->node, &masters); | |
309 | ||
310 | ret = try_to_bring_up_master(master, NULL); | |
311 | ||
312 | if (ret < 0) { | |
313 | /* Delete off the list if we weren't successful */ | |
314 | list_del(&master->node); | |
315 | kfree(master); | |
316 | } | |
317 | mutex_unlock(&component_mutex); | |
318 | ||
319 | return ret < 0 ? ret : 0; | |
320 | } | |
6955b582 RK |
321 | EXPORT_SYMBOL_GPL(component_master_add_with_match); |
322 | ||
2a41e607 RK |
323 | void component_master_del(struct device *dev, |
324 | const struct component_master_ops *ops) | |
325 | { | |
326 | struct master *master; | |
ffc30b74 | 327 | int i; |
2a41e607 RK |
328 | |
329 | mutex_lock(&component_mutex); | |
330 | master = __master_find(dev, ops); | |
331 | if (master) { | |
ffc30b74 RK |
332 | struct component_match *match = master->match; |
333 | ||
2a41e607 RK |
334 | take_down_master(master); |
335 | ||
336 | list_del(&master->node); | |
ffc30b74 RK |
337 | |
338 | if (match) { | |
339 | for (i = 0; i < match->num; i++) { | |
340 | struct component *c = match->compare[i].component; | |
341 | if (c) | |
342 | c->master = NULL; | |
343 | } | |
344 | } | |
2a41e607 RK |
345 | kfree(master); |
346 | } | |
347 | mutex_unlock(&component_mutex); | |
348 | } | |
349 | EXPORT_SYMBOL_GPL(component_master_del); | |
350 | ||
351 | static void component_unbind(struct component *component, | |
352 | struct master *master, void *data) | |
353 | { | |
354 | WARN_ON(!component->bound); | |
355 | ||
356 | component->ops->unbind(component->dev, master->dev, data); | |
357 | component->bound = false; | |
358 | ||
359 | /* Release all resources claimed in the binding of this component */ | |
360 | devres_release_group(component->dev, component); | |
361 | } | |
362 | ||
363 | void component_unbind_all(struct device *master_dev, void *data) | |
364 | { | |
365 | struct master *master; | |
366 | struct component *c; | |
ffc30b74 | 367 | size_t i; |
2a41e607 RK |
368 | |
369 | WARN_ON(!mutex_is_locked(&component_mutex)); | |
370 | ||
371 | master = __master_find(master_dev, NULL); | |
372 | if (!master) | |
373 | return; | |
374 | ||
ffc30b74 RK |
375 | /* Unbind components in reverse order */ |
376 | for (i = master->match->num; i--; ) | |
377 | if (!master->match->compare[i].duplicate) { | |
378 | c = master->match->compare[i].component; | |
379 | component_unbind(c, master, data); | |
380 | } | |
2a41e607 RK |
381 | } |
382 | EXPORT_SYMBOL_GPL(component_unbind_all); | |
383 | ||
384 | static int component_bind(struct component *component, struct master *master, | |
385 | void *data) | |
386 | { | |
387 | int ret; | |
388 | ||
389 | /* | |
390 | * Each component initialises inside its own devres group. | |
391 | * This allows us to roll-back a failed component without | |
392 | * affecting anything else. | |
393 | */ | |
394 | if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) | |
395 | return -ENOMEM; | |
396 | ||
397 | /* | |
398 | * Also open a group for the device itself: this allows us | |
399 | * to release the resources claimed against the sub-device | |
400 | * at the appropriate moment. | |
401 | */ | |
402 | if (!devres_open_group(component->dev, component, GFP_KERNEL)) { | |
403 | devres_release_group(master->dev, NULL); | |
404 | return -ENOMEM; | |
405 | } | |
406 | ||
407 | dev_dbg(master->dev, "binding %s (ops %ps)\n", | |
408 | dev_name(component->dev), component->ops); | |
409 | ||
410 | ret = component->ops->bind(component->dev, master->dev, data); | |
411 | if (!ret) { | |
412 | component->bound = true; | |
413 | ||
414 | /* | |
415 | * Close the component device's group so that resources | |
416 | * allocated in the binding are encapsulated for removal | |
417 | * at unbind. Remove the group on the DRM device as we | |
418 | * can clean those resources up independently. | |
419 | */ | |
420 | devres_close_group(component->dev, NULL); | |
421 | devres_remove_group(master->dev, NULL); | |
422 | ||
423 | dev_info(master->dev, "bound %s (ops %ps)\n", | |
424 | dev_name(component->dev), component->ops); | |
425 | } else { | |
426 | devres_release_group(component->dev, NULL); | |
427 | devres_release_group(master->dev, NULL); | |
428 | ||
429 | dev_err(master->dev, "failed to bind %s (ops %ps): %d\n", | |
430 | dev_name(component->dev), component->ops, ret); | |
431 | } | |
432 | ||
433 | return ret; | |
434 | } | |
435 | ||
436 | int component_bind_all(struct device *master_dev, void *data) | |
437 | { | |
438 | struct master *master; | |
439 | struct component *c; | |
ffc30b74 | 440 | size_t i; |
2a41e607 RK |
441 | int ret = 0; |
442 | ||
443 | WARN_ON(!mutex_is_locked(&component_mutex)); | |
444 | ||
445 | master = __master_find(master_dev, NULL); | |
446 | if (!master) | |
447 | return -EINVAL; | |
448 | ||
ffc30b74 RK |
449 | /* Bind components in match order */ |
450 | for (i = 0; i < master->match->num; i++) | |
451 | if (!master->match->compare[i].duplicate) { | |
452 | c = master->match->compare[i].component; | |
453 | ret = component_bind(c, master, data); | |
454 | if (ret) | |
455 | break; | |
456 | } | |
2a41e607 RK |
457 | |
458 | if (ret != 0) { | |
ffc30b74 RK |
459 | for (; i--; ) |
460 | if (!master->match->compare[i].duplicate) { | |
461 | c = master->match->compare[i].component; | |
462 | component_unbind(c, master, data); | |
463 | } | |
2a41e607 RK |
464 | } |
465 | ||
466 | return ret; | |
467 | } | |
468 | EXPORT_SYMBOL_GPL(component_bind_all); | |
469 | ||
470 | int component_add(struct device *dev, const struct component_ops *ops) | |
471 | { | |
472 | struct component *component; | |
473 | int ret; | |
474 | ||
475 | component = kzalloc(sizeof(*component), GFP_KERNEL); | |
476 | if (!component) | |
477 | return -ENOMEM; | |
478 | ||
479 | component->ops = ops; | |
480 | component->dev = dev; | |
481 | ||
482 | dev_dbg(dev, "adding component (ops %ps)\n", ops); | |
483 | ||
484 | mutex_lock(&component_mutex); | |
485 | list_add_tail(&component->node, &component_list); | |
486 | ||
487 | ret = try_to_bring_up_masters(component); | |
488 | if (ret < 0) { | |
489 | list_del(&component->node); | |
490 | ||
491 | kfree(component); | |
492 | } | |
493 | mutex_unlock(&component_mutex); | |
494 | ||
495 | return ret < 0 ? ret : 0; | |
496 | } | |
497 | EXPORT_SYMBOL_GPL(component_add); | |
498 | ||
499 | void component_del(struct device *dev, const struct component_ops *ops) | |
500 | { | |
501 | struct component *c, *component = NULL; | |
502 | ||
503 | mutex_lock(&component_mutex); | |
504 | list_for_each_entry(c, &component_list, node) | |
505 | if (c->dev == dev && c->ops == ops) { | |
506 | list_del(&c->node); | |
507 | component = c; | |
508 | break; | |
509 | } | |
510 | ||
ffc30b74 | 511 | if (component && component->master) { |
2a41e607 | 512 | take_down_master(component->master); |
ffc30b74 RK |
513 | remove_component(component->master, component); |
514 | } | |
2a41e607 RK |
515 | |
516 | mutex_unlock(&component_mutex); | |
517 | ||
518 | WARN_ON(!component); | |
519 | kfree(component); | |
520 | } | |
521 | EXPORT_SYMBOL_GPL(component_del); | |
522 | ||
523 | MODULE_LICENSE("GPL v2"); |