property.c 19 KB

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