property.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Thunderbolt XDomain property support
  4. *
  5. * Copyright (C) 2017, Intel Corporation
  6. * Authors: Michael Jamet <[email protected]>
  7. * Mika Westerberg <[email protected]>
  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(sizeof(*property), GFP_KERNEL);
  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(sizeof(*dir), GFP_KERNEL);
  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. * Upon success returns the parsed directory. In case of error returns
  178. * %NULL. 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. struct tb_property_dir *tb_property_parse_dir(const u32 *block,
  184. size_t block_len)
  185. {
  186. const struct tb_property_rootdir_entry *rootdir =
  187. (const struct tb_property_rootdir_entry *)block;
  188. if (rootdir->magic != TB_PROPERTY_ROOTDIR_MAGIC)
  189. return NULL;
  190. if (rootdir->length > block_len)
  191. return NULL;
  192. return __tb_property_parse_dir(block, block_len, 0, rootdir->length,
  193. true);
  194. }
  195. /**
  196. * tb_property_create_dir() - Creates new property directory
  197. * @uuid: UUID used to identify the particular directory
  198. *
  199. * Creates new, empty property directory. If @uuid is %NULL then the
  200. * directory is assumed to be root directory.
  201. */
  202. struct tb_property_dir *tb_property_create_dir(const uuid_t *uuid)
  203. {
  204. struct tb_property_dir *dir;
  205. dir = kzalloc(sizeof(*dir), GFP_KERNEL);
  206. if (!dir)
  207. return NULL;
  208. INIT_LIST_HEAD(&dir->properties);
  209. if (uuid) {
  210. dir->uuid = kmemdup(uuid, sizeof(*dir->uuid), GFP_KERNEL);
  211. if (!dir->uuid) {
  212. kfree(dir);
  213. return NULL;
  214. }
  215. }
  216. return dir;
  217. }
  218. EXPORT_SYMBOL_GPL(tb_property_create_dir);
  219. static void tb_property_free(struct tb_property *property)
  220. {
  221. switch (property->type) {
  222. case TB_PROPERTY_TYPE_DIRECTORY:
  223. tb_property_free_dir(property->value.dir);
  224. break;
  225. case TB_PROPERTY_TYPE_DATA:
  226. kfree(property->value.data);
  227. break;
  228. case TB_PROPERTY_TYPE_TEXT:
  229. kfree(property->value.text);
  230. break;
  231. default:
  232. break;
  233. }
  234. kfree(property);
  235. }
  236. /**
  237. * tb_property_free_dir() - Release memory allocated for property directory
  238. * @dir: Directory to release
  239. *
  240. * This will release all the memory the directory occupies including all
  241. * descendants. It is OK to pass %NULL @dir, then the function does
  242. * nothing.
  243. */
  244. void tb_property_free_dir(struct tb_property_dir *dir)
  245. {
  246. struct tb_property *property, *tmp;
  247. if (!dir)
  248. return;
  249. list_for_each_entry_safe(property, tmp, &dir->properties, list) {
  250. list_del(&property->list);
  251. tb_property_free(property);
  252. }
  253. kfree(dir->uuid);
  254. kfree(dir);
  255. }
  256. EXPORT_SYMBOL_GPL(tb_property_free_dir);
  257. static size_t tb_property_dir_length(const struct tb_property_dir *dir,
  258. bool recurse, size_t *data_len)
  259. {
  260. const struct tb_property *property;
  261. size_t len = 0;
  262. if (dir->uuid)
  263. len += sizeof(*dir->uuid) / 4;
  264. else
  265. len += sizeof(struct tb_property_rootdir_entry) / 4;
  266. list_for_each_entry(property, &dir->properties, list) {
  267. len += sizeof(struct tb_property_entry) / 4;
  268. switch (property->type) {
  269. case TB_PROPERTY_TYPE_DIRECTORY:
  270. if (recurse) {
  271. len += tb_property_dir_length(
  272. property->value.dir, recurse, data_len);
  273. }
  274. /* Reserve dword padding after each directory */
  275. if (data_len)
  276. *data_len += 1;
  277. break;
  278. case TB_PROPERTY_TYPE_DATA:
  279. case TB_PROPERTY_TYPE_TEXT:
  280. if (data_len)
  281. *data_len += property->length;
  282. break;
  283. default:
  284. break;
  285. }
  286. }
  287. return len;
  288. }
  289. static ssize_t __tb_property_format_dir(const struct tb_property_dir *dir,
  290. u32 *block, unsigned int start_offset, size_t block_len)
  291. {
  292. unsigned int data_offset, dir_end;
  293. const struct tb_property *property;
  294. struct tb_property_entry *entry;
  295. size_t dir_len, data_len = 0;
  296. int ret;
  297. /*
  298. * The structure of property block looks like following. Leaf
  299. * data/text is included right after the directory and each
  300. * directory follows each other (even nested ones).
  301. *
  302. * +----------+ <-- start_offset
  303. * | header | <-- root directory header
  304. * +----------+ ---
  305. * | entry 0 | -^--------------------.
  306. * +----------+ | |
  307. * | entry 1 | -|--------------------|--.
  308. * +----------+ | | |
  309. * | entry 2 | -|-----------------. | |
  310. * +----------+ | | | |
  311. * : : | dir_len | | |
  312. * . . | | | |
  313. * : : | | | |
  314. * +----------+ | | | |
  315. * | entry n | v | | |
  316. * +----------+ <-- data_offset | | |
  317. * | data 0 | <------------------|--' |
  318. * +----------+ | |
  319. * | data 1 | <------------------|-----'
  320. * +----------+ |
  321. * | 00000000 | padding |
  322. * +----------+ <-- dir_end <------'
  323. * | UUID | <-- directory UUID (child directory)
  324. * +----------+
  325. * | entry 0 |
  326. * +----------+
  327. * | entry 1 |
  328. * +----------+
  329. * : :
  330. * . .
  331. * : :
  332. * +----------+
  333. * | entry n |
  334. * +----------+
  335. * | data 0 |
  336. * +----------+
  337. *
  338. * We use dir_end to hold pointer to the end of the directory. It
  339. * will increase as we add directories and each directory should be
  340. * added starting from previous dir_end.
  341. */
  342. dir_len = tb_property_dir_length(dir, false, &data_len);
  343. data_offset = start_offset + dir_len;
  344. dir_end = start_offset + data_len + dir_len;
  345. if (data_offset > dir_end)
  346. return -EINVAL;
  347. if (dir_end > block_len)
  348. return -EINVAL;
  349. /* Write headers first */
  350. if (dir->uuid) {
  351. struct tb_property_dir_entry *pe;
  352. pe = (struct tb_property_dir_entry *)&block[start_offset];
  353. memcpy(pe->uuid, dir->uuid, sizeof(pe->uuid));
  354. entry = pe->entries;
  355. } else {
  356. struct tb_property_rootdir_entry *re;
  357. re = (struct tb_property_rootdir_entry *)&block[start_offset];
  358. re->magic = TB_PROPERTY_ROOTDIR_MAGIC;
  359. re->length = dir_len - sizeof(*re) / 4;
  360. entry = re->entries;
  361. }
  362. list_for_each_entry(property, &dir->properties, list) {
  363. const struct tb_property_dir *child;
  364. format_dwdata(entry, property->key, 2);
  365. entry->type = property->type;
  366. switch (property->type) {
  367. case TB_PROPERTY_TYPE_DIRECTORY:
  368. child = property->value.dir;
  369. ret = __tb_property_format_dir(child, block, dir_end,
  370. block_len);
  371. if (ret < 0)
  372. return ret;
  373. entry->length = tb_property_dir_length(child, false,
  374. NULL);
  375. entry->value = dir_end;
  376. dir_end = ret;
  377. break;
  378. case TB_PROPERTY_TYPE_DATA:
  379. format_dwdata(&block[data_offset], property->value.data,
  380. property->length);
  381. entry->length = property->length;
  382. entry->value = data_offset;
  383. data_offset += entry->length;
  384. break;
  385. case TB_PROPERTY_TYPE_TEXT:
  386. format_dwdata(&block[data_offset], property->value.text,
  387. property->length);
  388. entry->length = property->length;
  389. entry->value = data_offset;
  390. data_offset += entry->length;
  391. break;
  392. case TB_PROPERTY_TYPE_VALUE:
  393. entry->length = property->length;
  394. entry->value = property->value.immediate;
  395. break;
  396. default:
  397. break;
  398. }
  399. entry++;
  400. }
  401. return dir_end;
  402. }
  403. /**
  404. * tb_property_format_dir() - Formats directory to the packed XDomain format
  405. * @dir: Directory to format
  406. * @block: Property block where the packed data is placed
  407. * @block_len: Length of the property block
  408. *
  409. * This function formats the directory to the packed format that can be
  410. * then send over the thunderbolt fabric to receiving host. Returns %0 in
  411. * case of success and negative errno on faulure. Passing %NULL in @block
  412. * returns number of entries the block takes.
  413. */
  414. ssize_t tb_property_format_dir(const struct tb_property_dir *dir, u32 *block,
  415. size_t block_len)
  416. {
  417. ssize_t ret;
  418. if (!block) {
  419. size_t dir_len, data_len = 0;
  420. dir_len = tb_property_dir_length(dir, true, &data_len);
  421. return dir_len + data_len;
  422. }
  423. ret = __tb_property_format_dir(dir, block, 0, block_len);
  424. return ret < 0 ? ret : 0;
  425. }
  426. /**
  427. * tb_property_copy_dir() - Take a deep copy of directory
  428. * @dir: Directory to copy
  429. *
  430. * This function takes a deep copy of @dir and returns back the copy. In
  431. * case of error returns %NULL. The resulting directory needs to be
  432. * released by calling tb_property_free_dir().
  433. */
  434. struct tb_property_dir *tb_property_copy_dir(const struct tb_property_dir *dir)
  435. {
  436. struct tb_property *property, *p = NULL;
  437. struct tb_property_dir *d;
  438. if (!dir)
  439. return NULL;
  440. d = tb_property_create_dir(dir->uuid);
  441. if (!d)
  442. return NULL;
  443. list_for_each_entry(property, &dir->properties, list) {
  444. struct tb_property *p;
  445. p = tb_property_alloc(property->key, property->type);
  446. if (!p)
  447. goto err_free;
  448. p->length = property->length;
  449. switch (property->type) {
  450. case TB_PROPERTY_TYPE_DIRECTORY:
  451. p->value.dir = tb_property_copy_dir(property->value.dir);
  452. if (!p->value.dir)
  453. goto err_free;
  454. break;
  455. case TB_PROPERTY_TYPE_DATA:
  456. p->value.data = kmemdup(property->value.data,
  457. property->length * 4,
  458. GFP_KERNEL);
  459. if (!p->value.data)
  460. goto err_free;
  461. break;
  462. case TB_PROPERTY_TYPE_TEXT:
  463. p->value.text = kzalloc(p->length * 4, GFP_KERNEL);
  464. if (!p->value.text)
  465. goto err_free;
  466. strcpy(p->value.text, property->value.text);
  467. break;
  468. case TB_PROPERTY_TYPE_VALUE:
  469. p->value.immediate = property->value.immediate;
  470. break;
  471. default:
  472. break;
  473. }
  474. list_add_tail(&p->list, &d->properties);
  475. }
  476. return d;
  477. err_free:
  478. kfree(p);
  479. tb_property_free_dir(d);
  480. return NULL;
  481. }
  482. /**
  483. * tb_property_add_immediate() - Add immediate property to directory
  484. * @parent: Directory to add the property
  485. * @key: Key for the property
  486. * @value: Immediate value to store with the property
  487. */
  488. int tb_property_add_immediate(struct tb_property_dir *parent, const char *key,
  489. u32 value)
  490. {
  491. struct tb_property *property;
  492. if (!tb_property_key_valid(key))
  493. return -EINVAL;
  494. property = tb_property_alloc(key, TB_PROPERTY_TYPE_VALUE);
  495. if (!property)
  496. return -ENOMEM;
  497. property->length = 1;
  498. property->value.immediate = value;
  499. list_add_tail(&property->list, &parent->properties);
  500. return 0;
  501. }
  502. EXPORT_SYMBOL_GPL(tb_property_add_immediate);
  503. /**
  504. * tb_property_add_data() - Adds arbitrary data property to directory
  505. * @parent: Directory to add the property
  506. * @key: Key for the property
  507. * @buf: Data buffer to add
  508. * @buflen: Number of bytes in the data buffer
  509. *
  510. * Function takes a copy of @buf and adds it to the directory.
  511. */
  512. int tb_property_add_data(struct tb_property_dir *parent, const char *key,
  513. const void *buf, size_t buflen)
  514. {
  515. /* Need to pad to dword boundary */
  516. size_t size = round_up(buflen, 4);
  517. struct tb_property *property;
  518. if (!tb_property_key_valid(key))
  519. return -EINVAL;
  520. property = tb_property_alloc(key, TB_PROPERTY_TYPE_DATA);
  521. if (!property)
  522. return -ENOMEM;
  523. property->length = size / 4;
  524. property->value.data = kzalloc(size, GFP_KERNEL);
  525. if (!property->value.data) {
  526. kfree(property);
  527. return -ENOMEM;
  528. }
  529. memcpy(property->value.data, buf, buflen);
  530. list_add_tail(&property->list, &parent->properties);
  531. return 0;
  532. }
  533. EXPORT_SYMBOL_GPL(tb_property_add_data);
  534. /**
  535. * tb_property_add_text() - Adds string property to directory
  536. * @parent: Directory to add the property
  537. * @key: Key for the property
  538. * @text: String to add
  539. *
  540. * Function takes a copy of @text and adds it to the directory.
  541. */
  542. int tb_property_add_text(struct tb_property_dir *parent, const char *key,
  543. const char *text)
  544. {
  545. /* Need to pad to dword boundary */
  546. size_t size = round_up(strlen(text) + 1, 4);
  547. struct tb_property *property;
  548. if (!tb_property_key_valid(key))
  549. return -EINVAL;
  550. property = tb_property_alloc(key, TB_PROPERTY_TYPE_TEXT);
  551. if (!property)
  552. return -ENOMEM;
  553. property->length = size / 4;
  554. property->value.text = kzalloc(size, GFP_KERNEL);
  555. if (!property->value.text) {
  556. kfree(property);
  557. return -ENOMEM;
  558. }
  559. strcpy(property->value.text, text);
  560. list_add_tail(&property->list, &parent->properties);
  561. return 0;
  562. }
  563. EXPORT_SYMBOL_GPL(tb_property_add_text);
  564. /**
  565. * tb_property_add_dir() - Adds a directory to the parent directory
  566. * @parent: Directory to add the property
  567. * @key: Key for the property
  568. * @dir: Directory to add
  569. */
  570. int tb_property_add_dir(struct tb_property_dir *parent, const char *key,
  571. struct tb_property_dir *dir)
  572. {
  573. struct tb_property *property;
  574. if (!tb_property_key_valid(key))
  575. return -EINVAL;
  576. property = tb_property_alloc(key, TB_PROPERTY_TYPE_DIRECTORY);
  577. if (!property)
  578. return -ENOMEM;
  579. property->value.dir = dir;
  580. list_add_tail(&property->list, &parent->properties);
  581. return 0;
  582. }
  583. EXPORT_SYMBOL_GPL(tb_property_add_dir);
  584. /**
  585. * tb_property_remove() - Removes property from a parent directory
  586. * @property: Property to remove
  587. *
  588. * Note memory for @property is released as well so it is not allowed to
  589. * touch the object after call to this function.
  590. */
  591. void tb_property_remove(struct tb_property *property)
  592. {
  593. list_del(&property->list);
  594. kfree(property);
  595. }
  596. EXPORT_SYMBOL_GPL(tb_property_remove);
  597. /**
  598. * tb_property_find() - Find a property from a directory
  599. * @dir: Directory where the property is searched
  600. * @key: Key to look for
  601. * @type: Type of the property
  602. *
  603. * Finds and returns property from the given directory. Does not recurse
  604. * into sub-directories. Returns %NULL if the property was not found.
  605. */
  606. struct tb_property *tb_property_find(struct tb_property_dir *dir,
  607. const char *key, enum tb_property_type type)
  608. {
  609. struct tb_property *property;
  610. list_for_each_entry(property, &dir->properties, list) {
  611. if (property->type == type && !strcmp(property->key, key))
  612. return property;
  613. }
  614. return NULL;
  615. }
  616. EXPORT_SYMBOL_GPL(tb_property_find);
  617. /**
  618. * tb_property_get_next() - Get next property from directory
  619. * @dir: Directory holding properties
  620. * @prev: Previous property in the directory (%NULL returns the first)
  621. */
  622. struct tb_property *tb_property_get_next(struct tb_property_dir *dir,
  623. struct tb_property *prev)
  624. {
  625. if (prev) {
  626. if (list_is_last(&prev->list, &dir->properties))
  627. return NULL;
  628. return list_next_entry(prev, list);
  629. }
  630. return list_first_entry_or_null(&dir->properties, struct tb_property,
  631. list);
  632. }
  633. EXPORT_SYMBOL_GPL(tb_property_get_next);