|
7 | 7 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc |
8 | 8 | * Copyright (c) 2006-2007 Jiri Kosina |
9 | 9 | * Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com> |
| 10 | + * Copyright (c) 2019 Paul Pawlowski <paul@mrarm.io> |
10 | 11 | */ |
11 | 12 |
|
12 | 13 | /* |
|
33 | 34 | /* BIT(7) reserved, was: APPLE_IGNORE_HIDINPUT */ |
34 | 35 | #define APPLE_NUMLOCK_EMULATION BIT(8) |
35 | 36 | #define APPLE_RDESC_BATTERY BIT(9) |
| 37 | +#define APPLE_BACKLIGHT_CTL BIT(10) |
36 | 38 |
|
37 | 39 | #define APPLE_FLAG_FKEY 0x01 |
38 | 40 |
|
@@ -61,13 +63,20 @@ MODULE_PARM_DESC(swap_fn_leftctrl, "Swap the Fn and left Control keys. " |
61 | 63 | "(For people who want to keep PC keyboard muscle memory. " |
62 | 64 | "[0] = as-is, Mac layout, 1 = swapped, PC layout)"); |
63 | 65 |
|
| 66 | +struct apple_sc_backlight { |
| 67 | + struct led_classdev cdev; |
| 68 | + struct hid_device *hdev; |
| 69 | + unsigned short backlight_off, backlight_on_min, backlight_on_max; |
| 70 | +}; |
| 71 | + |
64 | 72 | struct apple_sc { |
65 | 73 | struct hid_device *hdev; |
66 | 74 | unsigned long quirks; |
67 | 75 | unsigned int fn_on; |
68 | 76 | unsigned int fn_found; |
69 | 77 | DECLARE_BITMAP(pressed_numlock, KEY_CNT); |
70 | 78 | struct timer_list battery_timer; |
| 79 | + struct apple_sc_backlight *backlight; |
71 | 80 | }; |
72 | 81 |
|
73 | 82 | struct apple_key_translation { |
@@ -117,6 +126,20 @@ static const struct apple_key_translation magic_keyboard_2015_fn_keys[] = { |
117 | 126 | { } |
118 | 127 | }; |
119 | 128 |
|
| 129 | +struct apple_backlight_config_report { |
| 130 | + u8 report_id; |
| 131 | + u8 version; |
| 132 | + u16 backlight_off, backlight_on_min, backlight_on_max; |
| 133 | +}; |
| 134 | + |
| 135 | +struct apple_backlight_set_report { |
| 136 | + u8 report_id; |
| 137 | + u8 version; |
| 138 | + u16 backlight; |
| 139 | + u16 rate; |
| 140 | +}; |
| 141 | + |
| 142 | + |
120 | 143 | static const struct apple_key_translation apple2021_fn_keys[] = { |
121 | 144 | { KEY_BACKSPACE, KEY_DELETE }, |
122 | 145 | { KEY_ENTER, KEY_INSERT }, |
@@ -582,6 +605,105 @@ static int apple_input_configured(struct hid_device *hdev, |
582 | 605 | return 0; |
583 | 606 | } |
584 | 607 |
|
| 608 | +static bool apple_backlight_check_support(struct hid_device *hdev) |
| 609 | +{ |
| 610 | + int i; |
| 611 | + unsigned int hid; |
| 612 | + struct hid_report *report; |
| 613 | + |
| 614 | + list_for_each_entry(report, &hdev->report_enum[HID_INPUT_REPORT].report_list, list) { |
| 615 | + for (i = 0; i < report->maxfield; i++) { |
| 616 | + hid = report->field[i]->usage->hid; |
| 617 | + if ((hid & HID_USAGE_PAGE) == HID_UP_MSVENDOR && (hid & HID_USAGE) == 0xf) |
| 618 | + return true; |
| 619 | + } |
| 620 | + } |
| 621 | + |
| 622 | + return false; |
| 623 | +} |
| 624 | + |
| 625 | +static int apple_backlight_set(struct hid_device *hdev, u16 value, u16 rate) |
| 626 | +{ |
| 627 | + int ret = 0; |
| 628 | + struct apple_backlight_set_report *rep; |
| 629 | + |
| 630 | + rep = kmalloc(sizeof(*rep), GFP_KERNEL); |
| 631 | + if (rep == NULL) |
| 632 | + return -ENOMEM; |
| 633 | + |
| 634 | + rep->report_id = 0xB0; |
| 635 | + rep->version = 1; |
| 636 | + rep->backlight = value; |
| 637 | + rep->rate = rate; |
| 638 | + |
| 639 | + ret = hid_hw_raw_request(hdev, 0xB0u, (u8 *) rep, sizeof(*rep), |
| 640 | + HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); |
| 641 | + |
| 642 | + kfree(rep); |
| 643 | + return ret; |
| 644 | +} |
| 645 | + |
| 646 | +static int apple_backlight_led_set(struct led_classdev *led_cdev, |
| 647 | + enum led_brightness brightness) |
| 648 | +{ |
| 649 | + struct apple_sc_backlight *backlight = container_of(led_cdev, |
| 650 | + struct apple_sc_backlight, cdev); |
| 651 | + |
| 652 | + return apple_backlight_set(backlight->hdev, brightness, 0); |
| 653 | +} |
| 654 | + |
| 655 | +static int apple_backlight_init(struct hid_device *hdev) |
| 656 | +{ |
| 657 | + int ret; |
| 658 | + struct apple_sc *asc = hid_get_drvdata(hdev); |
| 659 | + struct apple_backlight_config_report *rep; |
| 660 | + |
| 661 | + if (!apple_backlight_check_support(hdev)) |
| 662 | + return -EINVAL; |
| 663 | + |
| 664 | + rep = kmalloc(0x200, GFP_KERNEL); |
| 665 | + if (rep == NULL) |
| 666 | + return -ENOMEM; |
| 667 | + |
| 668 | + ret = hid_hw_raw_request(hdev, 0xBFu, (u8 *) rep, sizeof(*rep), |
| 669 | + HID_FEATURE_REPORT, HID_REQ_GET_REPORT); |
| 670 | + if (ret < 0) { |
| 671 | + hid_err(hdev, "backlight request failed: %d\n", ret); |
| 672 | + goto cleanup_and_exit; |
| 673 | + } |
| 674 | + if (ret < 8 || rep->version != 1) { |
| 675 | + hid_err(hdev, "backlight config struct: bad version %i\n", rep->version); |
| 676 | + ret = -EINVAL; |
| 677 | + goto cleanup_and_exit; |
| 678 | + } |
| 679 | + |
| 680 | + hid_dbg(hdev, "backlight config: off=%u, on_min=%u, on_max=%u\n", |
| 681 | + rep->backlight_off, rep->backlight_on_min, rep->backlight_on_max); |
| 682 | + |
| 683 | + asc->backlight = devm_kzalloc(&hdev->dev, sizeof(*asc->backlight), GFP_KERNEL); |
| 684 | + if (!asc->backlight) { |
| 685 | + ret = -ENOMEM; |
| 686 | + goto cleanup_and_exit; |
| 687 | + } |
| 688 | + |
| 689 | + asc->backlight->hdev = hdev; |
| 690 | + asc->backlight->cdev.name = "apple::kbd_backlight"; |
| 691 | + asc->backlight->cdev.max_brightness = rep->backlight_on_max; |
| 692 | + asc->backlight->cdev.brightness_set_blocking = apple_backlight_led_set; |
| 693 | + |
| 694 | + ret = apple_backlight_set(hdev, 0, 0); |
| 695 | + if (ret < 0) { |
| 696 | + hid_err(hdev, "backlight set request failed: %d\n", ret); |
| 697 | + goto cleanup_and_exit; |
| 698 | + } |
| 699 | + |
| 700 | + ret = devm_led_classdev_register(&hdev->dev, &asc->backlight->cdev); |
| 701 | + |
| 702 | +cleanup_and_exit: |
| 703 | + kfree(rep); |
| 704 | + return ret; |
| 705 | +} |
| 706 | + |
585 | 707 | static int apple_probe(struct hid_device *hdev, |
586 | 708 | const struct hid_device_id *id) |
587 | 709 | { |
@@ -617,6 +739,9 @@ static int apple_probe(struct hid_device *hdev, |
617 | 739 | jiffies + msecs_to_jiffies(APPLE_BATTERY_TIMEOUT_MS)); |
618 | 740 | apple_fetch_battery(hdev); |
619 | 741 |
|
| 742 | + if (quirks & APPLE_BACKLIGHT_CTL) |
| 743 | + apple_backlight_init(hdev); |
| 744 | + |
620 | 745 | return 0; |
621 | 746 | } |
622 | 747 |
|
|
0 commit comments