diff options
Diffstat (limited to 'drivers/platform/x86/hp-wmi.c')
-rw-r--r-- | drivers/platform/x86/hp-wmi.c | 502 |
1 files changed, 330 insertions, 172 deletions
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index c1741142a4cb..e2faa3cbb792 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * HP WMI hotkeys | 2 | * HP WMI hotkeys |
3 | * | 3 | * |
4 | * Copyright (C) 2008 Red Hat <mjg@redhat.com> | 4 | * Copyright (C) 2008 Red Hat <mjg@redhat.com> |
5 | * Copyright (C) 2010, 2011 Anssi Hannula <anssi.hannula@iki.fi> | ||
5 | * | 6 | * |
6 | * Portions based on wistron_btns.c: | 7 | * Portions based on wistron_btns.c: |
7 | * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz> | 8 | * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz> |
@@ -23,12 +24,15 @@ | |||
23 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 24 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
24 | */ | 25 | */ |
25 | 26 | ||
27 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
28 | |||
26 | #include <linux/kernel.h> | 29 | #include <linux/kernel.h> |
27 | #include <linux/module.h> | 30 | #include <linux/module.h> |
28 | #include <linux/init.h> | 31 | #include <linux/init.h> |
29 | #include <linux/slab.h> | 32 | #include <linux/slab.h> |
30 | #include <linux/types.h> | 33 | #include <linux/types.h> |
31 | #include <linux/input.h> | 34 | #include <linux/input.h> |
35 | #include <linux/input/sparse-keymap.h> | ||
32 | #include <linux/platform_device.h> | 36 | #include <linux/platform_device.h> |
33 | #include <linux/acpi.h> | 37 | #include <linux/acpi.h> |
34 | #include <linux/rfkill.h> | 38 | #include <linux/rfkill.h> |
@@ -50,9 +54,7 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); | |||
50 | #define HPWMI_HARDWARE_QUERY 0x4 | 54 | #define HPWMI_HARDWARE_QUERY 0x4 |
51 | #define HPWMI_WIRELESS_QUERY 0x5 | 55 | #define HPWMI_WIRELESS_QUERY 0x5 |
52 | #define HPWMI_HOTKEY_QUERY 0xc | 56 | #define HPWMI_HOTKEY_QUERY 0xc |
53 | 57 | #define HPWMI_WIRELESS2_QUERY 0x1b | |
54 | #define PREFIX "HP WMI: " | ||
55 | #define UNIMP "Unimplemented " | ||
56 | 58 | ||
57 | enum hp_wmi_radio { | 59 | enum hp_wmi_radio { |
58 | HPWMI_WIFI = 0, | 60 | HPWMI_WIFI = 0, |
@@ -85,27 +87,58 @@ struct bios_args { | |||
85 | struct bios_return { | 87 | struct bios_return { |
86 | u32 sigpass; | 88 | u32 sigpass; |
87 | u32 return_code; | 89 | u32 return_code; |
88 | u32 value; | ||
89 | }; | 90 | }; |
90 | 91 | ||
91 | struct key_entry { | 92 | enum hp_return_value { |
92 | char type; /* See KE_* below */ | 93 | HPWMI_RET_WRONG_SIGNATURE = 0x02, |
93 | u16 code; | 94 | HPWMI_RET_UNKNOWN_COMMAND = 0x03, |
94 | u16 keycode; | 95 | HPWMI_RET_UNKNOWN_CMDTYPE = 0x04, |
96 | HPWMI_RET_INVALID_PARAMETERS = 0x05, | ||
97 | }; | ||
98 | |||
99 | enum hp_wireless2_bits { | ||
100 | HPWMI_POWER_STATE = 0x01, | ||
101 | HPWMI_POWER_SOFT = 0x02, | ||
102 | HPWMI_POWER_BIOS = 0x04, | ||
103 | HPWMI_POWER_HARD = 0x08, | ||
95 | }; | 104 | }; |
96 | 105 | ||
97 | enum { KE_KEY, KE_END }; | 106 | #define IS_HWBLOCKED(x) ((x & (HPWMI_POWER_BIOS | HPWMI_POWER_HARD)) \ |
98 | 107 | != (HPWMI_POWER_BIOS | HPWMI_POWER_HARD)) | |
99 | static struct key_entry hp_wmi_keymap[] = { | 108 | #define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT) |
100 | {KE_KEY, 0x02, KEY_BRIGHTNESSUP}, | 109 | |
101 | {KE_KEY, 0x03, KEY_BRIGHTNESSDOWN}, | 110 | struct bios_rfkill2_device_state { |
102 | {KE_KEY, 0x20e6, KEY_PROG1}, | 111 | u8 radio_type; |
103 | {KE_KEY, 0x20e8, KEY_MEDIA}, | 112 | u8 bus_type; |
104 | {KE_KEY, 0x2142, KEY_MEDIA}, | 113 | u16 vendor_id; |
105 | {KE_KEY, 0x213b, KEY_INFO}, | 114 | u16 product_id; |
106 | {KE_KEY, 0x2169, KEY_DIRECTION}, | 115 | u16 subsys_vendor_id; |
107 | {KE_KEY, 0x231b, KEY_HELP}, | 116 | u16 subsys_product_id; |
108 | {KE_END, 0} | 117 | u8 rfkill_id; |
118 | u8 power; | ||
119 | u8 unknown[4]; | ||
120 | }; | ||
121 | |||
122 | /* 7 devices fit into the 128 byte buffer */ | ||
123 | #define HPWMI_MAX_RFKILL2_DEVICES 7 | ||
124 | |||
125 | struct bios_rfkill2_state { | ||
126 | u8 unknown[7]; | ||
127 | u8 count; | ||
128 | u8 pad[8]; | ||
129 | struct bios_rfkill2_device_state device[HPWMI_MAX_RFKILL2_DEVICES]; | ||
130 | }; | ||
131 | |||
132 | static const struct key_entry hp_wmi_keymap[] = { | ||
133 | { KE_KEY, 0x02, { KEY_BRIGHTNESSUP } }, | ||
134 | { KE_KEY, 0x03, { KEY_BRIGHTNESSDOWN } }, | ||
135 | { KE_KEY, 0x20e6, { KEY_PROG1 } }, | ||
136 | { KE_KEY, 0x20e8, { KEY_MEDIA } }, | ||
137 | { KE_KEY, 0x2142, { KEY_MEDIA } }, | ||
138 | { KE_KEY, 0x213b, { KEY_INFO } }, | ||
139 | { KE_KEY, 0x2169, { KEY_DIRECTION } }, | ||
140 | { KE_KEY, 0x231b, { KEY_HELP } }, | ||
141 | { KE_END, 0 } | ||
109 | }; | 142 | }; |
110 | 143 | ||
111 | static struct input_dev *hp_wmi_input_dev; | 144 | static struct input_dev *hp_wmi_input_dev; |
@@ -115,6 +148,15 @@ static struct rfkill *wifi_rfkill; | |||
115 | static struct rfkill *bluetooth_rfkill; | 148 | static struct rfkill *bluetooth_rfkill; |
116 | static struct rfkill *wwan_rfkill; | 149 | static struct rfkill *wwan_rfkill; |
117 | 150 | ||
151 | struct rfkill2_device { | ||
152 | u8 id; | ||
153 | int num; | ||
154 | struct rfkill *rfkill; | ||
155 | }; | ||
156 | |||
157 | static int rfkill2_count; | ||
158 | static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES]; | ||
159 | |||
118 | static const struct dev_pm_ops hp_wmi_pm_ops = { | 160 | static const struct dev_pm_ops hp_wmi_pm_ops = { |
119 | .resume = hp_wmi_resume_handler, | 161 | .resume = hp_wmi_resume_handler, |
120 | .restore = hp_wmi_resume_handler, | 162 | .restore = hp_wmi_resume_handler, |
@@ -136,7 +178,8 @@ static struct platform_driver hp_wmi_driver = { | |||
136 | * query: The commandtype -> What should be queried | 178 | * query: The commandtype -> What should be queried |
137 | * write: The command -> 0 read, 1 write, 3 ODM specific | 179 | * write: The command -> 0 read, 1 write, 3 ODM specific |
138 | * buffer: Buffer used as input and/or output | 180 | * buffer: Buffer used as input and/or output |
139 | * buffersize: Size of buffer | 181 | * insize: Size of input buffer |
182 | * outsize: Size of output buffer | ||
140 | * | 183 | * |
141 | * returns zero on success | 184 | * returns zero on success |
142 | * an HP WMI query specific error code (which is positive) | 185 | * an HP WMI query specific error code (which is positive) |
@@ -147,25 +190,30 @@ static struct platform_driver hp_wmi_driver = { | |||
147 | * size. E.g. Battery info query (0x7) is defined to have 1 byte input | 190 | * size. E.g. Battery info query (0x7) is defined to have 1 byte input |
148 | * and 128 byte output. The caller would do: | 191 | * and 128 byte output. The caller would do: |
149 | * buffer = kzalloc(128, GFP_KERNEL); | 192 | * buffer = kzalloc(128, GFP_KERNEL); |
150 | * ret = hp_wmi_perform_query(0x7, 0, buffer, 128) | 193 | * ret = hp_wmi_perform_query(0x7, 0, buffer, 1, 128) |
151 | */ | 194 | */ |
152 | static int hp_wmi_perform_query(int query, int write, u32 *buffer, | 195 | static int hp_wmi_perform_query(int query, int write, void *buffer, |
153 | int buffersize) | 196 | int insize, int outsize) |
154 | { | 197 | { |
155 | struct bios_return bios_return; | 198 | struct bios_return *bios_return; |
156 | acpi_status status; | 199 | int actual_outsize; |
157 | union acpi_object *obj; | 200 | union acpi_object *obj; |
158 | struct bios_args args = { | 201 | struct bios_args args = { |
159 | .signature = 0x55434553, | 202 | .signature = 0x55434553, |
160 | .command = write ? 0x2 : 0x1, | 203 | .command = write ? 0x2 : 0x1, |
161 | .commandtype = query, | 204 | .commandtype = query, |
162 | .datasize = buffersize, | 205 | .datasize = insize, |
163 | .data = *buffer, | 206 | .data = 0, |
164 | }; | 207 | }; |
165 | struct acpi_buffer input = { sizeof(struct bios_args), &args }; | 208 | struct acpi_buffer input = { sizeof(struct bios_args), &args }; |
166 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | 209 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; |
210 | u32 rc; | ||
211 | |||
212 | if (WARN_ON(insize > sizeof(args.data))) | ||
213 | return -EINVAL; | ||
214 | memcpy(&args.data, buffer, insize); | ||
167 | 215 | ||
168 | status = wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output); | 216 | wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output); |
169 | 217 | ||
170 | obj = output.pointer; | 218 | obj = output.pointer; |
171 | 219 | ||
@@ -176,9 +224,26 @@ static int hp_wmi_perform_query(int query, int write, u32 *buffer, | |||
176 | return -EINVAL; | 224 | return -EINVAL; |
177 | } | 225 | } |
178 | 226 | ||
179 | bios_return = *((struct bios_return *)obj->buffer.pointer); | 227 | bios_return = (struct bios_return *)obj->buffer.pointer; |
228 | rc = bios_return->return_code; | ||
229 | |||
230 | if (rc) { | ||
231 | if (rc != HPWMI_RET_UNKNOWN_CMDTYPE) | ||
232 | pr_warn("query 0x%x returned error 0x%x\n", query, rc); | ||
233 | kfree(obj); | ||
234 | return rc; | ||
235 | } | ||
236 | |||
237 | if (!outsize) { | ||
238 | /* ignore output data */ | ||
239 | kfree(obj); | ||
240 | return 0; | ||
241 | } | ||
180 | 242 | ||
181 | memcpy(buffer, &bios_return.value, sizeof(bios_return.value)); | 243 | actual_outsize = min(outsize, (int)(obj->buffer.length - sizeof(*bios_return))); |
244 | memcpy(buffer, obj->buffer.pointer + sizeof(*bios_return), actual_outsize); | ||
245 | memset(buffer + actual_outsize, 0, outsize - actual_outsize); | ||
246 | kfree(obj); | ||
182 | return 0; | 247 | return 0; |
183 | } | 248 | } |
184 | 249 | ||
@@ -186,7 +251,7 @@ static int hp_wmi_display_state(void) | |||
186 | { | 251 | { |
187 | int state = 0; | 252 | int state = 0; |
188 | int ret = hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, &state, | 253 | int ret = hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, &state, |
189 | sizeof(state)); | 254 | sizeof(state), sizeof(state)); |
190 | if (ret) | 255 | if (ret) |
191 | return -EINVAL; | 256 | return -EINVAL; |
192 | return state; | 257 | return state; |
@@ -196,7 +261,7 @@ static int hp_wmi_hddtemp_state(void) | |||
196 | { | 261 | { |
197 | int state = 0; | 262 | int state = 0; |
198 | int ret = hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, &state, | 263 | int ret = hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, &state, |
199 | sizeof(state)); | 264 | sizeof(state), sizeof(state)); |
200 | if (ret) | 265 | if (ret) |
201 | return -EINVAL; | 266 | return -EINVAL; |
202 | return state; | 267 | return state; |
@@ -206,7 +271,7 @@ static int hp_wmi_als_state(void) | |||
206 | { | 271 | { |
207 | int state = 0; | 272 | int state = 0; |
208 | int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, &state, | 273 | int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, &state, |
209 | sizeof(state)); | 274 | sizeof(state), sizeof(state)); |
210 | if (ret) | 275 | if (ret) |
211 | return -EINVAL; | 276 | return -EINVAL; |
212 | return state; | 277 | return state; |
@@ -216,7 +281,7 @@ static int hp_wmi_dock_state(void) | |||
216 | { | 281 | { |
217 | int state = 0; | 282 | int state = 0; |
218 | int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, &state, | 283 | int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, &state, |
219 | sizeof(state)); | 284 | sizeof(state), sizeof(state)); |
220 | 285 | ||
221 | if (ret) | 286 | if (ret) |
222 | return -EINVAL; | 287 | return -EINVAL; |
@@ -228,7 +293,7 @@ static int hp_wmi_tablet_state(void) | |||
228 | { | 293 | { |
229 | int state = 0; | 294 | int state = 0; |
230 | int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, &state, | 295 | int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, &state, |
231 | sizeof(state)); | 296 | sizeof(state), sizeof(state)); |
232 | if (ret) | 297 | if (ret) |
233 | return ret; | 298 | return ret; |
234 | 299 | ||
@@ -242,7 +307,7 @@ static int hp_wmi_set_block(void *data, bool blocked) | |||
242 | int ret; | 307 | int ret; |
243 | 308 | ||
244 | ret = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, | 309 | ret = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, |
245 | &query, sizeof(query)); | 310 | &query, sizeof(query), 0); |
246 | if (ret) | 311 | if (ret) |
247 | return -EINVAL; | 312 | return -EINVAL; |
248 | return 0; | 313 | return 0; |
@@ -257,7 +322,8 @@ static bool hp_wmi_get_sw_state(enum hp_wmi_radio r) | |||
257 | int wireless = 0; | 322 | int wireless = 0; |
258 | int mask; | 323 | int mask; |
259 | hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, | 324 | hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, |
260 | &wireless, sizeof(wireless)); | 325 | &wireless, sizeof(wireless), |
326 | sizeof(wireless)); | ||
261 | /* TBD: Pass error */ | 327 | /* TBD: Pass error */ |
262 | 328 | ||
263 | mask = 0x200 << (r * 8); | 329 | mask = 0x200 << (r * 8); |
@@ -273,7 +339,8 @@ static bool hp_wmi_get_hw_state(enum hp_wmi_radio r) | |||
273 | int wireless = 0; | 339 | int wireless = 0; |
274 | int mask; | 340 | int mask; |
275 | hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, | 341 | hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, |
276 | &wireless, sizeof(wireless)); | 342 | &wireless, sizeof(wireless), |
343 | sizeof(wireless)); | ||
277 | /* TBD: Pass error */ | 344 | /* TBD: Pass error */ |
278 | 345 | ||
279 | mask = 0x800 << (r * 8); | 346 | mask = 0x800 << (r * 8); |
@@ -284,6 +351,50 @@ static bool hp_wmi_get_hw_state(enum hp_wmi_radio r) | |||
284 | return true; | 351 | return true; |
285 | } | 352 | } |
286 | 353 | ||
354 | static int hp_wmi_rfkill2_set_block(void *data, bool blocked) | ||
355 | { | ||
356 | int rfkill_id = (int)(long)data; | ||
357 | char buffer[4] = { 0x01, 0x00, rfkill_id, !blocked }; | ||
358 | |||
359 | if (hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 1, | ||
360 | buffer, sizeof(buffer), 0)) | ||
361 | return -EINVAL; | ||
362 | return 0; | ||
363 | } | ||
364 | |||
365 | static const struct rfkill_ops hp_wmi_rfkill2_ops = { | ||
366 | .set_block = hp_wmi_rfkill2_set_block, | ||
367 | }; | ||
368 | |||
369 | static int hp_wmi_rfkill2_refresh(void) | ||
370 | { | ||
371 | int err, i; | ||
372 | struct bios_rfkill2_state state; | ||
373 | |||
374 | err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 0, &state, | ||
375 | 0, sizeof(state)); | ||
376 | if (err) | ||
377 | return err; | ||
378 | |||
379 | for (i = 0; i < rfkill2_count; i++) { | ||
380 | int num = rfkill2[i].num; | ||
381 | struct bios_rfkill2_device_state *devstate; | ||
382 | devstate = &state.device[num]; | ||
383 | |||
384 | if (num >= state.count || | ||
385 | devstate->rfkill_id != rfkill2[i].id) { | ||
386 | pr_warn("power configuration of the wireless devices unexpectedly changed\n"); | ||
387 | continue; | ||
388 | } | ||
389 | |||
390 | rfkill_set_states(rfkill2[i].rfkill, | ||
391 | IS_SWBLOCKED(devstate->power), | ||
392 | IS_HWBLOCKED(devstate->power)); | ||
393 | } | ||
394 | |||
395 | return 0; | ||
396 | } | ||
397 | |||
287 | static ssize_t show_display(struct device *dev, struct device_attribute *attr, | 398 | static ssize_t show_display(struct device *dev, struct device_attribute *attr, |
288 | char *buf) | 399 | char *buf) |
289 | { | 400 | { |
@@ -334,7 +445,7 @@ static ssize_t set_als(struct device *dev, struct device_attribute *attr, | |||
334 | { | 445 | { |
335 | u32 tmp = simple_strtoul(buf, NULL, 10); | 446 | u32 tmp = simple_strtoul(buf, NULL, 10); |
336 | int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, &tmp, | 447 | int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, &tmp, |
337 | sizeof(tmp)); | 448 | sizeof(tmp), sizeof(tmp)); |
338 | if (ret) | 449 | if (ret) |
339 | return -EINVAL; | 450 | return -EINVAL; |
340 | 451 | ||
@@ -347,64 +458,9 @@ static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als); | |||
347 | static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL); | 458 | static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL); |
348 | static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL); | 459 | static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL); |
349 | 460 | ||
350 | static struct key_entry *hp_wmi_get_entry_by_scancode(unsigned int code) | ||
351 | { | ||
352 | struct key_entry *key; | ||
353 | |||
354 | for (key = hp_wmi_keymap; key->type != KE_END; key++) | ||
355 | if (code == key->code) | ||
356 | return key; | ||
357 | |||
358 | return NULL; | ||
359 | } | ||
360 | |||
361 | static struct key_entry *hp_wmi_get_entry_by_keycode(unsigned int keycode) | ||
362 | { | ||
363 | struct key_entry *key; | ||
364 | |||
365 | for (key = hp_wmi_keymap; key->type != KE_END; key++) | ||
366 | if (key->type == KE_KEY && keycode == key->keycode) | ||
367 | return key; | ||
368 | |||
369 | return NULL; | ||
370 | } | ||
371 | |||
372 | static int hp_wmi_getkeycode(struct input_dev *dev, | ||
373 | unsigned int scancode, unsigned int *keycode) | ||
374 | { | ||
375 | struct key_entry *key = hp_wmi_get_entry_by_scancode(scancode); | ||
376 | |||
377 | if (key && key->type == KE_KEY) { | ||
378 | *keycode = key->keycode; | ||
379 | return 0; | ||
380 | } | ||
381 | |||
382 | return -EINVAL; | ||
383 | } | ||
384 | |||
385 | static int hp_wmi_setkeycode(struct input_dev *dev, | ||
386 | unsigned int scancode, unsigned int keycode) | ||
387 | { | ||
388 | struct key_entry *key; | ||
389 | unsigned int old_keycode; | ||
390 | |||
391 | key = hp_wmi_get_entry_by_scancode(scancode); | ||
392 | if (key && key->type == KE_KEY) { | ||
393 | old_keycode = key->keycode; | ||
394 | key->keycode = keycode; | ||
395 | set_bit(keycode, dev->keybit); | ||
396 | if (!hp_wmi_get_entry_by_keycode(old_keycode)) | ||
397 | clear_bit(old_keycode, dev->keybit); | ||
398 | return 0; | ||
399 | } | ||
400 | |||
401 | return -EINVAL; | ||
402 | } | ||
403 | |||
404 | static void hp_wmi_notify(u32 value, void *context) | 461 | static void hp_wmi_notify(u32 value, void *context) |
405 | { | 462 | { |
406 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; | 463 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; |
407 | static struct key_entry *key; | ||
408 | union acpi_object *obj; | 464 | union acpi_object *obj; |
409 | u32 event_id, event_data; | 465 | u32 event_id, event_data; |
410 | int key_code = 0, ret; | 466 | int key_code = 0, ret; |
@@ -413,7 +469,7 @@ static void hp_wmi_notify(u32 value, void *context) | |||
413 | 469 | ||
414 | status = wmi_get_event_data(value, &response); | 470 | status = wmi_get_event_data(value, &response); |
415 | if (status != AE_OK) { | 471 | if (status != AE_OK) { |
416 | printk(KERN_INFO PREFIX "bad event status 0x%x\n", status); | 472 | pr_info("bad event status 0x%x\n", status); |
417 | return; | 473 | return; |
418 | } | 474 | } |
419 | 475 | ||
@@ -422,8 +478,7 @@ static void hp_wmi_notify(u32 value, void *context) | |||
422 | if (!obj) | 478 | if (!obj) |
423 | return; | 479 | return; |
424 | if (obj->type != ACPI_TYPE_BUFFER) { | 480 | if (obj->type != ACPI_TYPE_BUFFER) { |
425 | printk(KERN_INFO "hp-wmi: Unknown response received %d\n", | 481 | pr_info("Unknown response received %d\n", obj->type); |
426 | obj->type); | ||
427 | kfree(obj); | 482 | kfree(obj); |
428 | return; | 483 | return; |
429 | } | 484 | } |
@@ -440,8 +495,7 @@ static void hp_wmi_notify(u32 value, void *context) | |||
440 | event_id = *location; | 495 | event_id = *location; |
441 | event_data = *(location + 2); | 496 | event_data = *(location + 2); |
442 | } else { | 497 | } else { |
443 | printk(KERN_INFO "hp-wmi: Unknown buffer length %d\n", | 498 | pr_info("Unknown buffer length %d\n", obj->buffer.length); |
444 | obj->buffer.length); | ||
445 | kfree(obj); | 499 | kfree(obj); |
446 | return; | 500 | return; |
447 | } | 501 | } |
@@ -462,26 +516,21 @@ static void hp_wmi_notify(u32 value, void *context) | |||
462 | case HPWMI_BEZEL_BUTTON: | 516 | case HPWMI_BEZEL_BUTTON: |
463 | ret = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0, | 517 | ret = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0, |
464 | &key_code, | 518 | &key_code, |
519 | sizeof(key_code), | ||
465 | sizeof(key_code)); | 520 | sizeof(key_code)); |
466 | if (ret) | 521 | if (ret) |
467 | break; | 522 | break; |
468 | key = hp_wmi_get_entry_by_scancode(key_code); | 523 | |
469 | if (key) { | 524 | if (!sparse_keymap_report_event(hp_wmi_input_dev, |
470 | switch (key->type) { | 525 | key_code, 1, true)) |
471 | case KE_KEY: | 526 | pr_info("Unknown key code - 0x%x\n", key_code); |
472 | input_report_key(hp_wmi_input_dev, | ||
473 | key->keycode, 1); | ||
474 | input_sync(hp_wmi_input_dev); | ||
475 | input_report_key(hp_wmi_input_dev, | ||
476 | key->keycode, 0); | ||
477 | input_sync(hp_wmi_input_dev); | ||
478 | break; | ||
479 | } | ||
480 | } else | ||
481 | printk(KERN_INFO PREFIX "Unknown key code - 0x%x\n", | ||
482 | key_code); | ||
483 | break; | 527 | break; |
484 | case HPWMI_WIRELESS: | 528 | case HPWMI_WIRELESS: |
529 | if (rfkill2_count) { | ||
530 | hp_wmi_rfkill2_refresh(); | ||
531 | break; | ||
532 | } | ||
533 | |||
485 | if (wifi_rfkill) | 534 | if (wifi_rfkill) |
486 | rfkill_set_states(wifi_rfkill, | 535 | rfkill_set_states(wifi_rfkill, |
487 | hp_wmi_get_sw_state(HPWMI_WIFI), | 536 | hp_wmi_get_sw_state(HPWMI_WIFI), |
@@ -496,21 +545,19 @@ static void hp_wmi_notify(u32 value, void *context) | |||
496 | hp_wmi_get_hw_state(HPWMI_WWAN)); | 545 | hp_wmi_get_hw_state(HPWMI_WWAN)); |
497 | break; | 546 | break; |
498 | case HPWMI_CPU_BATTERY_THROTTLE: | 547 | case HPWMI_CPU_BATTERY_THROTTLE: |
499 | printk(KERN_INFO PREFIX UNIMP "CPU throttle because of 3 Cell" | 548 | pr_info("Unimplemented CPU throttle because of 3 Cell battery event detected\n"); |
500 | " battery event detected\n"); | ||
501 | break; | 549 | break; |
502 | case HPWMI_LOCK_SWITCH: | 550 | case HPWMI_LOCK_SWITCH: |
503 | break; | 551 | break; |
504 | default: | 552 | default: |
505 | printk(KERN_INFO PREFIX "Unknown event_id - %d - 0x%x\n", | 553 | pr_info("Unknown event_id - %d - 0x%x\n", event_id, event_data); |
506 | event_id, event_data); | ||
507 | break; | 554 | break; |
508 | } | 555 | } |
509 | } | 556 | } |
510 | 557 | ||
511 | static int __init hp_wmi_input_setup(void) | 558 | static int __init hp_wmi_input_setup(void) |
512 | { | 559 | { |
513 | struct key_entry *key; | 560 | acpi_status status; |
514 | int err; | 561 | int err; |
515 | 562 | ||
516 | hp_wmi_input_dev = input_allocate_device(); | 563 | hp_wmi_input_dev = input_allocate_device(); |
@@ -520,21 +567,14 @@ static int __init hp_wmi_input_setup(void) | |||
520 | hp_wmi_input_dev->name = "HP WMI hotkeys"; | 567 | hp_wmi_input_dev->name = "HP WMI hotkeys"; |
521 | hp_wmi_input_dev->phys = "wmi/input0"; | 568 | hp_wmi_input_dev->phys = "wmi/input0"; |
522 | hp_wmi_input_dev->id.bustype = BUS_HOST; | 569 | hp_wmi_input_dev->id.bustype = BUS_HOST; |
523 | hp_wmi_input_dev->getkeycode = hp_wmi_getkeycode; | ||
524 | hp_wmi_input_dev->setkeycode = hp_wmi_setkeycode; | ||
525 | |||
526 | for (key = hp_wmi_keymap; key->type != KE_END; key++) { | ||
527 | switch (key->type) { | ||
528 | case KE_KEY: | ||
529 | set_bit(EV_KEY, hp_wmi_input_dev->evbit); | ||
530 | set_bit(key->keycode, hp_wmi_input_dev->keybit); | ||
531 | break; | ||
532 | } | ||
533 | } | ||
534 | 570 | ||
535 | set_bit(EV_SW, hp_wmi_input_dev->evbit); | 571 | __set_bit(EV_SW, hp_wmi_input_dev->evbit); |
536 | set_bit(SW_DOCK, hp_wmi_input_dev->swbit); | 572 | __set_bit(SW_DOCK, hp_wmi_input_dev->swbit); |
537 | set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit); | 573 | __set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit); |
574 | |||
575 | err = sparse_keymap_setup(hp_wmi_input_dev, hp_wmi_keymap, NULL); | ||
576 | if (err) | ||
577 | goto err_free_dev; | ||
538 | 578 | ||
539 | /* Set initial hardware state */ | 579 | /* Set initial hardware state */ |
540 | input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state()); | 580 | input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state()); |
@@ -542,14 +582,32 @@ static int __init hp_wmi_input_setup(void) | |||
542 | hp_wmi_tablet_state()); | 582 | hp_wmi_tablet_state()); |
543 | input_sync(hp_wmi_input_dev); | 583 | input_sync(hp_wmi_input_dev); |
544 | 584 | ||
545 | err = input_register_device(hp_wmi_input_dev); | 585 | status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL); |
546 | 586 | if (ACPI_FAILURE(status)) { | |
547 | if (err) { | 587 | err = -EIO; |
548 | input_free_device(hp_wmi_input_dev); | 588 | goto err_free_keymap; |
549 | return err; | ||
550 | } | 589 | } |
551 | 590 | ||
591 | err = input_register_device(hp_wmi_input_dev); | ||
592 | if (err) | ||
593 | goto err_uninstall_notifier; | ||
594 | |||
552 | return 0; | 595 | return 0; |
596 | |||
597 | err_uninstall_notifier: | ||
598 | wmi_remove_notify_handler(HPWMI_EVENT_GUID); | ||
599 | err_free_keymap: | ||
600 | sparse_keymap_free(hp_wmi_input_dev); | ||
601 | err_free_dev: | ||
602 | input_free_device(hp_wmi_input_dev); | ||
603 | return err; | ||
604 | } | ||
605 | |||
606 | static void hp_wmi_input_destroy(void) | ||
607 | { | ||
608 | wmi_remove_notify_handler(HPWMI_EVENT_GUID); | ||
609 | sparse_keymap_free(hp_wmi_input_dev); | ||
610 | input_unregister_device(hp_wmi_input_dev); | ||
553 | } | 611 | } |
554 | 612 | ||
555 | static void cleanup_sysfs(struct platform_device *device) | 613 | static void cleanup_sysfs(struct platform_device *device) |
@@ -561,32 +619,16 @@ static void cleanup_sysfs(struct platform_device *device) | |||
561 | device_remove_file(&device->dev, &dev_attr_tablet); | 619 | device_remove_file(&device->dev, &dev_attr_tablet); |
562 | } | 620 | } |
563 | 621 | ||
564 | static int __devinit hp_wmi_bios_setup(struct platform_device *device) | 622 | static int __devinit hp_wmi_rfkill_setup(struct platform_device *device) |
565 | { | 623 | { |
566 | int err; | 624 | int err; |
567 | int wireless = 0; | 625 | int wireless = 0; |
568 | 626 | ||
569 | err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, &wireless, | 627 | err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, &wireless, |
570 | sizeof(wireless)); | 628 | sizeof(wireless), sizeof(wireless)); |
571 | if (err) | 629 | if (err) |
572 | return err; | 630 | return err; |
573 | 631 | ||
574 | err = device_create_file(&device->dev, &dev_attr_display); | ||
575 | if (err) | ||
576 | goto add_sysfs_error; | ||
577 | err = device_create_file(&device->dev, &dev_attr_hddtemp); | ||
578 | if (err) | ||
579 | goto add_sysfs_error; | ||
580 | err = device_create_file(&device->dev, &dev_attr_als); | ||
581 | if (err) | ||
582 | goto add_sysfs_error; | ||
583 | err = device_create_file(&device->dev, &dev_attr_dock); | ||
584 | if (err) | ||
585 | goto add_sysfs_error; | ||
586 | err = device_create_file(&device->dev, &dev_attr_tablet); | ||
587 | if (err) | ||
588 | goto add_sysfs_error; | ||
589 | |||
590 | if (wireless & 0x1) { | 632 | if (wireless & 0x1) { |
591 | wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev, | 633 | wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev, |
592 | RFKILL_TYPE_WLAN, | 634 | RFKILL_TYPE_WLAN, |
@@ -632,14 +674,130 @@ static int __devinit hp_wmi_bios_setup(struct platform_device *device) | |||
632 | return 0; | 674 | return 0; |
633 | register_wwan_err: | 675 | register_wwan_err: |
634 | rfkill_destroy(wwan_rfkill); | 676 | rfkill_destroy(wwan_rfkill); |
677 | wwan_rfkill = NULL; | ||
635 | if (bluetooth_rfkill) | 678 | if (bluetooth_rfkill) |
636 | rfkill_unregister(bluetooth_rfkill); | 679 | rfkill_unregister(bluetooth_rfkill); |
637 | register_bluetooth_error: | 680 | register_bluetooth_error: |
638 | rfkill_destroy(bluetooth_rfkill); | 681 | rfkill_destroy(bluetooth_rfkill); |
682 | bluetooth_rfkill = NULL; | ||
639 | if (wifi_rfkill) | 683 | if (wifi_rfkill) |
640 | rfkill_unregister(wifi_rfkill); | 684 | rfkill_unregister(wifi_rfkill); |
641 | register_wifi_error: | 685 | register_wifi_error: |
642 | rfkill_destroy(wifi_rfkill); | 686 | rfkill_destroy(wifi_rfkill); |
687 | wifi_rfkill = NULL; | ||
688 | return err; | ||
689 | } | ||
690 | |||
691 | static int __devinit hp_wmi_rfkill2_setup(struct platform_device *device) | ||
692 | { | ||
693 | int err, i; | ||
694 | struct bios_rfkill2_state state; | ||
695 | err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 0, &state, | ||
696 | 0, sizeof(state)); | ||
697 | if (err) | ||
698 | return err; | ||
699 | |||
700 | if (state.count > HPWMI_MAX_RFKILL2_DEVICES) { | ||
701 | pr_warn("unable to parse 0x1b query output\n"); | ||
702 | return -EINVAL; | ||
703 | } | ||
704 | |||
705 | for (i = 0; i < state.count; i++) { | ||
706 | struct rfkill *rfkill; | ||
707 | enum rfkill_type type; | ||
708 | char *name; | ||
709 | switch (state.device[i].radio_type) { | ||
710 | case HPWMI_WIFI: | ||
711 | type = RFKILL_TYPE_WLAN; | ||
712 | name = "hp-wifi"; | ||
713 | break; | ||
714 | case HPWMI_BLUETOOTH: | ||
715 | type = RFKILL_TYPE_BLUETOOTH; | ||
716 | name = "hp-bluetooth"; | ||
717 | break; | ||
718 | case HPWMI_WWAN: | ||
719 | type = RFKILL_TYPE_WWAN; | ||
720 | name = "hp-wwan"; | ||
721 | break; | ||
722 | default: | ||
723 | pr_warn("unknown device type 0x%x\n", | ||
724 | state.device[i].radio_type); | ||
725 | continue; | ||
726 | } | ||
727 | |||
728 | if (!state.device[i].vendor_id) { | ||
729 | pr_warn("zero device %d while %d reported\n", | ||
730 | i, state.count); | ||
731 | continue; | ||
732 | } | ||
733 | |||
734 | rfkill = rfkill_alloc(name, &device->dev, type, | ||
735 | &hp_wmi_rfkill2_ops, (void *)(long)i); | ||
736 | if (!rfkill) { | ||
737 | err = -ENOMEM; | ||
738 | goto fail; | ||
739 | } | ||
740 | |||
741 | rfkill2[rfkill2_count].id = state.device[i].rfkill_id; | ||
742 | rfkill2[rfkill2_count].num = i; | ||
743 | rfkill2[rfkill2_count].rfkill = rfkill; | ||
744 | |||
745 | rfkill_init_sw_state(rfkill, | ||
746 | IS_SWBLOCKED(state.device[i].power)); | ||
747 | rfkill_set_hw_state(rfkill, | ||
748 | IS_HWBLOCKED(state.device[i].power)); | ||
749 | |||
750 | if (!(state.device[i].power & HPWMI_POWER_BIOS)) | ||
751 | pr_info("device %s blocked by BIOS\n", name); | ||
752 | |||
753 | err = rfkill_register(rfkill); | ||
754 | if (err) { | ||
755 | rfkill_destroy(rfkill); | ||
756 | goto fail; | ||
757 | } | ||
758 | |||
759 | rfkill2_count++; | ||
760 | } | ||
761 | |||
762 | return 0; | ||
763 | fail: | ||
764 | for (; rfkill2_count > 0; rfkill2_count--) { | ||
765 | rfkill_unregister(rfkill2[rfkill2_count - 1].rfkill); | ||
766 | rfkill_destroy(rfkill2[rfkill2_count - 1].rfkill); | ||
767 | } | ||
768 | return err; | ||
769 | } | ||
770 | |||
771 | static int __devinit hp_wmi_bios_setup(struct platform_device *device) | ||
772 | { | ||
773 | int err; | ||
774 | |||
775 | /* clear detected rfkill devices */ | ||
776 | wifi_rfkill = NULL; | ||
777 | bluetooth_rfkill = NULL; | ||
778 | wwan_rfkill = NULL; | ||
779 | rfkill2_count = 0; | ||
780 | |||
781 | if (hp_wmi_rfkill_setup(device)) | ||
782 | hp_wmi_rfkill2_setup(device); | ||
783 | |||
784 | err = device_create_file(&device->dev, &dev_attr_display); | ||
785 | if (err) | ||
786 | goto add_sysfs_error; | ||
787 | err = device_create_file(&device->dev, &dev_attr_hddtemp); | ||
788 | if (err) | ||
789 | goto add_sysfs_error; | ||
790 | err = device_create_file(&device->dev, &dev_attr_als); | ||
791 | if (err) | ||
792 | goto add_sysfs_error; | ||
793 | err = device_create_file(&device->dev, &dev_attr_dock); | ||
794 | if (err) | ||
795 | goto add_sysfs_error; | ||
796 | err = device_create_file(&device->dev, &dev_attr_tablet); | ||
797 | if (err) | ||
798 | goto add_sysfs_error; | ||
799 | return 0; | ||
800 | |||
643 | add_sysfs_error: | 801 | add_sysfs_error: |
644 | cleanup_sysfs(device); | 802 | cleanup_sysfs(device); |
645 | return err; | 803 | return err; |
@@ -647,8 +805,14 @@ add_sysfs_error: | |||
647 | 805 | ||
648 | static int __exit hp_wmi_bios_remove(struct platform_device *device) | 806 | static int __exit hp_wmi_bios_remove(struct platform_device *device) |
649 | { | 807 | { |
808 | int i; | ||
650 | cleanup_sysfs(device); | 809 | cleanup_sysfs(device); |
651 | 810 | ||
811 | for (i = 0; i < rfkill2_count; i++) { | ||
812 | rfkill_unregister(rfkill2[i].rfkill); | ||
813 | rfkill_destroy(rfkill2[i].rfkill); | ||
814 | } | ||
815 | |||
652 | if (wifi_rfkill) { | 816 | if (wifi_rfkill) { |
653 | rfkill_unregister(wifi_rfkill); | 817 | rfkill_unregister(wifi_rfkill); |
654 | rfkill_destroy(wifi_rfkill); | 818 | rfkill_destroy(wifi_rfkill); |
@@ -681,6 +845,9 @@ static int hp_wmi_resume_handler(struct device *device) | |||
681 | input_sync(hp_wmi_input_dev); | 845 | input_sync(hp_wmi_input_dev); |
682 | } | 846 | } |
683 | 847 | ||
848 | if (rfkill2_count) | ||
849 | hp_wmi_rfkill2_refresh(); | ||
850 | |||
684 | if (wifi_rfkill) | 851 | if (wifi_rfkill) |
685 | rfkill_set_states(wifi_rfkill, | 852 | rfkill_set_states(wifi_rfkill, |
686 | hp_wmi_get_sw_state(HPWMI_WIFI), | 853 | hp_wmi_get_sw_state(HPWMI_WIFI), |
@@ -704,15 +871,9 @@ static int __init hp_wmi_init(void) | |||
704 | int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID); | 871 | int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID); |
705 | 872 | ||
706 | if (event_capable) { | 873 | if (event_capable) { |
707 | err = wmi_install_notify_handler(HPWMI_EVENT_GUID, | ||
708 | hp_wmi_notify, NULL); | ||
709 | if (ACPI_FAILURE(err)) | ||
710 | return -EINVAL; | ||
711 | err = hp_wmi_input_setup(); | 874 | err = hp_wmi_input_setup(); |
712 | if (err) { | 875 | if (err) |
713 | wmi_remove_notify_handler(HPWMI_EVENT_GUID); | ||
714 | return err; | 876 | return err; |
715 | } | ||
716 | } | 877 | } |
717 | 878 | ||
718 | if (bios_capable) { | 879 | if (bios_capable) { |
@@ -739,20 +900,17 @@ err_device_add: | |||
739 | err_device_alloc: | 900 | err_device_alloc: |
740 | platform_driver_unregister(&hp_wmi_driver); | 901 | platform_driver_unregister(&hp_wmi_driver); |
741 | err_driver_reg: | 902 | err_driver_reg: |
742 | if (wmi_has_guid(HPWMI_EVENT_GUID)) { | 903 | if (event_capable) |
743 | input_unregister_device(hp_wmi_input_dev); | 904 | hp_wmi_input_destroy(); |
744 | wmi_remove_notify_handler(HPWMI_EVENT_GUID); | ||
745 | } | ||
746 | 905 | ||
747 | return err; | 906 | return err; |
748 | } | 907 | } |
749 | 908 | ||
750 | static void __exit hp_wmi_exit(void) | 909 | static void __exit hp_wmi_exit(void) |
751 | { | 910 | { |
752 | if (wmi_has_guid(HPWMI_EVENT_GUID)) { | 911 | if (wmi_has_guid(HPWMI_EVENT_GUID)) |
753 | wmi_remove_notify_handler(HPWMI_EVENT_GUID); | 912 | hp_wmi_input_destroy(); |
754 | input_unregister_device(hp_wmi_input_dev); | 913 | |
755 | } | ||
756 | if (hp_wmi_platform_dev) { | 914 | if (hp_wmi_platform_dev) { |
757 | platform_device_unregister(hp_wmi_platform_dev); | 915 | platform_device_unregister(hp_wmi_platform_dev); |
758 | platform_driver_unregister(&hp_wmi_driver); | 916 | platform_driver_unregister(&hp_wmi_driver); |