| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> |
|---|
| 3 | 4 | <http://rt2x00.serialmonkey.com> |
|---|
| 4 | 5 | |
|---|
| 5 | | - This program is free software; you can redistribute it and/or modify |
|---|
| 6 | | - it under the terms of the GNU General Public License as published by |
|---|
| 7 | | - the Free Software Foundation; either version 2 of the License, or |
|---|
| 8 | | - (at your option) any later version. |
|---|
| 9 | | - |
|---|
| 10 | | - This program is distributed in the hope that it will be useful, |
|---|
| 11 | | - but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 12 | | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 13 | | - GNU General Public License for more details. |
|---|
| 14 | | - |
|---|
| 15 | | - You should have received a copy of the GNU General Public License |
|---|
| 16 | | - along with this program; if not, see <http://www.gnu.org/licenses/>. |
|---|
| 17 | 6 | */ |
|---|
| 18 | 7 | |
|---|
| 19 | 8 | /* |
|---|
| .. | .. |
|---|
| 63 | 52 | * - chipset file |
|---|
| 64 | 53 | * - device state flags file |
|---|
| 65 | 54 | * - device capability flags file |
|---|
| 55 | + * - hardware restart file |
|---|
| 66 | 56 | * - register folder |
|---|
| 67 | 57 | * - csr offset/value files |
|---|
| 68 | 58 | * - eeprom offset/value files |
|---|
| .. | .. |
|---|
| 75 | 65 | * - crypto stats file |
|---|
| 76 | 66 | */ |
|---|
| 77 | 67 | struct dentry *driver_folder; |
|---|
| 78 | | - struct dentry *driver_entry; |
|---|
| 79 | | - struct dentry *chipset_entry; |
|---|
| 80 | | - struct dentry *dev_flags; |
|---|
| 81 | | - struct dentry *cap_flags; |
|---|
| 82 | | - struct dentry *register_folder; |
|---|
| 83 | | - struct dentry *csr_off_entry; |
|---|
| 84 | | - struct dentry *csr_val_entry; |
|---|
| 85 | | - struct dentry *eeprom_off_entry; |
|---|
| 86 | | - struct dentry *eeprom_val_entry; |
|---|
| 87 | | - struct dentry *bbp_off_entry; |
|---|
| 88 | | - struct dentry *bbp_val_entry; |
|---|
| 89 | | - struct dentry *rf_off_entry; |
|---|
| 90 | | - struct dentry *rf_val_entry; |
|---|
| 91 | | - struct dentry *rfcsr_off_entry; |
|---|
| 92 | | - struct dentry *rfcsr_val_entry; |
|---|
| 93 | | - struct dentry *queue_folder; |
|---|
| 94 | | - struct dentry *queue_frame_dump_entry; |
|---|
| 95 | | - struct dentry *queue_stats_entry; |
|---|
| 96 | | - struct dentry *crypto_stats_entry; |
|---|
| 97 | 68 | |
|---|
| 98 | 69 | /* |
|---|
| 99 | 70 | * The frame dump file only allows a single reader, |
|---|
| .. | .. |
|---|
| 464 | 435 | \ |
|---|
| 465 | 436 | size = sprintf(line, __format, value); \ |
|---|
| 466 | 437 | \ |
|---|
| 467 | | - if (copy_to_user(buf, line, size)) \ |
|---|
| 468 | | - return -EFAULT; \ |
|---|
| 469 | | - \ |
|---|
| 470 | | - *offset += size; \ |
|---|
| 471 | | - return size; \ |
|---|
| 438 | + return simple_read_from_buffer(buf, length, offset, line, size); \ |
|---|
| 472 | 439 | } |
|---|
| 473 | 440 | |
|---|
| 474 | 441 | #define RT2X00DEBUGFS_OPS_WRITE(__name, __type) \ |
|---|
| .. | .. |
|---|
| 545 | 512 | |
|---|
| 546 | 513 | size = sprintf(line, "0x%.8x\n", (unsigned int)intf->rt2x00dev->flags); |
|---|
| 547 | 514 | |
|---|
| 548 | | - if (copy_to_user(buf, line, size)) |
|---|
| 549 | | - return -EFAULT; |
|---|
| 550 | | - |
|---|
| 551 | | - *offset += size; |
|---|
| 552 | | - return size; |
|---|
| 515 | + return simple_read_from_buffer(buf, length, offset, line, size); |
|---|
| 553 | 516 | } |
|---|
| 554 | 517 | |
|---|
| 555 | 518 | static const struct file_operations rt2x00debug_fop_dev_flags = { |
|---|
| .. | .. |
|---|
| 574 | 537 | |
|---|
| 575 | 538 | size = sprintf(line, "0x%.8x\n", (unsigned int)intf->rt2x00dev->cap_flags); |
|---|
| 576 | 539 | |
|---|
| 577 | | - if (copy_to_user(buf, line, size)) |
|---|
| 578 | | - return -EFAULT; |
|---|
| 579 | | - |
|---|
| 580 | | - *offset += size; |
|---|
| 581 | | - return size; |
|---|
| 540 | + return simple_read_from_buffer(buf, length, offset, line, size); |
|---|
| 582 | 541 | } |
|---|
| 583 | 542 | |
|---|
| 584 | 543 | static const struct file_operations rt2x00debug_fop_cap_flags = { |
|---|
| .. | .. |
|---|
| 589 | 548 | .llseek = default_llseek, |
|---|
| 590 | 549 | }; |
|---|
| 591 | 550 | |
|---|
| 592 | | -static struct dentry *rt2x00debug_create_file_driver(const char *name, |
|---|
| 593 | | - struct rt2x00debug_intf |
|---|
| 594 | | - *intf, |
|---|
| 595 | | - struct debugfs_blob_wrapper |
|---|
| 596 | | - *blob) |
|---|
| 551 | +static ssize_t rt2x00debug_write_restart_hw(struct file *file, |
|---|
| 552 | + const char __user *buf, |
|---|
| 553 | + size_t length, |
|---|
| 554 | + loff_t *offset) |
|---|
| 555 | +{ |
|---|
| 556 | + struct rt2x00debug_intf *intf = file->private_data; |
|---|
| 557 | + struct rt2x00_dev *rt2x00dev = intf->rt2x00dev; |
|---|
| 558 | + static unsigned long last_reset = INITIAL_JIFFIES; |
|---|
| 559 | + |
|---|
| 560 | + if (!rt2x00_has_cap_restart_hw(rt2x00dev)) |
|---|
| 561 | + return -EOPNOTSUPP; |
|---|
| 562 | + |
|---|
| 563 | + if (time_before(jiffies, last_reset + msecs_to_jiffies(2000))) |
|---|
| 564 | + return -EBUSY; |
|---|
| 565 | + |
|---|
| 566 | + last_reset = jiffies; |
|---|
| 567 | + |
|---|
| 568 | + ieee80211_restart_hw(rt2x00dev->hw); |
|---|
| 569 | + return length; |
|---|
| 570 | +} |
|---|
| 571 | + |
|---|
| 572 | +static const struct file_operations rt2x00debug_restart_hw = { |
|---|
| 573 | + .owner = THIS_MODULE, |
|---|
| 574 | + .write = rt2x00debug_write_restart_hw, |
|---|
| 575 | + .open = simple_open, |
|---|
| 576 | + .llseek = generic_file_llseek, |
|---|
| 577 | +}; |
|---|
| 578 | + |
|---|
| 579 | +static void rt2x00debug_create_file_driver(const char *name, |
|---|
| 580 | + struct rt2x00debug_intf *intf, |
|---|
| 581 | + struct debugfs_blob_wrapper *blob) |
|---|
| 597 | 582 | { |
|---|
| 598 | 583 | char *data; |
|---|
| 599 | 584 | |
|---|
| 600 | 585 | data = kzalloc(3 * MAX_LINE_LENGTH, GFP_KERNEL); |
|---|
| 601 | 586 | if (!data) |
|---|
| 602 | | - return NULL; |
|---|
| 587 | + return; |
|---|
| 603 | 588 | |
|---|
| 604 | 589 | blob->data = data; |
|---|
| 605 | 590 | data += sprintf(data, "driver:\t%s\n", intf->rt2x00dev->ops->name); |
|---|
| 606 | 591 | data += sprintf(data, "version:\t%s\n", DRV_VERSION); |
|---|
| 607 | 592 | blob->size = strlen(blob->data); |
|---|
| 608 | 593 | |
|---|
| 609 | | - return debugfs_create_blob(name, 0400, intf->driver_folder, blob); |
|---|
| 594 | + debugfs_create_blob(name, 0400, intf->driver_folder, blob); |
|---|
| 610 | 595 | } |
|---|
| 611 | 596 | |
|---|
| 612 | | -static struct dentry *rt2x00debug_create_file_chipset(const char *name, |
|---|
| 613 | | - struct rt2x00debug_intf |
|---|
| 614 | | - *intf, |
|---|
| 615 | | - struct |
|---|
| 616 | | - debugfs_blob_wrapper |
|---|
| 617 | | - *blob) |
|---|
| 597 | +static void rt2x00debug_create_file_chipset(const char *name, |
|---|
| 598 | + struct rt2x00debug_intf *intf, |
|---|
| 599 | + struct debugfs_blob_wrapper *blob) |
|---|
| 618 | 600 | { |
|---|
| 619 | 601 | const struct rt2x00debug *debug = intf->debug; |
|---|
| 620 | 602 | char *data; |
|---|
| 621 | 603 | |
|---|
| 622 | 604 | data = kzalloc(9 * MAX_LINE_LENGTH, GFP_KERNEL); |
|---|
| 623 | 605 | if (!data) |
|---|
| 624 | | - return NULL; |
|---|
| 606 | + return; |
|---|
| 625 | 607 | |
|---|
| 626 | 608 | blob->data = data; |
|---|
| 627 | 609 | data += sprintf(data, "rt chip:\t%04x\n", intf->rt2x00dev->chip.rt); |
|---|
| .. | .. |
|---|
| 647 | 629 | |
|---|
| 648 | 630 | blob->size = strlen(blob->data); |
|---|
| 649 | 631 | |
|---|
| 650 | | - return debugfs_create_blob(name, 0400, intf->driver_folder, blob); |
|---|
| 632 | + debugfs_create_blob(name, 0400, intf->driver_folder, blob); |
|---|
| 651 | 633 | } |
|---|
| 652 | 634 | |
|---|
| 653 | 635 | void rt2x00debug_register(struct rt2x00_dev *rt2x00dev) |
|---|
| 654 | 636 | { |
|---|
| 655 | 637 | const struct rt2x00debug *debug = rt2x00dev->ops->debugfs; |
|---|
| 656 | 638 | struct rt2x00debug_intf *intf; |
|---|
| 639 | + struct dentry *queue_folder; |
|---|
| 640 | + struct dentry *register_folder; |
|---|
| 657 | 641 | |
|---|
| 658 | 642 | intf = kzalloc(sizeof(struct rt2x00debug_intf), GFP_KERNEL); |
|---|
| 659 | 643 | if (!intf) { |
|---|
| .. | .. |
|---|
| 668 | 652 | intf->driver_folder = |
|---|
| 669 | 653 | debugfs_create_dir(intf->rt2x00dev->ops->name, |
|---|
| 670 | 654 | rt2x00dev->hw->wiphy->debugfsdir); |
|---|
| 671 | | - if (IS_ERR(intf->driver_folder) || !intf->driver_folder) |
|---|
| 672 | | - goto exit; |
|---|
| 673 | 655 | |
|---|
| 674 | | - intf->driver_entry = |
|---|
| 675 | | - rt2x00debug_create_file_driver("driver", intf, &intf->driver_blob); |
|---|
| 676 | | - if (IS_ERR(intf->driver_entry) || !intf->driver_entry) |
|---|
| 677 | | - goto exit; |
|---|
| 656 | + rt2x00debug_create_file_driver("driver", intf, &intf->driver_blob); |
|---|
| 657 | + rt2x00debug_create_file_chipset("chipset", intf, &intf->chipset_blob); |
|---|
| 658 | + debugfs_create_file("dev_flags", 0400, intf->driver_folder, intf, |
|---|
| 659 | + &rt2x00debug_fop_dev_flags); |
|---|
| 660 | + debugfs_create_file("cap_flags", 0400, intf->driver_folder, intf, |
|---|
| 661 | + &rt2x00debug_fop_cap_flags); |
|---|
| 662 | + debugfs_create_file("restart_hw", 0200, intf->driver_folder, intf, |
|---|
| 663 | + &rt2x00debug_restart_hw); |
|---|
| 678 | 664 | |
|---|
| 679 | | - intf->chipset_entry = |
|---|
| 680 | | - rt2x00debug_create_file_chipset("chipset", |
|---|
| 681 | | - intf, &intf->chipset_blob); |
|---|
| 682 | | - if (IS_ERR(intf->chipset_entry) || !intf->chipset_entry) |
|---|
| 683 | | - goto exit; |
|---|
| 684 | | - |
|---|
| 685 | | - intf->dev_flags = debugfs_create_file("dev_flags", 0400, |
|---|
| 686 | | - intf->driver_folder, intf, |
|---|
| 687 | | - &rt2x00debug_fop_dev_flags); |
|---|
| 688 | | - if (IS_ERR(intf->dev_flags) || !intf->dev_flags) |
|---|
| 689 | | - goto exit; |
|---|
| 690 | | - |
|---|
| 691 | | - intf->cap_flags = debugfs_create_file("cap_flags", 0400, |
|---|
| 692 | | - intf->driver_folder, intf, |
|---|
| 693 | | - &rt2x00debug_fop_cap_flags); |
|---|
| 694 | | - if (IS_ERR(intf->cap_flags) || !intf->cap_flags) |
|---|
| 695 | | - goto exit; |
|---|
| 696 | | - |
|---|
| 697 | | - intf->register_folder = |
|---|
| 698 | | - debugfs_create_dir("register", intf->driver_folder); |
|---|
| 699 | | - if (IS_ERR(intf->register_folder) || !intf->register_folder) |
|---|
| 700 | | - goto exit; |
|---|
| 665 | + register_folder = debugfs_create_dir("register", intf->driver_folder); |
|---|
| 701 | 666 | |
|---|
| 702 | 667 | #define RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(__intf, __name) \ |
|---|
| 703 | 668 | ({ \ |
|---|
| 704 | 669 | if (debug->__name.read) { \ |
|---|
| 705 | | - (__intf)->__name##_off_entry = \ |
|---|
| 706 | | - debugfs_create_u32(__stringify(__name) "_offset", \ |
|---|
| 707 | | - 0600, \ |
|---|
| 708 | | - (__intf)->register_folder, \ |
|---|
| 709 | | - &(__intf)->offset_##__name); \ |
|---|
| 710 | | - if (IS_ERR((__intf)->__name##_off_entry) || \ |
|---|
| 711 | | - !(__intf)->__name##_off_entry) \ |
|---|
| 712 | | - goto exit; \ |
|---|
| 670 | + debugfs_create_u32(__stringify(__name) "_offset", 0600, \ |
|---|
| 671 | + register_folder, \ |
|---|
| 672 | + &(__intf)->offset_##__name); \ |
|---|
| 713 | 673 | \ |
|---|
| 714 | | - (__intf)->__name##_val_entry = \ |
|---|
| 715 | | - debugfs_create_file(__stringify(__name) "_value", \ |
|---|
| 716 | | - 0600, \ |
|---|
| 717 | | - (__intf)->register_folder, \ |
|---|
| 718 | | - (__intf), \ |
|---|
| 719 | | - &rt2x00debug_fop_##__name); \ |
|---|
| 720 | | - if (IS_ERR((__intf)->__name##_val_entry) || \ |
|---|
| 721 | | - !(__intf)->__name##_val_entry) \ |
|---|
| 722 | | - goto exit; \ |
|---|
| 674 | + debugfs_create_file(__stringify(__name) "_value", 0600, \ |
|---|
| 675 | + register_folder, (__intf), \ |
|---|
| 676 | + &rt2x00debug_fop_##__name); \ |
|---|
| 723 | 677 | } \ |
|---|
| 724 | 678 | }) |
|---|
| 725 | 679 | |
|---|
| .. | .. |
|---|
| 731 | 685 | |
|---|
| 732 | 686 | #undef RT2X00DEBUGFS_CREATE_REGISTER_ENTRY |
|---|
| 733 | 687 | |
|---|
| 734 | | - intf->queue_folder = |
|---|
| 735 | | - debugfs_create_dir("queue", intf->driver_folder); |
|---|
| 736 | | - if (IS_ERR(intf->queue_folder) || !intf->queue_folder) |
|---|
| 737 | | - goto exit; |
|---|
| 688 | + queue_folder = debugfs_create_dir("queue", intf->driver_folder); |
|---|
| 738 | 689 | |
|---|
| 739 | | - intf->queue_frame_dump_entry = |
|---|
| 740 | | - debugfs_create_file("dump", 0400, intf->queue_folder, |
|---|
| 741 | | - intf, &rt2x00debug_fop_queue_dump); |
|---|
| 742 | | - if (IS_ERR(intf->queue_frame_dump_entry) |
|---|
| 743 | | - || !intf->queue_frame_dump_entry) |
|---|
| 744 | | - goto exit; |
|---|
| 690 | + debugfs_create_file("dump", 0400, queue_folder, intf, |
|---|
| 691 | + &rt2x00debug_fop_queue_dump); |
|---|
| 745 | 692 | |
|---|
| 746 | 693 | skb_queue_head_init(&intf->frame_dump_skbqueue); |
|---|
| 747 | 694 | init_waitqueue_head(&intf->frame_dump_waitqueue); |
|---|
| 748 | 695 | |
|---|
| 749 | | - intf->queue_stats_entry = |
|---|
| 750 | | - debugfs_create_file("queue", 0400, intf->queue_folder, |
|---|
| 751 | | - intf, &rt2x00debug_fop_queue_stats); |
|---|
| 696 | + debugfs_create_file("queue", 0400, queue_folder, intf, |
|---|
| 697 | + &rt2x00debug_fop_queue_stats); |
|---|
| 752 | 698 | |
|---|
| 753 | 699 | #ifdef CONFIG_RT2X00_LIB_CRYPTO |
|---|
| 754 | 700 | if (rt2x00_has_cap_hw_crypto(rt2x00dev)) |
|---|
| 755 | | - intf->crypto_stats_entry = |
|---|
| 756 | | - debugfs_create_file("crypto", 0444, intf->queue_folder, |
|---|
| 757 | | - intf, |
|---|
| 758 | | - &rt2x00debug_fop_crypto_stats); |
|---|
| 701 | + debugfs_create_file("crypto", 0444, queue_folder, intf, |
|---|
| 702 | + &rt2x00debug_fop_crypto_stats); |
|---|
| 759 | 703 | #endif |
|---|
| 760 | 704 | |
|---|
| 761 | 705 | return; |
|---|
| 762 | | - |
|---|
| 763 | | -exit: |
|---|
| 764 | | - rt2x00debug_deregister(rt2x00dev); |
|---|
| 765 | | - rt2x00_err(rt2x00dev, "Failed to register debug handler\n"); |
|---|
| 766 | 706 | } |
|---|
| 767 | 707 | |
|---|
| 768 | 708 | void rt2x00debug_deregister(struct rt2x00_dev *rt2x00dev) |
|---|
| .. | .. |
|---|
| 774 | 714 | |
|---|
| 775 | 715 | skb_queue_purge(&intf->frame_dump_skbqueue); |
|---|
| 776 | 716 | |
|---|
| 777 | | -#ifdef CONFIG_RT2X00_LIB_CRYPTO |
|---|
| 778 | | - debugfs_remove(intf->crypto_stats_entry); |
|---|
| 779 | | -#endif |
|---|
| 780 | | - debugfs_remove(intf->queue_stats_entry); |
|---|
| 781 | | - debugfs_remove(intf->queue_frame_dump_entry); |
|---|
| 782 | | - debugfs_remove(intf->queue_folder); |
|---|
| 783 | | - debugfs_remove(intf->rfcsr_val_entry); |
|---|
| 784 | | - debugfs_remove(intf->rfcsr_off_entry); |
|---|
| 785 | | - debugfs_remove(intf->rf_val_entry); |
|---|
| 786 | | - debugfs_remove(intf->rf_off_entry); |
|---|
| 787 | | - debugfs_remove(intf->bbp_val_entry); |
|---|
| 788 | | - debugfs_remove(intf->bbp_off_entry); |
|---|
| 789 | | - debugfs_remove(intf->eeprom_val_entry); |
|---|
| 790 | | - debugfs_remove(intf->eeprom_off_entry); |
|---|
| 791 | | - debugfs_remove(intf->csr_val_entry); |
|---|
| 792 | | - debugfs_remove(intf->csr_off_entry); |
|---|
| 793 | | - debugfs_remove(intf->register_folder); |
|---|
| 794 | | - debugfs_remove(intf->dev_flags); |
|---|
| 795 | | - debugfs_remove(intf->cap_flags); |
|---|
| 796 | | - debugfs_remove(intf->chipset_entry); |
|---|
| 797 | | - debugfs_remove(intf->driver_entry); |
|---|
| 798 | | - debugfs_remove(intf->driver_folder); |
|---|
| 717 | + debugfs_remove_recursive(intf->driver_folder); |
|---|
| 799 | 718 | kfree(intf->chipset_blob.data); |
|---|
| 800 | 719 | kfree(intf->driver_blob.data); |
|---|
| 801 | 720 | kfree(intf); |
|---|