xref: /linux/drivers/thunderbolt/property.c (revision 364f4a55c661641c02c86a849f0608d8fc3c0006)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Thunderbolt XDomain property support
4  *
5  * Copyright (C) 2017, Intel Corporation
6  * Authors: Michael Jamet <michael.jamet@intel.com>
7  *          Mika Westerberg <mika.westerberg@linux.intel.com>
8  */
9 
10 #include <linux/err.h>
11 #include <linux/overflow.h>
12 #include <linux/slab.h>
13 #include <linux/string.h>
14 #include <linux/uuid.h>
15 #include <linux/thunderbolt.h>
16 
17 struct tb_property_entry {
18 	u32 key_hi;
19 	u32 key_lo;
20 	u16 length;
21 	u8 reserved;
22 	u8 type;
23 	u32 value;
24 };
25 
26 struct tb_property_rootdir_entry {
27 	u32 magic;
28 	u32 length;
29 	struct tb_property_entry entries[];
30 };
31 
32 struct tb_property_dir_entry {
33 	u32 uuid[4];
34 	struct tb_property_entry entries[];
35 };
36 
37 #define TB_PROPERTY_ROOTDIR_MAGIC	0x55584401
38 #define TB_PROPERTY_MAX_DEPTH		8
39 
40 static struct tb_property_dir *__tb_property_parse_dir(const u32 *block,
41 	size_t block_len, unsigned int dir_offset, size_t dir_len,
42 	bool is_root, unsigned int depth);
43 static struct tb_property *tb_property_copy(const struct tb_property *property);
44 
45 static inline void parse_dwdata(void *dst, const void *src, size_t dwords)
46 {
47 	be32_to_cpu_array(dst, src, dwords);
48 }
49 
50 static inline void format_dwdata(void *dst, const void *src, size_t dwords)
51 {
52 	cpu_to_be32_array(dst, src, dwords);
53 }
54 
55 static bool tb_property_entry_valid(const struct tb_property_entry *entry,
56 				  size_t block_len)
57 {
58 	u32 end;
59 
60 	switch (entry->type) {
61 	case TB_PROPERTY_TYPE_DIRECTORY:
62 	case TB_PROPERTY_TYPE_DATA:
63 	case TB_PROPERTY_TYPE_TEXT:
64 		if (!entry->length)
65 			return false;
66 		if (entry->length > block_len)
67 			return false;
68 		if (check_add_overflow(entry->value, entry->length, &end) ||
69 		    end > block_len)
70 			return false;
71 		break;
72 
73 	case TB_PROPERTY_TYPE_VALUE:
74 		if (entry->length != 1)
75 			return false;
76 		break;
77 	}
78 
79 	return true;
80 }
81 
82 static bool tb_property_key_valid(const char *key)
83 {
84 	return key && strlen(key) <= TB_PROPERTY_KEY_SIZE;
85 }
86 
87 static struct tb_property *
88 tb_property_alloc(const char *key, enum tb_property_type type)
89 {
90 	struct tb_property *property;
91 
92 	property = kzalloc_obj(*property);
93 	if (!property)
94 		return NULL;
95 
96 	strcpy(property->key, key);
97 	property->type = type;
98 	INIT_LIST_HEAD(&property->list);
99 
100 	return property;
101 }
102 
103 static struct tb_property *tb_property_parse(const u32 *block, size_t block_len,
104 					const struct tb_property_entry *entry,
105 					unsigned int depth)
106 {
107 	char key[TB_PROPERTY_KEY_SIZE + 1];
108 	struct tb_property *property;
109 	struct tb_property_dir *dir;
110 
111 	if (!tb_property_entry_valid(entry, block_len))
112 		return NULL;
113 
114 	parse_dwdata(key, entry, 2);
115 	key[TB_PROPERTY_KEY_SIZE] = '\0';
116 
117 	property = tb_property_alloc(key, entry->type);
118 	if (!property)
119 		return NULL;
120 
121 	property->length = entry->length;
122 
123 	switch (property->type) {
124 	case TB_PROPERTY_TYPE_DIRECTORY:
125 		dir = __tb_property_parse_dir(block, block_len, entry->value,
126 					      entry->length, false, depth + 1);
127 		if (!dir) {
128 			kfree(property);
129 			return NULL;
130 		}
131 		property->value.dir = dir;
132 		break;
133 
134 	case TB_PROPERTY_TYPE_DATA:
135 		property->value.data = kcalloc(property->length, sizeof(u32),
136 					       GFP_KERNEL);
137 		if (!property->value.data) {
138 			kfree(property);
139 			return NULL;
140 		}
141 		parse_dwdata(property->value.data, block + entry->value,
142 			     entry->length);
143 		break;
144 
145 	case TB_PROPERTY_TYPE_TEXT:
146 		property->value.text = kcalloc(property->length, sizeof(u32),
147 					       GFP_KERNEL);
148 		if (!property->value.text) {
149 			kfree(property);
150 			return NULL;
151 		}
152 		parse_dwdata(property->value.text, block + entry->value,
153 			     entry->length);
154 		/* Force null termination */
155 		property->value.text[property->length * 4 - 1] = '\0';
156 		break;
157 
158 	case TB_PROPERTY_TYPE_VALUE:
159 		property->value.immediate = entry->value;
160 		break;
161 
162 	default:
163 		property->type = TB_PROPERTY_TYPE_UNKNOWN;
164 		break;
165 	}
166 
167 	return property;
168 }
169 
170 static struct tb_property_dir *__tb_property_parse_dir(const u32 *block,
171 	size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root,
172 	unsigned int depth)
173 {
174 	const struct tb_property_entry *entries;
175 	size_t i, content_len, nentries;
176 	unsigned int content_offset;
177 	struct tb_property_dir *dir;
178 
179 	if (depth > TB_PROPERTY_MAX_DEPTH)
180 		return NULL;
181 
182 	dir = kzalloc_obj(*dir);
183 	if (!dir)
184 		return NULL;
185 
186 	INIT_LIST_HEAD(&dir->properties);
187 
188 	if (is_root) {
189 		content_offset = dir_offset + 2;
190 		content_len = dir_len;
191 		if (content_offset + content_len > block_len) {
192 			tb_property_free_dir(dir);
193 			return NULL;
194 		}
195 	} else {
196 		if (dir_len < 4) {
197 			tb_property_free_dir(dir);
198 			return NULL;
199 		}
200 		dir->uuid = kmemdup(&block[dir_offset], sizeof(*dir->uuid),
201 				    GFP_KERNEL);
202 		if (!dir->uuid) {
203 			tb_property_free_dir(dir);
204 			return NULL;
205 		}
206 		content_offset = dir_offset + 4;
207 		content_len = dir_len - 4; /* Length includes UUID */
208 	}
209 
210 	entries = (const struct tb_property_entry *)&block[content_offset];
211 	nentries = content_len / (sizeof(*entries) / 4);
212 
213 	for (i = 0; i < nentries; i++) {
214 		struct tb_property *property;
215 
216 		property = tb_property_parse(block, block_len, &entries[i], depth);
217 		if (!property) {
218 			tb_property_free_dir(dir);
219 			return NULL;
220 		}
221 
222 		list_add_tail(&property->list, &dir->properties);
223 	}
224 
225 	return dir;
226 }
227 
228 /**
229  * tb_property_parse_dir() - Parses properties from given property block
230  * @block: Property block to parse
231  * @block_len: Number of dword elements in the property block
232  *
233  * This function parses the XDomain properties data block into format that
234  * can be traversed using the helper functions provided by this module.
235  *
236  * The resulting &struct tb_property_dir needs to be released by
237  * calling tb_property_free_dir() when not needed anymore.
238  *
239  * The @block is expected to be root directory.
240  *
241  * Return: Pointer to &struct tb_property_dir, %NULL in case of failure.
242  */
243 struct tb_property_dir *tb_property_parse_dir(const u32 *block,
244 					      size_t block_len)
245 {
246 	const struct tb_property_rootdir_entry *rootdir =
247 		(const struct tb_property_rootdir_entry *)block;
248 
249 	if (rootdir->magic != TB_PROPERTY_ROOTDIR_MAGIC)
250 		return NULL;
251 	if (rootdir->length > block_len)
252 		return NULL;
253 
254 	return __tb_property_parse_dir(block, block_len, 0, rootdir->length,
255 				       true, 0);
256 }
257 
258 /**
259  * tb_property_create_dir() - Creates new property directory
260  * @uuid: UUID used to identify the particular directory
261  *
262  * Creates new, empty property directory. If @uuid is %NULL then the
263  * directory is assumed to be root directory.
264  *
265  * Return: Pointer to &struct tb_property_dir, %NULL in case of failure.
266  */
267 struct tb_property_dir *tb_property_create_dir(const uuid_t *uuid)
268 {
269 	struct tb_property_dir *dir;
270 
271 	dir = kzalloc_obj(*dir);
272 	if (!dir)
273 		return NULL;
274 
275 	INIT_LIST_HEAD(&dir->properties);
276 	if (uuid) {
277 		dir->uuid = kmemdup(uuid, sizeof(*dir->uuid), GFP_KERNEL);
278 		if (!dir->uuid) {
279 			kfree(dir);
280 			return NULL;
281 		}
282 	}
283 
284 	return dir;
285 }
286 EXPORT_SYMBOL_GPL(tb_property_create_dir);
287 
288 static void tb_property_free(struct tb_property *property)
289 {
290 	switch (property->type) {
291 	case TB_PROPERTY_TYPE_DIRECTORY:
292 		tb_property_free_dir(property->value.dir);
293 		break;
294 
295 	case TB_PROPERTY_TYPE_DATA:
296 		kfree(property->value.data);
297 		break;
298 
299 	case TB_PROPERTY_TYPE_TEXT:
300 		kfree(property->value.text);
301 		break;
302 
303 	default:
304 		break;
305 	}
306 
307 	kfree(property);
308 }
309 
310 /**
311  * tb_property_free_dir() - Release memory allocated for property directory
312  * @dir: Directory to release
313  *
314  * This will release all the memory the directory occupies including all
315  * descendants. It is OK to pass %NULL @dir, then the function does
316  * nothing.
317  */
318 void tb_property_free_dir(struct tb_property_dir *dir)
319 {
320 	struct tb_property *property, *tmp;
321 
322 	if (!dir)
323 		return;
324 
325 	list_for_each_entry_safe(property, tmp, &dir->properties, list) {
326 		list_del(&property->list);
327 		tb_property_free(property);
328 	}
329 	kfree(dir->uuid);
330 	kfree(dir);
331 }
332 EXPORT_SYMBOL_GPL(tb_property_free_dir);
333 
334 static size_t tb_property_dir_length(const struct tb_property_dir *dir,
335 				     bool recurse, size_t *data_len)
336 {
337 	const struct tb_property *property;
338 	size_t len = 0;
339 
340 	if (dir->uuid)
341 		len += sizeof(*dir->uuid) / 4;
342 	else
343 		len += sizeof(struct tb_property_rootdir_entry) / 4;
344 
345 	list_for_each_entry(property, &dir->properties, list) {
346 		len += sizeof(struct tb_property_entry) / 4;
347 
348 		switch (property->type) {
349 		case TB_PROPERTY_TYPE_DIRECTORY:
350 			if (recurse) {
351 				len += tb_property_dir_length(
352 					property->value.dir, recurse, data_len);
353 			}
354 			/* Reserve dword padding after each directory */
355 			if (data_len)
356 				*data_len += 1;
357 			break;
358 
359 		case TB_PROPERTY_TYPE_DATA:
360 		case TB_PROPERTY_TYPE_TEXT:
361 			if (data_len)
362 				*data_len += property->length;
363 			break;
364 
365 		default:
366 			break;
367 		}
368 	}
369 
370 	return len;
371 }
372 
373 static ssize_t __tb_property_format_dir(const struct tb_property_dir *dir,
374 	u32 *block, unsigned int start_offset, size_t block_len)
375 {
376 	unsigned int data_offset, dir_end;
377 	const struct tb_property *property;
378 	struct tb_property_entry *entry;
379 	size_t dir_len, data_len = 0;
380 	int ret;
381 
382 	/*
383 	 * The structure of property block looks like following. Leaf
384 	 * data/text is included right after the directory and each
385 	 * directory follows each other (even nested ones).
386 	 *
387 	 * +----------+ <-- start_offset
388 	 * |  header  | <-- root directory header
389 	 * +----------+ ---
390 	 * |  entry 0 | -^--------------------.
391 	 * +----------+  |                    |
392 	 * |  entry 1 | -|--------------------|--.
393 	 * +----------+  |                    |  |
394 	 * |  entry 2 | -|-----------------.  |  |
395 	 * +----------+  |                 |  |  |
396 	 * :          :  |  dir_len        |  |  |
397 	 * .          .  |                 |  |  |
398 	 * :          :  |                 |  |  |
399 	 * +----------+  |                 |  |  |
400 	 * |  entry n |  v                 |  |  |
401 	 * +----------+ <-- data_offset    |  |  |
402 	 * |  data 0  | <------------------|--'  |
403 	 * +----------+                    |     |
404 	 * |  data 1  | <------------------|-----'
405 	 * +----------+                    |
406 	 * | 00000000 | padding            |
407 	 * +----------+ <-- dir_end <------'
408 	 * |   UUID   | <-- directory UUID (child directory)
409 	 * +----------+
410 	 * |  entry 0 |
411 	 * +----------+
412 	 * |  entry 1 |
413 	 * +----------+
414 	 * :          :
415 	 * .          .
416 	 * :          :
417 	 * +----------+
418 	 * |  entry n |
419 	 * +----------+
420 	 * |  data 0  |
421 	 * +----------+
422 	 *
423 	 * We use dir_end to hold pointer to the end of the directory. It
424 	 * will increase as we add directories and each directory should be
425 	 * added starting from previous dir_end.
426 	 */
427 	dir_len = tb_property_dir_length(dir, false, &data_len);
428 	data_offset = start_offset + dir_len;
429 	dir_end = start_offset + data_len + dir_len;
430 
431 	if (data_offset > dir_end)
432 		return -EINVAL;
433 	if (dir_end > block_len)
434 		return -EINVAL;
435 
436 	/* Write headers first */
437 	if (dir->uuid) {
438 		struct tb_property_dir_entry *pe;
439 
440 		pe = (struct tb_property_dir_entry *)&block[start_offset];
441 		memcpy(pe->uuid, dir->uuid, sizeof(pe->uuid));
442 		entry = pe->entries;
443 	} else {
444 		struct tb_property_rootdir_entry *re;
445 
446 		re = (struct tb_property_rootdir_entry *)&block[start_offset];
447 		re->magic = TB_PROPERTY_ROOTDIR_MAGIC;
448 		re->length = dir_len - sizeof(*re) / 4;
449 		entry = re->entries;
450 	}
451 
452 	list_for_each_entry(property, &dir->properties, list) {
453 		const struct tb_property_dir *child;
454 
455 		format_dwdata(entry, property->key, 2);
456 		entry->type = property->type;
457 
458 		switch (property->type) {
459 		case TB_PROPERTY_TYPE_DIRECTORY:
460 			child = property->value.dir;
461 			ret = __tb_property_format_dir(child, block, dir_end,
462 						       block_len);
463 			if (ret < 0)
464 				return ret;
465 			entry->length = tb_property_dir_length(child, false,
466 							       NULL);
467 			entry->value = dir_end;
468 			dir_end = ret;
469 			break;
470 
471 		case TB_PROPERTY_TYPE_DATA:
472 			format_dwdata(&block[data_offset], property->value.data,
473 				      property->length);
474 			entry->length = property->length;
475 			entry->value = data_offset;
476 			data_offset += entry->length;
477 			break;
478 
479 		case TB_PROPERTY_TYPE_TEXT:
480 			format_dwdata(&block[data_offset], property->value.text,
481 				      property->length);
482 			entry->length = property->length;
483 			entry->value = data_offset;
484 			data_offset += entry->length;
485 			break;
486 
487 		case TB_PROPERTY_TYPE_VALUE:
488 			entry->length = property->length;
489 			entry->value = property->value.immediate;
490 			break;
491 
492 		default:
493 			break;
494 		}
495 
496 		entry++;
497 	}
498 
499 	return dir_end;
500 }
501 
502 /**
503  * tb_property_format_dir() - Formats directory to the packed XDomain format
504  * @dir: Directory to format
505  * @block: Property block where the packed data is placed
506  * @block_len: Length of the property block
507  *
508  * This function formats the directory to the packed format that can be
509  * then sent over the thunderbolt fabric to receiving host.
510  *
511  * Passing %NULL in @block returns number of entries the block takes.
512  *
513  * Return: %0 on success, negative errno otherwise.
514  */
515 ssize_t tb_property_format_dir(const struct tb_property_dir *dir, u32 *block,
516 			       size_t block_len)
517 {
518 	ssize_t ret;
519 
520 	if (!block) {
521 		size_t dir_len, data_len = 0;
522 
523 		dir_len = tb_property_dir_length(dir, true, &data_len);
524 		return dir_len + data_len;
525 	}
526 
527 	ret = __tb_property_format_dir(dir, block, 0, block_len);
528 	return ret < 0 ? ret : 0;
529 }
530 
531 static struct tb_property_dir *copy_dir(const struct tb_property_dir *dir)
532 {
533 	struct tb_property *property, *p;
534 	struct tb_property_dir *d;
535 
536 	if (!dir)
537 		return NULL;
538 
539 	d = tb_property_create_dir(dir->uuid);
540 	if (!d)
541 		return NULL;
542 
543 	list_for_each_entry(property, &dir->properties, list) {
544 		p = tb_property_copy(property);
545 		if (!p)
546 			goto err_free;
547 		list_add_tail(&p->list, &d->properties);
548 	}
549 
550 	return d;
551 
552 err_free:
553 	tb_property_free_dir(d);
554 	return NULL;
555 }
556 
557 static struct tb_property *tb_property_copy(const struct tb_property *property)
558 {
559 	struct tb_property *p;
560 
561 	p = tb_property_alloc(property->key, property->type);
562 	if (!p)
563 		return NULL;
564 
565 	p->length = property->length;
566 	switch (property->type) {
567 	case TB_PROPERTY_TYPE_DIRECTORY:
568 		p->value.dir = copy_dir(property->value.dir);
569 		if (!p->value.dir)
570 			goto err_free;
571 		break;
572 
573 	case TB_PROPERTY_TYPE_DATA:
574 		p->value.data = kmemdup(property->value.data,
575 					property->length * 4,
576 					GFP_KERNEL);
577 		if (!p->value.data)
578 			goto err_free;
579 		break;
580 
581 	case TB_PROPERTY_TYPE_TEXT:
582 		p->value.text = kzalloc(p->length * 4, GFP_KERNEL);
583 		if (!p->value.text)
584 			goto err_free;
585 		strcpy(p->value.text, property->value.text);
586 		break;
587 
588 	case TB_PROPERTY_TYPE_VALUE:
589 		p->value.immediate = property->value.immediate;
590 		break;
591 
592 	default:
593 		break;
594 	}
595 
596 	return p;
597 
598 err_free:
599 	kfree(p);
600 	return NULL;
601 }
602 
603 /**
604  * tb_property_copy_dir() - Take a deep copy of directory
605  * @dir: Directory to copy
606  *
607  * The resulting directory needs to be released by calling tb_property_free_dir().
608  *
609  * Return: Pointer to &struct tb_property_dir, %NULL in case of failure.
610  */
611 struct tb_property_dir *tb_property_copy_dir(const struct tb_property_dir *dir)
612 {
613 	return copy_dir(dir);
614 }
615 EXPORT_SYMBOL_GPL(tb_property_copy_dir);
616 
617 /**
618  * tb_property_merge_dir() - Merges directory into parent
619  * @parent: Directory to merge @dir
620  * @dir: Directory that is merged
621  * @replace: Replace existing entries
622  *
623  * This will merge @dir into @parent. Both must have same UUID. The
624  * properties in @dir will overwrite overlapping properties in @parent
625  * if @replace is %true. Contents of @dir is copied (so if it is not
626  * needed afterwards it needs to relesed by calling tb_property_free_dir()).
627  */
628 int tb_property_merge_dir(struct tb_property_dir *parent,
629 			  const struct tb_property_dir *dir,
630 			  bool replace)
631 {
632 	const struct tb_property *property;
633 
634 	if (WARN_ON(parent == dir))
635 		return -EINVAL;
636 
637 	if (!uuid_equal(parent->uuid, dir->uuid))
638 		return -EINVAL;
639 
640 	list_for_each_entry(property, &dir->properties, list) {
641 		struct tb_property *p, *tmp;
642 
643 		tmp = tb_property_copy(property);
644 		if (!tmp)
645 			return -ENOMEM;
646 
647 		p = tb_property_find(parent, property->key, property->type);
648 		if (p) {
649 			if (replace) {
650 				/*
651 				 * Found existing property in parent so
652 				 * replace with the new one.
653 				 */
654 				list_replace(&p->list, &tmp->list);
655 				tb_property_free(p);
656 			} else {
657 				tb_property_free(tmp);
658 				continue;
659 			}
660 		} else {
661 			list_add_tail(&tmp->list, &parent->properties);
662 		}
663 	}
664 
665 	return 0;
666 }
667 EXPORT_SYMBOL_GPL(tb_property_merge_dir);
668 
669 /**
670  * tb_property_add_immediate() - Add immediate property to directory
671  * @parent: Directory to add the property
672  * @key: Key for the property
673  * @value: Immediate value to store with the property
674  *
675  * Return: %0 on success, negative errno otherwise.
676  */
677 int tb_property_add_immediate(struct tb_property_dir *parent, const char *key,
678 			      u32 value)
679 {
680 	struct tb_property *property;
681 
682 	if (!tb_property_key_valid(key))
683 		return -EINVAL;
684 
685 	property = tb_property_alloc(key, TB_PROPERTY_TYPE_VALUE);
686 	if (!property)
687 		return -ENOMEM;
688 
689 	property->length = 1;
690 	property->value.immediate = value;
691 
692 	list_add_tail(&property->list, &parent->properties);
693 	return 0;
694 }
695 EXPORT_SYMBOL_GPL(tb_property_add_immediate);
696 
697 /**
698  * tb_property_add_data() - Adds arbitrary data property to directory
699  * @parent: Directory to add the property
700  * @key: Key for the property
701  * @buf: Data buffer to add
702  * @buflen: Number of bytes in the data buffer
703  *
704  * Function takes a copy of @buf and adds it to the directory.
705  *
706  * Return: %0 on success, negative errno otherwise.
707  */
708 int tb_property_add_data(struct tb_property_dir *parent, const char *key,
709 			 const void *buf, size_t buflen)
710 {
711 	/* Need to pad to dword boundary */
712 	size_t size = round_up(buflen, 4);
713 	struct tb_property *property;
714 
715 	if (!tb_property_key_valid(key))
716 		return -EINVAL;
717 
718 	property = tb_property_alloc(key, TB_PROPERTY_TYPE_DATA);
719 	if (!property)
720 		return -ENOMEM;
721 
722 	property->length = size / 4;
723 	property->value.data = kzalloc(size, GFP_KERNEL);
724 	if (!property->value.data) {
725 		kfree(property);
726 		return -ENOMEM;
727 	}
728 
729 	memcpy(property->value.data, buf, buflen);
730 
731 	list_add_tail(&property->list, &parent->properties);
732 	return 0;
733 }
734 EXPORT_SYMBOL_GPL(tb_property_add_data);
735 
736 /**
737  * tb_property_add_text() - Adds string property to directory
738  * @parent: Directory to add the property
739  * @key: Key for the property
740  * @text: String to add
741  *
742  * Function takes a copy of @text and adds it to the directory.
743  *
744  * Return: %0 on success, negative errno otherwise.
745  */
746 int tb_property_add_text(struct tb_property_dir *parent, const char *key,
747 			 const char *text)
748 {
749 	/* Need to pad to dword boundary */
750 	size_t size = round_up(strlen(text) + 1, 4);
751 	struct tb_property *property;
752 
753 	if (!tb_property_key_valid(key))
754 		return -EINVAL;
755 
756 	property = tb_property_alloc(key, TB_PROPERTY_TYPE_TEXT);
757 	if (!property)
758 		return -ENOMEM;
759 
760 	property->length = size / 4;
761 	property->value.text = kzalloc(size, GFP_KERNEL);
762 	if (!property->value.text) {
763 		kfree(property);
764 		return -ENOMEM;
765 	}
766 
767 	strcpy(property->value.text, text);
768 
769 	list_add_tail(&property->list, &parent->properties);
770 	return 0;
771 }
772 EXPORT_SYMBOL_GPL(tb_property_add_text);
773 
774 /**
775  * tb_property_add_dir() - Adds a directory to the parent directory
776  * @parent: Directory to add the property
777  * @key: Key for the property
778  * @dir: Directory to add
779  *
780  * Return: %0 on success, negative errno otherwise.
781  */
782 int tb_property_add_dir(struct tb_property_dir *parent, const char *key,
783 			struct tb_property_dir *dir)
784 {
785 	struct tb_property *property;
786 
787 	if (!tb_property_key_valid(key))
788 		return -EINVAL;
789 
790 	property = tb_property_alloc(key, TB_PROPERTY_TYPE_DIRECTORY);
791 	if (!property)
792 		return -ENOMEM;
793 
794 	property->value.dir = dir;
795 
796 	list_add_tail(&property->list, &parent->properties);
797 	return 0;
798 }
799 EXPORT_SYMBOL_GPL(tb_property_add_dir);
800 
801 /**
802  * tb_property_remove() - Removes property from a parent directory
803  * @property: Property to remove
804  *
805  * Note memory for @property is released as well so it is not allowed to
806  * touch the object after call to this function.
807  */
808 void tb_property_remove(struct tb_property *property)
809 {
810 	list_del(&property->list);
811 	kfree(property);
812 }
813 EXPORT_SYMBOL_GPL(tb_property_remove);
814 
815 /**
816  * tb_property_find() - Find a property from a directory
817  * @dir: Directory where the property is searched
818  * @key: Key to look for
819  * @type: Type of the property
820  *
821  * Finds and returns property from the given directory. Does not
822  * recurse into sub-directories.
823  *
824  * Return: Pointer to &struct tb_property, %NULL if the property was not found.
825  */
826 struct tb_property *tb_property_find(struct tb_property_dir *dir,
827 	const char *key, enum tb_property_type type)
828 {
829 	struct tb_property *property;
830 
831 	list_for_each_entry(property, &dir->properties, list) {
832 		if (property->type == type && !strcmp(property->key, key))
833 			return property;
834 	}
835 
836 	return NULL;
837 }
838 EXPORT_SYMBOL_GPL(tb_property_find);
839 
840 /**
841  * tb_property_get_next() - Get next property from directory
842  * @dir: Directory holding properties
843  * @prev: Previous property in the directory (%NULL returns the first)
844  *
845  * Return: Pointer to &struct tb_property, %NULL if property was not found.
846  */
847 struct tb_property *tb_property_get_next(struct tb_property_dir *dir,
848 					 struct tb_property *prev)
849 {
850 	if (prev) {
851 		if (list_is_last(&prev->list, &dir->properties))
852 			return NULL;
853 		return list_next_entry(prev, list);
854 	}
855 	return list_first_entry_or_null(&dir->properties, struct tb_property,
856 					list);
857 }
858 EXPORT_SYMBOL_GPL(tb_property_get_next);
859