Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

dell-laptop: Do not cache hwswitch state

The hwswitch state can be changed at runtime, so make sure dell-laptop
always knows the current state. It can be modified by the userspace
utility smbios-wireless-ctl.

Signed-off-by: Pali Rohár <pali.rohar@gmail.com>
Signed-off-by: Darren Hart <dvhart@linux.intel.com>

authored by

Pali Rohár and committed by
Darren Hart
22565ba0 715d0cdd

+61 -24
+61 -24
drivers/platform/x86/dell-laptop.c
··· 309 309 static struct calling_interface_buffer *buffer; 310 310 static DEFINE_MUTEX(buffer_mutex); 311 311 312 - static int hwswitch_state; 313 - 314 312 static void clear_buffer(void) 315 313 { 316 314 memset(buffer, 0, sizeof(struct calling_interface_buffer)); ··· 551 553 int disable = blocked ? 1 : 0; 552 554 unsigned long radio = (unsigned long)data; 553 555 int hwswitch_bit = (unsigned long)data - 1; 556 + int hwswitch; 557 + int status; 554 558 int ret; 555 559 556 560 get_buffer(); 557 561 558 562 dell_send_request(buffer, 17, 11); 559 563 ret = buffer->output[0]; 564 + status = buffer->output[1]; 560 565 561 566 if (ret != 0) 562 567 goto out; 563 568 569 + clear_buffer(); 570 + 571 + buffer->input[0] = 0x2; 572 + dell_send_request(buffer, 17, 11); 573 + ret = buffer->output[0]; 574 + hwswitch = buffer->output[1]; 575 + 564 576 /* If the hardware switch controls this radio, and the hardware 565 577 switch is disabled, always disable the radio */ 566 - if ((hwswitch_state & BIT(hwswitch_bit)) && 567 - !(buffer->output[1] & BIT(16))) 578 + if (ret == 0 && (hwswitch & BIT(hwswitch_bit)) && 579 + (status & BIT(0)) && !(status & BIT(16))) 568 580 disable = 1; 569 581 570 582 clear_buffer(); ··· 605 597 } 606 598 607 599 static void dell_rfkill_update_hw_state(struct rfkill *rfkill, int radio, 608 - int status) 600 + int status, int hwswitch) 609 601 { 610 - if (hwswitch_state & (BIT(radio - 1))) 602 + if (hwswitch & (BIT(radio - 1))) 611 603 rfkill_set_hw_state(rfkill, !(status & BIT(16))); 612 604 } 613 605 614 606 static void dell_rfkill_query(struct rfkill *rfkill, void *data) 615 607 { 608 + int radio = ((unsigned long)data & 0xF); 609 + int hwswitch; 616 610 int status; 617 611 int ret; 618 612 619 613 get_buffer(); 614 + 620 615 dell_send_request(buffer, 17, 11); 621 616 ret = buffer->output[0]; 622 617 status = buffer->output[1]; 618 + 619 + if (ret != 0 || !(status & BIT(0))) { 620 + release_buffer(); 621 + return; 622 + } 623 + 624 + clear_buffer(); 625 + 626 + buffer->input[0] = 0x2; 627 + dell_send_request(buffer, 17, 11); 628 + ret = buffer->output[0]; 629 + hwswitch = buffer->output[1]; 630 + 623 631 release_buffer(); 624 632 625 633 if (ret != 0) 626 634 return; 627 635 628 - dell_rfkill_update_hw_state(rfkill, (unsigned long)data, status); 636 + dell_rfkill_update_hw_state(rfkill, radio, status, hwswitch); 629 637 } 630 638 631 639 static const struct rfkill_ops dell_rfkill_ops = { ··· 653 629 654 630 static int dell_debugfs_show(struct seq_file *s, void *data) 655 631 { 632 + int hwswitch_state; 633 + int hwswitch_ret; 656 634 int status; 657 635 int ret; 658 636 659 637 get_buffer(); 638 + 660 639 dell_send_request(buffer, 17, 11); 661 640 ret = buffer->output[0]; 662 641 status = buffer->output[1]; 642 + 643 + clear_buffer(); 644 + 645 + buffer->input[0] = 0x2; 646 + dell_send_request(buffer, 17, 11); 647 + hwswitch_ret = buffer->output[0]; 648 + hwswitch_state = buffer->output[1]; 649 + 663 650 release_buffer(); 664 651 665 652 seq_printf(s, "return:\t%d\n", ret); ··· 715 680 seq_printf(s, "Bit 21: WiGig is blocked: %lu\n", 716 681 (status & BIT(21)) >> 21); 717 682 718 - seq_printf(s, "\nhwswitch_state:\t0x%X\n", hwswitch_state); 683 + seq_printf(s, "\nhwswitch_return:\t%d\n", hwswitch_ret); 684 + seq_printf(s, "hwswitch_state:\t0x%X\n", hwswitch_state); 719 685 seq_printf(s, "Bit 0 : Wifi controlled by switch: %lu\n", 720 686 hwswitch_state & BIT(0)); 721 687 seq_printf(s, "Bit 1 : Bluetooth controlled by switch: %lu\n", ··· 752 716 753 717 static void dell_update_rfkill(struct work_struct *ignored) 754 718 { 719 + int hwswitch = 0; 755 720 int status; 756 721 int ret; 757 722 ··· 765 728 if (ret != 0) 766 729 goto out; 767 730 731 + clear_buffer(); 732 + 733 + buffer->input[0] = 0x2; 734 + dell_send_request(buffer, 17, 11); 735 + ret = buffer->output[0]; 736 + 737 + if (ret == 0 && (status & BIT(0))) 738 + hwswitch = buffer->output[1]; 739 + 768 740 if (wifi_rfkill) { 769 - dell_rfkill_update_hw_state(wifi_rfkill, 1, status); 741 + dell_rfkill_update_hw_state(wifi_rfkill, 1, status, hwswitch); 770 742 dell_rfkill_update_sw_state(wifi_rfkill, 1, status); 771 743 } 772 744 if (bluetooth_rfkill) { 773 - dell_rfkill_update_hw_state(bluetooth_rfkill, 2, status); 745 + dell_rfkill_update_hw_state(bluetooth_rfkill, 2, status, 746 + hwswitch); 774 747 dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status); 775 748 } 776 749 if (wwan_rfkill) { 777 - dell_rfkill_update_hw_state(wwan_rfkill, 3, status); 750 + dell_rfkill_update_hw_state(wwan_rfkill, 3, status, hwswitch); 778 751 dell_rfkill_update_sw_state(wwan_rfkill, 3, status); 779 752 } 780 753 ··· 852 805 dell_send_request(buffer, 17, 11); 853 806 ret = buffer->output[0]; 854 807 status = buffer->output[1]; 855 - clear_buffer(); 856 - buffer->input[0] = 0x2; 857 - dell_send_request(buffer, 17, 11); 858 - hwswitch_state = buffer->output[1]; 859 808 release_buffer(); 860 809 861 810 /* dell wireless info smbios call is not supported */ 862 811 if (ret != 0) 863 812 return 0; 864 813 865 - if (!(status & BIT(0))) { 866 - if (force_rfkill) { 867 - /* No hwsitch, clear all hw-controlled bits */ 868 - hwswitch_state &= ~7; 869 - } else { 870 - /* rfkill is only tested on laptops with a hwswitch */ 871 - return 0; 872 - } 873 - } 814 + /* rfkill is only tested on laptops with a hwswitch */ 815 + if (!(status & BIT(0)) && !force_rfkill) 816 + return 0; 874 817 875 818 if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) { 876 819 wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev,