diff --git a/led.c b/ap_led.c similarity index 56% rename from led.c rename to ap_led.c index 1caccab..848635a 100644 --- a/led.c +++ b/ap_led.c @@ -1,6 +1,6 @@ /* * led.c - * + * * Copyright (C) 2017 Jeremy Soller * Copyright (C) 2014-2016 Arnoud Willemsen * Copyright (C) 2013-2015 TUXEDO Computers GmbH @@ -19,18 +19,18 @@ * along with this program. If not, see . */ -static struct workqueue_struct *led_workqueue; +static struct workqueue_struct *ap_led_workqueue; -static struct _led_work { +static struct _ap_led_work { struct work_struct work; int wk; -} led_work; +} ap_led_work; -static void airplane_led_update(struct work_struct *work) { +static void ap_led_update(struct work_struct *work) { u8 byte; - struct _led_work *w; + struct _ap_led_work *w; - w = container_of(work, struct _led_work, work); + w = container_of(work, struct _ap_led_work, work); ec_read(0xD9, &byte); @@ -39,7 +39,7 @@ static void airplane_led_update(struct work_struct *work) { /* wmbb 0x6C 1 (?) */ } -static enum led_brightness airplane_led_get(struct led_classdev *led_cdev) { +static enum led_brightness ap_led_get(struct led_classdev *led_cdev) { u8 byte; ec_read(0xD9, &byte); @@ -48,30 +48,30 @@ static enum led_brightness airplane_led_get(struct led_classdev *led_cdev) { } /* must not sleep */ -static void airplane_led_set(struct led_classdev *led_cdev, enum led_brightness value) { - led_work.wk = value; - queue_work(led_workqueue, &led_work.work); +static void ap_led_set(struct led_classdev *led_cdev, enum led_brightness value) { + ap_led_work.wk = value; + queue_work(ap_led_workqueue, &ap_led_work.work); } -static struct led_classdev airplane_led = { +static struct led_classdev ap_led = { .name = "system76::airplane", - .brightness_get = airplane_led_get, - .brightness_set = airplane_led_set, + .brightness_get = ap_led_get, + .brightness_set = ap_led_set, .max_brightness = 1, .default_trigger = "rfkill-any" }; -static int __init s76_led_init(void) { +static int __init ap_led_init(struct device *dev) { int err; - led_workqueue = create_singlethread_workqueue("led_workqueue"); - if (unlikely(!led_workqueue)) { + ap_led_workqueue = create_singlethread_workqueue("ap_led_workqueue"); + if (unlikely(!ap_led_workqueue)) { return -ENOMEM; } - INIT_WORK(&led_work.work, airplane_led_update); + INIT_WORK(&ap_led_work.work, ap_led_update); - err = led_classdev_register(&s76_platform_device->dev, &airplane_led); + err = led_classdev_register(dev, &ap_led); if (unlikely(err)) { goto err_destroy_workqueue; } @@ -79,15 +79,15 @@ static int __init s76_led_init(void) { return 0; err_destroy_workqueue: - destroy_workqueue(led_workqueue); - led_workqueue = NULL; + destroy_workqueue(ap_led_workqueue); + ap_led_workqueue = NULL; return err; } -static void __exit s76_led_exit(void) { - if (!IS_ERR_OR_NULL(airplane_led.dev)) - led_classdev_unregister(&airplane_led); - if (led_workqueue) - destroy_workqueue(led_workqueue); -} \ No newline at end of file +static void __exit ap_led_exit(void) { + if (!IS_ERR_OR_NULL(ap_led.dev)) + led_classdev_unregister(&ap_led); + if (ap_led_workqueue) + destroy_workqueue(ap_led_workqueue); +} diff --git a/input.c b/input.c index d195bb1..6797b1a 100644 --- a/input.c +++ b/input.c @@ -138,7 +138,7 @@ static void s76_input_close(struct input_dev *dev) { s76_input_polling_task = NULL; } -static int __init s76_input_init(void) { +static int __init s76_input_init(struct device *dev) { int err; u8 byte; @@ -151,7 +151,7 @@ static int __init s76_input_init(void) { s76_input_device->name = "System76 Airplane-Mode Hotkey"; s76_input_device->phys = "system76/input0"; s76_input_device->id.bustype = BUS_HOST; - s76_input_device->dev.parent = &s76_platform_device->dev; + s76_input_device->dev.parent = dev; set_bit(EV_KEY, s76_input_device->evbit); set_bit(AIRPLANE_KEY, s76_input_device->keybit); set_bit(KEY_TOUCHPAD_ON, s76_input_device->keybit); diff --git a/kb.c b/kb.c index 14a9d0e..0b0d535 100644 --- a/kb.c +++ b/kb.c @@ -289,6 +289,8 @@ static void kb_full_color__set_brightness(unsigned i) { u8 lvl_to_raw[] = { 63, 126, 189, 252 }; + led_classdev_notify_brightness_hw_changed(&kb_led, i); + i = clamp_t(unsigned, i, 0, ARRAY_SIZE(lvl_to_raw) - 1); if (!s76_wmbb(SET_KB_LED, @@ -334,9 +336,11 @@ static void kb_full_color__set_state(enum kb_state state) switch (state) { case KB_STATE_OFF: + led_classdev_notify_brightness_hw_changed(&kb_led, 0); cmd |= 0x003001; break; case KB_STATE_ON: + led_classdev_notify_brightness_hw_changed(&kb_led, kb_backlight.brightness); cmd |= 0x07F001; break; default: diff --git a/kb_led.c b/kb_led.c new file mode 100644 index 0000000..672ef12 --- /dev/null +++ b/kb_led.c @@ -0,0 +1,95 @@ +/* + * led.c + * + * Copyright (C) 2017 Jeremy Soller + * Copyright (C) 2014-2016 Arnoud Willemsen + * Copyright (C) 2013-2015 TUXEDO Computers GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +static struct workqueue_struct *kb_led_workqueue; + +static struct _kb_led_work { + struct work_struct work; + int wk; +} kb_led_work; + +static void kb_led_update(struct work_struct *work) { + u8 byte; + struct _kb_led_work *w; + + w = container_of(work, struct _kb_led_work, work); + // + // ec_read(0xD9, &byte); + // + // ec_write(0xD9, w->wk ? byte & ~0x40 : byte | 0x40); + // + // /* wmbb 0x6C 1 (?) */ +} + +static enum led_brightness kb_led_get(struct led_classdev *led_cdev) { + // u8 byte; + // + // ec_read(0xD9, &byte); + // + // return byte & 0x40 ? LED_OFF : LED_FULL; + + return LED_OFF; +} + +/* must not sleep */ +static void kb_led_set(struct led_classdev *led_cdev, enum led_brightness value) { + kb_led_work.wk = value; + queue_work(kb_led_workqueue, &kb_led_work.work); +} + +static struct led_classdev kb_led = { + .name = "system76::kbd_backlight", + .flags = LED_BRIGHT_HW_CHANGED, + .brightness_get = kb_led_get, + .brightness_set = kb_led_set, + .max_brightness = 3, +}; + +static int __init kb_led_init(struct device *dev) { + int err; + + kb_led_workqueue = create_singlethread_workqueue("kb_led_workqueue"); + if (unlikely(!kb_led_workqueue)) { + return -ENOMEM; + } + + INIT_WORK(&kb_led_work.work, kb_led_update); + + err = led_classdev_register(dev, &kb_led); + if (unlikely(err)) { + goto err_destroy_workqueue; + } + + return 0; + +err_destroy_workqueue: + destroy_workqueue(kb_led_workqueue); + kb_led_workqueue = NULL; + + return err; +} + +static void __exit kb_led_exit(void) { + if (!IS_ERR_OR_NULL(kb_led.dev)) + led_classdev_unregister(&kb_led); + if (kb_led_workqueue) + destroy_workqueue(kb_led_workqueue); +} diff --git a/system76.c b/system76.c index 0f8bb1f..f6e577f 100644 --- a/system76.c +++ b/system76.c @@ -91,8 +91,9 @@ static int s76_wmbb(u32 method_id, u32 arg, u32 *retval) { } #include "ec.c" -#include "led.c" +#include "ap_led.c" #include "input.c" +#include "kb_led.c" #include "kb.c" #include "hwmon.c" @@ -177,11 +178,55 @@ static void s76_wmi_notify(u32 value, void *context) { } static int s76_probe(struct platform_device *dev) { - int status; + int err; - status = wmi_install_notify_handler(S76_EVENT_GUID, s76_wmi_notify, NULL); - if (unlikely(ACPI_FAILURE(status))) { - S76_ERROR("Could not register WMI notify handler (%0#6x)\n", status); + err = ec_init(); + if (unlikely(err)) { + S76_ERROR("Could not register EC device\n"); + } + + err = ap_led_init(&dev->dev); + if (unlikely(err)) { + S76_ERROR("Could not register LED device\n"); + } + + err = kb_led_init(&dev->dev); + if (unlikely(err)) { + S76_ERROR("Could not register LED device\n"); + } + + err = s76_input_init(&dev->dev); + if (unlikely(err)) { + S76_ERROR("Could not register input device\n"); + } + + if (device_create_file(&dev->dev, &dev_attr_kb_brightness) != 0) { + S76_ERROR("Sysfs attribute creation failed for brightness\n"); + } + + if (device_create_file(&dev->dev, &dev_attr_kb_state) != 0) { + S76_ERROR("Sysfs attribute creation failed for state\n"); + } + + if (device_create_file(&dev->dev, &dev_attr_kb_mode) != 0) { + S76_ERROR("Sysfs attribute creation failed for mode\n"); + } + + if (device_create_file(&dev->dev, &dev_attr_kb_color) != 0) { + S76_ERROR("Sysfs attribute creation failed for color\n"); + } + + if (kb_backlight.ops) { + kb_backlight.ops->init(); + } + +#ifdef S76_HAS_HWMON + s76_hwmon_init(&dev->dev); +#endif + + err = wmi_install_notify_handler(S76_EVENT_GUID, s76_wmi_notify, NULL); + if (unlikely(ACPI_FAILURE(err))) { + S76_ERROR("Could not register WMI notify handler (%0#6x)\n", err); return -EIO; } @@ -193,16 +238,27 @@ static int s76_probe(struct platform_device *dev) { //i8042_command(NULL, 0x97); //i8042_unlock_chip(); - if (kb_backlight.ops) { - kb_backlight.ops->init(); - } - return 0; } static int s76_remove(struct platform_device *dev) { wmi_remove_notify_handler(S76_EVENT_GUID); + #ifdef S76_HAS_HWMON + s76_hwmon_fini(&dev->dev); + #endif + + device_remove_file(&dev->dev, &dev_attr_kb_color); + device_remove_file(&dev->dev, &dev_attr_kb_mode); + device_remove_file(&dev->dev, &dev_attr_kb_state); + device_remove_file(&dev->dev, &dev_attr_kb_brightness); + + s76_input_exit(); + kb_led_exit(); + ap_led_exit(); + + ec_exit(); + return 0; } @@ -280,59 +336,10 @@ static int __init s76_init(void) { return PTR_ERR(s76_platform_device); } - err = s76_input_init(); - if (unlikely(err)) { - S76_ERROR("Could not register input device\n"); - } - - err = s76_led_init(); - if (unlikely(err)) { - S76_ERROR("Could not register LED device\n"); - } - - if (device_create_file(&s76_platform_device->dev, &dev_attr_kb_brightness) != 0) { - S76_ERROR("Sysfs attribute creation failed for brightness\n"); - } - - if (device_create_file(&s76_platform_device->dev, &dev_attr_kb_state) != 0) { - S76_ERROR("Sysfs attribute creation failed for state\n"); - } - - if (device_create_file(&s76_platform_device->dev, &dev_attr_kb_mode) != 0) { - S76_ERROR("Sysfs attribute creation failed for mode\n"); - } - - if (device_create_file(&s76_platform_device->dev, &dev_attr_kb_color) != 0) { - S76_ERROR("Sysfs attribute creation failed for color\n"); - } - -#ifdef S76_HAS_HWMON - s76_hwmon_init(&s76_platform_device->dev); -#endif - - err = ec_init(); - if (unlikely(err)) { - S76_ERROR("Could not register EC device\n"); - } - return 0; } static void __exit s76_exit(void) { - ec_exit(); - - #ifdef S76_HAS_HWMON - s76_hwmon_fini(&s76_platform_device->dev); - #endif - - device_remove_file(&s76_platform_device->dev, &dev_attr_kb_color); - device_remove_file(&s76_platform_device->dev, &dev_attr_kb_mode); - device_remove_file(&s76_platform_device->dev, &dev_attr_kb_state); - device_remove_file(&s76_platform_device->dev, &dev_attr_kb_brightness); - - s76_led_exit(); - s76_input_exit(); - platform_device_unregister(s76_platform_device); platform_driver_unregister(&s76_platform_driver); }