diff options
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/x86/sony-laptop.c | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index f6cdc8929fd7..f458870c30b6 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 | ||
127 | enum sony_nc_rfkill { | ||
128 | SONY_WIFI, | ||
129 | SONY_BLUETOOTH, | ||
130 | SONY_WWAN, | ||
131 | SONY_WIMAX, | ||
132 | SONY_RFKILL_MAX, | ||
133 | }; | ||
134 | |||
135 | static struct rfkill *sony_rfkill_devices[SONY_RFKILL_MAX]; | ||
136 | static int sony_rfkill_address[SONY_RFKILL_MAX] = {0x300, 0x500, 0x700, 0x900}; | ||
137 | static 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 | |||
137 | static struct sony_laptop_input_s sony_laptop_input = { | 151 | static 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 | ||
993 | static 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 | |||
1003 | static 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 | |||
1022 | static 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 | |||
1033 | static 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 | |||
1054 | static 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 | |||
1076 | static 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 | |||
1097 | static 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 | |||
1118 | static 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 | |||
1133 | static 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 | |||
976 | static int sony_nc_add(struct acpi_device *device) | 1159 | static 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; |