* [PATCH 0/1] Add new driver for AlienFX control
@ 2014-02-05 16:22 Mario Limonciello
2014-02-05 16:22 ` [PATCH 1/1] Add WMI driver for controlling AlienFX on Alienware Mario Limonciello
2014-02-05 16:24 ` [PATCH 0/1] Add new driver for AlienFX control Matthew Garrett
0 siblings, 2 replies; 5+ messages in thread
From: Mario Limonciello @ 2014-02-05 16:22 UTC (permalink / raw)
To: matthew.garrett; +Cc: platform-driver-x86, Mario Limonciello
Alienware is known for being able to control the lighting zones around the
machine independently. Previously this was done via an MCU.
The Alienware X51 R2 (as well as a few others) no longer uses an MCU to control
it's different lighting zones. It does however have a WMI interface.
At a first glance it seems reasonable to set up each LED for each zone
individually using the kernel's LED infrastructure.
After digging further in, there actually is control for setting up the LEDs to
perform different tasks in S0 and S3 power states which becomes more difficult
to represent in the LED class. So instead I've created a series of sysfs nodes
to represent the different colors in the different zones.
Normally the system will be in the RUNNING lighting control state and
immediately change based on the values (0-255) that you place in these sysfs nodes.
If you change the lighting control state to SUSPEND or BOOTING then modifying
the nodes will set up the lights for those alternate states.
Later platforms will be adding support for an HDMI mux. I've included some
skeleton code here for this, but will add more as they get closer to launching.
Mario Limonciello (1):
Add WMI driver for controlling AlienFX on Alienware
drivers/platform/x86/Kconfig | 13 ++
drivers/platform/x86/Makefile | 1 +
drivers/platform/x86/alienware-wmi.c | 345 ++++++++++++++++++++++++++++++++++
3 files changed, 359 insertions(+)
create mode 100644 drivers/platform/x86/alienware-wmi.c
--
1.7.9.5
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/1] Add WMI driver for controlling AlienFX on Alienware
2014-02-05 16:22 [PATCH 0/1] Add new driver for AlienFX control Mario Limonciello
@ 2014-02-05 16:22 ` Mario Limonciello
2014-02-05 16:24 ` [PATCH 0/1] Add new driver for AlienFX control Matthew Garrett
1 sibling, 0 replies; 5+ messages in thread
From: Mario Limonciello @ 2014-02-05 16:22 UTC (permalink / raw)
To: matthew.garrett; +Cc: platform-driver-x86, Mario Limonciello
Specifically for platforms that don't contain an AlienFX USB based MCU
such as the Alienware X51-R2.
Signed-off-by: Mario Limonciello <mario_limonciello@dell.com>
---
drivers/platform/x86/Kconfig | 13 ++
drivers/platform/x86/Makefile | 1 +
drivers/platform/x86/alienware-wmi.c | 345 ++++++++++++++++++++++++++++++++++
3 files changed, 359 insertions(+)
create mode 100644 drivers/platform/x86/alienware-wmi.c
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 5ae65c1..d030525 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -55,6 +55,19 @@ config ACERHDF
If you have an Acer Aspire One netbook, say Y or M
here.
+config ALIENWARE_WMI
+ tristate "Alienware Special feature control"
+ depends on ACPI
+ depends on ACPI_WMI
+ ---help---
+ This is a driver for controlling ALienware BIOS driven
+ features. It exposes an interface for controlling the AlienFX
+ zones on Alienware machines that don't contain a dedicated AlienFX
+ USB MCU such as the X51-R2.
+
+ If you have an ACPI-WMI compatible Alienware desktop, say Y or M
+ here.
+
config ASUS_LAPTOP
tristate "Asus Laptop Extras"
depends on ACPI
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 9b87cfc..0e9b157 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -56,3 +56,4 @@ obj-$(CONFIG_INTEL_SMARTCONNECT) += intel-smartconnect.o
obj-$(CONFIG_PVPANIC) += pvpanic.o
obj-$(CONFIG_INTEL_BAYTRAIL_MBI) += intel_baytrail.o
+obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o
diff --git a/drivers/platform/x86/alienware-wmi.c b/drivers/platform/x86/alienware-wmi.c
new file mode 100644
index 0000000..76087c9
--- /dev/null
+++ b/drivers/platform/x86/alienware-wmi.c
@@ -0,0 +1,345 @@
+/*
+ * Alienware AlienFX control
+ *
+ * Copyright (C) 2014 Dell Inc <mario_limonciello@dell.com>
+ *
+ * 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.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/dmi.h>
+#include <linux/acpi.h>
+
+MODULE_AUTHOR("Mario Limonciello <mario_limonciello@dell.com>");
+MODULE_DESCRIPTION("Alienware special feature control");
+MODULE_LICENSE("GPL");
+
+static struct platform_driver platform_driver = {
+ .driver = {
+ .name = "alienware-wmi",
+ .owner = THIS_MODULE,
+ }
+};
+
+static struct platform_device *platform_device;
+
+static const struct dmi_system_id alienware_device_table[] __initconst = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TBD"),
+ },
+ },
+ {}
+};
+
+MODULE_DEVICE_TABLE(dmi, alienware_device_table);
+
+#define RUNNING_CONTROL_GUID "A90597CE-A997-11DA-B012-B622A1EF5492"
+#define POWERSTATE_CONTROL_GUID "A80593CE-A997-11DA-B012-B622A1EF5492"
+#define HDMI_TOGGLE_GUID "TBD"
+#define HDMI_MUX_GUID "TBD"
+
+MODULE_ALIAS("wmi:" RUNNING_CONTROL_GUID);
+
+/*
+ Lighting Zone control groups
+*/
+
+#define ALIENWARE_HEAD_ZONE 1
+#define ALIENWARE_LEFT_ZONE 2
+#define ALIENWARE_RIGHT_ZONE 3
+
+enum LIGHTING_CONTROL_STATE {
+ RUNNING = 1,
+ BOOTING = 0,
+ SUSPEND = 3,
+};
+
+struct color_platform {
+ u8 blue;
+ u8 green;
+ u8 red;
+ u8 brightness;
+} __packed;
+
+struct platform_zone {
+ struct color_platform colors;
+ u8 location;
+};
+
+static struct platform_zone head = {
+ .location = ALIENWARE_HEAD_ZONE,
+};
+
+static struct platform_zone left = {
+ .location = ALIENWARE_LEFT_ZONE,
+};
+
+static struct platform_zone right = {
+ .location = ALIENWARE_RIGHT_ZONE,
+};
+
+static u8 lighting_control_state = RUNNING;
+
+static int update_leds(struct platform_zone zone, unsigned long count)
+{
+ acpi_status status;
+ char *guid;
+ struct platform_zone args = {
+ .colors = zone.colors,
+ .location = lighting_control_state
+ };
+ struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
+ if (lighting_control_state == BOOTING ||
+ lighting_control_state == SUSPEND)
+ guid = POWERSTATE_CONTROL_GUID;
+ else
+ guid = RUNNING_CONTROL_GUID;
+ pr_debug("alienware-wmi: evaluate [ guid %s | zone %d ]\n",
+ guid, zone.location);
+
+ status = wmi_evaluate_method(guid, 1, zone.location, &input, NULL);
+ if (ACPI_FAILURE(status))
+ pr_err("alienware-wmi: zone set failure: %u\n", status);
+
+ return count;
+}
+
+/*
+ All color values need to be in range from 0 - 255.
+*/
+static long sanitize_input(const char *buf, unsigned long count, int *val)
+{
+ if (!count)
+ return 0;
+ if (sscanf(buf, "%i", val) != 1)
+ return -EINVAL;
+ if (*val < 0)
+ *val = 0;
+ if (*val > 255)
+ *val = 255;
+ return count;
+}
+
+#define ALIENWARE_WMI_CREATE_DEVICE_ATTR(_zone, _color, _mode) \
+ static ssize_t show_##_zone##_color(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+ { \
+ return sprintf(buf, "%d\n", _zone.colors._color); \
+ } \
+ static ssize_t store_##_zone##_color(struct device *dev, \
+ struct device_attribute *attr, const char *buf, \
+ size_t count) \
+ { \
+ int val, rc; \
+ rc = sanitize_input(buf, count, &val); \
+ _zone.colors._color = val; \
+ return update_leds(_zone, rc); \
+ } \
+ static struct device_attribute dev_attr_##_zone##_##_color = { \
+ .attr = { \
+ .name = __stringify(_zone##_##_color), \
+ .mode = _mode }, \
+ .show = show_##_zone##_color, \
+ .store = store_##_zone##_color, \
+ }
+
+static ssize_t show_control_state(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", lighting_control_state);
+}
+
+static ssize_t set_control_state(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, ssize_t count)
+{
+ int val, rc;
+ rc = sanitize_input(buf, count, &val);
+ if (val != BOOTING && val != SUSPEND)
+ val = RUNNING;
+ lighting_control_state = val;
+ pr_debug("alienware-wmi: updated control state to %d\n", val);
+ return rc;
+}
+
+ALIENWARE_WMI_CREATE_DEVICE_ATTR(head, blue, 0644);
+ALIENWARE_WMI_CREATE_DEVICE_ATTR(head, green, 0644);
+ALIENWARE_WMI_CREATE_DEVICE_ATTR(head, red, 0644);
+ALIENWARE_WMI_CREATE_DEVICE_ATTR(head, brightness, 0644);
+ALIENWARE_WMI_CREATE_DEVICE_ATTR(left, blue, 0644);
+ALIENWARE_WMI_CREATE_DEVICE_ATTR(left, green, 0644);
+ALIENWARE_WMI_CREATE_DEVICE_ATTR(left, red, 0644);
+ALIENWARE_WMI_CREATE_DEVICE_ATTR(left, brightness, 0644);
+ALIENWARE_WMI_CREATE_DEVICE_ATTR(right, blue, 0644);
+ALIENWARE_WMI_CREATE_DEVICE_ATTR(right, green, 0644);
+ALIENWARE_WMI_CREATE_DEVICE_ATTR(right, red, 0644);
+ALIENWARE_WMI_CREATE_DEVICE_ATTR(right, brightness, 0644);
+
+static DEVICE_ATTR(lighting_control_state, S_IRUGO | S_IWUSR,
+ show_control_state, set_control_state);
+
+static struct attribute *zone_attributes[] = {
+ &dev_attr_head_blue.attr,
+ &dev_attr_head_red.attr,
+ &dev_attr_head_green.attr,
+ &dev_attr_head_brightness.attr,
+ &dev_attr_left_blue.attr,
+ &dev_attr_left_red.attr,
+ &dev_attr_left_green.attr,
+ &dev_attr_left_brightness.attr,
+ &dev_attr_right_blue.attr,
+ &dev_attr_right_red.attr,
+ &dev_attr_right_green.attr,
+ &dev_attr_right_brightness.attr,
+ &dev_attr_lighting_control_state.attr,
+ NULL
+};
+
+static struct attribute_group zone_attribute_group = {
+ .attrs = zone_attributes
+};
+
+static void remove_zones(struct platform_device *dev)
+{
+ sysfs_remove_group(&dev->dev.kobj, &zone_attribute_group);
+}
+
+static int prepare_zones(struct platform_device *dev)
+{
+ return sysfs_create_group(&dev->dev.kobj, &zone_attribute_group);
+}
+
+/*
+ HDMI toggle control (interface and platform still TBD)
+*/
+
+static ssize_t show_hdmi(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ acpi_status status;
+ status = wmi_evaluate_method(HDMI_MUX_GUID, 1, 3, NULL, NULL);
+ if (status == 1) {
+ sprintf(buf, "hdmi-i\n");
+ return 0;
+ } else if (status == 2) {
+ sprintf(buf, "gpu\n");
+ return 0;
+ }
+ sprintf(buf, "error reading mux\n");
+ pr_err("alienware-wmi: HDMI mux read failed: results: %u\n", status);
+ return status;
+}
+
+static ssize_t toggle_hdmi(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ acpi_status status;
+ status = wmi_evaluate_method(HDMI_TOGGLE_GUID, 1, 3, NULL, NULL);
+ if (ACPI_FAILURE(status))
+ pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
+ status);
+ return count;
+}
+
+static DEVICE_ATTR(hdmi, S_IRUGO | S_IWUSR, show_hdmi, toggle_hdmi);
+
+static void remove_hdmi(struct platform_device *device)
+{
+ device_remove_file(&device->dev, &dev_attr_hdmi);
+}
+
+static int create_hdmi(void)
+{
+ int ret = -ENOMEM;
+
+ ret = device_create_file(&platform_device->dev, &dev_attr_hdmi);
+ if (ret)
+ goto error_create_hdmi;
+ return 0;
+
+error_create_hdmi:
+ remove_hdmi(platform_device);
+ return ret;
+}
+
+static int __init alienware_wmi_init(void)
+{
+ int ret;
+
+ if (!wmi_has_guid(RUNNING_CONTROL_GUID)) {
+ pr_warn("No known WMI GUID found\n");
+ return -ENODEV;
+ }
+
+ ret = platform_driver_register(&platform_driver);
+ if (ret)
+ goto fail_platform_driver;
+ platform_device = platform_device_alloc("alienware-wmi", -1);
+ if (!platform_device) {
+ ret = -ENOMEM;
+ goto fail_platform_device1;
+ }
+ ret = platform_device_add(platform_device);
+ if (ret)
+ goto fail_platform_device2;
+
+ /*
+ No systems with HDMI-i yet, for later.
+ might make this a WMI check at that time.
+ */
+ if (dmi_check_system(alienware_device_table)) {
+ ret = create_hdmi();
+ if (ret)
+ goto fail_prep_hdmi;
+ }
+ /*
+ Only present on AW platforms w/o MCU.
+ */
+ if (wmi_has_guid(RUNNING_CONTROL_GUID)) {
+ ret = prepare_zones(platform_device);
+ if (ret)
+ goto fail_prep_zones;
+ }
+
+ return 0;
+
+fail_prep_zones:
+ remove_zones(platform_device);
+fail_prep_hdmi:
+ platform_device_del(platform_device);
+fail_platform_device2:
+ platform_device_put(platform_device);
+fail_platform_device1:
+ platform_driver_unregister(&platform_driver);
+fail_platform_driver:
+ return ret;
+}
+
+module_init(alienware_wmi_init);
+
+static void __exit alienware_wmi_exit(void)
+{
+ remove_zones(platform_device);
+ remove_hdmi(platform_device);
+ if (platform_device) {
+ platform_device_unregister(platform_device);
+ platform_driver_unregister(&platform_driver);
+ }
+}
+
+module_exit(alienware_wmi_exit);
--
1.7.9.5
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 0/1] Add new driver for AlienFX control
2014-02-05 16:22 [PATCH 0/1] Add new driver for AlienFX control Mario Limonciello
2014-02-05 16:22 ` [PATCH 1/1] Add WMI driver for controlling AlienFX on Alienware Mario Limonciello
@ 2014-02-05 16:24 ` Matthew Garrett
2014-02-05 16:34 ` Mario Limonciello
1 sibling, 1 reply; 5+ messages in thread
From: Matthew Garrett @ 2014-02-05 16:24 UTC (permalink / raw)
To: mario_limonciello@dell.com; +Cc: platform-driver-x86@vger.kernel.org
On Wed, 2014-02-05 at 10:22 -0600, Mario Limonciello wrote:
> After digging further in, there actually is control for setting up the LEDs to
> perform different tasks in S0 and S3 power states which becomes more difficult
> to represent in the LED class. So instead I've created a series of sysfs nodes
> to represent the different colors in the different zones.
Have you discussed the best way to represent this with the LED class
maintainer?
--
Matthew Garrett <matthew.garrett@nebula.com>
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 0/1] Add new driver for AlienFX control
2014-02-05 16:24 ` [PATCH 0/1] Add new driver for AlienFX control Matthew Garrett
@ 2014-02-05 16:34 ` Mario Limonciello
2014-02-05 17:57 ` Matthew Garrett
0 siblings, 1 reply; 5+ messages in thread
From: Mario Limonciello @ 2014-02-05 16:34 UTC (permalink / raw)
To: Matthew Garrett; +Cc: platform-driver-x86@vger.kernel.org
On 02/05/2014 10:24 AM, Matthew Garrett wrote:
> On Wed, 2014-02-05 at 10:22 -0600, Mario Limonciello wrote:
>
>> After digging further in, there actually is control for setting up the LEDs to
>> perform different tasks in S0 and S3 power states which becomes more difficult
>> to represent in the LED class. So instead I've created a series of sysfs nodes
>> to represent the different colors in the different zones.
> Have you discussed the best way to represent this with the LED class
> maintainer?
No I haven't yet. I initially coded it using the LED class, but I kept finding more details for it that didn't seem to match what the LED class provided properly.
The LED class is intended to represent the "brightness" of an individual LED.
There is a single brightness setting for each zone, but you can also individually choose the shade of every color.
Eg. you can pick from 0x00 to 0xFF for each color in the zone and brightness from 0x00 to 0xFF for the brightness of the zone.
I also mocked up having individual controls for each possible state and color in the LED class but it turned into about 36 different nodes in /sys/class/led/alienware-wmi and really confusing.
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 0/1] Add new driver for AlienFX control
2014-02-05 16:34 ` Mario Limonciello
@ 2014-02-05 17:57 ` Matthew Garrett
0 siblings, 0 replies; 5+ messages in thread
From: Matthew Garrett @ 2014-02-05 17:57 UTC (permalink / raw)
To: mario_limonciello@dell.com; +Cc: platform-driver-x86@vger.kernel.org
On Wed, 2014-02-05 at 10:34 -0600, Mario Limonciello wrote:
> On 02/05/2014 10:24 AM, Matthew Garrett wrote:
> > On Wed, 2014-02-05 at 10:22 -0600, Mario Limonciello wrote:
> >
> >> After digging further in, there actually is control for setting up the LEDs to
> >> perform different tasks in S0 and S3 power states which becomes more difficult
> >> to represent in the LED class. So instead I've created a series of sysfs nodes
> >> to represent the different colors in the different zones.
> > Have you discussed the best way to represent this with the LED class
> > maintainer?
> No I haven't yet. I initially coded it using the LED class, but I kept finding more details for it that didn't seem to match what the LED class provided properly.
>
> The LED class is intended to represent the "brightness" of an individual LED.
Sure, that's the current state. But it makes sense to work on extending
an existing class rather than implementing a custom interface.
--
Matthew Garrett <matthew.garrett@nebula.com>
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2014-02-05 17:57 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-02-05 16:22 [PATCH 0/1] Add new driver for AlienFX control Mario Limonciello
2014-02-05 16:22 ` [PATCH 1/1] Add WMI driver for controlling AlienFX on Alienware Mario Limonciello
2014-02-05 16:24 ` [PATCH 0/1] Add new driver for AlienFX control Matthew Garrett
2014-02-05 16:34 ` Mario Limonciello
2014-02-05 17:57 ` Matthew Garrett
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.