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