aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc
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 /drivers/misc
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>
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/Kconfig2
-rw-r--r--drivers/misc/thinkpad_acpi.c208
2 files changed, 184 insertions, 26 deletions
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 }