diff options
author | Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com> | 2010-04-27 12:15:00 -0400 |
---|---|---|
committer | Matthew Garrett <mjg@redhat.com> | 2010-05-17 12:06:01 -0400 |
commit | d5c051f1080e0eec55f3fc42c37d941681941628 (patch) | |
tree | 2851e28345f99f7f45a62932b1b8274caaa3a342 | |
parent | e40152ee1e1c7a63f4777791863215e3faa37a86 (diff) |
classmate-laptop: Add RFKILL support.
The RFKILL device shares the same ACPI device used for backlight. So, it
required a new struct sharing both a backlight_device and a rfkill
device.
Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
-rw-r--r-- | drivers/platform/x86/classmate-laptop.c | 170 |
1 files changed, 148 insertions, 22 deletions
diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 7f9e5ddc9498..3bf399fe2bbc 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <acpi/acpi_drivers.h> | 24 | #include <acpi/acpi_drivers.h> |
25 | #include <linux/backlight.h> | 25 | #include <linux/backlight.h> |
26 | #include <linux/input.h> | 26 | #include <linux/input.h> |
27 | #include <linux/rfkill.h> | ||
27 | 28 | ||
28 | MODULE_LICENSE("GPL"); | 29 | MODULE_LICENSE("GPL"); |
29 | 30 | ||
@@ -37,7 +38,7 @@ struct cmpc_accel { | |||
37 | 38 | ||
38 | #define CMPC_ACCEL_HID "ACCE0000" | 39 | #define CMPC_ACCEL_HID "ACCE0000" |
39 | #define CMPC_TABLET_HID "TBLT0000" | 40 | #define CMPC_TABLET_HID "TBLT0000" |
40 | #define CMPC_BL_HID "IPML200" | 41 | #define CMPC_IPML_HID "IPML200" |
41 | #define CMPC_KEYS_HID "FnBT0000" | 42 | #define CMPC_KEYS_HID "FnBT0000" |
42 | 43 | ||
43 | /* | 44 | /* |
@@ -461,43 +462,168 @@ static const struct backlight_ops cmpc_bl_ops = { | |||
461 | .update_status = cmpc_bl_update_status | 462 | .update_status = cmpc_bl_update_status |
462 | }; | 463 | }; |
463 | 464 | ||
464 | static int cmpc_bl_add(struct acpi_device *acpi) | 465 | /* |
466 | * RFKILL code. | ||
467 | */ | ||
468 | |||
469 | static acpi_status cmpc_get_rfkill_wlan(acpi_handle handle, | ||
470 | unsigned long long *value) | ||
465 | { | 471 | { |
466 | struct backlight_properties props; | 472 | union acpi_object param; |
473 | struct acpi_object_list input; | ||
474 | unsigned long long output; | ||
475 | acpi_status status; | ||
476 | |||
477 | param.type = ACPI_TYPE_INTEGER; | ||
478 | param.integer.value = 0xC1; | ||
479 | input.count = 1; | ||
480 | input.pointer = ¶m; | ||
481 | status = acpi_evaluate_integer(handle, "GRDI", &input, &output); | ||
482 | if (ACPI_SUCCESS(status)) | ||
483 | *value = output; | ||
484 | return status; | ||
485 | } | ||
486 | |||
487 | static acpi_status cmpc_set_rfkill_wlan(acpi_handle handle, | ||
488 | unsigned long long value) | ||
489 | { | ||
490 | union acpi_object param[2]; | ||
491 | struct acpi_object_list input; | ||
492 | acpi_status status; | ||
493 | unsigned long long output; | ||
494 | |||
495 | param[0].type = ACPI_TYPE_INTEGER; | ||
496 | param[0].integer.value = 0xC1; | ||
497 | param[1].type = ACPI_TYPE_INTEGER; | ||
498 | param[1].integer.value = value; | ||
499 | input.count = 2; | ||
500 | input.pointer = param; | ||
501 | status = acpi_evaluate_integer(handle, "GWRI", &input, &output); | ||
502 | return status; | ||
503 | } | ||
504 | |||
505 | static void cmpc_rfkill_query(struct rfkill *rfkill, void *data) | ||
506 | { | ||
507 | acpi_status status; | ||
508 | acpi_handle handle; | ||
509 | unsigned long long state; | ||
510 | bool blocked; | ||
511 | |||
512 | handle = data; | ||
513 | status = cmpc_get_rfkill_wlan(handle, &state); | ||
514 | if (ACPI_SUCCESS(status)) { | ||
515 | blocked = state & 1 ? false : true; | ||
516 | rfkill_set_sw_state(rfkill, blocked); | ||
517 | } | ||
518 | } | ||
519 | |||
520 | static int cmpc_rfkill_block(void *data, bool blocked) | ||
521 | { | ||
522 | acpi_status status; | ||
523 | acpi_handle handle; | ||
524 | unsigned long long state; | ||
525 | |||
526 | handle = data; | ||
527 | status = cmpc_get_rfkill_wlan(handle, &state); | ||
528 | if (ACPI_FAILURE(status)) | ||
529 | return -ENODEV; | ||
530 | if (blocked) | ||
531 | state &= ~1; | ||
532 | else | ||
533 | state |= 1; | ||
534 | status = cmpc_set_rfkill_wlan(handle, state); | ||
535 | if (ACPI_FAILURE(status)) | ||
536 | return -ENODEV; | ||
537 | return 0; | ||
538 | } | ||
539 | |||
540 | static const struct rfkill_ops cmpc_rfkill_ops = { | ||
541 | .query = cmpc_rfkill_query, | ||
542 | .set_block = cmpc_rfkill_block, | ||
543 | }; | ||
544 | |||
545 | /* | ||
546 | * Common backlight and rfkill code. | ||
547 | */ | ||
548 | |||
549 | struct ipml200_dev { | ||
467 | struct backlight_device *bd; | 550 | struct backlight_device *bd; |
551 | struct rfkill *rf; | ||
552 | }; | ||
553 | |||
554 | static int cmpc_ipml_add(struct acpi_device *acpi) | ||
555 | { | ||
556 | int retval; | ||
557 | struct ipml200_dev *ipml; | ||
558 | struct backlight_properties props; | ||
559 | |||
560 | ipml = kmalloc(sizeof(*ipml), GFP_KERNEL); | ||
561 | if (ipml == NULL) | ||
562 | return -ENOMEM; | ||
468 | 563 | ||
469 | memset(&props, 0, sizeof(struct backlight_properties)); | 564 | memset(&props, 0, sizeof(struct backlight_properties)); |
470 | props.max_brightness = 7; | 565 | props.max_brightness = 7; |
471 | bd = backlight_device_register("cmpc_bl", &acpi->dev, acpi->handle, | 566 | ipml->bd = backlight_device_register("cmpc_bl", &acpi->dev, |
472 | &cmpc_bl_ops, &props); | 567 | acpi->handle, &cmpc_bl_ops, |
473 | if (IS_ERR(bd)) | 568 | &props); |
474 | return PTR_ERR(bd); | 569 | if (IS_ERR(ipml->bd)) { |
475 | dev_set_drvdata(&acpi->dev, bd); | 570 | retval = PTR_ERR(ipml->bd); |
571 | goto out_bd; | ||
572 | } | ||
573 | |||
574 | ipml->rf = rfkill_alloc("cmpc_rfkill", &acpi->dev, RFKILL_TYPE_WLAN, | ||
575 | &cmpc_rfkill_ops, acpi->handle); | ||
576 | /* rfkill_alloc may fail if RFKILL is disabled. We should still work | ||
577 | * anyway. */ | ||
578 | if (!IS_ERR(ipml->rf)) { | ||
579 | retval = rfkill_register(ipml->rf); | ||
580 | if (retval) { | ||
581 | rfkill_destroy(ipml->rf); | ||
582 | ipml->rf = NULL; | ||
583 | } | ||
584 | } else { | ||
585 | ipml->rf = NULL; | ||
586 | } | ||
587 | |||
588 | dev_set_drvdata(&acpi->dev, ipml); | ||
476 | return 0; | 589 | return 0; |
590 | |||
591 | out_bd: | ||
592 | kfree(ipml); | ||
593 | return retval; | ||
477 | } | 594 | } |
478 | 595 | ||
479 | static int cmpc_bl_remove(struct acpi_device *acpi, int type) | 596 | static int cmpc_ipml_remove(struct acpi_device *acpi, int type) |
480 | { | 597 | { |
481 | struct backlight_device *bd; | 598 | struct ipml200_dev *ipml; |
599 | |||
600 | ipml = dev_get_drvdata(&acpi->dev); | ||
601 | |||
602 | backlight_device_unregister(ipml->bd); | ||
603 | |||
604 | if (ipml->rf) { | ||
605 | rfkill_unregister(ipml->rf); | ||
606 | rfkill_destroy(ipml->rf); | ||
607 | } | ||
608 | |||
609 | kfree(ipml); | ||
482 | 610 | ||
483 | bd = dev_get_drvdata(&acpi->dev); | ||
484 | backlight_device_unregister(bd); | ||
485 | return 0; | 611 | return 0; |
486 | } | 612 | } |
487 | 613 | ||
488 | static const struct acpi_device_id cmpc_bl_device_ids[] = { | 614 | static const struct acpi_device_id cmpc_ipml_device_ids[] = { |
489 | {CMPC_BL_HID, 0}, | 615 | {CMPC_IPML_HID, 0}, |
490 | {"", 0} | 616 | {"", 0} |
491 | }; | 617 | }; |
492 | 618 | ||
493 | static struct acpi_driver cmpc_bl_acpi_driver = { | 619 | static struct acpi_driver cmpc_ipml_acpi_driver = { |
494 | .owner = THIS_MODULE, | 620 | .owner = THIS_MODULE, |
495 | .name = "cmpc", | 621 | .name = "cmpc", |
496 | .class = "cmpc", | 622 | .class = "cmpc", |
497 | .ids = cmpc_bl_device_ids, | 623 | .ids = cmpc_ipml_device_ids, |
498 | .ops = { | 624 | .ops = { |
499 | .add = cmpc_bl_add, | 625 | .add = cmpc_ipml_add, |
500 | .remove = cmpc_bl_remove | 626 | .remove = cmpc_ipml_remove |
501 | } | 627 | } |
502 | }; | 628 | }; |
503 | 629 | ||
@@ -580,7 +706,7 @@ static int cmpc_init(void) | |||
580 | if (r) | 706 | if (r) |
581 | goto failed_keys; | 707 | goto failed_keys; |
582 | 708 | ||
583 | r = acpi_bus_register_driver(&cmpc_bl_acpi_driver); | 709 | r = acpi_bus_register_driver(&cmpc_ipml_acpi_driver); |
584 | if (r) | 710 | if (r) |
585 | goto failed_bl; | 711 | goto failed_bl; |
586 | 712 | ||
@@ -598,7 +724,7 @@ failed_accel: | |||
598 | acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); | 724 | acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); |
599 | 725 | ||
600 | failed_tablet: | 726 | failed_tablet: |
601 | acpi_bus_unregister_driver(&cmpc_bl_acpi_driver); | 727 | acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); |
602 | 728 | ||
603 | failed_bl: | 729 | failed_bl: |
604 | acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); | 730 | acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); |
@@ -611,7 +737,7 @@ static void cmpc_exit(void) | |||
611 | { | 737 | { |
612 | acpi_bus_unregister_driver(&cmpc_accel_acpi_driver); | 738 | acpi_bus_unregister_driver(&cmpc_accel_acpi_driver); |
613 | acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); | 739 | acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); |
614 | acpi_bus_unregister_driver(&cmpc_bl_acpi_driver); | 740 | acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); |
615 | acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); | 741 | acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); |
616 | } | 742 | } |
617 | 743 | ||
@@ -621,7 +747,7 @@ module_exit(cmpc_exit); | |||
621 | static const struct acpi_device_id cmpc_device_ids[] = { | 747 | static const struct acpi_device_id cmpc_device_ids[] = { |
622 | {CMPC_ACCEL_HID, 0}, | 748 | {CMPC_ACCEL_HID, 0}, |
623 | {CMPC_TABLET_HID, 0}, | 749 | {CMPC_TABLET_HID, 0}, |
624 | {CMPC_BL_HID, 0}, | 750 | {CMPC_IPML_HID, 0}, |
625 | {CMPC_KEYS_HID, 0}, | 751 | {CMPC_KEYS_HID, 0}, |
626 | {"", 0} | 752 | {"", 0} |
627 | }; | 753 | }; |