Re: [PATCH v3 09/11] platform/x86: asus-wmi: Control RGB keyboard backlight

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, Apr 19, 2019 at 1:14 PM Yurii Pavlovskyi
<yurii.pavlovskyi@xxxxxxxxx> wrote:
>
> The WMI exposes two methods for controlling RGB keyboard backlight, which
> allows controlling:
> * RGB components in range 00 - ff,
> * Switch between 4 effects,
> * Switch between 3 effect speed modes,
> * Separately enable the backlight on boot, in the awake state (after driver
>   load), in sleep mode, and probably in something called shutdown mode (no
>   observable effects of enabling it are known so far).
>
> The configuration should be written to several sysfs parameter buffers
> which are then written via WMI by writing either 1 or 2 to the "kbbl_set"
> parameter. When reading the buffers the last written value is returned.
>
> If the 2 is written to "kbbl_set", the parameters will be reset on reboot
> (temporary mode), 1 is permanent mode, parameters are retained.
>
> The calls use new 3-dword input buffer method call.
>
> The functionality is only enabled if corresponding DSTS methods return
> exact valid values.
>
> The following script demonstrates usage:
>
> echo Red [00 - ff]
> echo 33 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_red
> echo Green [00 - ff]
> echo ff > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_green
> echo Blue [00 - ff]
> echo 0 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_blue
> echo Mode: 0 - static color, 1 - breathing, 2 - color cycle, 3 - strobing
> echo 0 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_mode
> echo Speed for modes 1 and 2: 0 - slow, 1 - medium, 2 - fast
> echo 0 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_speed
> echo Enable: 02 - on boot, before module load, 08 - awake, 20 - sleep,
> echo 2a or ff to set all
> echo 2a > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_flags
> echo Save: 1 - permanently, 2 - temporarily, reset after reboot
> echo 1 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_set
>

Shouldn't be the LED subsystem driver for this?

> Signed-off-by: Yurii Pavlovskyi <yurii.pavlovskyi@xxxxxxxxx>
> ---
>  .../ABI/testing/sysfs-platform-asus-wmi       |  61 ++++
>  drivers/platform/x86/asus-wmi.c               | 331 ++++++++++++++++++
>  include/linux/platform_data/x86/asus-wmi.h    |   2 +
>  3 files changed, 394 insertions(+)
>
> diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi
> index 019e1e29370e..1cc54d5e3e10 100644
> --- a/Documentation/ABI/testing/sysfs-platform-asus-wmi
> +++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi
> @@ -36,3 +36,64 @@ KernelVersion:       3.5
>  Contact:       "AceLan Kao" <acelan.kao@xxxxxxxxxxxxx>
>  Description:
>                 Resume on lid open. 1 means on, 0 means off.
> +
> +What:          /sys/devices/platform/<platform>/kbbl/kbbl_red
> +Date:          Apr 2019
> +KernelVersion: 5.1
> +Contact:       "Yurii Pavlovskyi" <yurii.pavlovskyi@xxxxxxxxx>
> +Description:
> +               RGB keyboard backlight red component: 00 .. ff.
> +
> +What:          /sys/devices/platform/<platform>/kbbl/kbbl_green
> +Date:          Apr 2019
> +KernelVersion: 5.1
> +Contact:       "Yurii Pavlovskyi" <yurii.pavlovskyi@xxxxxxxxx>
> +Description:
> +               RGB keyboard backlight green component: 00 .. ff.
> +
> +What:          /sys/devices/platform/<platform>/kbbl/kbbl_blue
> +Date:          Apr 2019
> +KernelVersion: 5.1
> +Contact:       "Yurii Pavlovskyi" <yurii.pavlovskyi@xxxxxxxxx>
> +Description:
> +               RGB keyboard backlight blue component: 00 .. ff.
> +
> +What:          /sys/devices/platform/<platform>/kbbl/kbbl_mode
> +Date:          Apr 2019
> +KernelVersion: 5.1
> +Contact:       "Yurii Pavlovskyi" <yurii.pavlovskyi@xxxxxxxxx>
> +Description:
> +               RGB keyboard backlight mode:
> +                       * 0 - static color,
> +                       * 1 - breathing,
> +                       * 2 - color cycle,
> +                       * 3 - strobing.
> +
> +What:          /sys/devices/platform/<platform>/kbbl/kbbl_speed
> +Date:          Apr 2019
> +KernelVersion: 5.1
> +Contact:       "Yurii Pavlovskyi" <yurii.pavlovskyi@xxxxxxxxx>
> +Description:
> +               RGB keyboard backlight speed for modes 1 and 2:
> +                       * 0 - slow,
> +                       * 1 - medium,
> +                       * 2 - fast.
> +
> +What:          /sys/devices/platform/<platform>/kbbl/kbbl_flags
> +Date:          Apr 2019
> +KernelVersion: 5.1
> +Contact:       "Yurii Pavlovskyi" <yurii.pavlovskyi@xxxxxxxxx>
> +Description:
> +               RGB keyboard backlight enable flags (2a to enable everything), OR of:
> +                       * 02 - on boot (until module load),
> +                       * 08 - awake,
> +                       * 20 - sleep.
> +
> +What:          /sys/devices/platform/<platform>/kbbl/kbbl_set
> +Date:          Apr 2019
> +KernelVersion: 5.1
> +Contact:       "Yurii Pavlovskyi" <yurii.pavlovskyi@xxxxxxxxx>
> +Description:
> +               Write changed RGB keyboard backlight parameters:
> +                       * 1 - permanently,
> +                       * 2 - temporarily.
> diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
> index 1b8272374660..0a32079336d8 100644
> --- a/drivers/platform/x86/asus-wmi.c
> +++ b/drivers/platform/x86/asus-wmi.c
> @@ -148,6 +148,21 @@ struct asus_rfkill {
>         u32 dev_id;
>  };
>
> +struct asus_kbbl_rgb {
> +       u8 kbbl_red;
> +       u8 kbbl_green;
> +       u8 kbbl_blue;
> +       u8 kbbl_mode;
> +       u8 kbbl_speed;
> +
> +       u8 kbbl_set_red;
> +       u8 kbbl_set_green;
> +       u8 kbbl_set_blue;
> +       u8 kbbl_set_mode;
> +       u8 kbbl_set_speed;
> +       u8 kbbl_set_flags;
> +};
> +
>  struct asus_wmi {
>         int dsts_id;
>         int spec;
> @@ -183,6 +198,9 @@ struct asus_wmi {
>         int asus_hwmon_num_fans;
>         int asus_hwmon_pwm;
>
> +       bool kbbl_rgb_available;
> +       struct asus_kbbl_rgb kbbl_rgb;
> +
>         struct hotplug_slot hotplug_slot;
>         struct mutex hotplug_lock;
>         struct mutex wmi_lock;
> @@ -658,6 +676,312 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
>         return rv;
>  }
>
> +/* RGB keyboard backlight *****************************************************/
> +
> +static ssize_t show_u8(u8 value, char *buf)
> +{
> +       return scnprintf(buf, PAGE_SIZE, "%02x\n", value);
> +}
> +
> +static ssize_t store_u8(u8 *value, const char *buf, int count)
> +{
> +       int err;
> +       u8 result;
> +
> +       err = kstrtou8(buf, 16, &result);
> +       if (err < 0) {
> +               pr_warn("Trying to store invalid value\n");
> +               return err;
> +       }
> +
> +       *value = result;
> +
> +       return count;
> +}
> +
> +static ssize_t kbbl_red_show(struct device *dev, struct device_attribute *attr,
> +               char *buf)
> +{
> +       struct asus_wmi *asus = dev_get_drvdata(dev);
> +
> +       return show_u8(asus->kbbl_rgb.kbbl_red, buf);
> +}
> +
> +static ssize_t kbbl_red_store(struct device *dev, struct device_attribute *attr,
> +               const char *buf, size_t count)
> +{
> +       struct asus_wmi *asus = dev_get_drvdata(dev);
> +
> +       return store_u8(&asus->kbbl_rgb.kbbl_set_red, buf, count);
> +}
> +
> +static ssize_t kbbl_green_show(struct device *dev,
> +               struct device_attribute *attr, char *buf)
> +{
> +       struct asus_wmi *asus = dev_get_drvdata(dev);
> +
> +       return show_u8(asus->kbbl_rgb.kbbl_green, buf);
> +}
> +
> +static ssize_t kbbl_green_store(struct device *dev,
> +               struct device_attribute *attr, const char *buf, size_t count)
> +{
> +       struct asus_wmi *asus = dev_get_drvdata(dev);
> +
> +       return store_u8(&asus->kbbl_rgb.kbbl_set_green, buf, count);
> +}
> +
> +static ssize_t kbbl_blue_show(struct device *dev, struct device_attribute *attr,
> +               char *buf)
> +{
> +       struct asus_wmi *asus = dev_get_drvdata(dev);
> +
> +       return show_u8(asus->kbbl_rgb.kbbl_blue, buf);
> +}
> +
> +static ssize_t kbbl_blue_store(struct device *dev,
> +               struct device_attribute *attr, const char *buf, size_t count)
> +{
> +       struct asus_wmi *asus = dev_get_drvdata(dev);
> +
> +       return store_u8(&asus->kbbl_rgb.kbbl_set_blue, buf, count);
> +}
> +
> +static ssize_t kbbl_mode_show(struct device *dev, struct device_attribute *attr,
> +               char *buf)
> +{
> +       struct asus_wmi *asus = dev_get_drvdata(dev);
> +
> +       return show_u8(asus->kbbl_rgb.kbbl_mode, buf);
> +}
> +
> +static ssize_t kbbl_mode_store(struct device *dev,
> +               struct device_attribute *attr, const char *buf, size_t count)
> +{
> +       struct asus_wmi *asus = dev_get_drvdata(dev);
> +
> +       return store_u8(&asus->kbbl_rgb.kbbl_set_mode, buf, count);
> +}
> +
> +static ssize_t kbbl_speed_show(struct device *dev,
> +               struct device_attribute *attr, char *buf)
> +{
> +       struct asus_wmi *asus = dev_get_drvdata(dev);
> +
> +       return show_u8(asus->kbbl_rgb.kbbl_speed, buf);
> +}
> +
> +static ssize_t kbbl_speed_store(struct device *dev,
> +               struct device_attribute *attr, const char *buf, size_t count)
> +{
> +       struct asus_wmi *asus = dev_get_drvdata(dev);
> +
> +       return store_u8(&asus->kbbl_rgb.kbbl_set_speed, buf, count);
> +}
> +
> +static ssize_t kbbl_flags_show(struct device *dev,
> +               struct device_attribute *attr, char *buf)
> +{
> +       struct asus_wmi *asus = dev_get_drvdata(dev);
> +
> +       return show_u8(asus->kbbl_rgb.kbbl_set_flags, buf);
> +}
> +
> +static ssize_t kbbl_flags_store(struct device *dev,
> +               struct device_attribute *attr, const char *buf, size_t count)
> +{
> +       struct asus_wmi *asus = dev_get_drvdata(dev);
> +
> +       return store_u8(&asus->kbbl_rgb.kbbl_set_flags, buf, count);
> +}
> +
> +static ssize_t kbbl_set_show(struct device *dev,
> +               struct device_attribute *attr, char *buf)
> +{
> +       return scnprintf(buf, PAGE_SIZE,
> +                       "Write to configure RGB keyboard backlight\n");
> +}
> +
> +static int kbbl_rgb_write(struct asus_wmi *asus, int persistent)
> +{
> +       int err;
> +       u32 retval;
> +       u8 speed_byte;
> +       u8 mode_byte;
> +       u8 speed;
> +       u8 mode;
> +
> +       speed = asus->kbbl_rgb.kbbl_set_speed;
> +       switch (speed) {
> +       case 0:
> +       default:
> +               speed_byte = 0xe1; // slow
> +               speed = 0;
> +               break;
> +       case 1:
> +               speed_byte = 0xeb; // medium
> +               break;
> +       case 2:
> +               speed_byte = 0xf5; // fast
> +               break;
> +       }
> +
> +       mode = asus->kbbl_rgb.kbbl_set_mode;
> +       switch (mode) {
> +       case 0:
> +       default:
> +               mode_byte = 0x00; // static color
> +               mode = 0;
> +               break;
> +       case 1:
> +               mode_byte = 0x01; // breathing
> +               break;
> +       case 2:
> +               mode_byte = 0x02; // color cycle
> +               break;
> +       case 3:
> +               mode_byte = 0x0a; // strobing
> +               break;
> +       }
> +
> +       err = asus_wmi_evaluate_method_3dw(ASUS_WMI_METHODID_DEVS,
> +               ASUS_WMI_DEVID_KBD_RGB,
> +               (persistent ? 0xb4 : 0xb3) |
> +               (mode_byte << 8) |
> +               (asus->kbbl_rgb.kbbl_set_red << 16) |
> +               (asus->kbbl_rgb.kbbl_set_green << 24),
> +               (asus->kbbl_rgb.kbbl_set_blue) |
> +               (speed_byte << 8), &retval);
> +       if (err) {
> +               pr_warn("RGB keyboard device 1, write error: %d\n", err);
> +               return err;
> +       }
> +
> +       if (retval != 1) {
> +               pr_warn("RGB keyboard device 1, write error (retval): %x\n",
> +                               retval);
> +               return -EIO;
> +       }
> +
> +       err = asus_wmi_evaluate_method_3dw(ASUS_WMI_METHODID_DEVS,
> +               ASUS_WMI_DEVID_KBD_RGB2,
> +               (0xbd) |
> +               (asus->kbbl_rgb.kbbl_set_flags << 16) |
> +               (persistent ? 0x0100 : 0x0000), 0, &retval);
> +       if (err) {
> +               pr_warn("RGB keyboard device 2, write error: %d\n", err);
> +               return err;
> +       }
> +
> +       if (retval != 1) {
> +               pr_warn("RGB keyboard device 2, write error (retval): %x\n",
> +                               retval);
> +               return -EIO;
> +       }
> +
> +       asus->kbbl_rgb.kbbl_red = asus->kbbl_rgb.kbbl_set_red;
> +       asus->kbbl_rgb.kbbl_green = asus->kbbl_rgb.kbbl_set_green;
> +       asus->kbbl_rgb.kbbl_blue = asus->kbbl_rgb.kbbl_set_blue;
> +       asus->kbbl_rgb.kbbl_mode = mode;
> +       asus->kbbl_rgb.kbbl_speed = speed;
> +
> +       return 0;
> +}
> +
> +static ssize_t kbbl_set_store(struct device *dev,
> +               struct device_attribute *attr, const char *buf, size_t count)
> +{
> +       u8 value;
> +       struct asus_wmi *asus;
> +       int result;
> +
> +       asus = dev_get_drvdata(dev);
> +       result = store_u8(&value, buf, count);
> +       if (result < 0)
> +               return result;
> +
> +       if (value == 1)
> +               kbbl_rgb_write(asus, 1);
> +       else if (value == 2)
> +               kbbl_rgb_write(asus, 0);
> +
> +       return count;
> +}
> +
> +/* RGB values: 00 .. ff */
> +static DEVICE_ATTR_RW(kbbl_red);
> +static DEVICE_ATTR_RW(kbbl_green);
> +static DEVICE_ATTR_RW(kbbl_blue);
> +
> +/*
> + * Color modes: 0 - static color, 1 - breathing, 2 - color cycle, 3 - strobing
> + */
> +static DEVICE_ATTR_RW(kbbl_mode);
> +
> +/* Speed for modes 1 and 2: 0 - slow, 1 - medium, 2 - fast */
> +static DEVICE_ATTR_RW(kbbl_speed);
> +
> +/*
> + * Enable: 02 - on boot (until module load) | 08 - awake | 20 - sleep
> + * (2a or ff to enable everything)
> + *
> + * Logically 80 would be shutdown, but no visible effects of this option
> + * were observed so far
> + */
> +static DEVICE_ATTR_RW(kbbl_flags);
> +
> +/* Write data: 1 - permanently, 2 - temporarily (reset after reboot) */
> +static DEVICE_ATTR_RW(kbbl_set);
> +
> +static struct attribute *rgbkb_sysfs_attributes[] = {
> +       &dev_attr_kbbl_red.attr,
> +       &dev_attr_kbbl_green.attr,
> +       &dev_attr_kbbl_blue.attr,
> +       &dev_attr_kbbl_mode.attr,
> +       &dev_attr_kbbl_speed.attr,
> +       &dev_attr_kbbl_flags.attr,
> +       &dev_attr_kbbl_set.attr,
> +       NULL,
> +};
> +
> +static const struct attribute_group kbbl_attribute_group = {
> +       .name = "kbbl",
> +       .attrs = rgbkb_sysfs_attributes
> +};
> +
> +static int kbbl_rgb_init(struct asus_wmi *asus)
> +{
> +       int err;
> +
> +       err = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_KBD_RGB);
> +       if (err) {
> +               if (err == -ENODEV)
> +                       return 0;
> +               else
> +                       return err;
> +       }
> +
> +       err = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_KBD_RGB2);
> +       if (err) {
> +               if (err == -ENODEV)
> +                       return 0;
> +               else
> +                       return err;
> +       }
> +
> +       asus->kbbl_rgb_available = true;
> +       return sysfs_create_group(&asus->platform_device->dev.kobj,
> +                       &kbbl_attribute_group);
> +}
> +
> +static void kbbl_rgb_exit(struct asus_wmi *asus)
> +{
> +       if (asus->kbbl_rgb_available) {
> +               sysfs_remove_group(&asus->platform_device->dev.kobj,
> +                               &kbbl_attribute_group);
> +       }
> +}
> +
>  /* RF *************************************************************************/
>
>  /*
> @@ -2230,6 +2554,10 @@ static int asus_wmi_add(struct platform_device *pdev)
>         if (err)
>                 goto fail_leds;
>
> +       err = kbbl_rgb_init(asus);
> +       if (err)
> +               goto fail_rgbkb;
> +
>         asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WLAN, &result);
>         if (result & (ASUS_WMI_DSTS_PRESENCE_BIT | ASUS_WMI_DSTS_USER_BIT))
>                 asus->driver->wlan_ctrl_by_user = 1;
> @@ -2287,6 +2615,8 @@ static int asus_wmi_add(struct platform_device *pdev)
>  fail_backlight:
>         asus_wmi_rfkill_exit(asus);
>  fail_rfkill:
> +       kbbl_rgb_exit(asus);
> +fail_rgbkb:
>         asus_wmi_led_exit(asus);
>  fail_leds:
>  fail_hwmon:
> @@ -2307,6 +2637,7 @@ static int asus_wmi_remove(struct platform_device *device)
>         asus_wmi_backlight_exit(asus);
>         asus_wmi_input_exit(asus);
>         asus_wmi_led_exit(asus);
> +       kbbl_rgb_exit(asus);
>         asus_wmi_rfkill_exit(asus);
>         asus_wmi_debugfs_exit(asus);
>         asus_wmi_platform_exit(asus);
> diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
> index a5fe7e68944b..c8c6e939e196 100644
> --- a/include/linux/platform_data/x86/asus-wmi.h
> +++ b/include/linux/platform_data/x86/asus-wmi.h
> @@ -57,6 +57,8 @@
>  #define ASUS_WMI_DEVID_KBD_BACKLIGHT   0x00050021
>  #define ASUS_WMI_DEVID_LIGHT_SENSOR    0x00050022 /* ?? */
>  #define ASUS_WMI_DEVID_LIGHTBAR                0x00050025
> +#define ASUS_WMI_DEVID_KBD_RGB         0x00100056
> +#define ASUS_WMI_DEVID_KBD_RGB2                0x00100057
>
>  /* Misc */
>  #define ASUS_WMI_DEVID_CAMERA          0x00060013
> --
> 2.17.1
>


-- 
With Best Regards,
Andy Shevchenko



[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux