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