diff options
Diffstat (limited to 'drivers/misc/eeepc-laptop.c')
-rw-r--r-- | drivers/misc/eeepc-laptop.c | 228 |
1 files changed, 209 insertions, 19 deletions
diff --git a/drivers/misc/eeepc-laptop.c b/drivers/misc/eeepc-laptop.c index 1ee8501e90f1..5baa10bcaf6d 100644 --- a/drivers/misc/eeepc-laptop.c +++ b/drivers/misc/eeepc-laptop.c | |||
@@ -28,6 +28,8 @@ | |||
28 | #include <acpi/acpi_drivers.h> | 28 | #include <acpi/acpi_drivers.h> |
29 | #include <acpi/acpi_bus.h> | 29 | #include <acpi/acpi_bus.h> |
30 | #include <linux/uaccess.h> | 30 | #include <linux/uaccess.h> |
31 | #include <linux/input.h> | ||
32 | #include <linux/rfkill.h> | ||
31 | 33 | ||
32 | #define EEEPC_LAPTOP_VERSION "0.1" | 34 | #define EEEPC_LAPTOP_VERSION "0.1" |
33 | 35 | ||
@@ -125,6 +127,10 @@ struct eeepc_hotk { | |||
125 | by this BIOS */ | 127 | by this BIOS */ |
126 | uint init_flag; /* Init flags */ | 128 | uint init_flag; /* Init flags */ |
127 | u16 event_count[128]; /* count for each event */ | 129 | u16 event_count[128]; /* count for each event */ |
130 | struct input_dev *inputdev; | ||
131 | u16 *keycode_map; | ||
132 | struct rfkill *eeepc_wlan_rfkill; | ||
133 | struct rfkill *eeepc_bluetooth_rfkill; | ||
128 | }; | 134 | }; |
129 | 135 | ||
130 | /* The actual device the driver binds to */ | 136 | /* The actual device the driver binds to */ |
@@ -140,6 +146,27 @@ static struct platform_driver platform_driver = { | |||
140 | 146 | ||
141 | static struct platform_device *platform_device; | 147 | static struct platform_device *platform_device; |
142 | 148 | ||
149 | struct key_entry { | ||
150 | char type; | ||
151 | u8 code; | ||
152 | u16 keycode; | ||
153 | }; | ||
154 | |||
155 | enum { KE_KEY, KE_END }; | ||
156 | |||
157 | static struct key_entry eeepc_keymap[] = { | ||
158 | /* Sleep already handled via generic ACPI code */ | ||
159 | {KE_KEY, 0x10, KEY_WLAN }, | ||
160 | {KE_KEY, 0x12, KEY_PROG1 }, | ||
161 | {KE_KEY, 0x13, KEY_MUTE }, | ||
162 | {KE_KEY, 0x14, KEY_VOLUMEDOWN }, | ||
163 | {KE_KEY, 0x15, KEY_VOLUMEUP }, | ||
164 | {KE_KEY, 0x30, KEY_SWITCHVIDEOMODE }, | ||
165 | {KE_KEY, 0x31, KEY_SWITCHVIDEOMODE }, | ||
166 | {KE_KEY, 0x32, KEY_SWITCHVIDEOMODE }, | ||
167 | {KE_END, 0}, | ||
168 | }; | ||
169 | |||
143 | /* | 170 | /* |
144 | * The hotkey driver declaration | 171 | * The hotkey driver declaration |
145 | */ | 172 | */ |
@@ -261,6 +288,44 @@ static int update_bl_status(struct backlight_device *bd) | |||
261 | } | 288 | } |
262 | 289 | ||
263 | /* | 290 | /* |
291 | * Rfkill helpers | ||
292 | */ | ||
293 | |||
294 | static int eeepc_wlan_rfkill_set(void *data, enum rfkill_state state) | ||
295 | { | ||
296 | if (state == RFKILL_STATE_SOFT_BLOCKED) | ||
297 | return set_acpi(CM_ASL_WLAN, 0); | ||
298 | else | ||
299 | return set_acpi(CM_ASL_WLAN, 1); | ||
300 | } | ||
301 | |||
302 | static int eeepc_wlan_rfkill_state(void *data, enum rfkill_state *state) | ||
303 | { | ||
304 | if (get_acpi(CM_ASL_WLAN) == 1) | ||
305 | *state = RFKILL_STATE_UNBLOCKED; | ||
306 | else | ||
307 | *state = RFKILL_STATE_SOFT_BLOCKED; | ||
308 | return 0; | ||
309 | } | ||
310 | |||
311 | static int eeepc_bluetooth_rfkill_set(void *data, enum rfkill_state state) | ||
312 | { | ||
313 | if (state == RFKILL_STATE_SOFT_BLOCKED) | ||
314 | return set_acpi(CM_ASL_BLUETOOTH, 0); | ||
315 | else | ||
316 | return set_acpi(CM_ASL_BLUETOOTH, 1); | ||
317 | } | ||
318 | |||
319 | static int eeepc_bluetooth_rfkill_state(void *data, enum rfkill_state *state) | ||
320 | { | ||
321 | if (get_acpi(CM_ASL_BLUETOOTH) == 1) | ||
322 | *state = RFKILL_STATE_UNBLOCKED; | ||
323 | else | ||
324 | *state = RFKILL_STATE_SOFT_BLOCKED; | ||
325 | return 0; | ||
326 | } | ||
327 | |||
328 | /* | ||
264 | * Sys helpers | 329 | * Sys helpers |
265 | */ | 330 | */ |
266 | static int parse_arg(const char *buf, unsigned long count, int *val) | 331 | static int parse_arg(const char *buf, unsigned long count, int *val) |
@@ -311,13 +376,11 @@ static ssize_t show_sys_acpi(int cm, char *buf) | |||
311 | EEEPC_CREATE_DEVICE_ATTR(camera, CM_ASL_CAMERA); | 376 | EEEPC_CREATE_DEVICE_ATTR(camera, CM_ASL_CAMERA); |
312 | EEEPC_CREATE_DEVICE_ATTR(cardr, CM_ASL_CARDREADER); | 377 | EEEPC_CREATE_DEVICE_ATTR(cardr, CM_ASL_CARDREADER); |
313 | EEEPC_CREATE_DEVICE_ATTR(disp, CM_ASL_DISPLAYSWITCH); | 378 | EEEPC_CREATE_DEVICE_ATTR(disp, CM_ASL_DISPLAYSWITCH); |
314 | EEEPC_CREATE_DEVICE_ATTR(wlan, CM_ASL_WLAN); | ||
315 | 379 | ||
316 | static struct attribute *platform_attributes[] = { | 380 | static struct attribute *platform_attributes[] = { |
317 | &dev_attr_camera.attr, | 381 | &dev_attr_camera.attr, |
318 | &dev_attr_cardr.attr, | 382 | &dev_attr_cardr.attr, |
319 | &dev_attr_disp.attr, | 383 | &dev_attr_disp.attr, |
320 | &dev_attr_wlan.attr, | ||
321 | NULL | 384 | NULL |
322 | }; | 385 | }; |
323 | 386 | ||
@@ -328,8 +391,64 @@ static struct attribute_group platform_attribute_group = { | |||
328 | /* | 391 | /* |
329 | * Hotkey functions | 392 | * Hotkey functions |
330 | */ | 393 | */ |
394 | static struct key_entry *eepc_get_entry_by_scancode(int code) | ||
395 | { | ||
396 | struct key_entry *key; | ||
397 | |||
398 | for (key = eeepc_keymap; key->type != KE_END; key++) | ||
399 | if (code == key->code) | ||
400 | return key; | ||
401 | |||
402 | return NULL; | ||
403 | } | ||
404 | |||
405 | static struct key_entry *eepc_get_entry_by_keycode(int code) | ||
406 | { | ||
407 | struct key_entry *key; | ||
408 | |||
409 | for (key = eeepc_keymap; key->type != KE_END; key++) | ||
410 | if (code == key->keycode && key->type == KE_KEY) | ||
411 | return key; | ||
412 | |||
413 | return NULL; | ||
414 | } | ||
415 | |||
416 | static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode) | ||
417 | { | ||
418 | struct key_entry *key = eepc_get_entry_by_scancode(scancode); | ||
419 | |||
420 | if (key && key->type == KE_KEY) { | ||
421 | *keycode = key->keycode; | ||
422 | return 0; | ||
423 | } | ||
424 | |||
425 | return -EINVAL; | ||
426 | } | ||
427 | |||
428 | static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode) | ||
429 | { | ||
430 | struct key_entry *key; | ||
431 | int old_keycode; | ||
432 | |||
433 | if (keycode < 0 || keycode > KEY_MAX) | ||
434 | return -EINVAL; | ||
435 | |||
436 | key = eepc_get_entry_by_scancode(scancode); | ||
437 | if (key && key->type == KE_KEY) { | ||
438 | old_keycode = key->keycode; | ||
439 | key->keycode = keycode; | ||
440 | set_bit(keycode, dev->keybit); | ||
441 | if (!eepc_get_entry_by_keycode(old_keycode)) | ||
442 | clear_bit(old_keycode, dev->keybit); | ||
443 | return 0; | ||
444 | } | ||
445 | |||
446 | return -EINVAL; | ||
447 | } | ||
448 | |||
331 | static int eeepc_hotk_check(void) | 449 | static int eeepc_hotk_check(void) |
332 | { | 450 | { |
451 | const struct key_entry *key; | ||
333 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | 452 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; |
334 | int result; | 453 | int result; |
335 | 454 | ||
@@ -356,6 +475,31 @@ static int eeepc_hotk_check(void) | |||
356 | "Get control methods supported: 0x%x\n", | 475 | "Get control methods supported: 0x%x\n", |
357 | ehotk->cm_supported); | 476 | ehotk->cm_supported); |
358 | } | 477 | } |
478 | ehotk->inputdev = input_allocate_device(); | ||
479 | if (!ehotk->inputdev) { | ||
480 | printk(EEEPC_INFO "Unable to allocate input device\n"); | ||
481 | return 0; | ||
482 | } | ||
483 | ehotk->inputdev->name = "Asus EeePC extra buttons"; | ||
484 | ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0"; | ||
485 | ehotk->inputdev->id.bustype = BUS_HOST; | ||
486 | ehotk->inputdev->getkeycode = eeepc_getkeycode; | ||
487 | ehotk->inputdev->setkeycode = eeepc_setkeycode; | ||
488 | |||
489 | for (key = eeepc_keymap; key->type != KE_END; key++) { | ||
490 | switch (key->type) { | ||
491 | case KE_KEY: | ||
492 | set_bit(EV_KEY, ehotk->inputdev->evbit); | ||
493 | set_bit(key->keycode, ehotk->inputdev->keybit); | ||
494 | break; | ||
495 | } | ||
496 | } | ||
497 | result = input_register_device(ehotk->inputdev); | ||
498 | if (result) { | ||
499 | printk(EEEPC_INFO "Unable to register input device\n"); | ||
500 | input_free_device(ehotk->inputdev); | ||
501 | return 0; | ||
502 | } | ||
359 | } else { | 503 | } else { |
360 | printk(EEEPC_ERR "Hotkey device not present, aborting\n"); | 504 | printk(EEEPC_ERR "Hotkey device not present, aborting\n"); |
361 | return -EINVAL; | 505 | return -EINVAL; |
@@ -363,21 +507,6 @@ static int eeepc_hotk_check(void) | |||
363 | return 0; | 507 | return 0; |
364 | } | 508 | } |
365 | 509 | ||
366 | static void notify_wlan(u32 *event) | ||
367 | { | ||
368 | /* if DISABLE_ASL_WLAN is set, the notify code for fn+f2 | ||
369 | will always be 0x10 */ | ||
370 | if (ehotk->cm_supported & (0x1 << CM_ASL_WLAN)) { | ||
371 | const char *method = cm_getv[CM_ASL_WLAN]; | ||
372 | int value; | ||
373 | if (read_acpi_int(ehotk->handle, method, &value)) | ||
374 | printk(EEEPC_WARNING "Error reading %s\n", | ||
375 | method); | ||
376 | else if (value == 1) | ||
377 | *event = 0x11; | ||
378 | } | ||
379 | } | ||
380 | |||
381 | static void notify_brn(void) | 510 | static void notify_brn(void) |
382 | { | 511 | { |
383 | struct backlight_device *bd = eeepc_backlight_device; | 512 | struct backlight_device *bd = eeepc_backlight_device; |
@@ -386,14 +515,28 @@ static void notify_brn(void) | |||
386 | 515 | ||
387 | static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data) | 516 | static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data) |
388 | { | 517 | { |
518 | static struct key_entry *key; | ||
389 | if (!ehotk) | 519 | if (!ehotk) |
390 | return; | 520 | return; |
391 | if (event == NOTIFY_WLAN_ON && (DISABLE_ASL_WLAN & ehotk->init_flag)) | ||
392 | notify_wlan(&event); | ||
393 | if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX) | 521 | if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX) |
394 | notify_brn(); | 522 | notify_brn(); |
395 | acpi_bus_generate_proc_event(ehotk->device, event, | 523 | acpi_bus_generate_proc_event(ehotk->device, event, |
396 | ehotk->event_count[event % 128]++); | 524 | ehotk->event_count[event % 128]++); |
525 | if (ehotk->inputdev) { | ||
526 | key = eepc_get_entry_by_scancode(event); | ||
527 | if (key) { | ||
528 | switch (key->type) { | ||
529 | case KE_KEY: | ||
530 | input_report_key(ehotk->inputdev, key->keycode, | ||
531 | 1); | ||
532 | input_sync(ehotk->inputdev); | ||
533 | input_report_key(ehotk->inputdev, key->keycode, | ||
534 | 0); | ||
535 | input_sync(ehotk->inputdev); | ||
536 | break; | ||
537 | } | ||
538 | } | ||
539 | } | ||
397 | } | 540 | } |
398 | 541 | ||
399 | static int eeepc_hotk_add(struct acpi_device *device) | 542 | static int eeepc_hotk_add(struct acpi_device *device) |
@@ -420,6 +563,47 @@ static int eeepc_hotk_add(struct acpi_device *device) | |||
420 | eeepc_hotk_notify, ehotk); | 563 | eeepc_hotk_notify, ehotk); |
421 | if (ACPI_FAILURE(status)) | 564 | if (ACPI_FAILURE(status)) |
422 | printk(EEEPC_ERR "Error installing notify handler\n"); | 565 | printk(EEEPC_ERR "Error installing notify handler\n"); |
566 | |||
567 | if (get_acpi(CM_ASL_WLAN) != -1) { | ||
568 | ehotk->eeepc_wlan_rfkill = rfkill_allocate(&device->dev, | ||
569 | RFKILL_TYPE_WLAN); | ||
570 | |||
571 | if (!ehotk->eeepc_wlan_rfkill) | ||
572 | goto end; | ||
573 | |||
574 | ehotk->eeepc_wlan_rfkill->name = "eeepc-wlan"; | ||
575 | ehotk->eeepc_wlan_rfkill->toggle_radio = eeepc_wlan_rfkill_set; | ||
576 | ehotk->eeepc_wlan_rfkill->get_state = eeepc_wlan_rfkill_state; | ||
577 | if (get_acpi(CM_ASL_WLAN) == 1) | ||
578 | ehotk->eeepc_wlan_rfkill->state = | ||
579 | RFKILL_STATE_UNBLOCKED; | ||
580 | else | ||
581 | ehotk->eeepc_wlan_rfkill->state = | ||
582 | RFKILL_STATE_SOFT_BLOCKED; | ||
583 | rfkill_register(ehotk->eeepc_wlan_rfkill); | ||
584 | } | ||
585 | |||
586 | if (get_acpi(CM_ASL_BLUETOOTH) != -1) { | ||
587 | ehotk->eeepc_bluetooth_rfkill = | ||
588 | rfkill_allocate(&device->dev, RFKILL_TYPE_BLUETOOTH); | ||
589 | |||
590 | if (!ehotk->eeepc_bluetooth_rfkill) | ||
591 | goto end; | ||
592 | |||
593 | ehotk->eeepc_bluetooth_rfkill->name = "eeepc-bluetooth"; | ||
594 | ehotk->eeepc_bluetooth_rfkill->toggle_radio = | ||
595 | eeepc_bluetooth_rfkill_set; | ||
596 | ehotk->eeepc_bluetooth_rfkill->get_state = | ||
597 | eeepc_bluetooth_rfkill_state; | ||
598 | if (get_acpi(CM_ASL_BLUETOOTH) == 1) | ||
599 | ehotk->eeepc_bluetooth_rfkill->state = | ||
600 | RFKILL_STATE_UNBLOCKED; | ||
601 | else | ||
602 | ehotk->eeepc_bluetooth_rfkill->state = | ||
603 | RFKILL_STATE_SOFT_BLOCKED; | ||
604 | rfkill_register(ehotk->eeepc_bluetooth_rfkill); | ||
605 | } | ||
606 | |||
423 | end: | 607 | end: |
424 | if (result) { | 608 | if (result) { |
425 | kfree(ehotk); | 609 | kfree(ehotk); |
@@ -553,6 +737,12 @@ static void eeepc_backlight_exit(void) | |||
553 | { | 737 | { |
554 | if (eeepc_backlight_device) | 738 | if (eeepc_backlight_device) |
555 | backlight_device_unregister(eeepc_backlight_device); | 739 | backlight_device_unregister(eeepc_backlight_device); |
740 | if (ehotk->inputdev) | ||
741 | input_unregister_device(ehotk->inputdev); | ||
742 | if (ehotk->eeepc_wlan_rfkill) | ||
743 | rfkill_unregister(ehotk->eeepc_wlan_rfkill); | ||
744 | if (ehotk->eeepc_bluetooth_rfkill) | ||
745 | rfkill_unregister(ehotk->eeepc_bluetooth_rfkill); | ||
556 | eeepc_backlight_device = NULL; | 746 | eeepc_backlight_device = NULL; |
557 | } | 747 | } |
558 | 748 | ||