aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform
diff options
context:
space:
mode:
authorMatthew Garrett <mjg59@srcf.ucam.org>2009-03-26 08:58:15 -0400
committerLen Brown <len.brown@intel.com>2009-03-27 12:18:19 -0400
commit6cc056bc31ea9910afb01adc0848bb6ae68e0205 (patch)
tree98306e731ea6bb11224ec2b974d574310f166af7 /drivers/platform
parent9b57896e62bfa752ee7435e6cfe57fb210c0db8c (diff)
sony-laptop: Add rfkill support on new models
Newer Vaios provide a full featured rfkill implementation via their platform methods. Add support for enumerating the available devices and providing rfkill access to them. Support for the physical kill switch is added, with the devices moving into the HARD_BLOCKED state when toggled. Signed-off-by: Matthew Garrett <mjg@redhat.com> Signed-off-by: Mattia Dongili <malattia@linux.it> Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/x86/sony-laptop.c186
1 files changed, 186 insertions, 0 deletions
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c
index f6cdc8929fd..f458870c30b 100644
--- a/drivers/platform/x86/sony-laptop.c
+++ b/drivers/platform/x86/sony-laptop.c
@@ -64,6 +64,7 @@
64#include <asm/uaccess.h> 64#include <asm/uaccess.h>
65#include <linux/sonypi.h> 65#include <linux/sonypi.h>
66#include <linux/sony-laptop.h> 66#include <linux/sony-laptop.h>
67#include <linux/rfkill.h>
67#ifdef CONFIG_SONYPI_COMPAT 68#ifdef CONFIG_SONYPI_COMPAT
68#include <linux/poll.h> 69#include <linux/poll.h>
69#include <linux/miscdevice.h> 70#include <linux/miscdevice.h>
@@ -123,6 +124,18 @@ MODULE_PARM_DESC(minor,
123 "default is -1 (automatic)"); 124 "default is -1 (automatic)");
124#endif 125#endif
125 126
127enum sony_nc_rfkill {
128 SONY_WIFI,
129 SONY_BLUETOOTH,
130 SONY_WWAN,
131 SONY_WIMAX,
132 SONY_RFKILL_MAX,
133};
134
135static struct rfkill *sony_rfkill_devices[SONY_RFKILL_MAX];
136static int sony_rfkill_address[SONY_RFKILL_MAX] = {0x300, 0x500, 0x700, 0x900};
137static void sony_nc_rfkill_update(void);
138
126/*********** Input Devices ***********/ 139/*********** Input Devices ***********/
127 140
128#define SONY_LAPTOP_BUF_SIZE 128 141#define SONY_LAPTOP_BUF_SIZE 128
@@ -134,6 +147,7 @@ struct sony_laptop_input_s {
134 spinlock_t fifo_lock; 147 spinlock_t fifo_lock;
135 struct workqueue_struct *wq; 148 struct workqueue_struct *wq;
136}; 149};
150
137static struct sony_laptop_input_s sony_laptop_input = { 151static struct sony_laptop_input_s sony_laptop_input = {
138 .users = ATOMIC_INIT(0), 152 .users = ATOMIC_INIT(0),
139}; 153};
@@ -891,6 +905,9 @@ static void sony_acpi_notify(acpi_handle handle, u32 event, void *data)
891 if (!sony_nc_events[i].data) 905 if (!sony_nc_events[i].data)
892 printk(KERN_INFO DRV_PFX 906 printk(KERN_INFO DRV_PFX
893 "Unknown event: %x %x\n", origev, ev); 907 "Unknown event: %x %x\n", origev, ev);
908 } else if (sony_find_snc_handle(0x124) == ev) {
909 sony_nc_rfkill_update();
910 return;
894 } 911 }
895 } 912 }
896 913
@@ -973,6 +990,172 @@ static int sony_nc_resume(struct acpi_device *device)
973 return 0; 990 return 0;
974} 991}
975 992
993static void sony_nc_rfkill_cleanup(void)
994{
995 int i;
996
997 for (i = 0; i < SONY_RFKILL_MAX; i++) {
998 if (sony_rfkill_devices[i])
999 rfkill_unregister(sony_rfkill_devices[i]);
1000 }
1001}
1002
1003static int sony_nc_rfkill_get(void *data, enum rfkill_state *state)
1004{
1005 int result;
1006 int argument = sony_rfkill_address[(long) data];
1007
1008 sony_call_snc_handle(0x124, 0x200, &result);
1009 if (result & 0x1) {
1010 sony_call_snc_handle(0x124, argument, &result);
1011 if (result & 0xf)
1012 *state = RFKILL_STATE_UNBLOCKED;
1013 else
1014 *state = RFKILL_STATE_SOFT_BLOCKED;
1015 } else {
1016 *state = RFKILL_STATE_HARD_BLOCKED;
1017 }
1018
1019 return 0;
1020}
1021
1022static int sony_nc_rfkill_set(void *data, enum rfkill_state state)
1023{
1024 int result;
1025 int argument = sony_rfkill_address[(long) data] + 0x100;
1026
1027 if (state == RFKILL_STATE_UNBLOCKED)
1028 argument |= 0xff0000;
1029
1030 return sony_call_snc_handle(0x124, argument, &result);
1031}
1032
1033static int sony_nc_setup_wifi_rfkill(struct acpi_device *device)
1034{
1035 int err = 0;
1036 struct rfkill *sony_wifi_rfkill;
1037
1038 sony_wifi_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WLAN);
1039 if (!sony_wifi_rfkill)
1040 return -1;
1041 sony_wifi_rfkill->name = "sony-wifi";
1042 sony_wifi_rfkill->toggle_radio = sony_nc_rfkill_set;
1043 sony_wifi_rfkill->get_state = sony_nc_rfkill_get;
1044 sony_wifi_rfkill->user_claim_unsupported = 1;
1045 sony_wifi_rfkill->data = (void *)SONY_WIFI;
1046 err = rfkill_register(sony_wifi_rfkill);
1047 if (err)
1048 rfkill_free(sony_wifi_rfkill);
1049 else
1050 sony_rfkill_devices[SONY_WIFI] = sony_wifi_rfkill;
1051 return err;
1052}
1053
1054static int sony_nc_setup_bluetooth_rfkill(struct acpi_device *device)
1055{
1056 int err = 0;
1057 struct rfkill *sony_bluetooth_rfkill;
1058
1059 sony_bluetooth_rfkill = rfkill_allocate(&device->dev,
1060 RFKILL_TYPE_BLUETOOTH);
1061 if (!sony_bluetooth_rfkill)
1062 return -1;
1063 sony_bluetooth_rfkill->name = "sony-bluetooth";
1064 sony_bluetooth_rfkill->toggle_radio = sony_nc_rfkill_set;
1065 sony_bluetooth_rfkill->get_state = sony_nc_rfkill_get;
1066 sony_bluetooth_rfkill->user_claim_unsupported = 1;
1067 sony_bluetooth_rfkill->data = (void *)SONY_BLUETOOTH;
1068 err = rfkill_register(sony_bluetooth_rfkill);
1069 if (err)
1070 rfkill_free(sony_bluetooth_rfkill);
1071 else
1072 sony_rfkill_devices[SONY_BLUETOOTH] = sony_bluetooth_rfkill;
1073 return err;
1074}
1075
1076static int sony_nc_setup_wwan_rfkill(struct acpi_device *device)
1077{
1078 int err = 0;
1079 struct rfkill *sony_wwan_rfkill;
1080
1081 sony_wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WWAN);
1082 if (!sony_wwan_rfkill)
1083 return -1;
1084 sony_wwan_rfkill->name = "sony-wwan";
1085 sony_wwan_rfkill->toggle_radio = sony_nc_rfkill_set;
1086 sony_wwan_rfkill->get_state = sony_nc_rfkill_get;
1087 sony_wwan_rfkill->user_claim_unsupported = 1;
1088 sony_wwan_rfkill->data = (void *)SONY_WWAN;
1089 err = rfkill_register(sony_wwan_rfkill);
1090 if (err)
1091 rfkill_free(sony_wwan_rfkill);
1092 else
1093 sony_rfkill_devices[SONY_WWAN] = sony_wwan_rfkill;
1094 return err;
1095}
1096
1097static int sony_nc_setup_wimax_rfkill(struct acpi_device *device)
1098{
1099 int err = 0;
1100 struct rfkill *sony_wimax_rfkill;
1101
1102 sony_wimax_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WIMAX);
1103 if (!sony_wimax_rfkill)
1104 return -1;
1105 sony_wimax_rfkill->name = "sony-wimax";
1106 sony_wimax_rfkill->toggle_radio = sony_nc_rfkill_set;
1107 sony_wimax_rfkill->get_state = sony_nc_rfkill_get;
1108 sony_wimax_rfkill->user_claim_unsupported = 1;
1109 sony_wimax_rfkill->data = (void *)SONY_WIMAX;
1110 err = rfkill_register(sony_wimax_rfkill);
1111 if (err)
1112 rfkill_free(sony_wimax_rfkill);
1113 else
1114 sony_rfkill_devices[SONY_WIMAX] = sony_wimax_rfkill;
1115 return err;
1116}
1117
1118static void sony_nc_rfkill_update()
1119{
1120 int i;
1121 enum rfkill_state state;
1122
1123 for (i = 0; i < SONY_RFKILL_MAX; i++) {
1124 if (sony_rfkill_devices[i]) {
1125 sony_rfkill_devices[i]->
1126 get_state(sony_rfkill_devices[i]->data,
1127 &state);
1128 rfkill_force_state(sony_rfkill_devices[i], state);
1129 }
1130 }
1131}
1132
1133static int sony_nc_rfkill_setup(struct acpi_device *device)
1134{
1135 int result, ret;
1136
1137 if (sony_find_snc_handle(0x124) == -1)
1138 return -1;
1139
1140 ret = sony_call_snc_handle(0x124, 0xb00, &result);
1141 if (ret) {
1142 printk(KERN_INFO DRV_PFX
1143 "Unable to enumerate rfkill devices: %x\n", ret);
1144 return ret;
1145 }
1146
1147 if (result & 0x1)
1148 sony_nc_setup_wifi_rfkill(device);
1149 if (result & 0x2)
1150 sony_nc_setup_bluetooth_rfkill(device);
1151 if (result & 0x1c)
1152 sony_nc_setup_wwan_rfkill(device);
1153 if (result & 0x20)
1154 sony_nc_setup_wimax_rfkill(device);
1155
1156 return 0;
1157}
1158
976static int sony_nc_add(struct acpi_device *device) 1159static int sony_nc_add(struct acpi_device *device)
977{ 1160{
978 acpi_status status; 1161 acpi_status status;
@@ -1026,6 +1209,7 @@ static int sony_nc_add(struct acpi_device *device)
1026 &handle))) { 1209 &handle))) {
1027 dprintk("Doing SNC setup\n"); 1210 dprintk("Doing SNC setup\n");
1028 sony_nc_function_setup(device); 1211 sony_nc_function_setup(device);
1212 sony_nc_rfkill_setup(device);
1029 } 1213 }
1030 1214
1031 /* setup input devices and helper fifo */ 1215 /* setup input devices and helper fifo */
@@ -1132,6 +1316,7 @@ static int sony_nc_add(struct acpi_device *device)
1132 sony_laptop_remove_input(); 1316 sony_laptop_remove_input();
1133 1317
1134 outwalk: 1318 outwalk:
1319 sony_nc_rfkill_cleanup();
1135 return result; 1320 return result;
1136} 1321}
1137 1322
@@ -1157,6 +1342,7 @@ static int sony_nc_remove(struct acpi_device *device, int type)
1157 1342
1158 sony_pf_remove(); 1343 sony_pf_remove();
1159 sony_laptop_remove_input(); 1344 sony_laptop_remove_input();
1345 sony_nc_rfkill_cleanup();
1160 dprintk(SONY_NC_DRIVER_NAME " removed.\n"); 1346 dprintk(SONY_NC_DRIVER_NAME " removed.\n");
1161 1347
1162 return 0; 1348 return 0;