aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHenrique de Moraes Holschuh <hmh@hmh.eng.br>2008-07-21 08:15:51 -0400
committerHenrique de Moraes Holschuh <hmh@hmh.eng.br>2008-07-21 08:15:51 -0400
commit0e74dc2646db04b644faa8ea10ff4f408d55cf90 (patch)
treed1729fca9b925ec972d1ad3c40295cc7740a31dd
parent133ec3bd3ae409895eacdce326cdc8d73c249e8a (diff)
ACPI: thinkpad-acpi: add bluetooth and WWAN rfkill support
Add a read/write rfkill interface to the bluetooth radio switch on the bluetooth submodule, and one for the wireless wan radio switch to the wan submodule. Since rfkill does care for when a switch changes state, use WLSW notifications to also check if the WWAN or Bluetooth switches did not change state (due to them being slaves of WLSW in firmware/hardware, but that reality not being always properly exported by the thinkpad firmware). Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br> Cc: Ivo van Doorn <IvDoorn@gmail.com> Cc: John W. Linville <linville@tuxdriver.com>
-rw-r--r--Documentation/laptops/thinkpad-acpi.txt22
-rw-r--r--drivers/misc/Kconfig2
-rw-r--r--drivers/misc/thinkpad_acpi.c208
3 files changed, 200 insertions, 32 deletions
diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt
index 64b3f146e4b0..1c1c0217ebd1 100644
--- a/Documentation/laptops/thinkpad-acpi.txt
+++ b/Documentation/laptops/thinkpad-acpi.txt
@@ -621,7 +621,8 @@ Bluetooth
621--------- 621---------
622 622
623procfs: /proc/acpi/ibm/bluetooth 623procfs: /proc/acpi/ibm/bluetooth
624sysfs device attribute: bluetooth_enable 624sysfs device attribute: bluetooth_enable (deprecated)
625sysfs rfkill class: switch "tpacpi_bluetooth_sw"
625 626
626This feature shows the presence and current state of a ThinkPad 627This feature shows the presence and current state of a ThinkPad
627Bluetooth device in the internal ThinkPad CDC slot. 628Bluetooth device in the internal ThinkPad CDC slot.
@@ -643,8 +644,12 @@ Sysfs notes:
643 0: disables Bluetooth / Bluetooth is disabled 644 0: disables Bluetooth / Bluetooth is disabled
644 1: enables Bluetooth / Bluetooth is enabled. 645 1: enables Bluetooth / Bluetooth is enabled.
645 646
646 Note: this interface will be probably be superseded by the 647 Note: this interface has been superseded by the generic rfkill
647 generic rfkill class, so it is NOT to be considered stable yet. 648 class. It has been deprecated, and it will be removed in year
649 2010.
650
651 rfkill controller switch "tpacpi_bluetooth_sw": refer to
652 Documentation/rfkill.txt for details.
648 653
649Video output control -- /proc/acpi/ibm/video 654Video output control -- /proc/acpi/ibm/video
650-------------------------------------------- 655--------------------------------------------
@@ -1374,7 +1379,8 @@ EXPERIMENTAL: WAN
1374----------------- 1379-----------------
1375 1380
1376procfs: /proc/acpi/ibm/wan 1381procfs: /proc/acpi/ibm/wan
1377sysfs device attribute: wwan_enable 1382sysfs device attribute: wwan_enable (deprecated)
1383sysfs rfkill class: switch "tpacpi_wwan_sw"
1378 1384
1379This feature is marked EXPERIMENTAL because the implementation 1385This feature is marked EXPERIMENTAL because the implementation
1380directly accesses hardware registers and may not work as expected. USE 1386directly accesses hardware registers and may not work as expected. USE
@@ -1404,8 +1410,12 @@ Sysfs notes:
1404 0: disables WWAN card / WWAN card is disabled 1410 0: disables WWAN card / WWAN card is disabled
1405 1: enables WWAN card / WWAN card is enabled. 1411 1: enables WWAN card / WWAN card is enabled.
1406 1412
1407 Note: this interface will be probably be superseded by the 1413 Note: this interface has been superseded by the generic rfkill
1408 generic rfkill class, so it is NOT to be considered stable yet. 1414 class. It has been deprecated, and it will be removed in year
1415 2010.
1416
1417 rfkill controller switch "tpacpi_wwan_sw": refer to
1418 Documentation/rfkill.txt for details.
1409 1419
1410Multiple Commands, Module Parameters 1420Multiple Commands, Module Parameters
1411------------------------------------ 1421------------------------------------
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 1921b8dbb242..b27ca91fd15e 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -279,6 +279,8 @@ config THINKPAD_ACPI
279 select INPUT 279 select INPUT
280 select NEW_LEDS 280 select NEW_LEDS
281 select LEDS_CLASS 281 select LEDS_CLASS
282 select NET
283 select RFKILL
282 ---help--- 284 ---help---
283 This is a driver for the IBM and Lenovo ThinkPad laptops. It adds 285 This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
284 support for Fn-Fx key combinations, Bluetooth control, video 286 support for Fn-Fx key combinations, Bluetooth control, video
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
index 202d63e1b391..dc8d00a45701 100644
--- a/drivers/misc/thinkpad_acpi.c
+++ b/drivers/misc/thinkpad_acpi.c
@@ -68,6 +68,7 @@
68#include <linux/hwmon-sysfs.h> 68#include <linux/hwmon-sysfs.h>
69#include <linux/input.h> 69#include <linux/input.h>
70#include <linux/leds.h> 70#include <linux/leds.h>
71#include <linux/rfkill.h>
71#include <asm/uaccess.h> 72#include <asm/uaccess.h>
72 73
73#include <linux/dmi.h> 74#include <linux/dmi.h>
@@ -144,6 +145,12 @@ enum {
144 145
145#define TPACPI_MAX_ACPI_ARGS 3 146#define TPACPI_MAX_ACPI_ARGS 3
146 147
148/* rfkill switches */
149enum {
150 TPACPI_RFK_BLUETOOTH_SW_ID = 0,
151 TPACPI_RFK_WWAN_SW_ID,
152};
153
147/* Debugging */ 154/* Debugging */
148#define TPACPI_LOG TPACPI_FILE ": " 155#define TPACPI_LOG TPACPI_FILE ": "
149#define TPACPI_ERR KERN_ERR TPACPI_LOG 156#define TPACPI_ERR KERN_ERR TPACPI_LOG
@@ -905,6 +912,43 @@ static int __init tpacpi_check_std_acpi_brightness_support(void)
905 return 0; 912 return 0;
906} 913}
907 914
915static int __init tpacpi_new_rfkill(const unsigned int id,
916 struct rfkill **rfk,
917 const enum rfkill_type rfktype,
918 const char *name,
919 int (*toggle_radio)(void *, enum rfkill_state),
920 int (*get_state)(void *, enum rfkill_state *))
921{
922 int res;
923 enum rfkill_state initial_state;
924
925 *rfk = rfkill_allocate(&tpacpi_pdev->dev, rfktype);
926 if (!*rfk) {
927 printk(TPACPI_ERR
928 "failed to allocate memory for rfkill class\n");
929 return -ENOMEM;
930 }
931
932 (*rfk)->name = name;
933 (*rfk)->get_state = get_state;
934 (*rfk)->toggle_radio = toggle_radio;
935
936 if (!get_state(NULL, &initial_state))
937 (*rfk)->state = initial_state;
938
939 res = rfkill_register(*rfk);
940 if (res < 0) {
941 printk(TPACPI_ERR
942 "failed to register %s rfkill switch: %d\n",
943 name, res);
944 rfkill_free(*rfk);
945 *rfk = NULL;
946 return res;
947 }
948
949 return 0;
950}
951
908/************************************************************************* 952/*************************************************************************
909 * thinkpad-acpi driver attributes 953 * thinkpad-acpi driver attributes
910 */ 954 */
@@ -1906,10 +1950,18 @@ static struct attribute *hotkey_mask_attributes[] __initdata = {
1906 &dev_attr_hotkey_wakeup_hotunplug_complete.attr, 1950 &dev_attr_hotkey_wakeup_hotunplug_complete.attr,
1907}; 1951};
1908 1952
1953static void bluetooth_update_rfk(void);
1954static void wan_update_rfk(void);
1909static void tpacpi_send_radiosw_update(void) 1955static void tpacpi_send_radiosw_update(void)
1910{ 1956{
1911 int wlsw; 1957 int wlsw;
1912 1958
1959 /* Sync these BEFORE sending any rfkill events */
1960 if (tp_features.bluetooth)
1961 bluetooth_update_rfk();
1962 if (tp_features.wan)
1963 wan_update_rfk();
1964
1913 if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) { 1965 if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) {
1914 mutex_lock(&tpacpi_inputdev_send_mutex); 1966 mutex_lock(&tpacpi_inputdev_send_mutex);
1915 1967
@@ -2581,6 +2633,8 @@ enum {
2581 TP_ACPI_BLUETOOTH_UNK = 0x04, /* unknown function */ 2633 TP_ACPI_BLUETOOTH_UNK = 0x04, /* unknown function */
2582}; 2634};
2583 2635
2636static struct rfkill *tpacpi_bluetooth_rfkill;
2637
2584static int bluetooth_get_radiosw(void) 2638static int bluetooth_get_radiosw(void)
2585{ 2639{
2586 int status; 2640 int status;
@@ -2590,15 +2644,29 @@ static int bluetooth_get_radiosw(void)
2590 2644
2591 /* WLSW overrides bluetooth in firmware/hardware, reflect that */ 2645 /* WLSW overrides bluetooth in firmware/hardware, reflect that */
2592 if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) 2646 if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status)
2593 return 0; 2647 return RFKILL_STATE_HARD_BLOCKED;
2594 2648
2595 if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) 2649 if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
2596 return -EIO; 2650 return -EIO;
2597 2651
2598 return (status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0; 2652 return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0) ?
2653 RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
2599} 2654}
2600 2655
2601static int bluetooth_set_radiosw(int radio_on) 2656static void bluetooth_update_rfk(void)
2657{
2658 int status;
2659
2660 if (!tpacpi_bluetooth_rfkill)
2661 return;
2662
2663 status = bluetooth_get_radiosw();
2664 if (status < 0)
2665 return;
2666 rfkill_force_state(tpacpi_bluetooth_rfkill, status);
2667}
2668
2669static int bluetooth_set_radiosw(int radio_on, int update_rfk)
2602{ 2670{
2603 int status; 2671 int status;
2604 2672
@@ -2620,6 +2688,9 @@ static int bluetooth_set_radiosw(int radio_on)
2620 if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) 2688 if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
2621 return -EIO; 2689 return -EIO;
2622 2690
2691 if (update_rfk)
2692 bluetooth_update_rfk();
2693
2623 return 0; 2694 return 0;
2624} 2695}
2625 2696
@@ -2634,7 +2705,8 @@ static ssize_t bluetooth_enable_show(struct device *dev,
2634 if (status < 0) 2705 if (status < 0)
2635 return status; 2706 return status;
2636 2707
2637 return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0); 2708 return snprintf(buf, PAGE_SIZE, "%d\n",
2709 (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0);
2638} 2710}
2639 2711
2640static ssize_t bluetooth_enable_store(struct device *dev, 2712static ssize_t bluetooth_enable_store(struct device *dev,
@@ -2647,7 +2719,7 @@ static ssize_t bluetooth_enable_store(struct device *dev,
2647 if (parse_strtoul(buf, 1, &t)) 2719 if (parse_strtoul(buf, 1, &t))
2648 return -EINVAL; 2720 return -EINVAL;
2649 2721
2650 res = bluetooth_set_radiosw(t); 2722 res = bluetooth_set_radiosw(t, 1);
2651 2723
2652 return (res) ? res : count; 2724 return (res) ? res : count;
2653} 2725}
@@ -2667,8 +2739,27 @@ static const struct attribute_group bluetooth_attr_group = {
2667 .attrs = bluetooth_attributes, 2739 .attrs = bluetooth_attributes,
2668}; 2740};
2669 2741
2742static int tpacpi_bluetooth_rfk_get(void *data, enum rfkill_state *state)
2743{
2744 int bts = bluetooth_get_radiosw();
2745
2746 if (bts < 0)
2747 return bts;
2748
2749 *state = bts;
2750 return 0;
2751}
2752
2753static int tpacpi_bluetooth_rfk_set(void *data, enum rfkill_state state)
2754{
2755 return bluetooth_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0);
2756}
2757
2670static void bluetooth_exit(void) 2758static void bluetooth_exit(void)
2671{ 2759{
2760 if (tpacpi_bluetooth_rfkill)
2761 rfkill_unregister(tpacpi_bluetooth_rfkill);
2762
2672 sysfs_remove_group(&tpacpi_pdev->dev.kobj, 2763 sysfs_remove_group(&tpacpi_pdev->dev.kobj,
2673 &bluetooth_attr_group); 2764 &bluetooth_attr_group);
2674} 2765}
@@ -2699,14 +2790,26 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm)
2699 "bluetooth hardware not installed\n"); 2790 "bluetooth hardware not installed\n");
2700 } 2791 }
2701 2792
2702 if (tp_features.bluetooth) { 2793 if (!tp_features.bluetooth)
2703 res = sysfs_create_group(&tpacpi_pdev->dev.kobj, 2794 return 1;
2795
2796 res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
2704 &bluetooth_attr_group); 2797 &bluetooth_attr_group);
2705 if (res) 2798 if (res)
2706 return res; 2799 return res;
2800
2801 res = tpacpi_new_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID,
2802 &tpacpi_bluetooth_rfkill,
2803 RFKILL_TYPE_BLUETOOTH,
2804 "tpacpi_bluetooth_sw",
2805 tpacpi_bluetooth_rfk_set,
2806 tpacpi_bluetooth_rfk_get);
2807 if (res) {
2808 bluetooth_exit();
2809 return res;
2707 } 2810 }
2708 2811
2709 return (tp_features.bluetooth)? 0 : 1; 2812 return 0;
2710} 2813}
2711 2814
2712/* procfs -------------------------------------------------------------- */ 2815/* procfs -------------------------------------------------------------- */
@@ -2719,7 +2822,8 @@ static int bluetooth_read(char *p)
2719 len += sprintf(p + len, "status:\t\tnot supported\n"); 2822 len += sprintf(p + len, "status:\t\tnot supported\n");
2720 else { 2823 else {
2721 len += sprintf(p + len, "status:\t\t%s\n", 2824 len += sprintf(p + len, "status:\t\t%s\n",
2722 (status)? "enabled" : "disabled"); 2825 (status == RFKILL_STATE_UNBLOCKED) ?
2826 "enabled" : "disabled");
2723 len += sprintf(p + len, "commands:\tenable, disable\n"); 2827 len += sprintf(p + len, "commands:\tenable, disable\n");
2724 } 2828 }
2725 2829
@@ -2735,9 +2839,9 @@ static int bluetooth_write(char *buf)
2735 2839
2736 while ((cmd = next_cmd(&buf))) { 2840 while ((cmd = next_cmd(&buf))) {
2737 if (strlencmp(cmd, "enable") == 0) { 2841 if (strlencmp(cmd, "enable") == 0) {
2738 bluetooth_set_radiosw(1); 2842 bluetooth_set_radiosw(1, 1);
2739 } else if (strlencmp(cmd, "disable") == 0) { 2843 } else if (strlencmp(cmd, "disable") == 0) {
2740 bluetooth_set_radiosw(0); 2844 bluetooth_set_radiosw(0, 1);
2741 } else 2845 } else
2742 return -EINVAL; 2846 return -EINVAL;
2743 } 2847 }
@@ -2763,6 +2867,8 @@ enum {
2763 TP_ACPI_WANCARD_UNK = 0x04, /* unknown function */ 2867 TP_ACPI_WANCARD_UNK = 0x04, /* unknown function */
2764}; 2868};
2765 2869
2870static struct rfkill *tpacpi_wan_rfkill;
2871
2766static int wan_get_radiosw(void) 2872static int wan_get_radiosw(void)
2767{ 2873{
2768 int status; 2874 int status;
@@ -2772,15 +2878,29 @@ static int wan_get_radiosw(void)
2772 2878
2773 /* WLSW overrides WWAN in firmware/hardware, reflect that */ 2879 /* WLSW overrides WWAN in firmware/hardware, reflect that */
2774 if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) 2880 if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status)
2775 return 0; 2881 return RFKILL_STATE_HARD_BLOCKED;
2776 2882
2777 if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) 2883 if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
2778 return -EIO; 2884 return -EIO;
2779 2885
2780 return (status & TP_ACPI_WANCARD_RADIOSSW) != 0; 2886 return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0) ?
2887 RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
2781} 2888}
2782 2889
2783static int wan_set_radiosw(int radio_on) 2890static void wan_update_rfk(void)
2891{
2892 int status;
2893
2894 if (!tpacpi_wan_rfkill)
2895 return;
2896
2897 status = wan_get_radiosw();
2898 if (status < 0)
2899 return;
2900 rfkill_force_state(tpacpi_wan_rfkill, status);
2901}
2902
2903static int wan_set_radiosw(int radio_on, int update_rfk)
2784{ 2904{
2785 int status; 2905 int status;
2786 2906
@@ -2802,6 +2922,9 @@ static int wan_set_radiosw(int radio_on)
2802 if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) 2922 if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status))
2803 return -EIO; 2923 return -EIO;
2804 2924
2925 if (update_rfk)
2926 wan_update_rfk();
2927
2805 return 0; 2928 return 0;
2806} 2929}
2807 2930
@@ -2816,7 +2939,8 @@ static ssize_t wan_enable_show(struct device *dev,
2816 if (status < 0) 2939 if (status < 0)
2817 return status; 2940 return status;
2818 2941
2819 return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0); 2942 return snprintf(buf, PAGE_SIZE, "%d\n",
2943 (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0);
2820} 2944}
2821 2945
2822static ssize_t wan_enable_store(struct device *dev, 2946static ssize_t wan_enable_store(struct device *dev,
@@ -2829,7 +2953,7 @@ static ssize_t wan_enable_store(struct device *dev,
2829 if (parse_strtoul(buf, 1, &t)) 2953 if (parse_strtoul(buf, 1, &t))
2830 return -EINVAL; 2954 return -EINVAL;
2831 2955
2832 res = wan_set_radiosw(t); 2956 res = wan_set_radiosw(t, 1);
2833 2957
2834 return (res) ? res : count; 2958 return (res) ? res : count;
2835} 2959}
@@ -2849,8 +2973,27 @@ static const struct attribute_group wan_attr_group = {
2849 .attrs = wan_attributes, 2973 .attrs = wan_attributes,
2850}; 2974};
2851 2975
2976static int tpacpi_wan_rfk_get(void *data, enum rfkill_state *state)
2977{
2978 int wans = wan_get_radiosw();
2979
2980 if (wans < 0)
2981 return wans;
2982
2983 *state = wans;
2984 return 0;
2985}
2986
2987static int tpacpi_wan_rfk_set(void *data, enum rfkill_state state)
2988{
2989 return wan_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0);
2990}
2991
2852static void wan_exit(void) 2992static void wan_exit(void)
2853{ 2993{
2994 if (tpacpi_wan_rfkill)
2995 rfkill_unregister(tpacpi_wan_rfkill);
2996
2854 sysfs_remove_group(&tpacpi_pdev->dev.kobj, 2997 sysfs_remove_group(&tpacpi_pdev->dev.kobj,
2855 &wan_attr_group); 2998 &wan_attr_group);
2856} 2999}
@@ -2879,14 +3022,26 @@ static int __init wan_init(struct ibm_init_struct *iibm)
2879 "wan hardware not installed\n"); 3022 "wan hardware not installed\n");
2880 } 3023 }
2881 3024
2882 if (tp_features.wan) { 3025 if (!tp_features.wan)
2883 res = sysfs_create_group(&tpacpi_pdev->dev.kobj, 3026 return 1;
3027
3028 res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
2884 &wan_attr_group); 3029 &wan_attr_group);
2885 if (res) 3030 if (res)
2886 return res; 3031 return res;
3032
3033 res = tpacpi_new_rfkill(TPACPI_RFK_WWAN_SW_ID,
3034 &tpacpi_wan_rfkill,
3035 RFKILL_TYPE_WWAN,
3036 "tpacpi_wwan_sw",
3037 tpacpi_wan_rfk_set,
3038 tpacpi_wan_rfk_get);
3039 if (res) {
3040 wan_exit();
3041 return res;
2887 } 3042 }
2888 3043
2889 return (tp_features.wan)? 0 : 1; 3044 return 0;
2890} 3045}
2891 3046
2892/* procfs -------------------------------------------------------------- */ 3047/* procfs -------------------------------------------------------------- */
@@ -2899,7 +3054,8 @@ static int wan_read(char *p)
2899 len += sprintf(p + len, "status:\t\tnot supported\n"); 3054 len += sprintf(p + len, "status:\t\tnot supported\n");
2900 else { 3055 else {
2901 len += sprintf(p + len, "status:\t\t%s\n", 3056 len += sprintf(p + len, "status:\t\t%s\n",
2902 (status)? "enabled" : "disabled"); 3057 (status == RFKILL_STATE_UNBLOCKED) ?
3058 "enabled" : "disabled");
2903 len += sprintf(p + len, "commands:\tenable, disable\n"); 3059 len += sprintf(p + len, "commands:\tenable, disable\n");
2904 } 3060 }
2905 3061
@@ -2915,9 +3071,9 @@ static int wan_write(char *buf)
2915 3071
2916 while ((cmd = next_cmd(&buf))) { 3072 while ((cmd = next_cmd(&buf))) {
2917 if (strlencmp(cmd, "enable") == 0) { 3073 if (strlencmp(cmd, "enable") == 0) {
2918 wan_set_radiosw(1); 3074 wan_set_radiosw(1, 1);
2919 } else if (strlencmp(cmd, "disable") == 0) { 3075 } else if (strlencmp(cmd, "disable") == 0) {
2920 wan_set_radiosw(0); 3076 wan_set_radiosw(0, 1);
2921 } else 3077 } else
2922 return -EINVAL; 3078 return -EINVAL;
2923 } 3079 }