Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0
2 : : /*
3 : : * Software nodes for the firmware node framework.
4 : : *
5 : : * Copyright (C) 2018, Intel Corporation
6 : : * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
7 : : */
8 : :
9 : : #include <linux/device.h>
10 : : #include <linux/kernel.h>
11 : : #include <linux/property.h>
12 : : #include <linux/slab.h>
13 : :
14 : : struct swnode {
15 : : int id;
16 : : struct kobject kobj;
17 : : struct fwnode_handle fwnode;
18 : : const struct software_node *node;
19 : :
20 : : /* hierarchy */
21 : : struct ida child_ids;
22 : : struct list_head entry;
23 : : struct list_head children;
24 : : struct swnode *parent;
25 : :
26 : : unsigned int allocated:1;
27 : : };
28 : :
29 : : static DEFINE_IDA(swnode_root_ids);
30 : : static struct kset *swnode_kset;
31 : :
32 : : #define kobj_to_swnode(_kobj_) container_of(_kobj_, struct swnode, kobj)
33 : :
34 : : static const struct fwnode_operations software_node_ops;
35 : :
36 : 404 : bool is_software_node(const struct fwnode_handle *fwnode)
37 : : {
38 [ + - + - : 32724 : return !IS_ERR_OR_NULL(fwnode) && fwnode->ops == &software_node_ops;
- + # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # -
+ # # ]
39 : : }
40 : : EXPORT_SYMBOL_GPL(is_software_node);
41 : :
42 : : #define to_swnode(__fwnode) \
43 : : ({ \
44 : : typeof(__fwnode) __to_swnode_fwnode = __fwnode; \
45 : : \
46 : : is_software_node(__to_swnode_fwnode) ? \
47 : : container_of(__to_swnode_fwnode, \
48 : : struct swnode, fwnode) : NULL; \
49 : : })
50 : :
51 : : static struct swnode *
52 : 0 : software_node_to_swnode(const struct software_node *node)
53 : : {
54 : : struct swnode *swnode = NULL;
55 : : struct kobject *k;
56 : :
57 [ # # ]: 0 : if (!node)
58 : : return NULL;
59 : :
60 : 0 : spin_lock(&swnode_kset->list_lock);
61 : :
62 [ # # ]: 0 : list_for_each_entry(k, &swnode_kset->list, entry) {
63 : 0 : swnode = kobj_to_swnode(k);
64 [ # # ]: 0 : if (swnode->node == node)
65 : : break;
66 : : swnode = NULL;
67 : : }
68 : :
69 : : spin_unlock(&swnode_kset->list_lock);
70 : :
71 : 0 : return swnode;
72 : : }
73 : :
74 : 0 : const struct software_node *to_software_node(struct fwnode_handle *fwnode)
75 : : {
76 [ # # ]: 0 : struct swnode *swnode = to_swnode(fwnode);
77 : :
78 [ # # ]: 0 : return swnode ? swnode->node : NULL;
79 : : }
80 : : EXPORT_SYMBOL_GPL(to_software_node);
81 : :
82 : 0 : struct fwnode_handle *software_node_fwnode(const struct software_node *node)
83 : : {
84 : 0 : struct swnode *swnode = software_node_to_swnode(node);
85 : :
86 [ # # # # ]: 0 : return swnode ? &swnode->fwnode : NULL;
87 : : }
88 : : EXPORT_SYMBOL_GPL(software_node_fwnode);
89 : :
90 : : /* -------------------------------------------------------------------------- */
91 : : /* property_entry processing */
92 : :
93 : : static const struct property_entry *
94 : 0 : property_entry_get(const struct property_entry *prop, const char *name)
95 : : {
96 [ # # ]: 0 : if (!prop)
97 : : return NULL;
98 : :
99 [ # # ]: 0 : for (; prop->name; prop++)
100 [ # # ]: 0 : if (!strcmp(name, prop->name))
101 : 0 : return prop;
102 : :
103 : : return NULL;
104 : : }
105 : :
106 : : static void
107 : 0 : property_set_pointer(struct property_entry *prop, const void *pointer)
108 : : {
109 [ # # # # : 0 : switch (prop->type) {
# # ]
110 : : case DEV_PROP_U8:
111 [ # # ]: 0 : if (prop->is_array)
112 : 0 : prop->pointer.u8_data = pointer;
113 : : else
114 : 0 : prop->value.u8_data = *((u8 *)pointer);
115 : : break;
116 : : case DEV_PROP_U16:
117 [ # # ]: 0 : if (prop->is_array)
118 : 0 : prop->pointer.u16_data = pointer;
119 : : else
120 : 0 : prop->value.u16_data = *((u16 *)pointer);
121 : : break;
122 : : case DEV_PROP_U32:
123 [ # # ]: 0 : if (prop->is_array)
124 : 0 : prop->pointer.u32_data = pointer;
125 : : else
126 : 0 : prop->value.u32_data = *((u32 *)pointer);
127 : : break;
128 : : case DEV_PROP_U64:
129 [ # # ]: 0 : if (prop->is_array)
130 : 0 : prop->pointer.u64_data = pointer;
131 : : else
132 : 0 : prop->value.u64_data = *((u64 *)pointer);
133 : : break;
134 : : case DEV_PROP_STRING:
135 [ # # ]: 0 : if (prop->is_array)
136 : 0 : prop->pointer.str = pointer;
137 : : else
138 : 0 : prop->value.str = pointer;
139 : : break;
140 : : default:
141 : : break;
142 : : }
143 : 0 : }
144 : :
145 : 0 : static const void *property_get_pointer(const struct property_entry *prop)
146 : : {
147 [ # # # # : 0 : switch (prop->type) {
# # ]
148 : : case DEV_PROP_U8:
149 [ # # ]: 0 : if (prop->is_array)
150 : 0 : return prop->pointer.u8_data;
151 : 0 : return &prop->value.u8_data;
152 : : case DEV_PROP_U16:
153 [ # # ]: 0 : if (prop->is_array)
154 : 0 : return prop->pointer.u16_data;
155 : 0 : return &prop->value.u16_data;
156 : : case DEV_PROP_U32:
157 [ # # ]: 0 : if (prop->is_array)
158 : 0 : return prop->pointer.u32_data;
159 : 0 : return &prop->value.u32_data;
160 : : case DEV_PROP_U64:
161 [ # # ]: 0 : if (prop->is_array)
162 : 0 : return prop->pointer.u64_data;
163 : 0 : return &prop->value.u64_data;
164 : : case DEV_PROP_STRING:
165 [ # # ]: 0 : if (prop->is_array)
166 : 0 : return prop->pointer.str;
167 : 0 : return &prop->value.str;
168 : : default:
169 : : return NULL;
170 : : }
171 : : }
172 : :
173 : 0 : static const void *property_entry_find(const struct property_entry *props,
174 : : const char *propname, size_t length)
175 : : {
176 : : const struct property_entry *prop;
177 : : const void *pointer;
178 : :
179 : 0 : prop = property_entry_get(props, propname);
180 [ # # ]: 0 : if (!prop)
181 : : return ERR_PTR(-EINVAL);
182 : 0 : pointer = property_get_pointer(prop);
183 [ # # ]: 0 : if (!pointer)
184 : : return ERR_PTR(-ENODATA);
185 [ # # ]: 0 : if (length > prop->length)
186 : : return ERR_PTR(-EOVERFLOW);
187 : 0 : return pointer;
188 : : }
189 : :
190 : 0 : static int property_entry_read_u8_array(const struct property_entry *props,
191 : : const char *propname,
192 : : u8 *values, size_t nval)
193 : : {
194 : : const void *pointer;
195 : : size_t length = nval * sizeof(*values);
196 : :
197 : 0 : pointer = property_entry_find(props, propname, length);
198 [ # # ]: 0 : if (IS_ERR(pointer))
199 : 0 : return PTR_ERR(pointer);
200 : :
201 : 0 : memcpy(values, pointer, length);
202 : 0 : return 0;
203 : : }
204 : :
205 : 0 : static int property_entry_read_u16_array(const struct property_entry *props,
206 : : const char *propname,
207 : : u16 *values, size_t nval)
208 : : {
209 : : const void *pointer;
210 : 0 : size_t length = nval * sizeof(*values);
211 : :
212 : 0 : pointer = property_entry_find(props, propname, length);
213 [ # # ]: 0 : if (IS_ERR(pointer))
214 : 0 : return PTR_ERR(pointer);
215 : :
216 : 0 : memcpy(values, pointer, length);
217 : 0 : return 0;
218 : : }
219 : :
220 : 0 : static int property_entry_read_u32_array(const struct property_entry *props,
221 : : const char *propname,
222 : : u32 *values, size_t nval)
223 : : {
224 : : const void *pointer;
225 : 0 : size_t length = nval * sizeof(*values);
226 : :
227 : 0 : pointer = property_entry_find(props, propname, length);
228 [ # # ]: 0 : if (IS_ERR(pointer))
229 : 0 : return PTR_ERR(pointer);
230 : :
231 : 0 : memcpy(values, pointer, length);
232 : 0 : return 0;
233 : : }
234 : :
235 : 0 : static int property_entry_read_u64_array(const struct property_entry *props,
236 : : const char *propname,
237 : : u64 *values, size_t nval)
238 : : {
239 : : const void *pointer;
240 : 0 : size_t length = nval * sizeof(*values);
241 : :
242 : 0 : pointer = property_entry_find(props, propname, length);
243 [ # # ]: 0 : if (IS_ERR(pointer))
244 : 0 : return PTR_ERR(pointer);
245 : :
246 : 0 : memcpy(values, pointer, length);
247 : 0 : return 0;
248 : : }
249 : :
250 : : static int
251 : : property_entry_count_elems_of_size(const struct property_entry *props,
252 : : const char *propname, size_t length)
253 : : {
254 : : const struct property_entry *prop;
255 : :
256 : 0 : prop = property_entry_get(props, propname);
257 [ # # # # ]: 0 : if (!prop)
258 : : return -EINVAL;
259 : :
260 : 0 : return prop->length / length;
261 : : }
262 : :
263 : 0 : static int property_entry_read_int_array(const struct property_entry *props,
264 : : const char *name,
265 : : unsigned int elem_size, void *val,
266 : : size_t nval)
267 : : {
268 [ # # ]: 0 : if (!val)
269 : 0 : return property_entry_count_elems_of_size(props, name,
270 : : elem_size);
271 [ # # # # : 0 : switch (elem_size) {
# ]
272 : : case sizeof(u8):
273 : 0 : return property_entry_read_u8_array(props, name, val, nval);
274 : : case sizeof(u16):
275 : 0 : return property_entry_read_u16_array(props, name, val, nval);
276 : : case sizeof(u32):
277 : 0 : return property_entry_read_u32_array(props, name, val, nval);
278 : : case sizeof(u64):
279 : 0 : return property_entry_read_u64_array(props, name, val, nval);
280 : : }
281 : :
282 : : return -ENXIO;
283 : : }
284 : :
285 : 0 : static int property_entry_read_string_array(const struct property_entry *props,
286 : : const char *propname,
287 : : const char **strings, size_t nval)
288 : : {
289 : : const struct property_entry *prop;
290 : : const void *pointer;
291 : : size_t array_len, length;
292 : :
293 : : /* Find out the array length. */
294 : 0 : prop = property_entry_get(props, propname);
295 [ # # ]: 0 : if (!prop)
296 : : return -EINVAL;
297 : :
298 [ # # ]: 0 : if (prop->is_array)
299 : : /* Find the length of an array. */
300 : 0 : array_len = property_entry_count_elems_of_size(props, propname,
301 : : sizeof(const char *));
302 : : else
303 : : /* The array length for a non-array string property is 1. */
304 : : array_len = 1;
305 : :
306 : : /* Return how many there are if strings is NULL. */
307 [ # # ]: 0 : if (!strings)
308 : 0 : return array_len;
309 : :
310 : 0 : array_len = min(nval, array_len);
311 : 0 : length = array_len * sizeof(*strings);
312 : :
313 : 0 : pointer = property_entry_find(props, propname, length);
314 [ # # ]: 0 : if (IS_ERR(pointer))
315 : 0 : return PTR_ERR(pointer);
316 : :
317 : 0 : memcpy(strings, pointer, length);
318 : :
319 : 0 : return array_len;
320 : : }
321 : :
322 : 0 : static void property_entry_free_data(const struct property_entry *p)
323 : : {
324 : 0 : const void *pointer = property_get_pointer(p);
325 : : size_t i, nval;
326 : :
327 [ # # ]: 0 : if (p->is_array) {
328 [ # # # # ]: 0 : if (p->type == DEV_PROP_STRING && p->pointer.str) {
329 : 0 : nval = p->length / sizeof(const char *);
330 [ # # ]: 0 : for (i = 0; i < nval; i++)
331 : 0 : kfree(p->pointer.str[i]);
332 : : }
333 : 0 : kfree(pointer);
334 [ # # ]: 0 : } else if (p->type == DEV_PROP_STRING) {
335 : 0 : kfree(p->value.str);
336 : : }
337 : 0 : kfree(p->name);
338 : 0 : }
339 : :
340 : 0 : static int property_copy_string_array(struct property_entry *dst,
341 : : const struct property_entry *src)
342 : : {
343 : : const char **d;
344 : 0 : size_t nval = src->length / sizeof(*d);
345 : : int i;
346 : :
347 : : d = kcalloc(nval, sizeof(*d), GFP_KERNEL);
348 [ # # ]: 0 : if (!d)
349 : : return -ENOMEM;
350 : :
351 [ # # ]: 0 : for (i = 0; i < nval; i++) {
352 : 0 : d[i] = kstrdup(src->pointer.str[i], GFP_KERNEL);
353 [ # # # # ]: 0 : if (!d[i] && src->pointer.str[i]) {
354 [ # # ]: 0 : while (--i >= 0)
355 : 0 : kfree(d[i]);
356 : 0 : kfree(d);
357 : 0 : return -ENOMEM;
358 : : }
359 : : }
360 : :
361 : 0 : dst->pointer.str = d;
362 : 0 : return 0;
363 : : }
364 : :
365 : 0 : static int property_entry_copy_data(struct property_entry *dst,
366 : : const struct property_entry *src)
367 : : {
368 : 0 : const void *pointer = property_get_pointer(src);
369 : : const void *new;
370 : : int error;
371 : :
372 [ # # ]: 0 : if (src->is_array) {
373 [ # # ]: 0 : if (!src->length)
374 : : return -ENODATA;
375 : :
376 [ # # ]: 0 : if (src->type == DEV_PROP_STRING) {
377 : 0 : error = property_copy_string_array(dst, src);
378 [ # # ]: 0 : if (error)
379 : : return error;
380 : 0 : new = dst->pointer.str;
381 : : } else {
382 : 0 : new = kmemdup(pointer, src->length, GFP_KERNEL);
383 [ # # ]: 0 : if (!new)
384 : : return -ENOMEM;
385 : : }
386 [ # # ]: 0 : } else if (src->type == DEV_PROP_STRING) {
387 : 0 : new = kstrdup(src->value.str, GFP_KERNEL);
388 [ # # # # ]: 0 : if (!new && src->value.str)
389 : : return -ENOMEM;
390 : : } else {
391 : : new = pointer;
392 : : }
393 : :
394 : 0 : dst->length = src->length;
395 : 0 : dst->is_array = src->is_array;
396 : 0 : dst->type = src->type;
397 : :
398 : 0 : property_set_pointer(dst, new);
399 : :
400 : 0 : dst->name = kstrdup(src->name, GFP_KERNEL);
401 [ # # ]: 0 : if (!dst->name)
402 : : goto out_free_data;
403 : :
404 : : return 0;
405 : :
406 : : out_free_data:
407 : 0 : property_entry_free_data(dst);
408 : 0 : return -ENOMEM;
409 : : }
410 : :
411 : : /**
412 : : * property_entries_dup - duplicate array of properties
413 : : * @properties: array of properties to copy
414 : : *
415 : : * This function creates a deep copy of the given NULL-terminated array
416 : : * of property entries.
417 : : */
418 : : struct property_entry *
419 : 0 : property_entries_dup(const struct property_entry *properties)
420 : : {
421 : : struct property_entry *p;
422 : : int i, n = 0;
423 : : int ret;
424 : :
425 [ # # ]: 0 : if (!properties)
426 : : return NULL;
427 : :
428 [ # # ]: 0 : while (properties[n].name)
429 : 0 : n++;
430 : :
431 : 0 : p = kcalloc(n + 1, sizeof(*p), GFP_KERNEL);
432 [ # # ]: 0 : if (!p)
433 : : return ERR_PTR(-ENOMEM);
434 : :
435 [ # # ]: 0 : for (i = 0; i < n; i++) {
436 : 0 : ret = property_entry_copy_data(&p[i], &properties[i]);
437 [ # # ]: 0 : if (ret) {
438 [ # # ]: 0 : while (--i >= 0)
439 : 0 : property_entry_free_data(&p[i]);
440 : 0 : kfree(p);
441 : 0 : return ERR_PTR(ret);
442 : : }
443 : : }
444 : :
445 : : return p;
446 : : }
447 : : EXPORT_SYMBOL_GPL(property_entries_dup);
448 : :
449 : : /**
450 : : * property_entries_free - free previously allocated array of properties
451 : : * @properties: array of properties to destroy
452 : : *
453 : : * This function frees given NULL-terminated array of property entries,
454 : : * along with their data.
455 : : */
456 : 0 : void property_entries_free(const struct property_entry *properties)
457 : : {
458 : : const struct property_entry *p;
459 : :
460 [ # # ]: 0 : if (!properties)
461 : 0 : return;
462 : :
463 [ # # ]: 0 : for (p = properties; p->name; p++)
464 : 0 : property_entry_free_data(p);
465 : :
466 : 0 : kfree(properties);
467 : : }
468 : : EXPORT_SYMBOL_GPL(property_entries_free);
469 : :
470 : : /* -------------------------------------------------------------------------- */
471 : : /* fwnode operations */
472 : :
473 : 0 : static struct fwnode_handle *software_node_get(struct fwnode_handle *fwnode)
474 : : {
475 [ # # ]: 0 : struct swnode *swnode = to_swnode(fwnode);
476 : :
477 : 0 : kobject_get(&swnode->kobj);
478 : :
479 : 0 : return &swnode->fwnode;
480 : : }
481 : :
482 : 0 : static void software_node_put(struct fwnode_handle *fwnode)
483 : : {
484 [ # # ]: 0 : struct swnode *swnode = to_swnode(fwnode);
485 : :
486 : 0 : kobject_put(&swnode->kobj);
487 : 0 : }
488 : :
489 : 0 : static bool software_node_property_present(const struct fwnode_handle *fwnode,
490 : : const char *propname)
491 : : {
492 [ # # ]: 0 : struct swnode *swnode = to_swnode(fwnode);
493 : :
494 : 0 : return !!property_entry_get(swnode->node->properties, propname);
495 : : }
496 : :
497 : 0 : static int software_node_read_int_array(const struct fwnode_handle *fwnode,
498 : : const char *propname,
499 : : unsigned int elem_size, void *val,
500 : : size_t nval)
501 : : {
502 [ # # ]: 0 : struct swnode *swnode = to_swnode(fwnode);
503 : :
504 : 0 : return property_entry_read_int_array(swnode->node->properties, propname,
505 : : elem_size, val, nval);
506 : : }
507 : :
508 : 0 : static int software_node_read_string_array(const struct fwnode_handle *fwnode,
509 : : const char *propname,
510 : : const char **val, size_t nval)
511 : : {
512 [ # # ]: 0 : struct swnode *swnode = to_swnode(fwnode);
513 : :
514 : 0 : return property_entry_read_string_array(swnode->node->properties,
515 : : propname, val, nval);
516 : : }
517 : :
518 : : static struct fwnode_handle *
519 : 0 : software_node_get_parent(const struct fwnode_handle *fwnode)
520 : : {
521 [ # # ]: 0 : struct swnode *swnode = to_swnode(fwnode);
522 : :
523 [ # # # # ]: 0 : if (!swnode || !swnode->parent)
524 : : return NULL;
525 : :
526 : 0 : return fwnode_handle_get(&swnode->parent->fwnode);
527 : : }
528 : :
529 : : static struct fwnode_handle *
530 : 0 : software_node_get_next_child(const struct fwnode_handle *fwnode,
531 : : struct fwnode_handle *child)
532 : : {
533 [ # # ]: 0 : struct swnode *p = to_swnode(fwnode);
534 [ # # ]: 0 : struct swnode *c = to_swnode(child);
535 : :
536 [ # # # # : 0 : if (!p || list_empty(&p->children) ||
# # ]
537 [ # # ]: 0 : (c && list_is_last(&c->entry, &p->children)))
538 : : return NULL;
539 : :
540 [ # # ]: 0 : if (c)
541 : 0 : c = list_next_entry(c, entry);
542 : : else
543 : 0 : c = list_first_entry(&p->children, struct swnode, entry);
544 : 0 : return &c->fwnode;
545 : : }
546 : :
547 : : static struct fwnode_handle *
548 : 0 : software_node_get_named_child_node(const struct fwnode_handle *fwnode,
549 : : const char *childname)
550 : : {
551 [ # # ]: 0 : struct swnode *swnode = to_swnode(fwnode);
552 : : struct swnode *child;
553 : :
554 [ # # # # ]: 0 : if (!swnode || list_empty(&swnode->children))
555 : : return NULL;
556 : :
557 [ # # ]: 0 : list_for_each_entry(child, &swnode->children, entry) {
558 [ # # ]: 0 : if (!strcmp(childname, kobject_name(&child->kobj))) {
559 : 0 : kobject_get(&child->kobj);
560 : 0 : return &child->fwnode;
561 : : }
562 : : }
563 : : return NULL;
564 : : }
565 : :
566 : : static int
567 : 0 : software_node_get_reference_args(const struct fwnode_handle *fwnode,
568 : : const char *propname, const char *nargs_prop,
569 : : unsigned int nargs, unsigned int index,
570 : : struct fwnode_reference_args *args)
571 : : {
572 [ # # ]: 0 : struct swnode *swnode = to_swnode(fwnode);
573 : : const struct software_node_reference *ref;
574 : : const struct property_entry *prop;
575 : : struct fwnode_handle *refnode;
576 : : int i;
577 : :
578 [ # # # # ]: 0 : if (!swnode || !swnode->node->references)
579 : : return -ENOENT;
580 : :
581 [ # # ]: 0 : for (ref = swnode->node->references; ref->name; ref++)
582 [ # # ]: 0 : if (!strcmp(ref->name, propname))
583 : : break;
584 : :
585 [ # # # # ]: 0 : if (!ref->name || index > (ref->nrefs - 1))
586 : : return -ENOENT;
587 : :
588 : 0 : refnode = software_node_fwnode(ref->refs[index].node);
589 [ # # ]: 0 : if (!refnode)
590 : : return -ENOENT;
591 : :
592 [ # # ]: 0 : if (nargs_prop) {
593 : 0 : prop = property_entry_get(swnode->node->properties, nargs_prop);
594 [ # # ]: 0 : if (!prop)
595 : : return -EINVAL;
596 : :
597 : 0 : nargs = prop->value.u32_data;
598 : : }
599 : :
600 [ # # ]: 0 : if (nargs > NR_FWNODE_REFERENCE_ARGS)
601 : : return -EINVAL;
602 : :
603 : 0 : args->fwnode = software_node_get(refnode);
604 : 0 : args->nargs = nargs;
605 : :
606 [ # # ]: 0 : for (i = 0; i < nargs; i++)
607 : 0 : args->args[i] = ref->refs[index].args[i];
608 : :
609 : : return 0;
610 : : }
611 : :
612 : : static const struct fwnode_operations software_node_ops = {
613 : : .get = software_node_get,
614 : : .put = software_node_put,
615 : : .property_present = software_node_property_present,
616 : : .property_read_int_array = software_node_read_int_array,
617 : : .property_read_string_array = software_node_read_string_array,
618 : : .get_parent = software_node_get_parent,
619 : : .get_next_child_node = software_node_get_next_child,
620 : : .get_named_child_node = software_node_get_named_child_node,
621 : : .get_reference_args = software_node_get_reference_args
622 : : };
623 : :
624 : : /* -------------------------------------------------------------------------- */
625 : :
626 : : /**
627 : : * software_node_find_by_name - Find software node by name
628 : : * @parent: Parent of the software node
629 : : * @name: Name of the software node
630 : : *
631 : : * The function will find a node that is child of @parent and that is named
632 : : * @name. If no node is found, the function returns NULL.
633 : : *
634 : : * NOTE: you will need to drop the reference with fwnode_handle_put() after use.
635 : : */
636 : : const struct software_node *
637 : 0 : software_node_find_by_name(const struct software_node *parent, const char *name)
638 : : {
639 : : struct swnode *swnode = NULL;
640 : : struct kobject *k;
641 : :
642 [ # # ]: 0 : if (!name)
643 : : return NULL;
644 : :
645 : 0 : spin_lock(&swnode_kset->list_lock);
646 : :
647 [ # # ]: 0 : list_for_each_entry(k, &swnode_kset->list, entry) {
648 : 0 : swnode = kobj_to_swnode(k);
649 [ # # # # : 0 : if (parent == swnode->node->parent && swnode->node->name &&
# # ]
650 : 0 : !strcmp(name, swnode->node->name)) {
651 : 0 : kobject_get(&swnode->kobj);
652 : 0 : break;
653 : : }
654 : : swnode = NULL;
655 : : }
656 : :
657 : 0 : spin_unlock(&swnode_kset->list_lock);
658 : :
659 [ # # ]: 0 : return swnode ? swnode->node : NULL;
660 : : }
661 : : EXPORT_SYMBOL_GPL(software_node_find_by_name);
662 : :
663 : : static int
664 : : software_node_register_properties(struct software_node *node,
665 : : const struct property_entry *properties)
666 : : {
667 : : struct property_entry *props;
668 : :
669 : 0 : props = property_entries_dup(properties);
670 [ # # ]: 0 : if (IS_ERR(props))
671 : : return PTR_ERR(props);
672 : :
673 : 0 : node->properties = props;
674 : :
675 : : return 0;
676 : : }
677 : :
678 : 0 : static void software_node_release(struct kobject *kobj)
679 : : {
680 : 0 : struct swnode *swnode = kobj_to_swnode(kobj);
681 : :
682 [ # # ]: 0 : if (swnode->parent) {
683 : 0 : ida_simple_remove(&swnode->parent->child_ids, swnode->id);
684 : : list_del(&swnode->entry);
685 : : } else {
686 : 0 : ida_simple_remove(&swnode_root_ids, swnode->id);
687 : : }
688 : :
689 [ # # ]: 0 : if (swnode->allocated) {
690 : 0 : property_entries_free(swnode->node->properties);
691 : 0 : kfree(swnode->node);
692 : : }
693 : 0 : ida_destroy(&swnode->child_ids);
694 : 0 : kfree(swnode);
695 : 0 : }
696 : :
697 : : static struct kobj_type software_node_type = {
698 : : .release = software_node_release,
699 : : .sysfs_ops = &kobj_sysfs_ops,
700 : : };
701 : :
702 : : static struct fwnode_handle *
703 : 0 : swnode_register(const struct software_node *node, struct swnode *parent,
704 : : unsigned int allocated)
705 : : {
706 : : struct swnode *swnode;
707 : : int ret;
708 : :
709 : 0 : swnode = kzalloc(sizeof(*swnode), GFP_KERNEL);
710 [ # # ]: 0 : if (!swnode) {
711 : : ret = -ENOMEM;
712 : : goto out_err;
713 : : }
714 : :
715 [ # # ]: 0 : ret = ida_simple_get(parent ? &parent->child_ids : &swnode_root_ids,
716 : : 0, 0, GFP_KERNEL);
717 [ # # ]: 0 : if (ret < 0) {
718 : 0 : kfree(swnode);
719 : 0 : goto out_err;
720 : : }
721 : :
722 : 0 : swnode->id = ret;
723 : 0 : swnode->node = node;
724 : 0 : swnode->parent = parent;
725 : 0 : swnode->allocated = allocated;
726 : 0 : swnode->kobj.kset = swnode_kset;
727 : 0 : swnode->fwnode.ops = &software_node_ops;
728 : :
729 : : ida_init(&swnode->child_ids);
730 : 0 : INIT_LIST_HEAD(&swnode->entry);
731 : 0 : INIT_LIST_HEAD(&swnode->children);
732 : :
733 [ # # ]: 0 : if (node->name)
734 [ # # ]: 0 : ret = kobject_init_and_add(&swnode->kobj, &software_node_type,
735 : : parent ? &parent->kobj : NULL,
736 : : "%s", node->name);
737 : : else
738 [ # # ]: 0 : ret = kobject_init_and_add(&swnode->kobj, &software_node_type,
739 : : parent ? &parent->kobj : NULL,
740 : : "node%d", swnode->id);
741 [ # # ]: 0 : if (ret) {
742 : 0 : kobject_put(&swnode->kobj);
743 : 0 : return ERR_PTR(ret);
744 : : }
745 : :
746 [ # # ]: 0 : if (parent)
747 : 0 : list_add_tail(&swnode->entry, &parent->children);
748 : :
749 : 0 : kobject_uevent(&swnode->kobj, KOBJ_ADD);
750 : 0 : return &swnode->fwnode;
751 : :
752 : : out_err:
753 [ # # ]: 0 : if (allocated)
754 : 0 : property_entries_free(node->properties);
755 : 0 : return ERR_PTR(ret);
756 : : }
757 : :
758 : : /**
759 : : * software_node_register_nodes - Register an array of software nodes
760 : : * @nodes: Zero terminated array of software nodes to be registered
761 : : *
762 : : * Register multiple software nodes at once.
763 : : */
764 : 0 : int software_node_register_nodes(const struct software_node *nodes)
765 : : {
766 : : int ret;
767 : : int i;
768 : :
769 [ # # ]: 0 : for (i = 0; nodes[i].name; i++) {
770 : 0 : ret = software_node_register(&nodes[i]);
771 [ # # ]: 0 : if (ret) {
772 : 0 : software_node_unregister_nodes(nodes);
773 : 0 : return ret;
774 : : }
775 : : }
776 : :
777 : : return 0;
778 : : }
779 : : EXPORT_SYMBOL_GPL(software_node_register_nodes);
780 : :
781 : : /**
782 : : * software_node_unregister_nodes - Unregister an array of software nodes
783 : : * @nodes: Zero terminated array of software nodes to be unregistered
784 : : *
785 : : * Unregister multiple software nodes at once.
786 : : */
787 : 0 : void software_node_unregister_nodes(const struct software_node *nodes)
788 : : {
789 : : struct swnode *swnode;
790 : : int i;
791 : :
792 [ # # ]: 0 : for (i = 0; nodes[i].name; i++) {
793 : 0 : swnode = software_node_to_swnode(&nodes[i]);
794 [ # # ]: 0 : if (swnode)
795 : 0 : fwnode_remove_software_node(&swnode->fwnode);
796 : : }
797 : 0 : }
798 : : EXPORT_SYMBOL_GPL(software_node_unregister_nodes);
799 : :
800 : : /**
801 : : * software_node_register - Register static software node
802 : : * @node: The software node to be registered
803 : : */
804 : 0 : int software_node_register(const struct software_node *node)
805 : : {
806 : 0 : struct swnode *parent = software_node_to_swnode(node->parent);
807 : :
808 [ # # ]: 0 : if (software_node_to_swnode(node))
809 : : return -EEXIST;
810 : :
811 : 0 : return PTR_ERR_OR_ZERO(swnode_register(node, parent, 0));
812 : : }
813 : : EXPORT_SYMBOL_GPL(software_node_register);
814 : :
815 : : struct fwnode_handle *
816 : 0 : fwnode_create_software_node(const struct property_entry *properties,
817 : : const struct fwnode_handle *parent)
818 : : {
819 : : struct software_node *node;
820 : : struct swnode *p = NULL;
821 : : int ret;
822 : :
823 [ # # ]: 0 : if (parent) {
824 [ # # ]: 0 : if (IS_ERR(parent))
825 : : return ERR_CAST(parent);
826 [ # # ]: 0 : if (!is_software_node(parent))
827 : : return ERR_PTR(-EINVAL);
828 [ # # ]: 0 : p = to_swnode(parent);
829 : : }
830 : :
831 : 0 : node = kzalloc(sizeof(*node), GFP_KERNEL);
832 [ # # ]: 0 : if (!node)
833 : : return ERR_PTR(-ENOMEM);
834 : :
835 : : ret = software_node_register_properties(node, properties);
836 [ # # ]: 0 : if (ret) {
837 : 0 : kfree(node);
838 : 0 : return ERR_PTR(ret);
839 : : }
840 : :
841 [ # # ]: 0 : node->parent = p ? p->node : NULL;
842 : :
843 : 0 : return swnode_register(node, p, 1);
844 : : }
845 : : EXPORT_SYMBOL_GPL(fwnode_create_software_node);
846 : :
847 : 0 : void fwnode_remove_software_node(struct fwnode_handle *fwnode)
848 : : {
849 [ # # ]: 0 : struct swnode *swnode = to_swnode(fwnode);
850 : :
851 [ # # ]: 0 : if (!swnode)
852 : 0 : return;
853 : :
854 : 0 : kobject_put(&swnode->kobj);
855 : : }
856 : : EXPORT_SYMBOL_GPL(fwnode_remove_software_node);
857 : :
858 : 123628 : int software_node_notify(struct device *dev, unsigned long action)
859 : : {
860 : 123628 : struct fwnode_handle *fwnode = dev_fwnode(dev);
861 : : struct swnode *swnode;
862 : : int ret;
863 : :
864 [ + + ]: 123628 : if (!fwnode)
865 : : return 0;
866 : :
867 [ + - ]: 16160 : if (!is_software_node(fwnode))
868 : 16160 : fwnode = fwnode->secondary;
869 [ - + ]: 16160 : if (!is_software_node(fwnode))
870 : : return 0;
871 : :
872 [ # # ]: 0 : swnode = to_swnode(fwnode);
873 : :
874 [ # # # ]: 0 : switch (action) {
875 : : case KOBJ_ADD:
876 : 0 : ret = sysfs_create_link(&dev->kobj, &swnode->kobj,
877 : : "software_node");
878 [ # # ]: 0 : if (ret)
879 : : break;
880 : :
881 : 0 : ret = sysfs_create_link(&swnode->kobj, &dev->kobj,
882 : : dev_name(dev));
883 [ # # ]: 0 : if (ret) {
884 : 0 : sysfs_remove_link(&dev->kobj, "software_node");
885 : 0 : break;
886 : : }
887 : 0 : kobject_get(&swnode->kobj);
888 : 0 : break;
889 : : case KOBJ_REMOVE:
890 : 0 : sysfs_remove_link(&swnode->kobj, dev_name(dev));
891 : 0 : sysfs_remove_link(&dev->kobj, "software_node");
892 : 0 : kobject_put(&swnode->kobj);
893 : 0 : break;
894 : : default:
895 : : break;
896 : : }
897 : :
898 : : return 0;
899 : : }
900 : :
901 : 404 : static int __init software_node_init(void)
902 : : {
903 : 404 : swnode_kset = kset_create_and_add("software_nodes", NULL, kernel_kobj);
904 [ + - ]: 404 : if (!swnode_kset)
905 : : return -ENOMEM;
906 : 404 : return 0;
907 : : }
908 : : postcore_initcall(software_node_init);
909 : :
910 : 0 : static void __exit software_node_exit(void)
911 : : {
912 : 0 : ida_destroy(&swnode_root_ids);
913 : 0 : kset_unregister(swnode_kset);
914 : 0 : }
915 : : __exitcall(software_node_exit);
|