[PATCH 3/3] platform/x86: huawei-wmi: Remove unnecessary battery mutex

2019-10-20 Thread Ayman Bagabas
battery_lock mutex is never used and not needed.

Fixes: 355a070b09ab ("platform/x86: huawei-wmi: Add battery charging 
thresholds")
Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 7373a65a61d3..a2d846c4a7ee 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -67,7 +67,6 @@ struct huawei_wmi {
struct led_classdev cdev;
struct device *dev;
 
-   struct mutex battery_lock;
struct mutex wmi_lock;
 };
 
@@ -807,7 +806,6 @@ static int huawei_wmi_probe(struct platform_device *pdev)
 
if (wmi_has_guid(HWMI_METHOD_GUID)) {
mutex_init(&huawei_wmi->wmi_lock);
-   mutex_init(&huawei_wmi->battery_lock);
 
huawei_wmi_leds_setup(&pdev->dev);
huawei_wmi_fn_lock_setup(&pdev->dev);
-- 
2.21.0



[PATCH 2/3] platform/x86: huawei-wmi: No need to check for battery name

2019-10-20 Thread Ayman Bagabas
No need to check for battery name, we already check if the WMI function is
available in huawei_wmi_battery_setup.

Fixes: 355a070b09ab ("platform/x86: huawei-wmi: Add battery charging 
thresholds")
Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 4 
 1 file changed, 4 deletions(-)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 26041d44286a..7373a65a61d3 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -471,10 +471,6 @@ static DEVICE_ATTR_RW(charge_control_thresholds);
 
 static int huawei_wmi_battery_add(struct power_supply *battery)
 {
-   /* Huawei laptops come with one battery only */
-   if (strcmp(battery->desc->name, "BAT") != 1)
-   return -ENODEV;
-
device_create_file(&battery->dev, 
&dev_attr_charge_control_start_threshold);
device_create_file(&battery->dev, 
&dev_attr_charge_control_end_threshold);
 
-- 
2.21.0



[PATCH 1/3] platform/x86: huawei-wmi: Stricter battery thresholds set

2019-10-20 Thread Ayman Bagabas
Check if battery thresholds are within 0 and 100.

Fixes: 355a070b09ab ("platform/x86: huawei-wmi: Add battery charging 
thresholds")
Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 5837d1b8693d..26041d44286a 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -345,7 +345,7 @@ static int huawei_wmi_battery_set(int start, int end)
union hwmi_arg arg;
int err;
 
-   if (start < 0 || end > 100)
+   if (start < 0 || end < 0 || start > 100 || end > 100)
return -EINVAL;
 
arg.cmd = BATTERY_THRESH_SET;

base-commit: c4b9850b3676869ac0def5885d781d17f64b3a86
-- 
2.21.0



[PATCH] platform/x86: huawei-wmi: Remove unnecessary battery mutex

2019-10-19 Thread Ayman Bagabas
battery_lock mutex is never used and not needed.

Fixes: 355a070b09ab ("platform/x86: huawei-wmi: Add battery charging 
thresholds")
Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 7373a65a61d3..a2d846c4a7ee 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -67,7 +67,6 @@ struct huawei_wmi {
struct led_classdev cdev;
struct device *dev;
 
-   struct mutex battery_lock;
struct mutex wmi_lock;
 };
 
@@ -807,7 +806,6 @@ static int huawei_wmi_probe(struct platform_device *pdev)
 
if (wmi_has_guid(HWMI_METHOD_GUID)) {
mutex_init(&huawei_wmi->wmi_lock);
-   mutex_init(&huawei_wmi->battery_lock);
 
huawei_wmi_leds_setup(&pdev->dev);
huawei_wmi_fn_lock_setup(&pdev->dev);

base-commit: fd13c8622a5ad4f7317b64de4f6aa2de1962220e
prerequisite-patch-id: 7639f479e344d2a1be074f93dc5e5530611928f9
prerequisite-patch-id: ad5932635d0c0d531a99e15e239f81ecc69b47d6
-- 
2.21.0



Re: [PATCH 2/2] platform/x86: huawei-wmi: No need to check for battery name

2019-10-19 Thread ayman . bagabas
On Fri, 2019-10-18 at 18:42 -0400, Ayman Bagabas wrote:
> No need to check for battery name, we already check if the WMI
> function is
> available in huawei_wmi_battery_setup.

Signed-off-by: Ayman Bagabas 

Thank you,
Ayman
> ---
>  drivers/platform/x86/huawei-wmi.c | 4 
>  1 file changed, 4 deletions(-)
> 
> diff --git a/drivers/platform/x86/huawei-wmi.c
> b/drivers/platform/x86/huawei-wmi.c
> index 26041d44286a..7373a65a61d3 100644
> --- a/drivers/platform/x86/huawei-wmi.c
> +++ b/drivers/platform/x86/huawei-wmi.c
> @@ -471,10 +471,6 @@ static
> DEVICE_ATTR_RW(charge_control_thresholds);
>  
>  static int huawei_wmi_battery_add(struct power_supply *battery)
>  {
> - /* Huawei laptops come with one battery only */
> - if (strcmp(battery->desc->name, "BAT") != 1)
> - return -ENODEV;
> -
>   device_create_file(&battery->dev,
> &dev_attr_charge_control_start_threshold);
>   device_create_file(&battery->dev,
> &dev_attr_charge_control_end_threshold);
>  



Re: [PATCH 1/2] platform/x86: huawei-wmi: Stricter battery thresholds set

2019-10-19 Thread ayman . bagabas
On Sat, 2019-10-19 at 11:31 +0300, Dan Carpenter wrote:
> On Fri, Oct 18, 2019 at 06:42:13PM -0400, Ayman Bagabas wrote:
> > Check if battery thresholds are within 0 and 100.
> > ---
> 
> Thanks!
> 
> Don't forget to add your Signed-off-by: though.

Signed-off-by: Ayman Bagabas 

Thank you,
Ayman
> 
> regards,
> dan carpenter
> 



[PATCH 1/2] platform/x86: huawei-wmi: Stricter battery thresholds set

2019-10-18 Thread Ayman Bagabas
Check if battery thresholds are within 0 and 100.
---
 drivers/platform/x86/huawei-wmi.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 5837d1b8693d..26041d44286a 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -345,7 +345,7 @@ static int huawei_wmi_battery_set(int start, int end)
union hwmi_arg arg;
int err;
 
-   if (start < 0 || end > 100)
+   if (start < 0 || end < 0 || start > 100 || end > 100)
return -EINVAL;
 
arg.cmd = BATTERY_THRESH_SET;

base-commit: fd13c8622a5ad4f7317b64de4f6aa2de1962220e
-- 
2.21.0



[PATCH 2/2] platform/x86: huawei-wmi: No need to check for battery name

2019-10-18 Thread Ayman Bagabas
No need to check for battery name, we already check if the WMI function is
available in huawei_wmi_battery_setup.
---
 drivers/platform/x86/huawei-wmi.c | 4 
 1 file changed, 4 deletions(-)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 26041d44286a..7373a65a61d3 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -471,10 +471,6 @@ static DEVICE_ATTR_RW(charge_control_thresholds);
 
 static int huawei_wmi_battery_add(struct power_supply *battery)
 {
-   /* Huawei laptops come with one battery only */
-   if (strcmp(battery->desc->name, "BAT") != 1)
-   return -ENODEV;
-
device_create_file(&battery->dev, 
&dev_attr_charge_control_start_threshold);
device_create_file(&battery->dev, 
&dev_attr_charge_control_end_threshold);
 
-- 
2.21.0



Re: [PATCH] platform/x86: huawei-wmi: Fix misuse of strcmp() function

2019-10-15 Thread ayman . bagabas
Hi Gustavo,

On Tue, 2019-10-15 at 13:25 -0500, Gustavo A. R. Silva wrote:
> Comparing the result of strcmp directly with 1 may cause it to be
> misinterpreted. Note that strcmp may return an integer besides -1,
> 0, or 1.
> 
> Fix this by returning -ENODEV only when strcmp() returns a value
> other than 0.
> 
> Addresses-Coverity-ID: 1487035 ("Misuse of memcmp-style function")
> Fixes: b7527d0f4502 ("platform/x86: huawei-wmi: Add battery charging
> thresholds")
> Signed-off-by: Gustavo A. R. Silva 
> ---
>  drivers/platform/x86/huawei-wmi.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/platform/x86/huawei-wmi.c
> b/drivers/platform/x86/huawei-wmi.c
> index 6720f78c60c2..b43f76acbfea 100644
> --- a/drivers/platform/x86/huawei-wmi.c
> +++ b/drivers/platform/x86/huawei-wmi.c
> @@ -470,7 +470,7 @@ static DEVICE_ATTR_RW(charge_control_thresholds);
>  static int huawei_wmi_battery_add(struct power_supply *battery)
>  {
>   /* Huawei laptops come with one battery only */
> - if (strcmp(battery->desc->name, "BAT") != 1)

Note we don't have a battery number in BATx, strcmp would return 1 if
battery->desc->name is "BAT0" or any one digit. This is a desired
behavior where some Huawei laptops identify the first battery as "BAT1"
and this would match if name is greater than "BAT" by one digit.

Maybe strcmp(battery->desc->name, "BAT") < 0 is a better way to go.

> + if (strcmp(battery->desc->name, "BAT"))
>   return -ENODEV;

Now this would always return ENODEV.

Thank you,
Ayman

>  
>   device_create_file(&battery->dev,
> &dev_attr_charge_control_start_threshold);



Re: [PATCH] platform/x86: huawei-wmi: make validation stricter in huawei_wmi_battery_set()

2019-10-15 Thread ayman . bagabas
On Tue, 2019-10-15 at 15:46 +0300, Dan Carpenter wrote:
> On Tue, Oct 15, 2019 at 08:21:59AM -0400, Ayman Bagabas wrote:
> > Hi Dan
> > 
> > On Tue, Oct 15, 2019, 4:39 AM Dan Carpenter <
> > dan.carpen...@oracle.com>
> > wrote:
> > 
> > > I don't think it makes sense for "end" to be negative or for even
> > > for it
> > > to be less than "start".  That also means that "start" can't be
> > > more
> > > than 100 which is good.
> > > 
> > 
> > While this makes sense, you run into issues where you cannot set
> > "start"
> > before "end" and vice versa.
> > 
> > Take this scenario, you have start=70 and end=90, now you want to
> > set these
> > to start=40 and end=60, you would have to set "start" first before
> > you can
> > change the value of "end" otherwise you will run into EINVAL. Now
> > imagine
> > you wanna go the opposite direction, you would have to set "end"
> > before you
> > can change "start".
> > I think having a little wiggle room is fine for such scenarios.
> > 
> 
> I haven't tested this code...  What you're describing sounds really
> very weird to me, but I will accept that you know more about your
> use cases than I do.
> 
> My other concern is that right now you can set start > 100 or end <
> 0.

We should check for these cases.

> 
> regards,
> dan carpenter
> 



[PATCH v5 5/6] platform/x86: huawei-wmi: Add fn-lock support

2019-09-23 Thread Ayman Bagabas
Huawei Matebook laptops uses Fn key and toggle to access F1-F12 keys.
Along with that, there is this feature called fn-lock that inverts the
behavior of this Fn key and the F1-F12 row.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 85 +++
 1 file changed, 85 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 6e791f9ffc26..d550c63fcba7 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -54,6 +54,7 @@ static struct quirk_entry *quirks;
 
 struct huawei_wmi {
bool battery_available;
+   bool fn_lock_available;
 
struct input_dev *idev[2];
struct led_classdev cdev;
@@ -509,6 +510,88 @@ static void huawei_wmi_battery_exit(struct device *dev)
}
 }
 
+/* Fn lock */
+
+static int huawei_wmi_fn_lock_get(int *on)
+{
+   u8 ret[0x100] = { 0 };
+   int err, i;
+
+   err = huawei_wmi_cmd(FN_LOCK_GET, ret, 0x100);
+   if (err)
+   return err;
+
+   /* Find the first non-zero value. Return status is ignored. */
+   i = 1;
+   do {
+   if (on)
+   *on = ret[i] - 1; // -1 undefined, 0 off, 1 on.
+   } while (i < 0xff && !ret[i++]);
+
+   return 0;
+}
+
+static int huawei_wmi_fn_lock_set(int on)
+{
+   union hwmi_arg arg;
+
+   arg.cmd = FN_LOCK_SET;
+   arg.args[2] = on + 1; // 0 undefined, 1 off, 2 on.
+
+   return huawei_wmi_cmd(arg.cmd, NULL, 0);
+}
+
+static ssize_t fn_lock_state_show(struct device *dev,
+   struct device_attribute *attr,
+   char *buf)
+{
+   int err, on;
+
+   err = huawei_wmi_fn_lock_get(&on);
+   if (err)
+   return err;
+
+   return sprintf(buf, "%d\n", on);
+}
+
+static ssize_t fn_lock_state_store(struct device *dev,
+   struct device_attribute *attr,
+   const char *buf, size_t size)
+{
+   int on, err;
+
+   if (kstrtoint(buf, 10, &on) ||
+   on < 0 || on > 1)
+   return -EINVAL;
+
+   err = huawei_wmi_fn_lock_set(on);
+   if (err)
+   return err;
+
+   return size;
+}
+
+static DEVICE_ATTR_RW(fn_lock_state);
+
+static void huawei_wmi_fn_lock_setup(struct device *dev)
+{
+   struct huawei_wmi *huawei = dev_get_drvdata(dev);
+
+   huawei->fn_lock_available = true;
+   if (huawei_wmi_fn_lock_get(NULL)) {
+   huawei->fn_lock_available = false;
+   return;
+   }
+
+   device_create_file(dev, &dev_attr_fn_lock_state);
+}
+
+static void huawei_wmi_fn_lock_exit(struct device *dev)
+{
+   if (huawei_wmi->fn_lock_available)
+   device_remove_file(dev, &dev_attr_fn_lock_state);
+}
+
 /* Input */
 
 static void huawei_wmi_process_key(struct input_dev *idev, int code)
@@ -632,6 +715,7 @@ static int huawei_wmi_probe(struct platform_device *pdev)
mutex_init(&huawei_wmi->battery_lock);
 
huawei_wmi_leds_setup(&pdev->dev);
+   huawei_wmi_fn_lock_setup(&pdev->dev);
huawei_wmi_battery_setup(&pdev->dev);
}
 
@@ -651,6 +735,7 @@ static int huawei_wmi_remove(struct platform_device *pdev)
 
if (wmi_has_guid(HWMI_METHOD_GUID)) {
huawei_wmi_battery_exit(&pdev->dev);
+   huawei_wmi_fn_lock_exit(&pdev->dev);
}
 
return 0;
-- 
2.21.0



[PATCH v5 6/6] platform/x86: huawei-wmi: Add debugfs support

2019-09-23 Thread Ayman Bagabas
Add a debugfs interface that can be used to call the WMI management
interface function if available.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 97 +++
 1 file changed, 97 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index d550c63fcba7..6720f78c60c2 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -6,6 +6,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -52,10 +53,16 @@ struct quirk_entry {
 
 static struct quirk_entry *quirks;
 
+struct huawei_wmi_debug {
+   struct dentry *root;
+   u64 arg;
+};
+
 struct huawei_wmi {
bool battery_available;
bool fn_lock_available;
 
+   struct huawei_wmi_debug debug;
struct input_dev *idev[2];
struct led_classdev cdev;
struct platform_device *pdev;
@@ -592,6 +599,94 @@ static void huawei_wmi_fn_lock_exit(struct device *dev)
device_remove_file(dev, &dev_attr_fn_lock_state);
 }
 
+/* debugfs */
+
+static void huawei_wmi_debugfs_call_dump(struct seq_file *m, void *data,
+   union acpi_object *obj)
+{
+   struct huawei_wmi *huawei = m->private;
+   int i;
+
+   switch (obj->type) {
+   case ACPI_TYPE_INTEGER:
+   seq_printf(m, "0x%llx", obj->integer.value);
+   break;
+   case ACPI_TYPE_STRING:
+   seq_printf(m, "\"%*s\"", obj->string.length, 
obj->string.pointer);
+   break;
+   case ACPI_TYPE_BUFFER:
+   seq_puts(m, "{");
+   for (i = 0; i < obj->buffer.length; i++) {
+   seq_printf(m, "0x%02x", obj->buffer.pointer[i]);
+   if (i < obj->buffer.length - 1)
+   seq_puts(m, ",");
+   }
+   seq_puts(m, "}");
+   break;
+   case ACPI_TYPE_PACKAGE:
+   seq_puts(m, "[");
+   for (i = 0; i < obj->package.count; i++) {
+   huawei_wmi_debugfs_call_dump(m, huawei, 
&obj->package.elements[i]);
+   if (i < obj->package.count - 1)
+   seq_puts(m, ",");
+   }
+   seq_puts(m, "]");
+   break;
+   default:
+   dev_err(&huawei->pdev->dev, "Unexpected obj type, got %d\n", 
obj->type);
+   return;
+   }
+}
+
+static int huawei_wmi_debugfs_call_show(struct seq_file *m, void *data)
+{
+   struct huawei_wmi *huawei = m->private;
+   struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
+   struct acpi_buffer in;
+   union acpi_object *obj;
+   int err;
+
+   in.length = sizeof(u64);
+   in.pointer = &huawei->debug.arg;
+
+   err = huawei_wmi_call(&in, &out);
+   if (err)
+   return err;
+
+   obj = out.pointer;
+   if (!obj) {
+   err = -EIO;
+   goto fail_debugfs_call;
+   }
+
+   huawei_wmi_debugfs_call_dump(m, huawei, obj);
+
+fail_debugfs_call:
+   kfree(out.pointer);
+   return err;
+}
+
+DEFINE_SHOW_ATTRIBUTE(huawei_wmi_debugfs_call);
+
+static void huawei_wmi_debugfs_setup(struct device *dev)
+{
+   struct huawei_wmi *huawei = dev_get_drvdata(dev);
+
+   huawei->debug.root = debugfs_create_dir("huawei-wmi", NULL);
+
+   debugfs_create_x64("arg", 0644, huawei->debug.root,
+   &huawei->debug.arg);
+   debugfs_create_file("call", 0400,
+   huawei->debug.root, huawei, &huawei_wmi_debugfs_call_fops);
+}
+
+static void huawei_wmi_debugfs_exit(struct device *dev)
+{
+   struct huawei_wmi *huawei = dev_get_drvdata(dev);
+
+   debugfs_remove_recursive(huawei->debug.root);
+}
+
 /* Input */
 
 static void huawei_wmi_process_key(struct input_dev *idev, int code)
@@ -717,6 +812,7 @@ static int huawei_wmi_probe(struct platform_device *pdev)
huawei_wmi_leds_setup(&pdev->dev);
huawei_wmi_fn_lock_setup(&pdev->dev);
huawei_wmi_battery_setup(&pdev->dev);
+   huawei_wmi_debugfs_setup(&pdev->dev);
}
 
return 0;
@@ -734,6 +830,7 @@ static int huawei_wmi_remove(struct platform_device *pdev)
}
 
if (wmi_has_guid(HWMI_METHOD_GUID)) {
+   huawei_wmi_debugfs_exit(&pdev->dev);
huawei_wmi_battery_exit(&pdev->dev);
huawei_wmi_fn_lock_exit(&pdev->dev);
}
-- 
2.21.0



[PATCH v5 4/6] platform/x86: huawei-wmi: Add battery charging thresholds

2019-09-23 Thread Ayman Bagabas
Control battery charge thresholds through the battery API and driver's
attributes.

Setting battery charging thresholds can introduce a race condition with
MACH-WX9 where two or more threads are trying to read/write values
from/to EC memory.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/Kconfig  |   1 +
 drivers/platform/x86/huawei-wmi.c | 212 ++
 2 files changed, 213 insertions(+)

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 754405e19ef1..e64103789885 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1306,6 +1306,7 @@ config INTEL_ATOMISP2_PM
 
 config HUAWEI_WMI
tristate "Huawei WMI laptop extras driver"
+   depends on ACPI_BATTERY
depends on ACPI_WMI
depends on INPUT
select INPUT_SPARSEKMAP
diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 41904b1cc284..6e791f9ffc26 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -6,6 +6,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -13,7 +14,10 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 #include 
+#include 
 
 /*
  * Huawei WMI GUIDs
@@ -49,10 +53,13 @@ struct quirk_entry {
 static struct quirk_entry *quirks;
 
 struct huawei_wmi {
+   bool battery_available;
+
struct input_dev *idev[2];
struct led_classdev cdev;
struct platform_device *pdev;
 
+   struct mutex battery_lock;
struct mutex wmi_lock;
 };
 
@@ -300,6 +307,208 @@ static void huawei_wmi_leds_setup(struct device *dev)
devm_led_classdev_register(dev, &huawei->cdev);
 }
 
+/* Battery protection */
+
+static int huawei_wmi_battery_get(int *start, int *end)
+{
+   u8 ret[0x100];
+   int err, i;
+
+   err = huawei_wmi_cmd(BATTERY_THRESH_GET, ret, 0x100);
+   if (err)
+   return err;
+
+   /* Find the last two non-zero values. Return status is ignored. */
+   i = 0xff;
+   do {
+   if (start)
+   *start = ret[i-1];
+   if (end)
+   *end = ret[i];
+   } while (i > 2 && !ret[i--]);
+
+   return 0;
+}
+
+static int huawei_wmi_battery_set(int start, int end)
+{
+   union hwmi_arg arg;
+   int err;
+
+   if (start < 0 || end > 100)
+   return -EINVAL;
+
+   arg.cmd = BATTERY_THRESH_SET;
+   arg.args[2] = start;
+   arg.args[3] = end;
+
+   /* This is an edge case were some models turn battery protection
+* off without changing their thresholds values. We clear the
+* values before turning off protection. Sometimes we need a sleep 
delay to
+* make sure these values make their way to EC memory.
+*/
+   if (quirks && quirks->battery_reset && start == 0 && end == 100) {
+   err = huawei_wmi_battery_set(0, 0);
+   if (err)
+   return err;
+
+   msleep(1000);
+   }
+
+   err = huawei_wmi_cmd(arg.cmd, NULL, 0);
+
+   return err;
+}
+
+static ssize_t charge_control_start_threshold_show(struct device *dev,
+   struct device_attribute *attr,
+   char *buf)
+{
+   int err, start;
+
+   err = huawei_wmi_battery_get(&start, NULL);
+   if (err)
+   return err;
+
+   return sprintf(buf, "%d\n", start);
+}
+
+static ssize_t charge_control_end_threshold_show(struct device *dev,
+   struct device_attribute *attr,
+   char *buf)
+{
+   int err, end;
+
+   err = huawei_wmi_battery_get(NULL, &end);
+   if (err)
+   return err;
+
+   return sprintf(buf, "%d\n", end);
+}
+
+static ssize_t charge_control_thresholds_show(struct device *dev,
+   struct device_attribute *attr,
+   char *buf)
+{
+   int err, start, end;
+
+   err = huawei_wmi_battery_get(&start, &end);
+   if (err)
+   return err;
+
+   return sprintf(buf, "%d %d\n", start, end);
+}
+
+static ssize_t charge_control_start_threshold_store(struct device *dev,
+   struct device_attribute *attr,
+   const char *buf, size_t size)
+{
+   int err, start, end;
+
+   err = huawei_wmi_battery_get(NULL, &end);
+   if (err)
+   return err;
+
+   if (sscanf(buf, "%d", &start) != 1)
+   return -EINVAL;
+
+   err = huawei_wmi_battery_set(start, end);
+   if (err)
+   return err;
+
+   return size;
+}
+
+static ssize_t charge_control_end_threshold_store(struct device *dev,
+   struct device_attribute *attr,
+   const char *buf, size_t size)
+{
+   int err, start, end;
+
+   err = huawei_wmi_battery_get(&start, NULL);

[PATCH v5 1/6] platform/x86: huawei-wmi: Move to platform driver

2019-09-23 Thread Ayman Bagabas
Move from WMI driver to platform driver. This move is necessary since
the driver is no longer a hotkeys driver only. Platform driver makes it
easier for users to access sysfs attributes under (i.e.
/sys/devices/platform/huawei-wmi) compared to wmi driver.

Use WMI device UID, AMW0 has a UID of HWMI. WMI0 is the device name
and doesn't have a UID so keep it as it is.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/Kconfig  |   7 +-
 drivers/platform/x86/huawei-wmi.c | 226 --
 2 files changed, 156 insertions(+), 77 deletions(-)

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index ae21d08c65e8..754405e19ef1 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1305,7 +1305,7 @@ config INTEL_ATOMISP2_PM
  will be called intel_atomisp2_pm.
 
 config HUAWEI_WMI
-   tristate "Huawei WMI hotkeys driver"
+   tristate "Huawei WMI laptop extras driver"
depends on ACPI_WMI
depends on INPUT
select INPUT_SPARSEKMAP
@@ -1314,9 +1314,8 @@ config HUAWEI_WMI
select LEDS_TRIGGER_AUDIO
select NEW_LEDS
help
- This driver provides support for Huawei WMI hotkeys.
- It enables the missing keys and adds support to the micmute
- LED found on some of these laptops.
+ This driver provides support for Huawei WMI hotkeys, battery charge
+ control, fn-lock, mic-mute LED, and other extra features.
 
  To compile this driver as a module, choose M here: the module
  will be called huawei-wmi.
diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 195a7f3638cb..9496ea3c78b5 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- *  Huawei WMI hotkeys
+ *  Huawei WMI laptop extras driver
  *
  *  Copyright (C) 2018   Ayman Bagabas 
  */
@@ -10,23 +10,28 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 
 /*
  * Huawei WMI GUIDs
  */
-#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
-#define AMW0_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
+#define HWMI_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
 
+/* Legacy GUIDs */
 #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
+#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
 
-struct huawei_wmi_priv {
-   struct input_dev *idev;
+struct huawei_wmi {
+   struct input_dev *idev[2];
struct led_classdev cdev;
+   struct platform_device *pdev;
acpi_handle handle;
char *acpi_method;
 };
 
+struct huawei_wmi *huawei_wmi;
+
 static const struct key_entry huawei_wmi_keymap[] = {
{ KE_KEY,0x281, { KEY_BRIGHTNESSDOWN } },
{ KE_KEY,0x282, { KEY_BRIGHTNESSUP } },
@@ -37,7 +42,7 @@ static const struct key_entry huawei_wmi_keymap[] = {
{ KE_KEY,0x289, { KEY_WLAN } },
// Huawei |M| key
{ KE_KEY,0x28a, { KEY_CONFIG } },
-   // Keyboard backlight
+   // Keyboard backlit
{ KE_IGNORE, 0x293, { KEY_KBDILLUMTOGGLE } },
{ KE_IGNORE, 0x294, { KEY_KBDILLUMUP } },
{ KE_IGNORE, 0x295, { KEY_KBDILLUMUP } },
@@ -47,7 +52,7 @@ static const struct key_entry huawei_wmi_keymap[] = {
 static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
 {
-   struct huawei_wmi_priv *priv = dev_get_drvdata(led_cdev->dev->parent);
+   struct huawei_wmi *huawei = dev_get_drvdata(led_cdev->dev->parent);
acpi_status status;
union acpi_object args[3];
struct acpi_object_list arg_list = {
@@ -58,52 +63,53 @@ static int huawei_wmi_micmute_led_set(struct led_classdev 
*led_cdev,
args[0].type = args[1].type = args[2].type = ACPI_TYPE_INTEGER;
args[1].integer.value = 0x04;
 
-   if (strcmp(priv->acpi_method, "SPIN") == 0) {
+   if (strcmp(huawei->acpi_method, "SPIN") == 0) {
args[0].integer.value = 0;
args[2].integer.value = brightness ? 1 : 0;
-   } else if (strcmp(priv->acpi_method, "WPIN") == 0) {
+   } else if (strcmp(huawei->acpi_method, "WPIN") == 0) {
args[0].integer.value = 1;
args[2].integer.value = brightness ? 0 : 1;
} else {
return -EINVAL;
}
 
-   status = acpi_evaluate_object(priv->handle, priv->acpi_method, 
&arg_list, NULL);
+   status = acpi_evaluate_object(huawei->handle, huawei->acpi_method, 
&arg_list, NULL);
if (ACPI_FAILURE(status))
return -ENXIO;
 
return 0;
 }
 
-static int huawei_wmi_leds_setup(struct wmi_device *wdev)
+static void huawei_wmi_leds_setup(struct device *dev)
 {
-   struct huaw

[PATCH v5 0/6] platform/x86: Huawei WMI laptop extras driver

2019-09-23 Thread Ayman Bagabas
Changes in v5:
* Fix a possible buffer overflow error.

Changes in v4:
* Use int and bint for module params.

Changes in v3:
* Kconfig changes
* Fix NULL cast to int warning.
* Add ACPI_BATTERY as a dependency.

Changes in v2:
* Use battery charge control API.

This patch series introduce changes to huawei-wmi driver that includes:
* Move to platform driver
* Implement driver quirks and parameters
* Implement WMI management interface
* Add micmute LED support through WMI
* Add battery charging protection support through WMI
* Add fn-lock support through WMI
* Add a debugfs interface to WMI

# Move to platform driver

The current driver offers hotkeys and micmute led support only. With
these changes, a platform driver makes more sense since it handles these
changes pretty nicely.

# Implement WMI management interface

Huawei Matebook laptops come with two WMI interfaces. The first being
WMI0 which is considered "legacy" and AFAIK only found on the Matebook X
released in 2017. The second has a UID of "HWMI" and is found in pretty
much all models with a slight difference in implementation except for
the Matebook X (2017). Since this model has two interfaces, some aspects
are controlled through the legacy interface and some through the other
interface. Currently, the legacy interface is not fully implemented and
is only used for hotkeys and further debugging has to be done.

The WMI interface takes a 64 bit integer, although uses 32 bits most of
the time, and returns a 256-260 bytes buffer consists of either one ACPI
buffer of 260 bytes, in the case of Matebook X (2017), or one ACPI
package of two buffers, one with 4 bytes, and the other with 256 bytes.
We only care about the latter 256 buffer in both cases since the 4 bytes
always return zeros. The first byte of this 256 buffer always has the
return status where 1 indicated error. Some models require calling the
WMI interface twice to execute a command.

# Add micmute LED support through WMI

After implementing the WMI interface, micmute LED can be controlled
easily. Models with the legacy interface fall back to ACPI EC method
control since the legacy interface is not implemented.

# Add battery charging protection support through WMI

Most models, that has the WMI interface, are capable of battery
protection where it can control battery charging thresholds and limits
charging the battery to certain values.

# Add fn-lock support through WMI

The behavior of hotkeys is not the same among all models. Some models
require fn-lock to do things like `Ctrl-Ins` or `Alt-PrtSc`. By default,
hotkeys behave as special keys (media keys, Ins, etc), but if a modifier
is used (ctrl, alt, shift) these keys behave as F1-F12 keys. If the Fn
key is toggled on, the hotkeys with or without a modifier, behave as
F1-F12 keys. This makes it impossible to use a modifier and `PrtSc` or
`Ins`.

Now, some models fix this by excluding `PrtSc` and `Ins` keys from being
treated as F11 and F12 keys with the use of a modifier. However, some
models do not, and fixes this by the so called fn-lock.

Fn-lock inverts the behavior of the top row from special keys to F1-F12
keys. So a modifier and a special key would be possible which make
things like `Alt-Ins` possible. Now, with fn-lock we would have 4 modes:

* Fn-key off & fn-lock off - hotkeys treated as special keys using a
  modifier gives F1-F12 keys.
* Fn-key on & fn-lock off - hotkeys treated as F1-F12 keys and using a
  modifier gives F1-F12.
* Fn-key off & fn-lock on - hotkeys are treated as F1-F12 keys and using
  a modifier gives special keys.
* Fn-key on & fn-lock on - hotkeys are treated as special keys and using
  a modifier gives special keys.

# Implement driver quirks and parameters

The driver introduces 3 quirks and 2 parameters that can change the
driver's behavior. These quirks being as:
1. Fixes reporting brightness keys twice since it's already handled by
   acpi-video.
2. Some models need a short delay when setting battery thresholds to
   prevent a race condition when two processes read/write.
3. Matebook X (2017) handles micmute led through the "legacy" interface
   which is not currently implemented. Use ACPI EC method to control
   this led.

and the 2 parameters can enforce the behavior of quirk 1 & 2.

# Add a debugfs interface to WMI

An interface to the WMI management interface that allows easier
debugging.

Ayman Bagabas (6):
  platform/x86: huawei-wmi: Move to platform driver
  platform/x86: huawei-wmi: Add quirks and module parameters
  platform/x86: huawei-wmi: Implement huawei wmi management
  platform/x86: huawei-wmi: Add battery charging thresholds
  platform/x86: huawei-wmi: Add fn-lock support
  platform/x86: huawei-wmi: Add debugfs support

 drivers/platform/x86/Kconfig  |   8 +-
 drivers/platform/x86/huawei-wmi.c | 874 ++
 2 files changed, 786 insertions(+), 96 deletions(-)


base-commit: b5b3bd898ba99fb0fb6aed3b23ec6353a1724d6f
-- 
2.21.0



[PATCH v5 2/6] platform/x86: huawei-wmi: Add quirks and module parameters

2019-09-23 Thread Ayman Bagabas
Introduce quirks and module parameters. 3 quirks are added:
1. Fixes reporting brightness keys twice since it's already handled by
   acpi-video.
2. Some models need a short delay when setting battery thresholds to
   prevent a race condition when two processes read/write. (will be used later)
3. Matebook X (2017) handles micmute led through the "legacy" interface
   which is not currently implemented. Use ACPI EC method to control
   this led. (will be used later)

2 module parameters are added to enable this short delay and/or report
  brightness keys through this driver.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 73 +++
 1 file changed, 73 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 9496ea3c78b5..0e919bf56ad1 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -6,6 +6,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -22,6 +23,14 @@
 #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
 #define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
 
+struct quirk_entry {
+   bool battery_reset;
+   bool ec_micmute;
+   bool report_brightness;
+};
+
+static struct quirk_entry *quirks;
+
 struct huawei_wmi {
struct input_dev *idev[2];
struct led_classdev cdev;
@@ -49,6 +58,58 @@ static const struct key_entry huawei_wmi_keymap[] = {
{ KE_END,0 }
 };
 
+static int battery_reset = -1;
+static int report_brightness = -1;
+
+module_param(battery_reset, bint, 0444);
+MODULE_PARM_DESC(battery_reset,
+   "Reset battery charge values to (0-0) before disabling it using 
(0-100)");
+module_param(report_brightness, bint, 0444);
+MODULE_PARM_DESC(report_brightness,
+   "Report brightness keys.");
+
+/* Quirks */
+
+static int __init dmi_matched(const struct dmi_system_id *dmi)
+{
+   quirks = dmi->driver_data;
+   return 1;
+}
+
+static struct quirk_entry quirk_unknown = {
+};
+
+static struct quirk_entry quirk_battery_reset = {
+   .battery_reset = true,
+};
+
+static struct quirk_entry quirk_matebook_x = {
+   .ec_micmute = true,
+   .report_brightness = true,
+};
+
+static const struct dmi_system_id huawei_quirks[] = {
+   {
+   .callback = dmi_matched,
+   .ident = "Huawei MACH-WX9",
+   .matches = {
+   DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
+   DMI_MATCH(DMI_PRODUCT_NAME, "MACH-WX9"),
+   },
+   .driver_data = &quirk_battery_reset
+   },
+   {
+   .callback = dmi_matched,
+   .ident = "Huawei MateBook X",
+   .matches = {
+   DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
+   DMI_MATCH(DMI_PRODUCT_NAME, "HUAWEI MateBook X")
+   },
+   .driver_data = &quirk_matebook_x
+   },
+   {  }
+};
+
 static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
 {
@@ -139,6 +200,11 @@ static void huawei_wmi_process_key(struct input_dev *idev, 
int code)
return;
}
 
+   if (quirks && !quirks->report_brightness &&
+   (key->sw.code == KEY_BRIGHTNESSDOWN ||
+   key->sw.code == KEY_BRIGHTNESSUP))
+   return;
+
sparse_keymap_report_entry(idev, key, 1, true);
 }
 
@@ -253,6 +319,13 @@ static __init int huawei_wmi_init(void)
if (!huawei_wmi)
return -ENOMEM;
 
+   quirks = &quirk_unknown;
+   dmi_check_system(huawei_quirks);
+   if (battery_reset != -1)
+   quirks->battery_reset = battery_reset;
+   if (report_brightness != -1)
+   quirks->report_brightness = report_brightness;
+
err = platform_driver_register(&huawei_wmi_driver);
if (err)
goto pdrv_err;
-- 
2.21.0



[PATCH v5 3/6] platform/x86: huawei-wmi: Implement huawei wmi management

2019-09-23 Thread Ayman Bagabas
Huawei Matebook laptops come with a WMI management interface that can
control various aspects of the device. This interface is also found on
the old Matebook X released in 2017.

Use that to control the mic mute LED.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 217 +-
 1 file changed, 180 insertions(+), 37 deletions(-)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 0e919bf56ad1..41904b1cc284 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -11,18 +11,35 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 
 /*
  * Huawei WMI GUIDs
  */
+#define HWMI_METHOD_GUID "ABBC0F5B-8EA1-11D1-A000-C9062910"
 #define HWMI_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
 
 /* Legacy GUIDs */
 #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
 #define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
 
+/* HWMI commands */
+
+enum {
+   BATTERY_THRESH_GET  = 0x1103, /* \GBTT */
+   BATTERY_THRESH_SET  = 0x1003, /* \SBTT */
+   FN_LOCK_GET = 0x0604, /* \GFRS */
+   FN_LOCK_SET = 0x0704, /* \SFRS */
+   MICMUTE_LED_SET = 0x0b04, /* \SMLS */
+};
+
+union hwmi_arg {
+   u64 cmd;
+   u8 args[8];
+};
+
 struct quirk_entry {
bool battery_reset;
bool ec_micmute;
@@ -35,8 +52,8 @@ struct huawei_wmi {
struct input_dev *idev[2];
struct led_classdev cdev;
struct platform_device *pdev;
-   acpi_handle handle;
-   char *acpi_method;
+
+   struct mutex wmi_lock;
 };
 
 struct huawei_wmi *huawei_wmi;
@@ -110,52 +127,168 @@ static const struct dmi_system_id huawei_quirks[] = {
{  }
 };
 
+/* Utils */
+
+static int huawei_wmi_call(struct acpi_buffer *in, struct acpi_buffer *out)
+{
+   acpi_status status;
+
+   mutex_lock(&huawei_wmi->wmi_lock);
+   status = wmi_evaluate_method(HWMI_METHOD_GUID, 0, 1, in, out);
+   mutex_unlock(&huawei_wmi->wmi_lock);
+   if (ACPI_FAILURE(status)) {
+   dev_err(&huawei_wmi->pdev->dev, "Failed to evaluate wmi 
method\n");
+   return -ENODEV;
+   }
+
+   return 0;
+}
+
+/* HWMI takes a 64 bit input and returns either a package with 2 buffers, one 
of
+ * 4 bytes and the other of 256 bytes, or one buffer of size 0x104 (260) bytes.
+ * The first 4 bytes are ignored, we ignore the first 4 bytes buffer if we got 
a
+ * package, or skip the first 4 if a buffer of 0x104 is used. The first byte of
+ * the remaining 0x100 sized buffer has the return status of every call. In 
case
+ * the return status is non-zero, we return -ENODEV but still copy the returned
+ * buffer to the given buffer parameter (buf).
+ */
+static int huawei_wmi_cmd(u64 arg, u8 *buf, size_t buflen)
+{
+   struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
+   struct acpi_buffer in;
+   union acpi_object *obj;
+   size_t len;
+   int err, i;
+
+   in.length = sizeof(arg);
+   in.pointer = &arg;
+
+   /* Some models require calling HWMI twice to execute a command. We 
evaluate
+* HWMI and if we get a non-zero return status we evaluate it again.
+*/
+   for (i = 0; i < 2; i++) {
+   err = huawei_wmi_call(&in, &out);
+   if (err)
+   goto fail_cmd;
+
+   obj = out.pointer;
+   if (!obj) {
+   err = -EIO;
+   goto fail_cmd;
+   }
+
+   switch (obj->type) {
+   /* Models that implement both "legacy" and HWMI tend to return 
a 0x104
+* sized buffer instead of a package of 0x4 and 0x100 buffers.
+*/
+   case ACPI_TYPE_BUFFER:
+   if (obj->buffer.length == 0x104) {
+   // Skip the first 4 bytes.
+   obj->buffer.pointer += 4;
+   len = 0x100;
+   } else {
+   dev_err(&huawei_wmi->pdev->dev, "Bad buffer 
length, got %d\n", obj->buffer.length);
+   err = -EIO;
+   goto fail_cmd;
+   }
+
+   break;
+   /* HWMI returns a package with 2 buffer elements, one of 4 
bytes and the
+* other is 256 bytes.
+*/
+   case ACPI_TYPE_PACKAGE:
+   if (obj->package.count != 2) {
+   dev_err(&huawei_wmi->pdev->dev, "Bad package 
count, got %d\n", obj->package.count);
+   err = -EIO;
+

[PATCH v4 6/6] platform/x86: huawei-wmi: Add debugfs support

2019-09-20 Thread Ayman Bagabas
Add a debugfs interface that can be used to call the WMI management
interface function if available.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 97 +++
 1 file changed, 97 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 56697d49377d..5efb8d1bbda7 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -6,6 +6,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -52,10 +53,16 @@ struct quirk_entry {
 
 static struct quirk_entry *quirks;
 
+struct huawei_wmi_debug {
+   struct dentry *root;
+   u64 arg;
+};
+
 struct huawei_wmi {
bool battery_available;
bool fn_lock_available;
 
+   struct huawei_wmi_debug debug;
struct input_dev *idev[2];
struct led_classdev cdev;
struct platform_device *pdev;
@@ -592,6 +599,94 @@ static void huawei_wmi_fn_lock_exit(struct device *dev)
device_remove_file(dev, &dev_attr_fn_lock_state);
 }
 
+/* debugfs */
+
+static void huawei_wmi_debugfs_call_dump(struct seq_file *m, void *data,
+   union acpi_object *obj)
+{
+   struct huawei_wmi *huawei = m->private;
+   int i;
+
+   switch (obj->type) {
+   case ACPI_TYPE_INTEGER:
+   seq_printf(m, "0x%llx", obj->integer.value);
+   break;
+   case ACPI_TYPE_STRING:
+   seq_printf(m, "\"%*s\"", obj->string.length, 
obj->string.pointer);
+   break;
+   case ACPI_TYPE_BUFFER:
+   seq_puts(m, "{");
+   for (i = 0; i < obj->buffer.length; i++) {
+   seq_printf(m, "0x%02x", obj->buffer.pointer[i]);
+   if (i < obj->buffer.length - 1)
+   seq_puts(m, ",");
+   }
+   seq_puts(m, "}");
+   break;
+   case ACPI_TYPE_PACKAGE:
+   seq_puts(m, "[");
+   for (i = 0; i < obj->package.count; i++) {
+   huawei_wmi_debugfs_call_dump(m, huawei, 
&obj->package.elements[i]);
+   if (i < obj->package.count - 1)
+   seq_puts(m, ",");
+   }
+   seq_puts(m, "]");
+   break;
+   default:
+   dev_err(&huawei->pdev->dev, "Unexpected obj type, got %d\n", 
obj->type);
+   return;
+   }
+}
+
+static int huawei_wmi_debugfs_call_show(struct seq_file *m, void *data)
+{
+   struct huawei_wmi *huawei = m->private;
+   struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
+   struct acpi_buffer in;
+   union acpi_object *obj;
+   int err;
+
+   in.length = sizeof(u64);
+   in.pointer = &huawei->debug.arg;
+
+   err = huawei_wmi_call(&in, &out);
+   if (err)
+   return err;
+
+   obj = out.pointer;
+   if (!obj) {
+   err = -EIO;
+   goto fail_debugfs_call;
+   }
+
+   huawei_wmi_debugfs_call_dump(m, huawei, obj);
+
+fail_debugfs_call:
+   kfree(out.pointer);
+   return err;
+}
+
+DEFINE_SHOW_ATTRIBUTE(huawei_wmi_debugfs_call);
+
+static void huawei_wmi_debugfs_setup(struct device *dev)
+{
+   struct huawei_wmi *huawei = dev_get_drvdata(dev);
+
+   huawei->debug.root = debugfs_create_dir("huawei-wmi", NULL);
+
+   debugfs_create_x64("arg", 0644, huawei->debug.root,
+   &huawei->debug.arg);
+   debugfs_create_file("call", 0400,
+   huawei->debug.root, huawei, &huawei_wmi_debugfs_call_fops);
+}
+
+static void huawei_wmi_debugfs_exit(struct device *dev)
+{
+   struct huawei_wmi *huawei = dev_get_drvdata(dev);
+
+   debugfs_remove_recursive(huawei->debug.root);
+}
+
 /* Input */
 
 static void huawei_wmi_process_key(struct input_dev *idev, int code)
@@ -717,6 +812,7 @@ static int huawei_wmi_probe(struct platform_device *pdev)
huawei_wmi_leds_setup(&pdev->dev);
huawei_wmi_fn_lock_setup(&pdev->dev);
huawei_wmi_battery_setup(&pdev->dev);
+   huawei_wmi_debugfs_setup(&pdev->dev);
}
 
return 0;
@@ -734,6 +830,7 @@ static int huawei_wmi_remove(struct platform_device *pdev)
}
 
if (wmi_has_guid(HWMI_METHOD_GUID)) {
+   huawei_wmi_debugfs_exit(&pdev->dev);
huawei_wmi_battery_exit(&pdev->dev);
huawei_wmi_fn_lock_exit(&pdev->dev);
}
-- 
2.21.0



[PATCH v4 4/6] platform/x86: huawei-wmi: Add battery charging thresholds

2019-09-20 Thread Ayman Bagabas
Control battery charge thresholds through the battery API and driver's
attributes.

Setting battery charging thresholds can introduce a race condition with
MACH-WX9 where two or more threads are trying to read/write values
from/to EC memory.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/Kconfig  |   1 +
 drivers/platform/x86/huawei-wmi.c | 212 ++
 2 files changed, 213 insertions(+)

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 61bf180d25c7..0659589e46bb 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1305,6 +1305,7 @@ config INTEL_ATOMISP2_PM
 
 config HUAWEI_WMI
tristate "Huawei WMI laptop extras driver"
+   depends on ACPI_BATTERY
depends on ACPI_WMI
depends on INPUT
select INPUT_SPARSEKMAP
diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 41904b1cc284..e577786e6306 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -6,6 +6,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -13,7 +14,10 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 #include 
+#include 
 
 /*
  * Huawei WMI GUIDs
@@ -49,10 +53,13 @@ struct quirk_entry {
 static struct quirk_entry *quirks;
 
 struct huawei_wmi {
+   bool battery_available;
+
struct input_dev *idev[2];
struct led_classdev cdev;
struct platform_device *pdev;
 
+   struct mutex battery_lock;
struct mutex wmi_lock;
 };
 
@@ -300,6 +307,208 @@ static void huawei_wmi_leds_setup(struct device *dev)
devm_led_classdev_register(dev, &huawei->cdev);
 }
 
+/* Battery protection */
+
+static int huawei_wmi_battery_get(int *start, int *end)
+{
+   u8 ret[0x100];
+   int err, i;
+
+   err = huawei_wmi_cmd(BATTERY_THRESH_GET, ret, 0x100);
+   if (err)
+   return err;
+
+   /* Find the last two non-zero values. Return status is ignored. */
+   i = 0x100;
+   do {
+   if (start)
+   *start = ret[i-1];
+   if (end)
+   *end = ret[i];
+   } while (i > 2 && !ret[i--]);
+
+   return 0;
+}
+
+static int huawei_wmi_battery_set(int start, int end)
+{
+   union hwmi_arg arg;
+   int err;
+
+   if (start < 0 || end > 100)
+   return -EINVAL;
+
+   arg.cmd = BATTERY_THRESH_SET;
+   arg.args[2] = start;
+   arg.args[3] = end;
+
+   /* This is an edge case were some models turn battery protection
+* off without changing their thresholds values. We clear the
+* values before turning off protection. Sometimes we need a sleep 
delay to
+* make sure these values make their way to EC memory.
+*/
+   if (quirks && quirks->battery_reset && start == 0 && end == 100) {
+   err = huawei_wmi_battery_set(0, 0);
+   if (err)
+   return err;
+
+   msleep(1000);
+   }
+
+   err = huawei_wmi_cmd(arg.cmd, NULL, 0);
+
+   return err;
+}
+
+static ssize_t charge_control_start_threshold_show(struct device *dev,
+   struct device_attribute *attr,
+   char *buf)
+{
+   int err, start;
+
+   err = huawei_wmi_battery_get(&start, NULL);
+   if (err)
+   return err;
+
+   return sprintf(buf, "%d\n", start);
+}
+
+static ssize_t charge_control_end_threshold_show(struct device *dev,
+   struct device_attribute *attr,
+   char *buf)
+{
+   int err, end;
+
+   err = huawei_wmi_battery_get(NULL, &end);
+   if (err)
+   return err;
+
+   return sprintf(buf, "%d\n", end);
+}
+
+static ssize_t charge_control_thresholds_show(struct device *dev,
+   struct device_attribute *attr,
+   char *buf)
+{
+   int err, start, end;
+
+   err = huawei_wmi_battery_get(&start, &end);
+   if (err)
+   return err;
+
+   return sprintf(buf, "%d %d\n", start, end);
+}
+
+static ssize_t charge_control_start_threshold_store(struct device *dev,
+   struct device_attribute *attr,
+   const char *buf, size_t size)
+{
+   int err, start, end;
+
+   err = huawei_wmi_battery_get(NULL, &end);
+   if (err)
+   return err;
+
+   if (sscanf(buf, "%d", &start) != 1)
+   return -EINVAL;
+
+   err = huawei_wmi_battery_set(start, end);
+   if (err)
+   return err;
+
+   return size;
+}
+
+static ssize_t charge_control_end_threshold_store(struct device *dev,
+   struct device_attribute *attr,
+   const char *buf, size_t size)
+{
+   int err, start, end;
+
+   err = huawei_wmi_battery_get(&start, NULL);

[PATCH v4 5/6] platform/x86: huawei-wmi: Add fn-lock support

2019-09-20 Thread Ayman Bagabas
Huawei Matebook laptops uses Fn key and toggle to access F1-F12 keys.
Along with that, there is this feature called fn-lock that inverts the
behavior of this Fn key and the F1-F12 row.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 85 +++
 1 file changed, 85 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index e577786e6306..56697d49377d 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -54,6 +54,7 @@ static struct quirk_entry *quirks;
 
 struct huawei_wmi {
bool battery_available;
+   bool fn_lock_available;
 
struct input_dev *idev[2];
struct led_classdev cdev;
@@ -509,6 +510,88 @@ static void huawei_wmi_battery_exit(struct device *dev)
}
 }
 
+/* Fn lock */
+
+static int huawei_wmi_fn_lock_get(int *on)
+{
+   u8 ret[0x100] = { 0 };
+   int err, i;
+
+   err = huawei_wmi_cmd(FN_LOCK_GET, ret, 0x100);
+   if (err)
+   return err;
+
+   /* Find the first non-zero value. Return status is ignored. */
+   i = 1;
+   do {
+   if (on)
+   *on = ret[i] - 1; // -1 undefined, 0 off, 1 on.
+   } while (i < 0x100 && !ret[i++]);
+
+   return 0;
+}
+
+static int huawei_wmi_fn_lock_set(int on)
+{
+   union hwmi_arg arg;
+
+   arg.cmd = FN_LOCK_SET;
+   arg.args[2] = on + 1; // 0 undefined, 1 off, 2 on.
+
+   return huawei_wmi_cmd(arg.cmd, NULL, 0);
+}
+
+static ssize_t fn_lock_state_show(struct device *dev,
+   struct device_attribute *attr,
+   char *buf)
+{
+   int err, on;
+
+   err = huawei_wmi_fn_lock_get(&on);
+   if (err)
+   return err;
+
+   return sprintf(buf, "%d\n", on);
+}
+
+static ssize_t fn_lock_state_store(struct device *dev,
+   struct device_attribute *attr,
+   const char *buf, size_t size)
+{
+   int on, err;
+
+   if (kstrtoint(buf, 10, &on) ||
+   on < 0 || on > 1)
+   return -EINVAL;
+
+   err = huawei_wmi_fn_lock_set(on);
+   if (err)
+   return err;
+
+   return size;
+}
+
+static DEVICE_ATTR_RW(fn_lock_state);
+
+static void huawei_wmi_fn_lock_setup(struct device *dev)
+{
+   struct huawei_wmi *huawei = dev_get_drvdata(dev);
+
+   huawei->fn_lock_available = true;
+   if (huawei_wmi_fn_lock_get(NULL)) {
+   huawei->fn_lock_available = false;
+   return;
+   }
+
+   device_create_file(dev, &dev_attr_fn_lock_state);
+}
+
+static void huawei_wmi_fn_lock_exit(struct device *dev)
+{
+   if (huawei_wmi->fn_lock_available)
+   device_remove_file(dev, &dev_attr_fn_lock_state);
+}
+
 /* Input */
 
 static void huawei_wmi_process_key(struct input_dev *idev, int code)
@@ -632,6 +715,7 @@ static int huawei_wmi_probe(struct platform_device *pdev)
mutex_init(&huawei_wmi->battery_lock);
 
huawei_wmi_leds_setup(&pdev->dev);
+   huawei_wmi_fn_lock_setup(&pdev->dev);
huawei_wmi_battery_setup(&pdev->dev);
}
 
@@ -651,6 +735,7 @@ static int huawei_wmi_remove(struct platform_device *pdev)
 
if (wmi_has_guid(HWMI_METHOD_GUID)) {
huawei_wmi_battery_exit(&pdev->dev);
+   huawei_wmi_fn_lock_exit(&pdev->dev);
}
 
return 0;
-- 
2.21.0



[PATCH v4 3/6] platform/x86: huawei-wmi: Implement huawei wmi management

2019-09-20 Thread Ayman Bagabas
Huawei Matebook laptops come with a WMI management interface that can
control various aspects of the device. This interface is also found on
the old Matebook X released in 2017.

Use that to control the mic mute LED.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 217 +-
 1 file changed, 180 insertions(+), 37 deletions(-)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 0e919bf56ad1..41904b1cc284 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -11,18 +11,35 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 
 /*
  * Huawei WMI GUIDs
  */
+#define HWMI_METHOD_GUID "ABBC0F5B-8EA1-11D1-A000-C9062910"
 #define HWMI_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
 
 /* Legacy GUIDs */
 #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
 #define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
 
+/* HWMI commands */
+
+enum {
+   BATTERY_THRESH_GET  = 0x1103, /* \GBTT */
+   BATTERY_THRESH_SET  = 0x1003, /* \SBTT */
+   FN_LOCK_GET = 0x0604, /* \GFRS */
+   FN_LOCK_SET = 0x0704, /* \SFRS */
+   MICMUTE_LED_SET = 0x0b04, /* \SMLS */
+};
+
+union hwmi_arg {
+   u64 cmd;
+   u8 args[8];
+};
+
 struct quirk_entry {
bool battery_reset;
bool ec_micmute;
@@ -35,8 +52,8 @@ struct huawei_wmi {
struct input_dev *idev[2];
struct led_classdev cdev;
struct platform_device *pdev;
-   acpi_handle handle;
-   char *acpi_method;
+
+   struct mutex wmi_lock;
 };
 
 struct huawei_wmi *huawei_wmi;
@@ -110,52 +127,168 @@ static const struct dmi_system_id huawei_quirks[] = {
{  }
 };
 
+/* Utils */
+
+static int huawei_wmi_call(struct acpi_buffer *in, struct acpi_buffer *out)
+{
+   acpi_status status;
+
+   mutex_lock(&huawei_wmi->wmi_lock);
+   status = wmi_evaluate_method(HWMI_METHOD_GUID, 0, 1, in, out);
+   mutex_unlock(&huawei_wmi->wmi_lock);
+   if (ACPI_FAILURE(status)) {
+   dev_err(&huawei_wmi->pdev->dev, "Failed to evaluate wmi 
method\n");
+   return -ENODEV;
+   }
+
+   return 0;
+}
+
+/* HWMI takes a 64 bit input and returns either a package with 2 buffers, one 
of
+ * 4 bytes and the other of 256 bytes, or one buffer of size 0x104 (260) bytes.
+ * The first 4 bytes are ignored, we ignore the first 4 bytes buffer if we got 
a
+ * package, or skip the first 4 if a buffer of 0x104 is used. The first byte of
+ * the remaining 0x100 sized buffer has the return status of every call. In 
case
+ * the return status is non-zero, we return -ENODEV but still copy the returned
+ * buffer to the given buffer parameter (buf).
+ */
+static int huawei_wmi_cmd(u64 arg, u8 *buf, size_t buflen)
+{
+   struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
+   struct acpi_buffer in;
+   union acpi_object *obj;
+   size_t len;
+   int err, i;
+
+   in.length = sizeof(arg);
+   in.pointer = &arg;
+
+   /* Some models require calling HWMI twice to execute a command. We 
evaluate
+* HWMI and if we get a non-zero return status we evaluate it again.
+*/
+   for (i = 0; i < 2; i++) {
+   err = huawei_wmi_call(&in, &out);
+   if (err)
+   goto fail_cmd;
+
+   obj = out.pointer;
+   if (!obj) {
+   err = -EIO;
+   goto fail_cmd;
+   }
+
+   switch (obj->type) {
+   /* Models that implement both "legacy" and HWMI tend to return 
a 0x104
+* sized buffer instead of a package of 0x4 and 0x100 buffers.
+*/
+   case ACPI_TYPE_BUFFER:
+   if (obj->buffer.length == 0x104) {
+   // Skip the first 4 bytes.
+   obj->buffer.pointer += 4;
+   len = 0x100;
+   } else {
+   dev_err(&huawei_wmi->pdev->dev, "Bad buffer 
length, got %d\n", obj->buffer.length);
+   err = -EIO;
+   goto fail_cmd;
+   }
+
+   break;
+   /* HWMI returns a package with 2 buffer elements, one of 4 
bytes and the
+* other is 256 bytes.
+*/
+   case ACPI_TYPE_PACKAGE:
+   if (obj->package.count != 2) {
+   dev_err(&huawei_wmi->pdev->dev, "Bad package 
count, got %d\n", obj->package.count);
+   err = -EIO;
+

[PATCH v4 2/6] platform/x86: huawei-wmi: Add quirks and module parameters

2019-09-20 Thread Ayman Bagabas
Introduce quirks and module parameters. 3 quirks are added:
1. Fixes reporting brightness keys twice since it's already handled by
   acpi-video.
2. Some models need a short delay when setting battery thresholds to
   prevent a race condition when two processes read/write. (will be used later)
3. Matebook X (2017) handles micmute led through the "legacy" interface
   which is not currently implemented. Use ACPI EC method to control
   this led. (will be used later)

2 module parameters are added to enable this short delay and/or report
  brightness keys through this driver.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 73 +++
 1 file changed, 73 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 9496ea3c78b5..0e919bf56ad1 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -6,6 +6,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -22,6 +23,14 @@
 #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
 #define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
 
+struct quirk_entry {
+   bool battery_reset;
+   bool ec_micmute;
+   bool report_brightness;
+};
+
+static struct quirk_entry *quirks;
+
 struct huawei_wmi {
struct input_dev *idev[2];
struct led_classdev cdev;
@@ -49,6 +58,58 @@ static const struct key_entry huawei_wmi_keymap[] = {
{ KE_END,0 }
 };
 
+static int battery_reset = -1;
+static int report_brightness = -1;
+
+module_param(battery_reset, bint, 0444);
+MODULE_PARM_DESC(battery_reset,
+   "Reset battery charge values to (0-0) before disabling it using 
(0-100)");
+module_param(report_brightness, bint, 0444);
+MODULE_PARM_DESC(report_brightness,
+   "Report brightness keys.");
+
+/* Quirks */
+
+static int __init dmi_matched(const struct dmi_system_id *dmi)
+{
+   quirks = dmi->driver_data;
+   return 1;
+}
+
+static struct quirk_entry quirk_unknown = {
+};
+
+static struct quirk_entry quirk_battery_reset = {
+   .battery_reset = true,
+};
+
+static struct quirk_entry quirk_matebook_x = {
+   .ec_micmute = true,
+   .report_brightness = true,
+};
+
+static const struct dmi_system_id huawei_quirks[] = {
+   {
+   .callback = dmi_matched,
+   .ident = "Huawei MACH-WX9",
+   .matches = {
+   DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
+   DMI_MATCH(DMI_PRODUCT_NAME, "MACH-WX9"),
+   },
+   .driver_data = &quirk_battery_reset
+   },
+   {
+   .callback = dmi_matched,
+   .ident = "Huawei MateBook X",
+   .matches = {
+   DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
+   DMI_MATCH(DMI_PRODUCT_NAME, "HUAWEI MateBook X")
+   },
+   .driver_data = &quirk_matebook_x
+   },
+   {  }
+};
+
 static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
 {
@@ -139,6 +200,11 @@ static void huawei_wmi_process_key(struct input_dev *idev, 
int code)
return;
}
 
+   if (quirks && !quirks->report_brightness &&
+   (key->sw.code == KEY_BRIGHTNESSDOWN ||
+   key->sw.code == KEY_BRIGHTNESSUP))
+   return;
+
sparse_keymap_report_entry(idev, key, 1, true);
 }
 
@@ -253,6 +319,13 @@ static __init int huawei_wmi_init(void)
if (!huawei_wmi)
return -ENOMEM;
 
+   quirks = &quirk_unknown;
+   dmi_check_system(huawei_quirks);
+   if (battery_reset != -1)
+   quirks->battery_reset = battery_reset;
+   if (report_brightness != -1)
+   quirks->report_brightness = report_brightness;
+
err = platform_driver_register(&huawei_wmi_driver);
if (err)
goto pdrv_err;
-- 
2.21.0



[PATCH v4 1/6] platform/x86: huawei-wmi: Move to platform driver

2019-09-20 Thread Ayman Bagabas
Move from WMI driver to platform driver. This move is necessary since
the driver is no longer a hotkeys driver only. Platform driver makes it
easier for users to access sysfs attributes under (i.e.
/sys/devices/platform/huawei-wmi) compared to wmi driver.

Use WMI device UID, AMW0 has a UID of HWMI. WMI0 is the device name
and doesn't have a UID so keep it as it is.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/Kconfig  |   7 +-
 drivers/platform/x86/huawei-wmi.c | 226 --
 2 files changed, 156 insertions(+), 77 deletions(-)

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 1b67bb578f9f..61bf180d25c7 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1304,7 +1304,7 @@ config INTEL_ATOMISP2_PM
  will be called intel_atomisp2_pm.
 
 config HUAWEI_WMI
-   tristate "Huawei WMI hotkeys driver"
+   tristate "Huawei WMI laptop extras driver"
depends on ACPI_WMI
depends on INPUT
select INPUT_SPARSEKMAP
@@ -1313,9 +1313,8 @@ config HUAWEI_WMI
select LEDS_TRIGGER_AUDIO
select NEW_LEDS
help
- This driver provides support for Huawei WMI hotkeys.
- It enables the missing keys and adds support to the micmute
- LED found on some of these laptops.
+ This driver provides support for Huawei WMI hotkeys, battery charge
+ control, fn-lock, mic-mute LED, and other extra features.
 
  To compile this driver as a module, choose M here: the module
  will be called huawei-wmi.
diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 195a7f3638cb..9496ea3c78b5 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- *  Huawei WMI hotkeys
+ *  Huawei WMI laptop extras driver
  *
  *  Copyright (C) 2018   Ayman Bagabas 
  */
@@ -10,23 +10,28 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 
 /*
  * Huawei WMI GUIDs
  */
-#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
-#define AMW0_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
+#define HWMI_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
 
+/* Legacy GUIDs */
 #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
+#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
 
-struct huawei_wmi_priv {
-   struct input_dev *idev;
+struct huawei_wmi {
+   struct input_dev *idev[2];
struct led_classdev cdev;
+   struct platform_device *pdev;
acpi_handle handle;
char *acpi_method;
 };
 
+struct huawei_wmi *huawei_wmi;
+
 static const struct key_entry huawei_wmi_keymap[] = {
{ KE_KEY,0x281, { KEY_BRIGHTNESSDOWN } },
{ KE_KEY,0x282, { KEY_BRIGHTNESSUP } },
@@ -37,7 +42,7 @@ static const struct key_entry huawei_wmi_keymap[] = {
{ KE_KEY,0x289, { KEY_WLAN } },
// Huawei |M| key
{ KE_KEY,0x28a, { KEY_CONFIG } },
-   // Keyboard backlight
+   // Keyboard backlit
{ KE_IGNORE, 0x293, { KEY_KBDILLUMTOGGLE } },
{ KE_IGNORE, 0x294, { KEY_KBDILLUMUP } },
{ KE_IGNORE, 0x295, { KEY_KBDILLUMUP } },
@@ -47,7 +52,7 @@ static const struct key_entry huawei_wmi_keymap[] = {
 static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
 {
-   struct huawei_wmi_priv *priv = dev_get_drvdata(led_cdev->dev->parent);
+   struct huawei_wmi *huawei = dev_get_drvdata(led_cdev->dev->parent);
acpi_status status;
union acpi_object args[3];
struct acpi_object_list arg_list = {
@@ -58,52 +63,53 @@ static int huawei_wmi_micmute_led_set(struct led_classdev 
*led_cdev,
args[0].type = args[1].type = args[2].type = ACPI_TYPE_INTEGER;
args[1].integer.value = 0x04;
 
-   if (strcmp(priv->acpi_method, "SPIN") == 0) {
+   if (strcmp(huawei->acpi_method, "SPIN") == 0) {
args[0].integer.value = 0;
args[2].integer.value = brightness ? 1 : 0;
-   } else if (strcmp(priv->acpi_method, "WPIN") == 0) {
+   } else if (strcmp(huawei->acpi_method, "WPIN") == 0) {
args[0].integer.value = 1;
args[2].integer.value = brightness ? 0 : 1;
} else {
return -EINVAL;
}
 
-   status = acpi_evaluate_object(priv->handle, priv->acpi_method, 
&arg_list, NULL);
+   status = acpi_evaluate_object(huawei->handle, huawei->acpi_method, 
&arg_list, NULL);
if (ACPI_FAILURE(status))
return -ENXIO;
 
return 0;
 }
 
-static int huawei_wmi_leds_setup(struct wmi_device *wdev)
+static void huawei_wmi_leds_setup(struct device *dev)
 {
-   struct huaw

[PATCH v4 0/6] platform/x86: Huawei WMI laptop extras driver

2019-09-20 Thread Ayman Bagabas
Changes in v4:
* Use int and bint for module params.

Changes in v3:
* Kconfig changes
* Fix NULL cast to int warning.
* Add ACPI_BATTERY as a dependency.

Changes in v2:
* Use battery charge control API.

This patch series introduce changes to huawei-wmi driver that includes:
* Move to platform driver
* Implement driver quirks and parameters
* Implement WMI management interface
* Add micmute LED support through WMI
* Add battery charging protection support through WMI
* Add fn-lock support through WMI
* Add a debugfs interface to WMI

# Move to platform driver

The current driver offers hotkeys and micmute led support only. With
these changes, a platform driver makes more sense since it handles these
changes pretty nicely.

# Implement WMI management interface

Huawei Matebook laptops come with two WMI interfaces. The first being
WMI0 which is considered "legacy" and AFAIK only found on the Matebook X
released in 2017. The second has a UID of "HWMI" and is found in pretty
much all models with a slight difference in implementation except for
the Matebook X (2017). Since this model has two interfaces, some aspects
are controlled through the legacy interface and some through the other
interface. Currently, the legacy interface is not fully implemented and
is only used for hotkeys and further debugging has to be done.

The WMI interface takes a 64 bit integer, although uses 32 bits most of
the time, and returns a 256-260 bytes buffer consists of either one ACPI
buffer of 260 bytes, in the case of Matebook X (2017), or one ACPI
package of two buffers, one with 4 bytes, and the other with 256 bytes.
We only care about the latter 256 buffer in both cases since the 4 bytes
always return zeros. The first byte of this 256 buffer always has the
return status where 1 indicated error. Some models require calling the
WMI interface twice to execute a command.

# Add micmute LED support through WMI

After implementing the WMI interface, micmute LED can be controlled
easily. Models with the legacy interface fall back to ACPI EC method
control since the legacy interface is not implemented.

# Add battery charging protection support through WMI

Most models, that has the WMI interface, are capable of battery
protection where it can control battery charging thresholds and limits
charging the battery to certain values.

# Add fn-lock support through WMI

The behavior of hotkeys is not the same among all models. Some models
require fn-lock to do things like `Ctrl-Ins` or `Alt-PrtSc`. By default,
hotkeys behave as special keys (media keys, Ins, etc), but if a modifier
is used (ctrl, alt, shift) these keys behave as F1-F12 keys. If the Fn
key is toggled on, the hotkeys with or without a modifier, behave as
F1-F12 keys. This makes it impossible to use a modifier and `PrtSc` or
`Ins`.

Now, some models fix this by excluding `PrtSc` and `Ins` keys from being
treated as F11 and F12 keys with the use of a modifier. However, some
models do not, and fixes this by the so called fn-lock.

Fn-lock inverts the behavior of the top row from special keys to F1-F12
keys. So a modifier and a special key would be possible which make
things like `Alt-Ins` possible. Now, with fn-lock we would have 4 modes:

* Fn-key off & fn-lock off - hotkeys treated as special keys using a
  modifier gives F1-F12 keys.
* Fn-key on & fn-lock off - hotkeys treated as F1-F12 keys and using a
  modifier gives F1-F12.
* Fn-key off & fn-lock on - hotkeys are treated as F1-F12 keys and using
  a modifier gives special keys.
* Fn-key on & fn-lock on - hotkeys are treated as special keys and using
  a modifier gives special keys.

# Implement driver quirks and parameters

The driver introduces 3 quirks and 2 parameters that can change the
driver's behavior. These quirks being as:
1. Fixes reporting brightness keys twice since it's already handled by
   acpi-video.
2. Some models need a short delay when setting battery thresholds to
   prevent a race condition when two processes read/write.
3. Matebook X (2017) handles micmute led through the "legacy" interface
   which is not currently implemented. Use ACPI EC method to control
   this led.

and the 2 parameters can enforce the behavior of quirk 1 & 2.

# Add a debugfs interface to WMI

An interface to the WMI management interface that allows easier
debugging.

Ayman Bagabas (6):
  platform/x86: huawei-wmi: Move to platform driver
  platform/x86: huawei-wmi: Add quirks and module parameters
  platform/x86: huawei-wmi: Implement huawei wmi management
  platform/x86: huawei-wmi: Add battery charging thresholds
  platform/x86: huawei-wmi: Add fn-lock support
  platform/x86: huawei-wmi: Add debugfs support

 drivers/platform/x86/Kconfig  |   8 +-
 drivers/platform/x86/huawei-wmi.c | 874 ++
 2 files changed, 786 insertions(+), 96 deletions(-)


base-commit: 288b9117de5cc1b7fb80f54b7c17deed6f018641
-- 
2.21.0



[PATCH v3 4/6] platform/x86: huawei-wmi: Add battery charging thresholds

2019-09-19 Thread Ayman Bagabas
Control battery charge thresholds through the battery API and driver's
attributes.

Setting battery charging thresholds can introduce a race condition with
MACH-WX9 where two or more threads are trying to read/write values
from/to EC memory.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/Kconfig  |   1 +
 drivers/platform/x86/huawei-wmi.c | 212 ++
 2 files changed, 213 insertions(+)

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 61bf180d25c7..0659589e46bb 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1305,6 +1305,7 @@ config INTEL_ATOMISP2_PM
 
 config HUAWEI_WMI
tristate "Huawei WMI laptop extras driver"
+   depends on ACPI_BATTERY
depends on ACPI_WMI
depends on INPUT
select INPUT_SPARSEKMAP
diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 63e79b5f8282..4ca1a6896766 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -6,6 +6,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -13,7 +14,10 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 #include 
+#include 
 
 /*
  * Huawei WMI GUIDs
@@ -54,11 +58,14 @@ struct huawei_wmi_debug {
 };
 
 struct huawei_wmi {
+   bool battery_available;
+
struct huawei_wmi_debug debug;
struct input_dev *idev[2];
struct led_classdev cdev;
struct platform_device *pdev;
 
+   struct mutex battery_lock;
struct mutex wmi_lock;
 };
 
@@ -306,6 +313,208 @@ static void huawei_wmi_leds_setup(struct device *dev)
devm_led_classdev_register(dev, &huawei->cdev);
 }
 
+/* Battery protection */
+
+static int huawei_wmi_battery_get(int *start, int *end)
+{
+   u8 ret[0x100];
+   int err, i;
+
+   err = huawei_wmi_cmd(BATTERY_THRESH_GET, ret, 0x100);
+   if (err)
+   return err;
+
+   /* Find the last two non-zero values. Return status is ignored. */
+   i = 0x100;
+   do {
+   if (start)
+   *start = ret[i-1];
+   if (end)
+   *end = ret[i];
+   } while (i > 2 && !ret[i--]);
+
+   return 0;
+}
+
+static int huawei_wmi_battery_set(int start, int end)
+{
+   union hwmi_arg arg;
+   int err;
+
+   if (start < 0 || end > 100)
+   return -EINVAL;
+
+   arg.cmd = BATTERY_THRESH_SET;
+   arg.args[2] = start;
+   arg.args[3] = end;
+
+   /* This is an edge case were some models turn battery protection
+* off without changing their thresholds values. We clear the
+* values before turning off protection. Sometimes we need a sleep 
delay to
+* make sure these values make their way to EC memory.
+*/
+   if (quirks && quirks->battery_reset && start == 0 && end == 100) {
+   err = huawei_wmi_battery_set(0, 0);
+   if (err)
+   return err;
+
+   msleep(1000);
+   }
+
+   err = huawei_wmi_cmd(arg.cmd, NULL, 0);
+
+   return err;
+}
+
+static ssize_t charge_control_start_threshold_show(struct device *dev,
+   struct device_attribute *attr,
+   char *buf)
+{
+   int err, start;
+
+   err = huawei_wmi_battery_get(&start, NULL);
+   if (err)
+   return err;
+
+   return sprintf(buf, "%d\n", start);
+}
+
+static ssize_t charge_control_end_threshold_show(struct device *dev,
+   struct device_attribute *attr,
+   char *buf)
+{
+   int err, end;
+
+   err = huawei_wmi_battery_get(NULL, &end);
+   if (err)
+   return err;
+
+   return sprintf(buf, "%d\n", end);
+}
+
+static ssize_t charge_control_thresholds_show(struct device *dev,
+   struct device_attribute *attr,
+   char *buf)
+{
+   int err, start, end;
+
+   err = huawei_wmi_battery_get(&start, &end);
+   if (err)
+   return err;
+
+   return sprintf(buf, "%d %d\n", start, end);
+}
+
+static ssize_t charge_control_start_threshold_store(struct device *dev,
+   struct device_attribute *attr,
+   const char *buf, size_t size)
+{
+   int err, start, end;
+
+   err = huawei_wmi_battery_get(NULL, &end);
+   if (err)
+   return err;
+
+   if (sscanf(buf, "%d", &start) != 1)
+   return -EINVAL;
+
+   err = huawei_wmi_battery_set(start, end);
+   if (err)
+   return err;
+
+   return size;
+}
+
+static ssize_t charge_control_end_threshold_store(struct device *dev,
+   struct device_attribute *attr,
+   const char *buf, size_t size)
+{
+   int err, start, end;
+
+   err = huawei_wmi_battery_get(&start, NULL);

[PATCH v3 6/6] platform/x86: huawei-wmi: Add debugfs support

2019-09-19 Thread Ayman Bagabas
Add a debugfs interface that can be used to call the WMI management
interface function if available.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 91 +++
 1 file changed, 91 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 8fc11a296357..c8f41121160c 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -6,6 +6,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -598,6 +599,94 @@ static void huawei_wmi_fn_lock_exit(struct device *dev)
device_remove_file(dev, &dev_attr_fn_lock_state);
 }
 
+/* debugfs */
+
+static void huawei_wmi_debugfs_call_dump(struct seq_file *m, void *data,
+   union acpi_object *obj)
+{
+   struct huawei_wmi *huawei = m->private;
+   int i;
+
+   switch (obj->type) {
+   case ACPI_TYPE_INTEGER:
+   seq_printf(m, "0x%llx", obj->integer.value);
+   break;
+   case ACPI_TYPE_STRING:
+   seq_printf(m, "\"%*s\"", obj->string.length, 
obj->string.pointer);
+   break;
+   case ACPI_TYPE_BUFFER:
+   seq_puts(m, "{");
+   for (i = 0; i < obj->buffer.length; i++) {
+   seq_printf(m, "0x%02x", obj->buffer.pointer[i]);
+   if (i < obj->buffer.length - 1)
+   seq_puts(m, ",");
+   }
+   seq_puts(m, "}");
+   break;
+   case ACPI_TYPE_PACKAGE:
+   seq_puts(m, "[");
+   for (i = 0; i < obj->package.count; i++) {
+   huawei_wmi_debugfs_call_dump(m, huawei, 
&obj->package.elements[i]);
+   if (i < obj->package.count - 1)
+   seq_puts(m, ",");
+   }
+   seq_puts(m, "]");
+   break;
+   default:
+   dev_err(&huawei->pdev->dev, "Unexpected obj type, got %d\n", 
obj->type);
+   return;
+   }
+}
+
+static int huawei_wmi_debugfs_call_show(struct seq_file *m, void *data)
+{
+   struct huawei_wmi *huawei = m->private;
+   struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
+   struct acpi_buffer in;
+   union acpi_object *obj;
+   int err;
+
+   in.length = sizeof(u64);
+   in.pointer = &huawei->debug.arg;
+
+   err = huawei_wmi_call(&in, &out);
+   if (err)
+   return err;
+
+   obj = out.pointer;
+   if (!obj) {
+   err = -EIO;
+   goto fail_debugfs_call;
+   }
+
+   huawei_wmi_debugfs_call_dump(m, huawei, obj);
+
+fail_debugfs_call:
+   kfree(out.pointer);
+   return err;
+}
+
+DEFINE_SHOW_ATTRIBUTE(huawei_wmi_debugfs_call);
+
+static void huawei_wmi_debugfs_setup(struct device *dev)
+{
+   struct huawei_wmi *huawei = dev_get_drvdata(dev);
+
+   huawei->debug.root = debugfs_create_dir("huawei-wmi", NULL);
+
+   debugfs_create_x64("arg", 0644, huawei->debug.root,
+   &huawei->debug.arg);
+   debugfs_create_file("call", 0400,
+   huawei->debug.root, huawei, &huawei_wmi_debugfs_call_fops);
+}
+
+static void huawei_wmi_debugfs_exit(struct device *dev)
+{
+   struct huawei_wmi *huawei = dev_get_drvdata(dev);
+
+   debugfs_remove_recursive(huawei->debug.root);
+}
+
 /* Input */
 
 static void huawei_wmi_process_key(struct input_dev *idev, int code)
@@ -723,6 +812,7 @@ static int huawei_wmi_probe(struct platform_device *pdev)
huawei_wmi_leds_setup(&pdev->dev);
huawei_wmi_fn_lock_setup(&pdev->dev);
huawei_wmi_battery_setup(&pdev->dev);
+   huawei_wmi_debugfs_setup(&pdev->dev);
}
 
return 0;
@@ -740,6 +830,7 @@ static int huawei_wmi_remove(struct platform_device *pdev)
}
 
if (wmi_has_guid(HWMI_METHOD_GUID)) {
+   huawei_wmi_debugfs_exit(&pdev->dev);
huawei_wmi_battery_exit(&pdev->dev);
huawei_wmi_fn_lock_exit(&pdev->dev);
}
-- 
2.21.0



[PATCH v3 2/6] platform/x86: huawei-wmi: Add quirks and module parameters

2019-09-19 Thread Ayman Bagabas
Introduce quirks and module parameters. 3 quirks are added:
1. Fixes reporting brightness keys twice since it's already handled by
   acpi-video.
2. Some models need a short delay when setting battery thresholds to
   prevent a race condition when two processes read/write. (will be used later)
3. Matebook X (2017) handles micmute led through the "legacy" interface
   which is not currently implemented. Use ACPI EC method to control
   this led. (will be used later)

2 module parameters are added to enable this short delay and/or report
  brightness keys through this driver.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 77 +++
 1 file changed, 77 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 9496ea3c78b5..97ff3d868765 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -6,6 +6,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -22,7 +23,21 @@
 #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
 #define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
 
+struct quirk_entry {
+   bool battery_reset;
+   bool ec_micmute;
+   bool report_brightness;
+};
+
+static struct quirk_entry *quirks;
+
+struct huawei_wmi_debug {
+   struct dentry *root;
+   u64 arg;
+};
+
 struct huawei_wmi {
+   struct huawei_wmi_debug debug;
struct input_dev *idev[2];
struct led_classdev cdev;
struct platform_device *pdev;
@@ -49,6 +64,58 @@ static const struct key_entry huawei_wmi_keymap[] = {
{ KE_END,0 }
 };
 
+static bool battery_reset;
+static bool report_brightness;
+
+module_param(battery_reset, bool, 0444);
+MODULE_PARM_DESC(battery_reset,
+   "Reset battery charge values to (0-0) before disabling it using 
(0-100)");
+module_param(report_brightness, bool, 0444);
+MODULE_PARM_DESC(report_brightness,
+   "Report brightness keys.");
+
+/* Quirks */
+
+static int __init dmi_matched(const struct dmi_system_id *dmi)
+{
+   quirks = dmi->driver_data;
+   return 1;
+}
+
+static struct quirk_entry quirk_unknown = {
+};
+
+static struct quirk_entry quirk_battery_reset = {
+   .battery_reset = true,
+};
+
+static struct quirk_entry quirk_matebook_x = {
+   .ec_micmute = true,
+   .report_brightness = true,
+};
+
+static const struct dmi_system_id huawei_quirks[] = {
+   {
+   .callback = dmi_matched,
+   .ident = "Huawei MACH-WX9",
+   .matches = {
+   DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
+   DMI_MATCH(DMI_PRODUCT_NAME, "MACH-WX9"),
+   },
+   .driver_data = &quirk_battery_reset
+   },
+   {
+   .callback = dmi_matched,
+   .ident = "Huawei MateBook X",
+   .matches = {
+   DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
+   DMI_MATCH(DMI_PRODUCT_NAME, "HUAWEI MateBook X")
+   },
+   .driver_data = &quirk_matebook_x
+   },
+   {  }
+};
+
 static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
 {
@@ -139,6 +206,11 @@ static void huawei_wmi_process_key(struct input_dev *idev, 
int code)
return;
}
 
+   if (quirks && !quirks->report_brightness &&
+   (key->sw.code == KEY_BRIGHTNESSDOWN ||
+   key->sw.code == KEY_BRIGHTNESSUP))
+   return;
+
sparse_keymap_report_entry(idev, key, 1, true);
 }
 
@@ -253,6 +325,11 @@ static __init int huawei_wmi_init(void)
if (!huawei_wmi)
return -ENOMEM;
 
+   quirks = &quirk_unknown;
+   dmi_check_system(huawei_quirks);
+   quirks->battery_reset |= battery_reset;
+   quirks->report_brightness |= report_brightness;
+
err = platform_driver_register(&huawei_wmi_driver);
if (err)
goto pdrv_err;
-- 
2.21.0



[PATCH v3 3/6] platform/x86: huawei-wmi: Implement huawei wmi management

2019-09-19 Thread Ayman Bagabas
Huawei Matebook laptops come with a WMI management interface that can
control various aspects of the device. This interface is also found on
the old Matebook X released in 2017.

Use that to control the mic mute LED.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 217 +-
 1 file changed, 180 insertions(+), 37 deletions(-)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 97ff3d868765..63e79b5f8282 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -11,18 +11,35 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 
 /*
  * Huawei WMI GUIDs
  */
+#define HWMI_METHOD_GUID "ABBC0F5B-8EA1-11D1-A000-C9062910"
 #define HWMI_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
 
 /* Legacy GUIDs */
 #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
 #define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
 
+/* HWMI commands */
+
+enum {
+   BATTERY_THRESH_GET  = 0x1103, /* \GBTT */
+   BATTERY_THRESH_SET  = 0x1003, /* \SBTT */
+   FN_LOCK_GET = 0x0604, /* \GFRS */
+   FN_LOCK_SET = 0x0704, /* \SFRS */
+   MICMUTE_LED_SET = 0x0b04, /* \SMLS */
+};
+
+union hwmi_arg {
+   u64 cmd;
+   u8 args[8];
+};
+
 struct quirk_entry {
bool battery_reset;
bool ec_micmute;
@@ -41,8 +58,8 @@ struct huawei_wmi {
struct input_dev *idev[2];
struct led_classdev cdev;
struct platform_device *pdev;
-   acpi_handle handle;
-   char *acpi_method;
+
+   struct mutex wmi_lock;
 };
 
 struct huawei_wmi *huawei_wmi;
@@ -116,52 +133,168 @@ static const struct dmi_system_id huawei_quirks[] = {
{  }
 };
 
+/* Utils */
+
+static int huawei_wmi_call(struct acpi_buffer *in, struct acpi_buffer *out)
+{
+   acpi_status status;
+
+   mutex_lock(&huawei_wmi->wmi_lock);
+   status = wmi_evaluate_method(HWMI_METHOD_GUID, 0, 1, in, out);
+   mutex_unlock(&huawei_wmi->wmi_lock);
+   if (ACPI_FAILURE(status)) {
+   dev_err(&huawei_wmi->pdev->dev, "Failed to evaluate wmi 
method\n");
+   return -ENODEV;
+   }
+
+   return 0;
+}
+
+/* HWMI takes a 64 bit input and returns either a package with 2 buffers, one 
of
+ * 4 bytes and the other of 256 bytes, or one buffer of size 0x104 (260) bytes.
+ * The first 4 bytes are ignored, we ignore the first 4 bytes buffer if we got 
a
+ * package, or skip the first 4 if a buffer of 0x104 is used. The first byte of
+ * the remaining 0x100 sized buffer has the return status of every call. In 
case
+ * the return status is non-zero, we return -ENODEV but still copy the returned
+ * buffer to the given buffer parameter (buf).
+ */
+static int huawei_wmi_cmd(u64 arg, u8 *buf, size_t buflen)
+{
+   struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
+   struct acpi_buffer in;
+   union acpi_object *obj;
+   size_t len;
+   int err, i;
+
+   in.length = sizeof(arg);
+   in.pointer = &arg;
+
+   /* Some models require calling HWMI twice to execute a command. We 
evaluate
+* HWMI and if we get a non-zero return status we evaluate it again.
+*/
+   for (i = 0; i < 2; i++) {
+   err = huawei_wmi_call(&in, &out);
+   if (err)
+   goto fail_cmd;
+
+   obj = out.pointer;
+   if (!obj) {
+   err = -EIO;
+   goto fail_cmd;
+   }
+
+   switch (obj->type) {
+   /* Models that implement both "legacy" and HWMI tend to return 
a 0x104
+* sized buffer instead of a package of 0x4 and 0x100 buffers.
+*/
+   case ACPI_TYPE_BUFFER:
+   if (obj->buffer.length == 0x104) {
+   // Skip the first 4 bytes.
+   obj->buffer.pointer += 4;
+   len = 0x100;
+   } else {
+   dev_err(&huawei_wmi->pdev->dev, "Bad buffer 
length, got %d\n", obj->buffer.length);
+   err = -EIO;
+   goto fail_cmd;
+   }
+
+   break;
+   /* HWMI returns a package with 2 buffer elements, one of 4 
bytes and the
+* other is 256 bytes.
+*/
+   case ACPI_TYPE_PACKAGE:
+   if (obj->package.count != 2) {
+   dev_err(&huawei_wmi->pdev->dev, "Bad package 
count, got %d\n", obj->package.count);
+   err = -EIO;
+

[PATCH v3 5/6] platform/x86: huawei-wmi: Add fn-lock support

2019-09-19 Thread Ayman Bagabas
Huawei Matebook laptops uses Fn key and toggle to access F1-F12 keys.
Along with that, there is this feature called fn-lock that inverts the
behavior of this Fn key and the F1-F12 row.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 85 +++
 1 file changed, 85 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 4ca1a6896766..8fc11a296357 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -59,6 +59,7 @@ struct huawei_wmi_debug {
 
 struct huawei_wmi {
bool battery_available;
+   bool fn_lock_available;
 
struct huawei_wmi_debug debug;
struct input_dev *idev[2];
@@ -515,6 +516,88 @@ static void huawei_wmi_battery_exit(struct device *dev)
}
 }
 
+/* Fn lock */
+
+static int huawei_wmi_fn_lock_get(int *on)
+{
+   u8 ret[0x100] = { 0 };
+   int err, i;
+
+   err = huawei_wmi_cmd(FN_LOCK_GET, ret, 0x100);
+   if (err)
+   return err;
+
+   /* Find the first non-zero value. Return status is ignored. */
+   i = 1;
+   do {
+   if (on)
+   *on = ret[i] - 1; // -1 undefined, 0 off, 1 on.
+   } while (i < 0x100 && !ret[i++]);
+
+   return 0;
+}
+
+static int huawei_wmi_fn_lock_set(int on)
+{
+   union hwmi_arg arg;
+
+   arg.cmd = FN_LOCK_SET;
+   arg.args[2] = on + 1; // 0 undefined, 1 off, 2 on.
+
+   return huawei_wmi_cmd(arg.cmd, NULL, 0);
+}
+
+static ssize_t fn_lock_state_show(struct device *dev,
+   struct device_attribute *attr,
+   char *buf)
+{
+   int err, on;
+
+   err = huawei_wmi_fn_lock_get(&on);
+   if (err)
+   return err;
+
+   return sprintf(buf, "%d\n", on);
+}
+
+static ssize_t fn_lock_state_store(struct device *dev,
+   struct device_attribute *attr,
+   const char *buf, size_t size)
+{
+   int on, err;
+
+   if (kstrtoint(buf, 10, &on) ||
+   on < 0 || on > 1)
+   return -EINVAL;
+
+   err = huawei_wmi_fn_lock_set(on);
+   if (err)
+   return err;
+
+   return size;
+}
+
+static DEVICE_ATTR_RW(fn_lock_state);
+
+static void huawei_wmi_fn_lock_setup(struct device *dev)
+{
+   struct huawei_wmi *huawei = dev_get_drvdata(dev);
+
+   huawei->fn_lock_available = true;
+   if (huawei_wmi_fn_lock_get(NULL)) {
+   huawei->fn_lock_available = false;
+   return;
+   }
+
+   device_create_file(dev, &dev_attr_fn_lock_state);
+}
+
+static void huawei_wmi_fn_lock_exit(struct device *dev)
+{
+   if (huawei_wmi->fn_lock_available)
+   device_remove_file(dev, &dev_attr_fn_lock_state);
+}
+
 /* Input */
 
 static void huawei_wmi_process_key(struct input_dev *idev, int code)
@@ -638,6 +721,7 @@ static int huawei_wmi_probe(struct platform_device *pdev)
mutex_init(&huawei_wmi->battery_lock);
 
huawei_wmi_leds_setup(&pdev->dev);
+   huawei_wmi_fn_lock_setup(&pdev->dev);
huawei_wmi_battery_setup(&pdev->dev);
}
 
@@ -657,6 +741,7 @@ static int huawei_wmi_remove(struct platform_device *pdev)
 
if (wmi_has_guid(HWMI_METHOD_GUID)) {
huawei_wmi_battery_exit(&pdev->dev);
+   huawei_wmi_fn_lock_exit(&pdev->dev);
}
 
return 0;
-- 
2.21.0



[PATCH v3 1/6] platform/x86: huawei-wmi: Move to platform driver

2019-09-19 Thread Ayman Bagabas
Move from WMI driver to platform driver. This move is necessary since
the driver is no longer a hotkeys driver only. Platform driver makes it
easier for users to access sysfs attributes under (i.e.
/sys/devices/platform/huawei-wmi) compared to wmi driver.

Use WMI device UID, AMW0 has a UID of HWMI. WMI0 is the device name
and doesn't have a UID so keep it as it is.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/Kconfig  |   7 +-
 drivers/platform/x86/huawei-wmi.c | 226 --
 2 files changed, 156 insertions(+), 77 deletions(-)

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 1b67bb578f9f..61bf180d25c7 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1304,7 +1304,7 @@ config INTEL_ATOMISP2_PM
  will be called intel_atomisp2_pm.
 
 config HUAWEI_WMI
-   tristate "Huawei WMI hotkeys driver"
+   tristate "Huawei WMI laptop extras driver"
depends on ACPI_WMI
depends on INPUT
select INPUT_SPARSEKMAP
@@ -1313,9 +1313,8 @@ config HUAWEI_WMI
select LEDS_TRIGGER_AUDIO
select NEW_LEDS
help
- This driver provides support for Huawei WMI hotkeys.
- It enables the missing keys and adds support to the micmute
- LED found on some of these laptops.
+ This driver provides support for Huawei WMI hotkeys, battery charge
+ control, fn-lock, mic-mute LED, and other extra features.
 
  To compile this driver as a module, choose M here: the module
  will be called huawei-wmi.
diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 195a7f3638cb..9496ea3c78b5 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- *  Huawei WMI hotkeys
+ *  Huawei WMI laptop extras driver
  *
  *  Copyright (C) 2018   Ayman Bagabas 
  */
@@ -10,23 +10,28 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 
 /*
  * Huawei WMI GUIDs
  */
-#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
-#define AMW0_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
+#define HWMI_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
 
+/* Legacy GUIDs */
 #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
+#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
 
-struct huawei_wmi_priv {
-   struct input_dev *idev;
+struct huawei_wmi {
+   struct input_dev *idev[2];
struct led_classdev cdev;
+   struct platform_device *pdev;
acpi_handle handle;
char *acpi_method;
 };
 
+struct huawei_wmi *huawei_wmi;
+
 static const struct key_entry huawei_wmi_keymap[] = {
{ KE_KEY,0x281, { KEY_BRIGHTNESSDOWN } },
{ KE_KEY,0x282, { KEY_BRIGHTNESSUP } },
@@ -37,7 +42,7 @@ static const struct key_entry huawei_wmi_keymap[] = {
{ KE_KEY,0x289, { KEY_WLAN } },
// Huawei |M| key
{ KE_KEY,0x28a, { KEY_CONFIG } },
-   // Keyboard backlight
+   // Keyboard backlit
{ KE_IGNORE, 0x293, { KEY_KBDILLUMTOGGLE } },
{ KE_IGNORE, 0x294, { KEY_KBDILLUMUP } },
{ KE_IGNORE, 0x295, { KEY_KBDILLUMUP } },
@@ -47,7 +52,7 @@ static const struct key_entry huawei_wmi_keymap[] = {
 static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
 {
-   struct huawei_wmi_priv *priv = dev_get_drvdata(led_cdev->dev->parent);
+   struct huawei_wmi *huawei = dev_get_drvdata(led_cdev->dev->parent);
acpi_status status;
union acpi_object args[3];
struct acpi_object_list arg_list = {
@@ -58,52 +63,53 @@ static int huawei_wmi_micmute_led_set(struct led_classdev 
*led_cdev,
args[0].type = args[1].type = args[2].type = ACPI_TYPE_INTEGER;
args[1].integer.value = 0x04;
 
-   if (strcmp(priv->acpi_method, "SPIN") == 0) {
+   if (strcmp(huawei->acpi_method, "SPIN") == 0) {
args[0].integer.value = 0;
args[2].integer.value = brightness ? 1 : 0;
-   } else if (strcmp(priv->acpi_method, "WPIN") == 0) {
+   } else if (strcmp(huawei->acpi_method, "WPIN") == 0) {
args[0].integer.value = 1;
args[2].integer.value = brightness ? 0 : 1;
} else {
return -EINVAL;
}
 
-   status = acpi_evaluate_object(priv->handle, priv->acpi_method, 
&arg_list, NULL);
+   status = acpi_evaluate_object(huawei->handle, huawei->acpi_method, 
&arg_list, NULL);
if (ACPI_FAILURE(status))
return -ENXIO;
 
return 0;
 }
 
-static int huawei_wmi_leds_setup(struct wmi_device *wdev)
+static void huawei_wmi_leds_setup(struct device *dev)
 {
-   struct huaw

[PATCH v3 0/6] platform/x86: Huawei WMI laptop extras driver

2019-09-19 Thread Ayman Bagabas
Changes in v3:
* Kconfig changes
* Fix NULL cast to int warning.
* Add ACPI_BATTERY as a dependency.

Changes in v2:
* Use battery charge control API.

This patch series introduce changes to huawei-wmi driver that includes:
* Move to platform driver
* Implement driver quirks and parameters
* Implement WMI management interface
* Add micmute LED support through WMI
* Add battery charging protection support through WMI
* Add fn-lock support through WMI
* Add a debugfs interface to WMI

# Move to platform driver

The current driver offers hotkeys and micmute led support only. With
these changes, a platform driver makes more sense since it handles these
changes pretty nicely.

# Implement WMI management interface

Huawei Matebook laptops come with two WMI interfaces. The first being
WMI0 which is considered "legacy" and AFAIK only found on the Matebook X
released in 2017. The second has a UID of "HWMI" and is found in pretty
much all models with a slight difference in implementation except for
the Matebook X (2017). Since this model has two interfaces, some aspects
are controlled through the legacy interface and some through the other
interface. Currently, the legacy interface is not fully implemented and
is only used for hotkeys and further debugging has to be done.

The WMI interface takes a 64 bit integer, although uses 32 bits most of
the time, and returns a 256-260 bytes buffer consists of either one ACPI
buffer of 260 bytes, in the case of Matebook X (2017), or one ACPI
package of two buffers, one with 4 bytes, and the other with 256 bytes.
We only care about the latter 256 buffer in both cases since the 4 bytes
always return zeros. The first byte of this 256 buffer always has the
return status where 1 indicated error. Some models require calling the
WMI interface twice to execute a command.

# Add micmute LED support through WMI

After implementing the WMI interface, micmute LED can be controlled
easily. Models with the legacy interface fall back to ACPI EC method
control since the legacy interface is not implemented.

# Add battery charging protection support through WMI

Most models, that has the WMI interface, are capable of battery
protection where it can control battery charging thresholds and limits
charging the battery to certain values.

# Add fn-lock support through WMI

The behavior of hotkeys is not the same among all models. Some models
require fn-lock to do things like `Ctrl-Ins` or `Alt-PrtSc`. By default,
hotkeys behave as special keys (media keys, Ins, etc), but if a modifier
is used (ctrl, alt, shift) these keys behave as F1-F12 keys. If the Fn
key is toggled on, the hotkeys with or without a modifier, behave as
F1-F12 keys. This makes it impossible to use a modifier and `PrtSc` or
`Ins`.

Now, some models fix this by excluding `PrtSc` and `Ins` keys from being
treated as F11 and F12 keys with the use of a modifier. However, some
models do not, and fixes this by the so called fn-lock.

Fn-lock inverts the behavior of the top row from special keys to F1-F12
keys. So a modifier and a special key would be possible which make
things like `Alt-Ins` possible. Now, with fn-lock we would have 4 modes:

* Fn-key off & fn-lock off - hotkeys treated as special keys using a
  modifier gives F1-F12 keys.
* Fn-key on & fn-lock off - hotkeys treated as F1-F12 keys and using a
  modifier gives F1-F12.
* Fn-key off & fn-lock on - hotkeys are treated as F1-F12 keys and using
  a modifier gives special keys.
* Fn-key on & fn-lock on - hotkeys are treated as special keys and using
  a modifier gives special keys.

# Implement driver quirks and parameters

The driver introduces 3 quirks and 2 parameters that can change the
driver's behavior. These quirks being as:
1. Fixes reporting brightness keys twice since it's already handled by
   acpi-video.
2. Some models need a short delay when setting battery thresholds to
   prevent a race condition when two processes read/write.
3. Matebook X (2017) handles micmute led through the "legacy" interface
   which is not currently implemented. Use ACPI EC method to control
   this led.

and the 2 parameters can enforce the behavior of quirk 1 & 2.

# Add a debugfs interface to WMI

An interface to the WMI management interface that allows easier
debugging.

Ayman Bagabas (6):
  platform/x86: huawei-wmi: Move to platform driver
  platform/x86: huawei-wmi: Add quirks and module parameters
  platform/x86: huawei-wmi: Implement huawei wmi management
  platform/x86: huawei-wmi: Add battery charging thresholds
  platform/x86: huawei-wmi: Add fn-lock support
  platform/x86: huawei-wmi: Add debugfs support

 drivers/platform/x86/Kconfig  |   1 +
 drivers/platform/x86/huawei-wmi.c | 872 ++
 2 files changed, 781 insertions(+), 92 deletions(-)


base-commit: 288b9117de5cc1b7fb80f54b7c17deed6f018641
-- 
2.21.0



[PATCH v2 5/6] platform/x86: huawei-wmi: Add fn-lock support

2019-09-18 Thread Ayman Bagabas
Huawei Matebook laptops uses Fn key and toggle to access F1-F12 keys.
Along with that, there is this feature called fn-lock that inverts the
behavior of this Fn key and the F1-F12 row.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 85 +++
 1 file changed, 85 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 231b5cce00db..48be55c6027e 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -59,6 +59,7 @@ struct huawei_wmi_debug {
 
 struct huawei_wmi {
bool battery_available;
+   bool fn_lock_available;
 
struct huawei_wmi_debug debug;
struct input_dev *idev[2];
@@ -515,6 +516,88 @@ static void huawei_wmi_battery_exit(struct device *dev)
}
 }
 
+/* Fn lock */
+
+static int huawei_wmi_fn_lock_get(int *on)
+{
+   u8 ret[0x100] = { 0 };
+   int err, i;
+
+   err = huawei_wmi_cmd(FN_LOCK_GET, ret, 0x100);
+   if (err)
+   return err;
+
+   /* Find the first non-zero value. Return status is ignored. */
+   i = 1;
+   do {
+   if (on)
+   *on = ret[i] - 1; // -1 undefined, 0 off, 1 on.
+   } while (i < 0x100 && !ret[i++]);
+
+   return 0;
+}
+
+static int huawei_wmi_fn_lock_set(int on)
+{
+   union hwmi_arg arg;
+
+   arg.cmd = FN_LOCK_SET;
+   arg.args[2] = on + 1; // 0 undefined, 1 off, 2 on.
+
+   return huawei_wmi_cmd(arg.cmd, NULL, NULL);
+}
+
+static ssize_t fn_lock_state_show(struct device *dev,
+   struct device_attribute *attr,
+   char *buf)
+{
+   int err, on;
+
+   err = huawei_wmi_fn_lock_get(&on);
+   if (err)
+   return err;
+
+   return sprintf(buf, "%d\n", on);
+}
+
+static ssize_t fn_lock_state_store(struct device *dev,
+   struct device_attribute *attr,
+   const char *buf, size_t size)
+{
+   int on, err;
+
+   if (kstrtoint(buf, 10, &on) ||
+   on < 0 || on > 1)
+   return -EINVAL;
+
+   err = huawei_wmi_fn_lock_set(on);
+   if (err)
+   return err;
+
+   return size;
+}
+
+static DEVICE_ATTR_RW(fn_lock_state);
+
+static void huawei_wmi_fn_lock_setup(struct device *dev)
+{
+   struct huawei_wmi *huawei = dev_get_drvdata(dev);
+
+   huawei->fn_lock_available = true;
+   if (huawei_wmi_fn_lock_get(NULL)) {
+   huawei->fn_lock_available = false;
+   return;
+   }
+
+   device_create_file(dev, &dev_attr_fn_lock_state);
+}
+
+static void huawei_wmi_fn_lock_exit(struct device *dev)
+{
+   if (huawei_wmi->fn_lock_available)
+   device_remove_file(dev, &dev_attr_fn_lock_state);
+}
+
 /* Input */
 
 static void huawei_wmi_process_key(struct input_dev *idev, int code)
@@ -638,6 +721,7 @@ static int huawei_wmi_probe(struct platform_device *pdev)
mutex_init(&huawei_wmi->battery_lock);
 
huawei_wmi_leds_setup(&pdev->dev);
+   huawei_wmi_fn_lock_setup(&pdev->dev);
huawei_wmi_battery_setup(&pdev->dev);
}
 
@@ -657,6 +741,7 @@ static int huawei_wmi_remove(struct platform_device *pdev)
 
if (wmi_has_guid(HWMI_METHOD_GUID)) {
huawei_wmi_battery_exit(&pdev->dev);
+   huawei_wmi_fn_lock_exit(&pdev->dev);
}
 
return 0;
-- 
2.21.0



[PATCH v2 4/6] platform/x86: huawei-wmi: Add battery charging thresholds

2019-09-18 Thread Ayman Bagabas
Controll battery charge thresholds through the battery API and driver's
attributes.

Setting battery charging thresholds can introduce a race condition with
MACH-WX9 where two or more threads are trying to read/write values
from/to EC memory.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 212 ++
 1 file changed, 212 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 904ef38944b6..231b5cce00db 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -6,6 +6,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -13,7 +14,10 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 #include 
+#include 
 
 /*
  * Huawei WMI GUIDs
@@ -54,11 +58,14 @@ struct huawei_wmi_debug {
 };
 
 struct huawei_wmi {
+   bool battery_available;
+
struct huawei_wmi_debug debug;
struct input_dev *idev[2];
struct led_classdev cdev;
struct platform_device *pdev;
 
+   struct mutex battery_lock;
struct mutex wmi_lock;
 };
 
@@ -306,6 +313,208 @@ static void huawei_wmi_leds_setup(struct device *dev)
devm_led_classdev_register(dev, &huawei->cdev);
 }
 
+/* Battery protection */
+
+static int huawei_wmi_battery_get(int *start, int *end)
+{
+   u8 ret[0x100];
+   int err, i;
+
+   err = huawei_wmi_cmd(BATTERY_THRESH_GET, ret, 0x100);
+   if (err)
+   return err;
+
+   /* Find the last two non-zero values. Return status is ignored. */
+   i = 0x100;
+   do {
+   if (start)
+   *start = ret[i-1];
+   if (end)
+   *end = ret[i];
+   } while (i > 2 && !ret[i--]);
+
+   return 0;
+}
+
+static int huawei_wmi_battery_set(int start, int end)
+{
+   union hwmi_arg arg;
+   int err;
+
+   if (start < 0 || end > 100)
+   return -EINVAL;
+
+   arg.cmd = BATTERY_THRESH_SET;
+   arg.args[2] = start;
+   arg.args[3] = end;
+
+   /* This is an edge case were some models turn battery protection
+* off without changing their thresholds values. We clear the
+* values before turning off protection. Sometimes we need a sleep 
delay to
+* make sure these values make their way to EC memory.
+*/
+   if (quirks && quirks->battery_reset && start == 0 && end == 100) {
+   err = huawei_wmi_battery_set(0, 0);
+   if (err)
+   return err;
+
+   msleep(1000);
+   }
+
+   err = huawei_wmi_cmd(arg.cmd, NULL, NULL);
+
+   return err;
+}
+
+static ssize_t charge_control_start_threshold_show(struct device *dev,
+   struct device_attribute *attr,
+   char *buf)
+{
+   int err, start;
+
+   err = huawei_wmi_battery_get(&start, NULL);
+   if (err)
+   return err;
+
+   return sprintf(buf, "%d\n", start);
+}
+
+static ssize_t charge_control_end_threshold_show(struct device *dev,
+   struct device_attribute *attr,
+   char *buf)
+{
+   int err, end;
+
+   err = huawei_wmi_battery_get(NULL, &end);
+   if (err)
+   return err;
+
+   return sprintf(buf, "%d\n", end);
+}
+
+static ssize_t charge_control_thresholds_show(struct device *dev,
+   struct device_attribute *attr,
+   char *buf)
+{
+   int err, start, end;
+
+   err = huawei_wmi_battery_get(&start, &end);
+   if (err)
+   return err;
+
+   return sprintf(buf, "%d %d\n", start, end);
+}
+
+static ssize_t charge_control_start_threshold_store(struct device *dev,
+   struct device_attribute *attr,
+   const char *buf, size_t size)
+{
+   int err, start, end;
+
+   err = huawei_wmi_battery_get(NULL, &end);
+   if (err)
+   return err;
+
+   if (sscanf(buf, "%d", &start) != 1)
+   return -EINVAL;
+
+   err = huawei_wmi_battery_set(start, end);
+   if (err)
+   return err;
+
+   return size;
+}
+
+static ssize_t charge_control_end_threshold_store(struct device *dev,
+   struct device_attribute *attr,
+   const char *buf, size_t size)
+{
+   int err, start, end;
+
+   err = huawei_wmi_battery_get(&start, NULL);
+   if (err)
+   return err;
+
+   if (sscanf(buf, "%d", &end) != 1)
+   return -EINVAL;
+
+   err = huawei_wmi_battery_set(start, end);
+   if (err)
+   return err;
+
+   return size;
+}
+
+static ssize_t charge_control_thresholds_store(struct device *dev,
+   struct device_attribute *attr,
+   const char *buf, size_t size)
+{
+   int err, start, end;
+
+   if (ss

[PATCH v2 6/6] platform/x86: huawei-wmi: Add debugfs support

2019-09-18 Thread Ayman Bagabas
Add a debugfs interface that can be used to call the WMI management
interface function if available.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 91 +++
 1 file changed, 91 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 48be55c6027e..9ae2ecadeb10 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -6,6 +6,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -598,6 +599,94 @@ static void huawei_wmi_fn_lock_exit(struct device *dev)
device_remove_file(dev, &dev_attr_fn_lock_state);
 }
 
+/* debugfs */
+
+static void huawei_wmi_debugfs_call_dump(struct seq_file *m, void *data,
+   union acpi_object *obj)
+{
+   struct huawei_wmi *huawei = m->private;
+   int i;
+
+   switch (obj->type) {
+   case ACPI_TYPE_INTEGER:
+   seq_printf(m, "0x%llx", obj->integer.value);
+   break;
+   case ACPI_TYPE_STRING:
+   seq_printf(m, "\"%*s\"", obj->string.length, 
obj->string.pointer);
+   break;
+   case ACPI_TYPE_BUFFER:
+   seq_puts(m, "{");
+   for (i = 0; i < obj->buffer.length; i++) {
+   seq_printf(m, "0x%02x", obj->buffer.pointer[i]);
+   if (i < obj->buffer.length - 1)
+   seq_puts(m, ",");
+   }
+   seq_puts(m, "}");
+   break;
+   case ACPI_TYPE_PACKAGE:
+   seq_puts(m, "[");
+   for (i = 0; i < obj->package.count; i++) {
+   huawei_wmi_debugfs_call_dump(m, huawei, 
&obj->package.elements[i]);
+   if (i < obj->package.count - 1)
+   seq_puts(m, ",");
+   }
+   seq_puts(m, "]");
+   break;
+   default:
+   dev_err(&huawei->pdev->dev, "Unexpected obj type, got %d\n", 
obj->type);
+   return;
+   }
+}
+
+static int huawei_wmi_debugfs_call_show(struct seq_file *m, void *data)
+{
+   struct huawei_wmi *huawei = m->private;
+   struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
+   struct acpi_buffer in;
+   union acpi_object *obj;
+   int err;
+
+   in.length = sizeof(u64);
+   in.pointer = &huawei->debug.arg;
+
+   err = huawei_wmi_call(&in, &out);
+   if (err)
+   return err;
+
+   obj = out.pointer;
+   if (!obj) {
+   err = -EIO;
+   goto fail_debugfs_call;
+   }
+
+   huawei_wmi_debugfs_call_dump(m, huawei, obj);
+
+fail_debugfs_call:
+   kfree(out.pointer);
+   return err;
+}
+
+DEFINE_SHOW_ATTRIBUTE(huawei_wmi_debugfs_call);
+
+static void huawei_wmi_debugfs_setup(struct device *dev)
+{
+   struct huawei_wmi *huawei = dev_get_drvdata(dev);
+
+   huawei->debug.root = debugfs_create_dir("huawei-wmi", NULL);
+
+   debugfs_create_x64("arg", 0644, huawei->debug.root,
+   &huawei->debug.arg);
+   debugfs_create_file("call", 0400,
+   huawei->debug.root, huawei, &huawei_wmi_debugfs_call_fops);
+}
+
+static void huawei_wmi_debugfs_exit(struct device *dev)
+{
+   struct huawei_wmi *huawei = dev_get_drvdata(dev);
+
+   debugfs_remove_recursive(huawei->debug.root);
+}
+
 /* Input */
 
 static void huawei_wmi_process_key(struct input_dev *idev, int code)
@@ -723,6 +812,7 @@ static int huawei_wmi_probe(struct platform_device *pdev)
huawei_wmi_leds_setup(&pdev->dev);
huawei_wmi_fn_lock_setup(&pdev->dev);
huawei_wmi_battery_setup(&pdev->dev);
+   huawei_wmi_debugfs_setup(&pdev->dev);
}
 
return 0;
@@ -740,6 +830,7 @@ static int huawei_wmi_remove(struct platform_device *pdev)
}
 
if (wmi_has_guid(HWMI_METHOD_GUID)) {
+   huawei_wmi_debugfs_exit(&pdev->dev);
huawei_wmi_battery_exit(&pdev->dev);
huawei_wmi_fn_lock_exit(&pdev->dev);
}
-- 
2.21.0



[PATCH v2 3/6] platform/x86: huawei-wmi: Implement huawei wmi management

2019-09-18 Thread Ayman Bagabas
Huawei Matebook laptops come with a WMI management interface that can
control various aspects of the device. This interface is also found on
the old Matebook X released in 2017.

Use that to control the mic mute LED.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 217 +-
 1 file changed, 180 insertions(+), 37 deletions(-)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 97ff3d868765..904ef38944b6 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -11,18 +11,35 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 
 /*
  * Huawei WMI GUIDs
  */
+#define HWMI_METHOD_GUID "ABBC0F5B-8EA1-11D1-A000-C9062910"
 #define HWMI_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
 
 /* Legacy GUIDs */
 #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
 #define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
 
+/* HWMI commands */
+
+enum {
+   BATTERY_THRESH_GET  = 0x1103, /* \GBTT */
+   BATTERY_THRESH_SET  = 0x1003, /* \SBTT */
+   FN_LOCK_GET = 0x0604, /* \GFRS */
+   FN_LOCK_SET = 0x0704, /* \SFRS */
+   MICMUTE_LED_SET = 0x0b04, /* \SMLS */
+};
+
+union hwmi_arg {
+   u64 cmd;
+   u8 args[8];
+};
+
 struct quirk_entry {
bool battery_reset;
bool ec_micmute;
@@ -41,8 +58,8 @@ struct huawei_wmi {
struct input_dev *idev[2];
struct led_classdev cdev;
struct platform_device *pdev;
-   acpi_handle handle;
-   char *acpi_method;
+
+   struct mutex wmi_lock;
 };
 
 struct huawei_wmi *huawei_wmi;
@@ -116,52 +133,168 @@ static const struct dmi_system_id huawei_quirks[] = {
{  }
 };
 
+/* Utils */
+
+static int huawei_wmi_call(struct acpi_buffer *in, struct acpi_buffer *out)
+{
+   acpi_status status;
+
+   mutex_lock(&huawei_wmi->wmi_lock);
+   status = wmi_evaluate_method(HWMI_METHOD_GUID, 0, 1, in, out);
+   mutex_unlock(&huawei_wmi->wmi_lock);
+   if (ACPI_FAILURE(status)) {
+   dev_err(&huawei_wmi->pdev->dev, "Failed to evaluate wmi 
method\n");
+   return -ENODEV;
+   }
+
+   return 0;
+}
+
+/* HWMI takes a 64 bit input and returns either a package with 2 buffers, one 
of
+ * 4 bytes and the other of 256 bytes, or one buffer of size 0x104 (260) bytes.
+ * The first 4 bytes are ignored, we ignore the first 4 bytes buffer if we got 
a
+ * package, or skip the first 4 if a buffer of 0x104 is used. The first byte of
+ * the remaining 0x100 sized buffer has the return status of every call. In 
case
+ * the return status is non-zero, we return -ENODEV but still copy the returned
+ * buffer to the given buffer parameter (buf).
+ */
+static int huawei_wmi_cmd(u64 arg, u8 *buf, size_t buflen)
+{
+   struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
+   struct acpi_buffer in;
+   union acpi_object *obj;
+   size_t len;
+   int err, i;
+
+   in.length = sizeof(arg);
+   in.pointer = &arg;
+
+   /* Some models require calling HWMI twice to execute a command. We 
evaluate
+* HWMI and if we get a non-zero return status we evaluate it again.
+*/
+   for (i = 0; i < 2; i++) {
+   err = huawei_wmi_call(&in, &out);
+   if (err)
+   goto fail_cmd;
+
+   obj = out.pointer;
+   if (!obj) {
+   err = -EIO;
+   goto fail_cmd;
+   }
+
+   switch (obj->type) {
+   /* Models that implement both "legacy" and HWMI tend to return 
a 0x104
+* sized buffer instead of a package of 0x4 and 0x100 buffers.
+*/
+   case ACPI_TYPE_BUFFER:
+   if (obj->buffer.length == 0x104) {
+   // Skip the first 4 bytes.
+   obj->buffer.pointer += 4;
+   len = 0x100;
+   } else {
+   dev_err(&huawei_wmi->pdev->dev, "Bad buffer 
length, got %d\n", obj->buffer.length);
+   err = -EIO;
+   goto fail_cmd;
+   }
+
+   break;
+   /* HWMI returns a package with 2 buffer elements, one of 4 
bytes and the
+* other is 256 bytes.
+*/
+   case ACPI_TYPE_PACKAGE:
+   if (obj->package.count != 2) {
+   dev_err(&huawei_wmi->pdev->dev, "Bad package 
count, got %d\n", obj->package.count);
+   err = -EIO;
+

[PATCH v2 2/6] platform/x86: huawei-wmi: Add quirks and module parameters

2019-09-18 Thread Ayman Bagabas
Introduce quirks and module parameters. 3 quirks are added:
1. Fixes reporting brightness keys twice since it's already handled by
   acpi-video.
2. Some models need a short delay when setting battery thresholds to
   prevent a race condition when two processes read/write. (will be used later)
3. Matebook X (2017) handles micmute led through the "legacy" interface
   which is not currently implemented. Use ACPI EC method to control
   this led. (will be used later)

2 module parameters are added to enable this short delay and/or report
  brightness keys through this driver.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 77 +++
 1 file changed, 77 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 9496ea3c78b5..97ff3d868765 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -6,6 +6,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -22,7 +23,21 @@
 #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
 #define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
 
+struct quirk_entry {
+   bool battery_reset;
+   bool ec_micmute;
+   bool report_brightness;
+};
+
+static struct quirk_entry *quirks;
+
+struct huawei_wmi_debug {
+   struct dentry *root;
+   u64 arg;
+};
+
 struct huawei_wmi {
+   struct huawei_wmi_debug debug;
struct input_dev *idev[2];
struct led_classdev cdev;
struct platform_device *pdev;
@@ -49,6 +64,58 @@ static const struct key_entry huawei_wmi_keymap[] = {
{ KE_END,0 }
 };
 
+static bool battery_reset;
+static bool report_brightness;
+
+module_param(battery_reset, bool, 0444);
+MODULE_PARM_DESC(battery_reset,
+   "Reset battery charge values to (0-0) before disabling it using 
(0-100)");
+module_param(report_brightness, bool, 0444);
+MODULE_PARM_DESC(report_brightness,
+   "Report brightness keys.");
+
+/* Quirks */
+
+static int __init dmi_matched(const struct dmi_system_id *dmi)
+{
+   quirks = dmi->driver_data;
+   return 1;
+}
+
+static struct quirk_entry quirk_unknown = {
+};
+
+static struct quirk_entry quirk_battery_reset = {
+   .battery_reset = true,
+};
+
+static struct quirk_entry quirk_matebook_x = {
+   .ec_micmute = true,
+   .report_brightness = true,
+};
+
+static const struct dmi_system_id huawei_quirks[] = {
+   {
+   .callback = dmi_matched,
+   .ident = "Huawei MACH-WX9",
+   .matches = {
+   DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
+   DMI_MATCH(DMI_PRODUCT_NAME, "MACH-WX9"),
+   },
+   .driver_data = &quirk_battery_reset
+   },
+   {
+   .callback = dmi_matched,
+   .ident = "Huawei MateBook X",
+   .matches = {
+   DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
+   DMI_MATCH(DMI_PRODUCT_NAME, "HUAWEI MateBook X")
+   },
+   .driver_data = &quirk_matebook_x
+   },
+   {  }
+};
+
 static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
 {
@@ -139,6 +206,11 @@ static void huawei_wmi_process_key(struct input_dev *idev, 
int code)
return;
}
 
+   if (quirks && !quirks->report_brightness &&
+   (key->sw.code == KEY_BRIGHTNESSDOWN ||
+   key->sw.code == KEY_BRIGHTNESSUP))
+   return;
+
sparse_keymap_report_entry(idev, key, 1, true);
 }
 
@@ -253,6 +325,11 @@ static __init int huawei_wmi_init(void)
if (!huawei_wmi)
return -ENOMEM;
 
+   quirks = &quirk_unknown;
+   dmi_check_system(huawei_quirks);
+   quirks->battery_reset |= battery_reset;
+   quirks->report_brightness |= report_brightness;
+
err = platform_driver_register(&huawei_wmi_driver);
if (err)
goto pdrv_err;
-- 
2.21.0



[PATCH v2 1/6] platform/x86: huawei-wmi: Move to platform driver

2019-09-18 Thread Ayman Bagabas
Move from WMI driver to platform driver. This move is necessary since
the driver is no longer a hotkeys driver only. Platform driver makes it
easier for users to access sysfs attributes under (i.e.
/sys/devices/platform/huawei-wmi) compared to wmi driver.

Use WMI device UID, AMW0 has a UID of HWMI. WMI0 is the device name
and doesn't have a UID so keep it as it is.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 226 --
 1 file changed, 153 insertions(+), 73 deletions(-)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 195a7f3638cb..9496ea3c78b5 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- *  Huawei WMI hotkeys
+ *  Huawei WMI laptop extras driver
  *
  *  Copyright (C) 2018   Ayman Bagabas 
  */
@@ -10,23 +10,28 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 
 /*
  * Huawei WMI GUIDs
  */
-#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
-#define AMW0_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
+#define HWMI_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
 
+/* Legacy GUIDs */
 #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
+#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
 
-struct huawei_wmi_priv {
-   struct input_dev *idev;
+struct huawei_wmi {
+   struct input_dev *idev[2];
struct led_classdev cdev;
+   struct platform_device *pdev;
acpi_handle handle;
char *acpi_method;
 };
 
+struct huawei_wmi *huawei_wmi;
+
 static const struct key_entry huawei_wmi_keymap[] = {
{ KE_KEY,0x281, { KEY_BRIGHTNESSDOWN } },
{ KE_KEY,0x282, { KEY_BRIGHTNESSUP } },
@@ -37,7 +42,7 @@ static const struct key_entry huawei_wmi_keymap[] = {
{ KE_KEY,0x289, { KEY_WLAN } },
// Huawei |M| key
{ KE_KEY,0x28a, { KEY_CONFIG } },
-   // Keyboard backlight
+   // Keyboard backlit
{ KE_IGNORE, 0x293, { KEY_KBDILLUMTOGGLE } },
{ KE_IGNORE, 0x294, { KEY_KBDILLUMUP } },
{ KE_IGNORE, 0x295, { KEY_KBDILLUMUP } },
@@ -47,7 +52,7 @@ static const struct key_entry huawei_wmi_keymap[] = {
 static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
 {
-   struct huawei_wmi_priv *priv = dev_get_drvdata(led_cdev->dev->parent);
+   struct huawei_wmi *huawei = dev_get_drvdata(led_cdev->dev->parent);
acpi_status status;
union acpi_object args[3];
struct acpi_object_list arg_list = {
@@ -58,52 +63,53 @@ static int huawei_wmi_micmute_led_set(struct led_classdev 
*led_cdev,
args[0].type = args[1].type = args[2].type = ACPI_TYPE_INTEGER;
args[1].integer.value = 0x04;
 
-   if (strcmp(priv->acpi_method, "SPIN") == 0) {
+   if (strcmp(huawei->acpi_method, "SPIN") == 0) {
args[0].integer.value = 0;
args[2].integer.value = brightness ? 1 : 0;
-   } else if (strcmp(priv->acpi_method, "WPIN") == 0) {
+   } else if (strcmp(huawei->acpi_method, "WPIN") == 0) {
args[0].integer.value = 1;
args[2].integer.value = brightness ? 0 : 1;
} else {
return -EINVAL;
}
 
-   status = acpi_evaluate_object(priv->handle, priv->acpi_method, 
&arg_list, NULL);
+   status = acpi_evaluate_object(huawei->handle, huawei->acpi_method, 
&arg_list, NULL);
if (ACPI_FAILURE(status))
return -ENXIO;
 
return 0;
 }
 
-static int huawei_wmi_leds_setup(struct wmi_device *wdev)
+static void huawei_wmi_leds_setup(struct device *dev)
 {
-   struct huawei_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
+   struct huawei_wmi *huawei = dev_get_drvdata(dev);
 
-   priv->handle = ec_get_handle();
-   if (!priv->handle)
-   return 0;
+   huawei->handle = ec_get_handle();
+   if (!huawei->handle)
+   return;
 
-   if (acpi_has_method(priv->handle, "SPIN"))
-   priv->acpi_method = "SPIN";
-   else if (acpi_has_method(priv->handle, "WPIN"))
-   priv->acpi_method = "WPIN";
+   if (acpi_has_method(huawei->handle, "SPIN"))
+   huawei->acpi_method = "SPIN";
+   else if (acpi_has_method(huawei->handle, "WPIN"))
+   huawei->acpi_method = "WPIN";
else
-   return 0;
+   return;
 
-   priv->cdev.name = "platform::micmute";
-   priv->cdev.max_brightness = 1;
-   priv->cdev.brightness_set_blocking = huawei_wmi_micmute_led_set;
-   priv->cd

[PATCH v2 0/6] platform/x86: Huawei WMI laptop extras driver

2019-09-18 Thread Ayman Bagabas
Changes in v2:
* Use battery charge control API.

This patch series introduce changes to huawei-wmi driver that includes:
* Move to platform driver
* Implement driver quirks and parameters
* Implement WMI management interface
* Add micmute LED support through WMI
* Add battery charging protection support through WMI
* Add fn-lock support through WMI
* Add a debugfs interface to WMI

# Move to platform driver

The current driver offers hotkeys and micmute led support only. With
these changes, a platform driver makes more sense since it handles these
changes pretty nicely.

# Implement WMI management interface

Huawei Matebook laptops come with two WMI interfaces. The first being
WMI0 which is considered "legacy" and AFAIK only found on the Matebook X
released in 2017. The second has a UID of "HWMI" and is found in pretty
much all models with a slight difference in implementation except for
the Matebook X (2017). Since this model has two interfaces, some aspects
are controlled through the legacy interface and some through the other
interface. Currently, the legacy interface is not fully implemented and
is only used for hotkeys and further debugging has to be done.

The WMI interface takes a 64 bit integer, although uses 32 bits most of
the time, and returns a 256-260 bytes buffer consists of either one ACPI
buffer of 260 bytes, in the case of Matebook X (2017), or one ACPI
package of two buffers, one with 4 bytes, and the other with 256 bytes.
We only care about the latter 256 buffer in both cases since the 4 bytes
always return zeros. The first byte of this 256 buffer always has the
return status where 1 indicated error. Some models require calling the
WMI interface twice to execute a command.

# Add micmute LED support through WMI

After implementing the WMI interface, micmute LED can be controlled
easily. Models with the legacy interface fall back to ACPI EC method
control since the legacy interface is not implemented.

# Add battery charging protection support through WMI

Most models, that has the WMI interface, are capable of battery
protection where it can control battery charging thresholds and limits
charging the battery to certain values.

# Add fn-lock support through WMI

The behavior of hotkeys is not the same among all models. Some models
require fn-lock to do things like `Ctrl-Ins` or `Alt-PrtSc`. By default,
hotkeys behave as special keys (media keys, Ins, etc), but if a modifier
is used (ctrl, alt, shift) these keys behave as F1-F12 keys. If the Fn
key is toggled on, the hotkeys with or without a modifier, behave as
F1-F12 keys. This makes it impossible to use a modifier and `PrtSc` or
`Ins`.

Now, some models fix this by excluding `PrtSc` and `Ins` keys from being
treated as F11 and F12 keys with the use of a modifier. However, some
models do not, and fixes this by the so called fn-lock.

Fn-lock inverts the behavior of the top row from special keys to F1-F12
keys. So a modifier and a special key would be possible which make
things like `Alt-Ins` possible. Now, with fn-lock we would have 4 modes:

* Fn-key off & fn-lock off - hotkeys treated as special keys using a
  modifier gives F1-F12 keys.
* Fn-key on & fn-lock off - hotkeys treated as F1-F12 keys and using a
  modifier gives F1-F12.
* Fn-key off & fn-lock on - hotkeys are treated as F1-F12 keys and using
  a modifier gives special keys.
* Fn-key on & fn-lock on - hotkeys are treated as special keys and using
  a modifier gives special keys.

# Implement driver quirks and parameters

The driver introduces 3 quirks and 2 parameters that can change the
driver's behavior. These quirks being as:
1. Fixes reporting brightness keys twice since it's already handled by
   acpi-video.
2. Some models need a short delay when setting battery thresholds to
   prevent a race condition when two processes read/write.
3. Matebook X (2017) handles micmute led through the "legacy" interface
   which is not currently implemented. Use ACPI EC method to control
   this led.

and the 2 parameters can enforce the behavior of quirk 1 & 2.

# Add a debugfs interface to WMI

An interface to the WMI management interface that allows easier
debugging.

Ayman Bagabas (6):
  platform/x86: huawei-wmi: Move to platform driver
  platform/x86: huawei-wmi: Add quirks and module parameters
  platform/x86: huawei-wmi: Implement huawei wmi management
  platform/x86: huawei-wmi: Add battery charging thresholds
  platform/x86: huawei-wmi: Add fn-lock support
  platform/x86: huawei-wmi: Add debugfs support

 drivers/platform/x86/huawei-wmi.c | 872 ++
 1 file changed, 780 insertions(+), 92 deletions(-)

-- 
2.21.0



[RFC 1/9] platform/x86: huawei-wmi: Rename guid and driver name

2019-07-31 Thread Ayman Bagabas
Use WMI device UID, AMW0 has a UID of HWMI. WMI0 is the device name
and doesn't have a UID so keep it as it is.

Change module description to reflect the upcoming changes.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 14 --
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 52fcac5b393a..a1159850a16c 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- *  Huawei WMI hotkeys
+ *  Huawei WMI laptop extras driver
  *
  *  Copyright (C) 2018   Ayman Bagabas 
  */
@@ -15,10 +15,12 @@
 /*
  * Huawei WMI GUIDs
  */
-#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
-#define AMW0_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
+#define HWMI_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
 
+/* Legacy GUIDs */
 #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
+#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
+
 
 struct huawei_wmi_priv {
struct input_dev *idev;
@@ -37,7 +39,7 @@ static const struct key_entry huawei_wmi_keymap[] = {
{ KE_KEY,0x289, { KEY_WLAN } },
// Huawei |M| key
{ KE_KEY,0x28a, { KEY_CONFIG } },
-   // Keyboard backlight
+   // Keyboard backlit
{ KE_IGNORE, 0x293, { KEY_KBDILLUMTOGGLE } },
{ KE_IGNORE, 0x294, { KEY_KBDILLUMUP } },
{ KE_IGNORE, 0x295, { KEY_KBDILLUMUP } },
@@ -186,7 +188,7 @@ static int huawei_wmi_probe(struct wmi_device *wdev)
 
 static const struct wmi_device_id huawei_wmi_id_table[] = {
{ .guid_string = WMI0_EVENT_GUID },
-   { .guid_string = AMW0_EVENT_GUID },
+   { .guid_string = HWMI_EVENT_GUID },
{  }
 };
 
@@ -203,5 +205,5 @@ module_wmi_driver(huawei_wmi_driver);
 
 MODULE_DEVICE_TABLE(wmi, huawei_wmi_id_table);
 MODULE_AUTHOR("Ayman Bagabas ");
-MODULE_DESCRIPTION("Huawei WMI hotkeys");
+MODULE_DESCRIPTION("Huawei WMI laptop extras driver");
 MODULE_LICENSE("GPL v2");
-- 
2.20.1




[RFC 1/9] platform/x86: huawei-wmi: Rename guid and driver name

2019-07-31 Thread Ayman Bagabas
Use WMI device UID, AMW0 has a UID of HWMI. WMI0 is the device name
and doesn't have a UID so keep it as it is.

Change module description to reflect the upcoming changes.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 14 --
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 52fcac5b393a..a1159850a16c 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- *  Huawei WMI hotkeys
+ *  Huawei WMI laptop extras driver
  *
  *  Copyright (C) 2018   Ayman Bagabas 
  */
@@ -15,10 +15,12 @@
 /*
  * Huawei WMI GUIDs
  */
-#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
-#define AMW0_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
+#define HWMI_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
 
+/* Legacy GUIDs */
 #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
+#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
+
 
 struct huawei_wmi_priv {
struct input_dev *idev;
@@ -37,7 +39,7 @@ static const struct key_entry huawei_wmi_keymap[] = {
{ KE_KEY,0x289, { KEY_WLAN } },
// Huawei |M| key
{ KE_KEY,0x28a, { KEY_CONFIG } },
-   // Keyboard backlight
+   // Keyboard backlit
{ KE_IGNORE, 0x293, { KEY_KBDILLUMTOGGLE } },
{ KE_IGNORE, 0x294, { KEY_KBDILLUMUP } },
{ KE_IGNORE, 0x295, { KEY_KBDILLUMUP } },
@@ -186,7 +188,7 @@ static int huawei_wmi_probe(struct wmi_device *wdev)
 
 static const struct wmi_device_id huawei_wmi_id_table[] = {
{ .guid_string = WMI0_EVENT_GUID },
-   { .guid_string = AMW0_EVENT_GUID },
+   { .guid_string = HWMI_EVENT_GUID },
{  }
 };
 
@@ -203,5 +205,5 @@ module_wmi_driver(huawei_wmi_driver);
 
 MODULE_DEVICE_TABLE(wmi, huawei_wmi_id_table);
 MODULE_AUTHOR("Ayman Bagabas ");
-MODULE_DESCRIPTION("Huawei WMI hotkeys");
+MODULE_DESCRIPTION("Huawei WMI laptop extras driver");
 MODULE_LICENSE("GPL v2");
-- 
2.20.1




[RFC 7/9] platform/x86: huawei-wmi: Add fn-lock support

2019-07-31 Thread Ayman Bagabas
Huawei Matebook laptops uses Fn key and toggle to access F1-F12 keys.
Along with that, there is this feature called fn-lock that inverts the
behavior of this Fn key.

Implement the basic functionality of this feature to be used later by
sysfs interface support introduced in this series.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 30 ++
 1 file changed, 30 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index da3986cd0428..4159e10bda26 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -351,6 +351,36 @@ static int huawei_wmi_battery_set(struct device *dev, int 
low, int high)
return err;
 }
 
+/* Fn lock */
+
+static int huawei_wmi_fn_lock_get(struct device *dev, int *on)
+{
+   u8 ret[0x100] = { 0 };
+   int err, i;
+
+   err = huawei_wmi_cmd(dev, FN_LOCK_GET, ret, 0x100);
+   if (err)
+   return err;
+
+   /* Find the first non-zero value. Return status is ignored. */
+   i = 1;
+   do {
+   *on = ret[i] - 1; // -1 undefined, 0 off, 1 on.
+   } while (i < 0x100 && !ret[i++]);
+
+   return 0;
+}
+
+static int huawei_wmi_fn_lock_set(struct device *dev, int on)
+{
+   u8 arg[8];
+
+   *(u64 *)arg = FN_LOCK_SET;
+   arg[2] = on + 1; // 0 undefined, 1 off, 2 on.
+
+   return huawei_wmi_cmd(dev, *(u64 *)arg, NULL, NULL);
+}
+
 /* Input */
 
 static void huawei_wmi_process_key(struct input_dev *idev, int code)
-- 
2.20.1




[RFC 4/9] platform/x86: huawei-wmi: Add quirks and module parameters

2019-07-31 Thread Ayman Bagabas
Introduce quirks and module parameters. 3 quirks are added:
1. Fixes reporting brightness keys twice since it's already handled by
   acpi-video.
2. Some models need a short delay when setting battery thresholds to
   prevent a race condition when two processes read/write.
3. Matebook X (2017) handles micmute led through the "legacy" interface
   which is not currently implemented. Use ACPI EC method to control
   this led.

2 module parameters are added to enable this short delay and/or report
  brightness keys through this driver.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 71 +++
 1 file changed, 71 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 27520b0f8956..8f918138053a 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -6,6 +6,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -34,6 +35,14 @@ enum {
MICMUTE_LED_SET = 0x0b04, /* \SMLS */
 };
 
+struct quirk_entry {
+   bool battery_sleep;
+   bool ec_micmute;
+   bool report_brightness;
+};
+
+static struct quirk_entry *quirks;
+
 struct huawei_wmi_priv {
struct input_dev *idev[2];
struct led_classdev cdev;
@@ -62,6 +71,58 @@ static const struct key_entry huawei_wmi_keymap[] = {
{ KE_END,0 }
 };
 
+static bool battery_sleep;
+static bool report_brightness;
+
+module_param(battery_sleep, bool, 0444);
+MODULE_PARM_DESC(battery_sleep,
+   "Delay after setting battery charging thresholds.");
+module_param(report_brightness, bool, 0444);
+MODULE_PARM_DESC(report_brightness,
+   "Report brightness keys.");
+
+/* Quirks */
+
+static int __init dmi_matched(const struct dmi_system_id *dmi)
+{
+   quirks = dmi->driver_data;
+   return 1;
+}
+
+static struct quirk_entry quirk_unknown = {
+};
+
+static struct quirk_entry quirk_battery_sleep = {
+   .battery_sleep = true,
+};
+
+static struct quirk_entry quirk_matebook_x = {
+   .ec_micmute = true,
+   .report_brightness = true,
+};
+
+static const struct dmi_system_id huawei_quirks[] = {
+   {
+   .callback = dmi_matched,
+   .ident = "Huawei MACH-WX9",
+   .matches = {
+   DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
+   DMI_MATCH(DMI_PRODUCT_NAME, "MACH-WX9"),
+   },
+   .driver_data = &quirk_battery_sleep
+   },
+   {
+   .callback = dmi_matched,
+   .ident = "Huawei MateBook X",
+   .matches = {
+   DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
+   DMI_MATCH(DMI_PRODUCT_NAME, "HUAWEI MateBook X")
+   },
+   .driver_data = &quirk_matebook_x
+   },
+   {  }
+};
+
 /* Utils */
 
 static int huawei_wmi_call(struct device *dev, struct acpi_buffer *in,
@@ -264,6 +325,11 @@ static void huawei_wmi_process_key(struct input_dev *idev, 
int code)
return;
}
 
+   if (quirks && !quirks->report_brightness &&
+   (key->sw.code == KEY_BRIGHTNESSDOWN ||
+   key->sw.code == KEY_BRIGHTNESSUP))
+   return;
+
sparse_keymap_report_entry(idev, key, 1, true);
 }
 
@@ -377,6 +443,11 @@ static __init int huawei_wmi_init(void)
 {
int err;
 
+   quirks = &quirk_unknown;
+   dmi_check_system(huawei_quirks);
+   quirks->battery_sleep |= battery_sleep;
+   quirks->report_brightness |= report_brightness;
+
err = platform_driver_register(&huawei_wmi_driver);
if (err)
return err;
-- 
2.20.1




[RFC 6/9] platform/x86: huawei-wmi: Add battery charging thresholds

2019-07-31 Thread Ayman Bagabas
Implement battery charging thresholds functionality to be used with
sysfs interface support that is implemented in this series.

Setting battery charging thresholds can introduce a race condition where
two are trying to read/write values from/to EC memory. Even though a
mutex is used, this doesn't guarantee that these values will make it to
EC so a blocking sleep is necessary.

This sleep is enforced on the MACH-WX9 model since turning off battery
protection using (0,100) doesn't change the values in the EC memory,
instead it only disables the protection. So a workaround is to set the
thresholds to (0,0) before setting it to (0,100). This will ensure
that we update these values in EC memory and then turning it off with
(0,100). Thus, the msleep(1000).

Signed-off-by: Ayman Bagabas 
---
In huawei_wmi_battery_get and later in huawei_wmi_fn_lock_get, 256 bytes
buffer is allocated dynamically on the stack. Now I know this is
discouraged, should it be changed to use kmalloc?

 drivers/platform/x86/huawei-wmi.c | 54 +++
 1 file changed, 54 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 9013a05d2832..da3986cd0428 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -6,6 +6,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -46,6 +47,7 @@ static struct quirk_entry *quirks;
 struct huawei_wmi_priv {
struct input_dev *idev[2];
struct led_classdev cdev;
+   struct mutex battery_lock;
struct mutex wmi_lock;
struct platform_device *pdev;
 };
@@ -298,6 +300,57 @@ static int huawei_wmi_leds_setup(struct device *dev)
return devm_led_classdev_register(dev, &priv->cdev);
 }
 
+/* Battery protection */
+
+static int huawei_wmi_battery_get(struct device *dev, int *low, int *high)
+{
+   struct huawei_wmi_priv *priv = dev_get_drvdata(dev);
+   u8 ret[0x100];
+   int err, i;
+
+   mutex_lock(&priv->battery_lock);
+   err = huawei_wmi_cmd(dev, BATTERY_THRESH_GET, ret, 0x100);
+   mutex_unlock(&priv->battery_lock);
+   if (err)
+   return err;
+
+   /* Find the last two non-zero values. Return status is ignored. */
+   i = 0x100;
+   do {
+   *low = ret[i-1];
+   *high = ret[i];
+   } while (i > 2 && !ret[i--]);
+
+   return 0;
+}
+
+static int huawei_wmi_battery_set(struct device *dev, int low, int high)
+{
+   struct huawei_wmi_priv *priv = dev_get_drvdata(dev);
+   u8 arg[8];
+   int err;
+
+   *(u64 *)arg = BATTERY_THRESH_SET;
+   arg[2] = low;
+   arg[3] = high;
+
+   /* This is an edge case were some models turn battery protection
+* off without changing their thresholds values. We clear the
+* values before turning off protection. Sometimes we need a sleep 
delay to
+* make sure these values make their way to EC memory.
+*/
+   if (low == 0 && high == 100)
+   huawei_wmi_battery_set(dev, 0, 0);
+
+   mutex_lock(&priv->battery_lock);
+   err = huawei_wmi_cmd(dev, *(u64 *)arg, NULL, NULL);
+   if (quirks && quirks->battery_sleep)
+   msleep(1000);
+   mutex_unlock(&priv->battery_lock);
+
+   return err;
+}
+
 /* Input */
 
 static void huawei_wmi_process_key(struct input_dev *idev, int code)
@@ -418,6 +471,7 @@ static int huawei_wmi_probe(struct platform_device *pdev)
 
if (wmi_has_guid(HWMI_METHOD_GUID)) {
mutex_init(&priv->wmi_lock);
+   mutex_init(&priv->battery_lock);
 
err = huawei_wmi_leds_setup(&pdev->dev);
if (err) {
-- 
2.20.1




[RFC 8/9] platform/x86: huawei-wmi: Add sysfs interface support

2019-07-31 Thread Ayman Bagabas
Add sysfs interface to enable the use of battery charging thresholds and
fn-lock support introduced in this series.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 82 +++
 1 file changed, 82 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 4159e10bda26..f7041fb71026 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -14,6 +14,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 
 /*
@@ -381,6 +382,80 @@ static int huawei_wmi_fn_lock_set(struct device *dev, int 
on)
return huawei_wmi_cmd(dev, *(u64 *)arg, NULL, NULL);
 }
 
+/* sysfs */
+
+static ssize_t charge_thresholds_show(struct device *dev,
+   struct device_attribute *attr,
+   char *buf)
+{
+   int err, low, high;
+
+   err = huawei_wmi_battery_get(dev, &low, &high);
+   if (err)
+   return err;
+
+   return sprintf(buf, "%d %d\n", low, high);
+}
+
+static ssize_t charge_thresholds_store(struct device *dev,
+   struct device_attribute *attr,
+   const char *buf, size_t size)
+{
+   int low, high, err;
+
+   if (sscanf(buf, "%d %d", &low, &high) != 2 ||
+   low < 0 || high > 100 ||
+   low > high)
+   return -EINVAL;
+
+   err = huawei_wmi_battery_set(dev, low, high);
+   if (err)
+   return err;
+
+   return size;
+}
+
+static ssize_t fn_lock_state_show(struct device *dev,
+   struct device_attribute *attr,
+   char *buf)
+{
+   int err, on;
+
+   err = huawei_wmi_fn_lock_get(dev, &on);
+   if (err)
+   return err;
+
+   return sprintf(buf, "%d\n", on);
+}
+
+static ssize_t fn_lock_state_store(struct device *dev,
+   struct device_attribute *attr,
+   const char *buf, size_t size)
+{
+   int on, err;
+
+   if (kstrtoint(buf, 10, &on) ||
+   on < 0 || on > 1)
+   return -EINVAL;
+
+   err = huawei_wmi_fn_lock_set(dev, on);
+   if (err)
+   return err;
+
+   return size;
+}
+
+static DEVICE_ATTR_RW(charge_thresholds);
+static DEVICE_ATTR_RW(fn_lock_state);
+
+static struct attribute *huawei_wmi_attrs[] = {
+   &dev_attr_charge_thresholds.attr,
+   &dev_attr_fn_lock_state.attr,
+   NULL
+};
+
+ATTRIBUTE_GROUPS(huawei_wmi);
+
 /* Input */
 
 static void huawei_wmi_process_key(struct input_dev *idev, int code)
@@ -508,6 +583,12 @@ static int huawei_wmi_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Failed to setup leds\n");
return err;
}
+
+   err = sysfs_create_groups(&pdev->dev.kobj, huawei_wmi_groups);
+   if (err) {
+   dev_err(&pdev->dev, "Failed to create sysfs 
interface\n");
+   return err;
+   }
}
 
return 0;
@@ -522,6 +603,7 @@ static int huawei_wmi_remove(struct platform_device *pdev)
wmi_remove_notify_handler(HWMI_EVENT_GUID);
 
if (wmi_has_guid(HWMI_METHOD_GUID)) {
+   sysfs_remove_groups(&pdev->dev.kobj, huawei_wmi_groups);
}
 
return 0;
-- 
2.20.1




[RFC 9/9] platform/x86: huawei-wmi: Add debugfs support

2019-07-31 Thread Ayman Bagabas
Add a debugfs interface that can be used to call the WMI management
interface function.

The WMI interface takes a 64 bit integer and returns 256-260 bytes
buffer. This debugfs interface creates two files, one stores a 64 bit
int and the other calls the WMI interface and dumps out the returned
buffer.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 98 +++
 1 file changed, 98 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index f7041fb71026..bdca8bd76c8c 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -6,6 +6,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -45,7 +46,13 @@ struct quirk_entry {
 
 static struct quirk_entry *quirks;
 
+struct huawei_wmi_debug {
+   struct dentry *root;
+   u64 arg;
+};
+
 struct huawei_wmi_priv {
+   struct huawei_wmi_debug debug;
struct input_dev *idev[2];
struct led_classdev cdev;
struct mutex battery_lock;
@@ -456,6 +463,94 @@ static struct attribute *huawei_wmi_attrs[] = {
 
 ATTRIBUTE_GROUPS(huawei_wmi);
 
+/* debugfs */
+
+static void huawei_wmi_debugfs_call_dump(struct seq_file *m, void *data,
+   union acpi_object *obj)
+{
+   struct huawei_wmi_priv *priv = m->private;
+   int i;
+
+   switch (obj->type) {
+   case ACPI_TYPE_INTEGER:
+   seq_printf(m, "0x%llx", obj->integer.value);
+   break;
+   case ACPI_TYPE_STRING:
+   seq_printf(m, "\"%*s\"", obj->string.length, 
obj->string.pointer);
+   break;
+   case ACPI_TYPE_BUFFER:
+   seq_puts(m, "{");
+   for (i = 0; i < obj->buffer.length; i++) {
+   seq_printf(m, "0x%02x", obj->buffer.pointer[i]);
+   if (i < obj->buffer.length - 1)
+   seq_puts(m, ",");
+   }
+   seq_puts(m, "}");
+   break;
+   case ACPI_TYPE_PACKAGE:
+   seq_puts(m, "[");
+   for (i = 0; i < obj->package.count; i++) {
+   huawei_wmi_debugfs_call_dump(m, priv, 
&obj->package.elements[i]);
+   if (i < obj->package.count - 1)
+   seq_puts(m, ",");
+   }
+   seq_puts(m, "]");
+   break;
+   default:
+   dev_err(&priv->pdev->dev, "Unexpected obj type, got %d\n", 
obj->type);
+   return;
+   }
+}
+
+static int huawei_wmi_debugfs_call_show(struct seq_file *m, void *data)
+{
+   struct huawei_wmi_priv *priv = m->private;
+   struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
+   struct acpi_buffer in;
+   union acpi_object *obj;
+   int err;
+
+   in.length = sizeof(u64);
+   in.pointer = &priv->debug.arg;
+
+   err = huawei_wmi_call(&priv->pdev->dev, &in, &out);
+   if (err)
+   return err;
+
+   obj = out.pointer;
+   if (!obj) {
+   err = -EIO;
+   goto fail_debugfs_call;
+   }
+
+   huawei_wmi_debugfs_call_dump(m, priv, obj);
+
+fail_debugfs_call:
+   kfree(out.pointer);
+   return err;
+}
+
+DEFINE_SHOW_ATTRIBUTE(huawei_wmi_debugfs_call);
+
+static void huawei_wmi_debugfs_exit(struct device *dev)
+{
+   struct huawei_wmi_priv *priv = dev_get_drvdata(dev);
+
+   debugfs_remove_recursive(priv->debug.root);
+}
+
+static void huawei_wmi_debugfs_setup(struct device *dev)
+{
+   struct huawei_wmi_priv *priv = dev_get_drvdata(dev);
+
+   priv->debug.root = debugfs_create_dir("huawei-wmi", NULL);
+
+   debugfs_create_x64("arg", 0644, priv->debug.root,
+   &priv->debug.arg);
+   debugfs_create_file("call", 0400,
+   priv->debug.root, priv, &huawei_wmi_debugfs_call_fops);
+}
+
 /* Input */
 
 static void huawei_wmi_process_key(struct input_dev *idev, int code)
@@ -589,6 +684,8 @@ static int huawei_wmi_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Failed to create sysfs 
interface\n");
return err;
}
+
+   huawei_wmi_debugfs_setup(&pdev->dev);
}
 
return 0;
@@ -603,6 +700,7 @@ static int huawei_wmi_remove(struct platform_device *pdev)
wmi_remove_notify_handler(HWMI_EVENT_GUID);
 
if (wmi_has_guid(HWMI_METHOD_GUID)) {
+   huawei_wmi_debugfs_exit(&pdev->dev);
sysfs_remove_groups(&pdev->dev.kobj, huawei_wmi_groups);
}
 
-- 
2.20.1




[RFC 3/9] platform/x86: huawei-wmi: Implement huawei wmi management interface

2019-07-31 Thread Ayman Bagabas
Huawei Matebook laptops come with a WMI management interface that can
control various aspects of the device. This interface is also found on
the old Matebook X released in 2017.

Implement basic functionality of this interface along with supported
command IDs that are implemented in this series.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 130 ++
 1 file changed, 130 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 12cae450fc1f..27520b0f8956 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -10,24 +10,36 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 
 /*
  * Huawei WMI GUIDs
  */
+#define HWMI_METHOD_GUID "ABBC0F5B-8EA1-11D1-A000-C9062910"
 #define HWMI_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
 
 /* Legacy GUIDs */
 #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
 #define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
 
+/* HWMI commands */
+
+enum {
+   BATTERY_THRESH_GET  = 0x1103, /* \GBTT */
+   BATTERY_THRESH_SET  = 0x1003, /* \SBTT */
+   FN_LOCK_GET = 0x0604, /* \GFRS */
+   FN_LOCK_SET = 0x0704, /* \SFRS */
+   MICMUTE_LED_SET = 0x0b04, /* \SMLS */
+};
 
 struct huawei_wmi_priv {
struct input_dev *idev[2];
struct led_classdev cdev;
acpi_handle handle;
char *acpi_method;
+   struct mutex wmi_lock;
struct platform_device *pdev;
 };
 
@@ -50,6 +62,116 @@ static const struct key_entry huawei_wmi_keymap[] = {
{ KE_END,0 }
 };
 
+/* Utils */
+
+static int huawei_wmi_call(struct device *dev, struct acpi_buffer *in,
+   struct acpi_buffer *out)
+{
+   struct huawei_wmi_priv *priv = dev_get_drvdata(dev);
+   acpi_status status;
+
+   mutex_lock(&priv->wmi_lock);
+   status = wmi_evaluate_method(HWMI_METHOD_GUID, 0, 1, in, out);
+   mutex_unlock(&priv->wmi_lock);
+   if (ACPI_FAILURE(status)) {
+   dev_err(dev, "Failed to evaluate wmi method\n");
+   return -ENODEV;
+   }
+
+   return 0;
+}
+
+/* HWMI takes a 64 bit input and returns either a package with 2 buffers, one 
of
+ * 4 bytes and the other of 256 bytes, or one buffer of size 0x104 (260) bytes.
+ * The first 4 bytes are ignored, we ignore the first 4 bytes buffer if we got 
a
+ * package, or skip the first 4 if a buffer of 0x104 is used. The first byte of
+ * the remaining 0x100 sized buffer has the return status of every call. In 
case
+ * the return status is non-zero, we return -ENODEV but still copy the returned
+ * buffer to the given buffer parameter (buf).
+ */
+static int huawei_wmi_cmd(struct device *dev, u64 arg, u8 *buf, size_t buflen)
+{
+   struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
+   struct acpi_buffer in;
+   union acpi_object *obj;
+   size_t len;
+   int err, i;
+
+   in.length = sizeof(arg);
+   in.pointer = &arg;
+
+   /* Some models require calling HWMI twice to execute a command. We 
evaluate
+* HWMI and if we get a non-zero return status we evaluate it again.
+*/
+   for (i = 0; i < 2; i++) {
+   err = huawei_wmi_call(dev,  &in, &out);
+   if (err)
+   goto fail_cmd;
+
+   obj = out.pointer;
+   if (!obj) {
+   err = -EIO;
+   goto fail_cmd;
+   }
+
+   switch (obj->type) {
+   /* Models that implement both "legacy" and HWMI tend to return 
a 0x104
+* sized buffer instead of a package of 0x4 and 0x100 buffers.
+*/
+   case ACPI_TYPE_BUFFER:
+   if (obj->buffer.length == 0x104) {
+   // Skip the first 4 bytes.
+   obj->buffer.pointer += 4;
+   len = 0x100;
+   } else {
+   dev_err(dev, "Bad buffer length, got %d\n", 
obj->buffer.length);
+   err = -EIO;
+   goto fail_cmd;
+   }
+
+   break;
+   /* HWMI returns a package with 2 buffer elements, one of 4 
bytes and the
+* other is 256 bytes.
+*/
+   case ACPI_TYPE_PACKAGE:
+   if (obj->package.count != 2) {
+   dev_err(dev, "Bad package count, got %d\n", 
obj->package.count);
+   err = -EIO;
+   goto fail_cmd;
+  

[RFC 2/9] platform/x86: huawei-wmi: Move to platform driver

2019-07-31 Thread Ayman Bagabas
Move from WMI driver to platform driver. This move is necessary since
the driver is no longer a hotkeys driver only. Platform driver makes it
easier for users to access sysfs attributes under (i.e.
/sys/devices/platform/huawei-wmi) compared to wmi driver.

Signed-off-by: Ayman Bagabas 
---
Do you think we should stick to wmi driver especially after
"platform/x86: wmi: add context pointer field to struct wmi_device_id"
https://patchwork.kernel.org/patch/10963421/ which is basically was my
issue with wmi driver where there wasn't a way to distinguish what wmi
device is associated with which guid.

I believe that platform driver is more suitable for this since it has
grown from just a hotkeys driver and users can access sysfs of this
easily with platform driver. What do you think?

 drivers/platform/x86/huawei-wmi.c | 152 +-
 1 file changed, 110 insertions(+), 42 deletions(-)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index a1159850a16c..12cae450fc1f 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -10,6 +10,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 
 /*
@@ -23,12 +24,15 @@
 
 
 struct huawei_wmi_priv {
-   struct input_dev *idev;
+   struct input_dev *idev[2];
struct led_classdev cdev;
acpi_handle handle;
char *acpi_method;
+   struct platform_device *pdev;
 };
 
+struct platform_device *huawei_wmi_pdev;
+
 static const struct key_entry huawei_wmi_keymap[] = {
{ KE_KEY,0x281, { KEY_BRIGHTNESSDOWN } },
{ KE_KEY,0x282, { KEY_BRIGHTNESSUP } },
@@ -46,6 +50,8 @@ static const struct key_entry huawei_wmi_keymap[] = {
{ KE_END,0 }
 };
 
+/* LEDs */
+
 static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
 {
@@ -77,9 +83,9 @@ static int huawei_wmi_micmute_led_set(struct led_classdev 
*led_cdev,
return 0;
 }
 
-static int huawei_wmi_leds_setup(struct wmi_device *wdev)
+static int huawei_wmi_leds_setup(struct device *dev)
 {
-   struct huawei_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
+   struct huawei_wmi_priv *priv = dev_get_drvdata(dev);
 
priv->handle = ec_get_handle();
if (!priv->handle)
@@ -97,15 +103,16 @@ static int huawei_wmi_leds_setup(struct wmi_device *wdev)
priv->cdev.brightness_set_blocking = huawei_wmi_micmute_led_set;
priv->cdev.default_trigger = "audio-micmute";
priv->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
-   priv->cdev.dev = &wdev->dev;
+   priv->cdev.dev = dev;
priv->cdev.flags = LED_CORE_SUSPENDRESUME;
 
-   return devm_led_classdev_register(&wdev->dev, &priv->cdev);
+   return devm_led_classdev_register(dev, &priv->cdev);
 }
 
-static void huawei_wmi_process_key(struct wmi_device *wdev, int code)
+/* Input */
+
+static void huawei_wmi_process_key(struct input_dev *idev, int code)
 {
-   struct huawei_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
const struct key_entry *key;
 
/*
@@ -129,81 +136,142 @@ static void huawei_wmi_process_key(struct wmi_device 
*wdev, int code)
kfree(response.pointer);
}
 
-   key = sparse_keymap_entry_from_scancode(priv->idev, code);
+   key = sparse_keymap_entry_from_scancode(idev, code);
if (!key) {
-   dev_info(&wdev->dev, "Unknown key pressed, code: 0x%04x\n", 
code);
+   dev_info(&idev->dev, "Unknown key pressed, code: 0x%04x\n", 
code);
return;
}
 
-   sparse_keymap_report_entry(priv->idev, key, 1, true);
+   sparse_keymap_report_entry(idev, key, 1, true);
 }
 
-static void huawei_wmi_notify(struct wmi_device *wdev,
-   union acpi_object *obj)
+static void huawei_wmi_input_notify(u32 value, void *context)
 {
-   if (obj->type == ACPI_TYPE_INTEGER)
-   huawei_wmi_process_key(wdev, obj->integer.value);
+   struct input_dev *idev = (struct input_dev *)context;
+   struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
+   union acpi_object *obj;
+   acpi_status status;
+
+   status = wmi_get_event_data(value, &response);
+   if (ACPI_FAILURE(status)) {
+   dev_err(&idev->dev, "Unable to get event data\n");
+   return;
+   }
+
+   obj = (union acpi_object *)response.pointer;
+   if (obj && obj->type == ACPI_TYPE_INTEGER)
+   huawei_wmi_process_key(idev, obj->integer.value);
else
-   dev_info(&wdev->dev, "Bad response type %d\n", obj->type);
+   dev_err(&idev->dev, "Bad response type\n");
+
+   kfree(response.pointer);
 }
 
-static int hua

[RFC 5/9] platform/x86: huawei-wmi: Control micmute led through wmi interface

2019-07-31 Thread Ayman Bagabas
Now that huawei WMI management interface is implemented, micmute LED can
be controlled easily through this interface. Exception is the Matebook X
(2017) which continue to uses ACPI EC method to control the LED. This
model can control the LED through the legacy WMI interface which is not
implemented ATM.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 86 ++-
 1 file changed, 49 insertions(+), 37 deletions(-)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 8f918138053a..9013a05d2832 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -46,8 +46,6 @@ static struct quirk_entry *quirks;
 struct huawei_wmi_priv {
struct input_dev *idev[2];
struct led_classdev cdev;
-   acpi_handle handle;
-   char *acpi_method;
struct mutex wmi_lock;
struct platform_device *pdev;
 };
@@ -238,49 +236,57 @@ static int huawei_wmi_cmd(struct device *dev, u64 arg, u8 
*buf, size_t buflen)
 static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
 {
-   struct huawei_wmi_priv *priv = dev_get_drvdata(led_cdev->dev->parent);
-   acpi_status status;
-   union acpi_object args[3];
-   struct acpi_object_list arg_list = {
-   .pointer = args,
-   .count = ARRAY_SIZE(args),
-   };
-
-   args[0].type = args[1].type = args[2].type = ACPI_TYPE_INTEGER;
-   args[1].integer.value = 0x04;
-
-   if (strcmp(priv->acpi_method, "SPIN") == 0) {
-   args[0].integer.value = 0;
-   args[2].integer.value = brightness ? 1 : 0;
-   } else if (strcmp(priv->acpi_method, "WPIN") == 0) {
-   args[0].integer.value = 1;
-   args[2].integer.value = brightness ? 0 : 1;
+   /* This is a workaround until the "legacy" interface is implemented. */
+   if (quirks && quirks->ec_micmute) {
+   char *acpi_method;
+   acpi_handle handle;
+   acpi_status status;
+   union acpi_object args[3];
+   struct acpi_object_list arg_list = {
+   .pointer = args,
+   .count = ARRAY_SIZE(args),
+   };
+
+   handle = ec_get_handle();
+   if (!handle) {
+   dev_err(led_cdev->dev->parent, "Failed to get EC 
handle\n");
+   return -ENODEV;
+   }
+
+   args[0].type = args[1].type = args[2].type = ACPI_TYPE_INTEGER;
+   args[1].integer.value = 0x04;
+
+   if (acpi_has_method(handle, "SPIN")) {
+   acpi_method = "SPIN";
+   args[0].integer.value = 0;
+   args[2].integer.value = brightness ? 1 : 0;
+   } else if (acpi_has_method(handle, "WPIN")) {
+   acpi_method = "WPIN";
+   args[0].integer.value = 1;
+   args[2].integer.value = brightness ? 0 : 1;
+   } else {
+   return -ENODEV;
+   }
+
+   status = acpi_evaluate_object(handle, acpi_method, &arg_list, 
NULL);
+   if (ACPI_FAILURE(status))
+   return -ENODEV;
+
+   return 0;
} else {
-   return -EINVAL;
-   }
+   u8 arg[8];
 
-   status = acpi_evaluate_object(priv->handle, priv->acpi_method, 
&arg_list, NULL);
-   if (ACPI_FAILURE(status))
-   return -ENXIO;
+   *(u64 *)arg = MICMUTE_LED_SET;
+   arg[2] = brightness;
 
-   return 0;
+   return huawei_wmi_cmd(led_cdev->dev->parent, *(u64 *)arg, NULL, 
NULL);
+   }
 }
 
 static int huawei_wmi_leds_setup(struct device *dev)
 {
struct huawei_wmi_priv *priv = dev_get_drvdata(dev);
 
-   priv->handle = ec_get_handle();
-   if (!priv->handle)
-   return 0;
-
-   if (acpi_has_method(priv->handle, "SPIN"))
-   priv->acpi_method = "SPIN";
-   else if (acpi_has_method(priv->handle, "WPIN"))
-   priv->acpi_method = "WPIN";
-   else
-   return 0;
-
priv->cdev.name = "platform::micmute";
priv->cdev.max_brightness = 1;
priv->cdev.brightness_set_blocking = huawei_wmi_micmute_led_set;
@@ -412,9 +418,15 @@ static int huawei_wmi_probe(struct platform_device *pdev)
 
if (wmi_has_guid(HWMI_METHOD_GUID)) {
mutex_init(&priv->wmi_lock);
+
+   err = huawei_wmi_leds_setup(&pdev->dev);
+   if (err) {
+   dev_err(&pdev->dev, "Failed to setup leds\n");
+   return err;
+   }
}
 
-   return huawei_wmi_leds_setup(&pdev->dev);
+   return 0;
 }
 
 static int huawei_wmi_remove(struct platform_device *pdev)
-- 
2.20.1




[RFC 0/9] platform/x86: Huawei WMI laptop extras driver

2019-07-31 Thread Ayman Bagabas
This patch series introduce changes to huawei-wmi driver that includes:
* Move to platform driver
* Implement WMI management interface
* Add micmute LED support through WMI
* Add battery charging protection support through WMI
* Add fn-lock support through WMI
* Implement driver quirks and parameters
* Add a debugfs interface to WMI

# Move to platform driver

The current driver offers hotkeys and micmute led support only. With
these changes, a platform driver makes more sense since it handles these
changes pretty nicely.

# Implement WMI management interface

Huawei Matebook laptops come with two WMI interfaces. The first being
WMI0 which is considered "legacy" and AFAIK only found on the Matebook X
released in 2017. The second has a UID of "HWMI" and is found in pretty
much all models with a slight difference in implementation except for
the Matebook X (2017). Since this model has two interfaces, some aspects
are controlled through the legacy interface and some through the other
interface. Currently, the legacy interface is not fully implemented and
is only used for hotkeys and further debugging has to be done.

The WMI interface takes a 64 bit integer, although uses 32 bits most of
the time, and returns a 256-260 bytes buffer consists of either one ACPI
buffer of 260 bytes, in the case of Matebook X (2017), or one ACPI
package of two buffers, one with 4 bytes, and the other with 256 bytes.
We only care about the latter 256 buffer in both cases since the 4 bytes
always return zeros. The first byte of this 256 buffer always has the
return status where 1 indicated error. Some models require calling the
WMI interface twice to execute a command.

# Add micmute LED support through WMI

After implementing the WMI interface, micmute LED can be controlled
easily. Models with the legacy interface fall back to ACPI EC method
control since the legacy interface is not implemented.

# Add battery charging protection support through WMI

Most models, that has the WMI interface, are capable of battery
protection where it can control battery charging thresholds and limits
charging the battery to certain values.

# Add fn-lock support through WMI

The behavior of hotkeys is not the same among all models. Some models
require fn-lock to do things like `Ctrl-Ins` or `Alt-PrtSc`. By default,
hotkeys behave as special keys (media keys, Ins, etc), but if a modifier
is used (ctrl, alt, shift) these keys behave as F1-F12 keys. If the Fn
key is toggled on, the hotkeys with or without a modifier, behave as
F1-F12 keys. This makes it impossible to use a modifier and `PrtSc` or
`Ins`.

Now, some models fix this by excluding `PrtSc` and `Ins` keys from being
treated as F11 and F12 keys with the use of a modifier. However, some
models do not, and fixes this by the so called fn-lock.

Fn-lock inverts the behavior of the top row from special keys to F1-F12
keys. So a modifier and a special key would be possible which make
things like `Alt-Ins` possible. Now, with fn-lock we would have 4 modes:

* Fn-key off & fn-lock off - hotkeys treated as special keys using a
  modifier gives F1-F12 keys.
* Fn-key on & fn-lock off - hotkeys treated as F1-F12 keys and using a
  modifier gives F1-F12.
* Fn-key off & fn-lock on - hotkeys are treated as F1-F12 keys and using
  a modifier gives special keys.
* Fn-key on & fn-lock on - hotkeys are treated as special keys and using
  a modifier gives special keys.

# Implement driver quirks and parameters

The driver introduces 3 quirks and 2 parameters that can change the
driver's behavior. These quirks being as:
1. Fixes reporting brightness keys twice since it's already handled by
   acpi-video.
2. Some models need a short delay when setting battery thresholds to
   prevent a race condition when two processes read/write.
3. Matebook X (2017) handles micmute led through the "legacy" interface
   which is not currently implemented. Use ACPI EC method to control
   this led.

and the 2 parameters can enforce the behavior of quirk 1 & 2.

# Add a debugfs interface to WMI

An interface to the WMI management interface that allows easier
debugging.

Ayman Bagabas (9):
  platform/x86: huawei-wmi: rename guid and driver name
  platform/x86: huawei-wmi: move to platform driver
  platform/x86: huawei-wmi: implement huawei wmi management interface
  platform/x86: huawei-wmi: add quirks and module parameters
  platform/x86: huawei-wmi: control micmute led through wmi interface
  platform/x86: huawei-wmi: add battery charging thresholds
  platform/x86: huawei-wmi: add fn-lock support
  platform/x86: huawei-wmi: add sysfs interface support
  platform/x86: huawei-wmi: add debugfs support

 drivers/platform/x86/huawei-wmi.c | 710 ++
 1 file changed, 629 insertions(+), 81 deletions(-)

-- 
2.20.1




Re: [RFC 0/9] platform/x86: Huawei WMI laptop extras driver

2019-07-30 Thread Ayman Bagabas
On 19/07/25 04:05PM, Ayman Bagabas wrote:
> On 19/07/25 08:33PM, Andy Shevchenko wrote:
> > On Sun, Jun 30, 2019 at 8:41 AM Ayman Bagabas  
> > wrote:
> > >
> > > This patch series introduce changes to huawei-wmi driver that includes:
> > > * Move to platform driver
> > > * Implement WMI management interface
> > > * Add micmute LED support through WMI
> > > * Add battery charging protection support through WMI
> > > * Add fn-lock support through WMI
> > > * Implement driver quirks and parameters
> > > * Add a debugfs interface to WMI
> > >
> > > # Move to platform driver
> > >
> > > The current driver offers hotkeys and micmute led support only. With
> > > these changes, a platform driver makes more sense since it handles these
> > > changes pretty nicely.
> > >
> > > # Implement WMI management interface
> > >
> > > Huawei Matebook laptops come with two WMI interfaces. The first being
> > > WMI0 which is considered "legacy" and AFAIK only found on the Matebook X
> > > released in 2017. The second has a UID of "HWMI" and is found in pretty
> > > much all models with a slight difference in implementation except for
> > > the Matebook X (2017). Since this model has two interfaces, some aspects
> > > are controlled through the legacy interface and some through the other
> > > interface. Currently, the legacy interface is not fully implemented and
> > > is only used for hotkeys and further debugging has to be done.
> > >
> > > The WMI interface takes a 64 bit integer, although uses 32 bits most of
> > > the time, and returns a 256-260 bytes buffer consists of either one ACPI
> > > buffer of 260 bytes, in the case of Matebook X (2017), or one ACPI
> > > package of two buffers, one with 4 bytes, and the other with 256 bytes.
> > > We only care about the latter 256 buffer in both cases since the 4 bytes
> > > always return zeros. The first byte of this 256 buffer always has the
> > > return status where 1 indicated error. Some models require calling the
> > > WMI interface twice to execute a command.
> > >
> > > # Add micmute LED support through WMI
> > >
> > > After implementing the WMI interface, micmute LED can be controlled
> > > easily. Models with the legacy interface fall back to ACPI EC method
> > > control since the legacy interface is not implemented.
> > >
> > > # Add battery charging protection support through WMI
> > >
> > > Most models, that has the WMI interface, are capable of battery
> > > protection where it can control battery charging thresholds and limits
> > > charging the battery to certain values.
> > >
> > > # Add fn-lock support through WMI
> > >
> > > The behavior of hotkeys is not the same among all models. Some models
> > > require fn-lock to do things like `Ctrl-Ins` or `Alt-PrtSc`. By default,
> > > hotkeys behave as special keys (media keys, Ins, etc), but if a modifier
> > > is used (ctrl, alt, shift) these keys behave as F1-F12 keys. If the Fn
> > > key is toggled on, the hotkeys with or without a modifier, behave as
> > > F1-F12 keys. This makes it impossible to use a modifier and `PrtSc` or
> > > `Ins`.
> > >
> > > Now, some models fix this by excluding `PrtSc` and `Ins` keys from being
> > > treated as F11 and F12 keys with the use of a modifier. However, some
> > > models do not, and fixes this by the so called fn-lock.
> > >
> > > Fn-lock inverts the behavior of the top row from special keys to F1-F12
> > > keys. So a modifier and a special key would be possible which make
> > > things like `Alt-Ins` possible. Now, with fn-lock we would have 4 modes:
> > >
> > > * Fn-key off & fn-lock off - hotkeys treated as special keys using a
> > >   modifier gives F1-F12 keys.
> > > * Fn-key on & fn-lock off - hotkeys treated as F1-F12 keys and using a
> > >   modifier gives F1-F12.
> > > * Fn-key off & fn-lock on - hotkeys are treated as F1-F12 keys and using
> > >   a modifier gives special keys.
> > > * Fn-key on & fn-lock on - hotkeys are treated as special keys and using
> > >   a modifier gives special keys.
> > >
> > > # Implement driver quirks and parameters
> > >
> > > The driver introduces 3 quirks and 2 parameters that can change the
> > > driver's behavior. These quirks being as:
> > > 1. Fixes reportin

Re: [RFC 0/9] platform/x86: Huawei WMI laptop extras driver

2019-07-25 Thread Ayman Bagabas
On 19/07/25 08:33PM, Andy Shevchenko wrote:
> On Sun, Jun 30, 2019 at 8:41 AM Ayman Bagabas  wrote:
> >
> > This patch series introduce changes to huawei-wmi driver that includes:
> > * Move to platform driver
> > * Implement WMI management interface
> > * Add micmute LED support through WMI
> > * Add battery charging protection support through WMI
> > * Add fn-lock support through WMI
> > * Implement driver quirks and parameters
> > * Add a debugfs interface to WMI
> >
> > # Move to platform driver
> >
> > The current driver offers hotkeys and micmute led support only. With
> > these changes, a platform driver makes more sense since it handles these
> > changes pretty nicely.
> >
> > # Implement WMI management interface
> >
> > Huawei Matebook laptops come with two WMI interfaces. The first being
> > WMI0 which is considered "legacy" and AFAIK only found on the Matebook X
> > released in 2017. The second has a UID of "HWMI" and is found in pretty
> > much all models with a slight difference in implementation except for
> > the Matebook X (2017). Since this model has two interfaces, some aspects
> > are controlled through the legacy interface and some through the other
> > interface. Currently, the legacy interface is not fully implemented and
> > is only used for hotkeys and further debugging has to be done.
> >
> > The WMI interface takes a 64 bit integer, although uses 32 bits most of
> > the time, and returns a 256-260 bytes buffer consists of either one ACPI
> > buffer of 260 bytes, in the case of Matebook X (2017), or one ACPI
> > package of two buffers, one with 4 bytes, and the other with 256 bytes.
> > We only care about the latter 256 buffer in both cases since the 4 bytes
> > always return zeros. The first byte of this 256 buffer always has the
> > return status where 1 indicated error. Some models require calling the
> > WMI interface twice to execute a command.
> >
> > # Add micmute LED support through WMI
> >
> > After implementing the WMI interface, micmute LED can be controlled
> > easily. Models with the legacy interface fall back to ACPI EC method
> > control since the legacy interface is not implemented.
> >
> > # Add battery charging protection support through WMI
> >
> > Most models, that has the WMI interface, are capable of battery
> > protection where it can control battery charging thresholds and limits
> > charging the battery to certain values.
> >
> > # Add fn-lock support through WMI
> >
> > The behavior of hotkeys is not the same among all models. Some models
> > require fn-lock to do things like `Ctrl-Ins` or `Alt-PrtSc`. By default,
> > hotkeys behave as special keys (media keys, Ins, etc), but if a modifier
> > is used (ctrl, alt, shift) these keys behave as F1-F12 keys. If the Fn
> > key is toggled on, the hotkeys with or without a modifier, behave as
> > F1-F12 keys. This makes it impossible to use a modifier and `PrtSc` or
> > `Ins`.
> >
> > Now, some models fix this by excluding `PrtSc` and `Ins` keys from being
> > treated as F11 and F12 keys with the use of a modifier. However, some
> > models do not, and fixes this by the so called fn-lock.
> >
> > Fn-lock inverts the behavior of the top row from special keys to F1-F12
> > keys. So a modifier and a special key would be possible which make
> > things like `Alt-Ins` possible. Now, with fn-lock we would have 4 modes:
> >
> > * Fn-key off & fn-lock off - hotkeys treated as special keys using a
> >   modifier gives F1-F12 keys.
> > * Fn-key on & fn-lock off - hotkeys treated as F1-F12 keys and using a
> >   modifier gives F1-F12.
> > * Fn-key off & fn-lock on - hotkeys are treated as F1-F12 keys and using
> >   a modifier gives special keys.
> > * Fn-key on & fn-lock on - hotkeys are treated as special keys and using
> >   a modifier gives special keys.
> >
> > # Implement driver quirks and parameters
> >
> > The driver introduces 3 quirks and 2 parameters that can change the
> > driver's behavior. These quirks being as:
> > 1. Fixes reporting brightness keys twice since it's already handled by
> >acpi-video.
> > 2. Some models need a short delay when setting battery thresholds to
> >prevent a race condition when two processes read/write.
> > 3. Matebook X (2017) handles micmute led through the "legacy" interface
> >which is not currently implemented. Use ACPI EC method to control
> >this led.
> >
> > and the 2 parameters can en

Re: [PATCH v2 0/8] platform/x86: Huawei WMI laptop extras driver

2019-06-30 Thread ayman . bagabas
On Sat, 2019-06-29 at 17:27 +0300, Andy Shevchenko wrote:
> On Thu, Jun 13, 2019 at 6:04 AM Ayman Bagabas <
> ayman.baga...@gmail.com> wrote:
> > Changes from v1:
> > * introducing debugfs
> > * code reformatting
> > 
> > This patch series introduce new features to the driver and also
> > moves the
> > driver from wmi_driver to platform_driver. This move is necessary
> > because the
> > driver is no longer only a hotkeys driver and platform_driver
> > offers easier
> > future extensibility.
> > 
> > The patch series introduces a WMI BIOS interface that brings on new
> > features
> > and enables controlling micmute LED through this interface on
> > supported models.
> > It also enables controlling battery charging thresholds and fn-lock 
> > state.
> > These features are controlled through the HWMI WMI device present
> > in most of
> > these laptops.
> > 
> > Currently, micmute LED is controlled through an ACPI method under
> > EC.
> > This method ("SPIN", "WPIN") is specific to some models and
> > wouldn't
> > work on all Huawei laptops. Controlling this LED through the
> > interface provides
> > a better unified method to control the LED on models that
> > implements this
> > feature.
> > 
> > The behavior of hotkeys is not the same among all models. Some
> > models
> > require fn-lock to do things like `Ctrl-Ins` or `Alt-PrtSc`. Fn-
> > lock inverts the
> > behavior of the top row from special keys to F1-F12 keys.
> > 
> > A debugfs interface is also implemented to support unrepresented
> > features and to
> > provide debugging feedback from users.
> 
> You have sent few series regarding Huawei devices.
> And from the patch 1 this is not applicable.
> So, I dropped all your patches from patchwork queue and will wait for
> new versions in order they are applicable.

I've sent a newer version and rewrote commit messages and cover page.
I've also changed the tag to RFC.

Thank you,
Ayman

> 
> > Ayman Bagabas (8):
> >   platform/x86: huawei-wmi: move to platform driver
> >   platform/x86: huawei-wmi: implement WMI management interface
> >   platform/x86: huawei-wmi: use quirks and module parameters
> >   platform/x86: huawei-wmi: control micmute LED through WMI
> > interface
> >   platform/x86: huawei-wmi: add battery charging protection support
> >   platform/x86: huawei-wmi: add fn-lock support
> >   platform/x86: huawei-wmi: add sysfs interface support
> >   platform/x86: huawei-wmi: add debugfs files support
> > 
> >  drivers/platform/x86/huawei-wmi.c | 754
> > ++
> >  1 file changed, 665 insertions(+), 89 deletions(-)
> > 
> > --
> > 2.20.1
> > 
> 
> 



[RFC 7/9] platform/x86: huawei-wmi: Add fn-lock support

2019-06-29 Thread Ayman Bagabas
Huawei Matebook laptops uses Fn key and toggle to access F1-F12 keys.
Along with that, there is this feature called fn-lock that inverts the
behavior of this Fn key.

Implement the basic functionality of this feature to be used later by
sysfs interface support introduced in this series.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 30 ++
 1 file changed, 30 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index da3986cd0428..4159e10bda26 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -351,6 +351,36 @@ static int huawei_wmi_battery_set(struct device *dev, int 
low, int high)
return err;
 }
 
+/* Fn lock */
+
+static int huawei_wmi_fn_lock_get(struct device *dev, int *on)
+{
+   u8 ret[0x100] = { 0 };
+   int err, i;
+
+   err = huawei_wmi_cmd(dev, FN_LOCK_GET, ret, 0x100);
+   if (err)
+   return err;
+
+   /* Find the first non-zero value. Return status is ignored. */
+   i = 1;
+   do {
+   *on = ret[i] - 1; // -1 undefined, 0 off, 1 on.
+   } while (i < 0x100 && !ret[i++]);
+
+   return 0;
+}
+
+static int huawei_wmi_fn_lock_set(struct device *dev, int on)
+{
+   u8 arg[8];
+
+   *(u64 *)arg = FN_LOCK_SET;
+   arg[2] = on + 1; // 0 undefined, 1 off, 2 on.
+
+   return huawei_wmi_cmd(dev, *(u64 *)arg, NULL, NULL);
+}
+
 /* Input */
 
 static void huawei_wmi_process_key(struct input_dev *idev, int code)
-- 
2.20.1



[RFC 8/9] platform/x86: huawei-wmi: Add sysfs interface support

2019-06-29 Thread Ayman Bagabas
Add sysfs interface to enable the use of battery charging thresholds and
fn-lock support introduced in this series.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 82 +++
 1 file changed, 82 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 4159e10bda26..f7041fb71026 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -14,6 +14,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 
 /*
@@ -381,6 +382,80 @@ static int huawei_wmi_fn_lock_set(struct device *dev, int 
on)
return huawei_wmi_cmd(dev, *(u64 *)arg, NULL, NULL);
 }
 
+/* sysfs */
+
+static ssize_t charge_thresholds_show(struct device *dev,
+   struct device_attribute *attr,
+   char *buf)
+{
+   int err, low, high;
+
+   err = huawei_wmi_battery_get(dev, &low, &high);
+   if (err)
+   return err;
+
+   return sprintf(buf, "%d %d\n", low, high);
+}
+
+static ssize_t charge_thresholds_store(struct device *dev,
+   struct device_attribute *attr,
+   const char *buf, size_t size)
+{
+   int low, high, err;
+
+   if (sscanf(buf, "%d %d", &low, &high) != 2 ||
+   low < 0 || high > 100 ||
+   low > high)
+   return -EINVAL;
+
+   err = huawei_wmi_battery_set(dev, low, high);
+   if (err)
+   return err;
+
+   return size;
+}
+
+static ssize_t fn_lock_state_show(struct device *dev,
+   struct device_attribute *attr,
+   char *buf)
+{
+   int err, on;
+
+   err = huawei_wmi_fn_lock_get(dev, &on);
+   if (err)
+   return err;
+
+   return sprintf(buf, "%d\n", on);
+}
+
+static ssize_t fn_lock_state_store(struct device *dev,
+   struct device_attribute *attr,
+   const char *buf, size_t size)
+{
+   int on, err;
+
+   if (kstrtoint(buf, 10, &on) ||
+   on < 0 || on > 1)
+   return -EINVAL;
+
+   err = huawei_wmi_fn_lock_set(dev, on);
+   if (err)
+   return err;
+
+   return size;
+}
+
+static DEVICE_ATTR_RW(charge_thresholds);
+static DEVICE_ATTR_RW(fn_lock_state);
+
+static struct attribute *huawei_wmi_attrs[] = {
+   &dev_attr_charge_thresholds.attr,
+   &dev_attr_fn_lock_state.attr,
+   NULL
+};
+
+ATTRIBUTE_GROUPS(huawei_wmi);
+
 /* Input */
 
 static void huawei_wmi_process_key(struct input_dev *idev, int code)
@@ -508,6 +583,12 @@ static int huawei_wmi_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Failed to setup leds\n");
return err;
}
+
+   err = sysfs_create_groups(&pdev->dev.kobj, huawei_wmi_groups);
+   if (err) {
+   dev_err(&pdev->dev, "Failed to create sysfs 
interface\n");
+   return err;
+   }
}
 
return 0;
@@ -522,6 +603,7 @@ static int huawei_wmi_remove(struct platform_device *pdev)
wmi_remove_notify_handler(HWMI_EVENT_GUID);
 
if (wmi_has_guid(HWMI_METHOD_GUID)) {
+   sysfs_remove_groups(&pdev->dev.kobj, huawei_wmi_groups);
}
 
return 0;
-- 
2.20.1



[RFC 9/9] platform/x86: huawei-wmi: Add debugfs support

2019-06-29 Thread Ayman Bagabas
Add a debugfs interface that can be used to call the WMI management
interface function.

The WMI interface takes a 64 bit integer and returns 256-260 bytes
buffer. This debugfs interface creates two files, one stores a 64 bit
int and the other calls the WMI interface and dumps out the returned
buffer.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 98 +++
 1 file changed, 98 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index f7041fb71026..bdca8bd76c8c 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -6,6 +6,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -45,7 +46,13 @@ struct quirk_entry {
 
 static struct quirk_entry *quirks;
 
+struct huawei_wmi_debug {
+   struct dentry *root;
+   u64 arg;
+};
+
 struct huawei_wmi_priv {
+   struct huawei_wmi_debug debug;
struct input_dev *idev[2];
struct led_classdev cdev;
struct mutex battery_lock;
@@ -456,6 +463,94 @@ static struct attribute *huawei_wmi_attrs[] = {
 
 ATTRIBUTE_GROUPS(huawei_wmi);
 
+/* debugfs */
+
+static void huawei_wmi_debugfs_call_dump(struct seq_file *m, void *data,
+   union acpi_object *obj)
+{
+   struct huawei_wmi_priv *priv = m->private;
+   int i;
+
+   switch (obj->type) {
+   case ACPI_TYPE_INTEGER:
+   seq_printf(m, "0x%llx", obj->integer.value);
+   break;
+   case ACPI_TYPE_STRING:
+   seq_printf(m, "\"%*s\"", obj->string.length, 
obj->string.pointer);
+   break;
+   case ACPI_TYPE_BUFFER:
+   seq_puts(m, "{");
+   for (i = 0; i < obj->buffer.length; i++) {
+   seq_printf(m, "0x%02x", obj->buffer.pointer[i]);
+   if (i < obj->buffer.length - 1)
+   seq_puts(m, ",");
+   }
+   seq_puts(m, "}");
+   break;
+   case ACPI_TYPE_PACKAGE:
+   seq_puts(m, "[");
+   for (i = 0; i < obj->package.count; i++) {
+   huawei_wmi_debugfs_call_dump(m, priv, 
&obj->package.elements[i]);
+   if (i < obj->package.count - 1)
+   seq_puts(m, ",");
+   }
+   seq_puts(m, "]");
+   break;
+   default:
+   dev_err(&priv->pdev->dev, "Unexpected obj type, got %d\n", 
obj->type);
+   return;
+   }
+}
+
+static int huawei_wmi_debugfs_call_show(struct seq_file *m, void *data)
+{
+   struct huawei_wmi_priv *priv = m->private;
+   struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
+   struct acpi_buffer in;
+   union acpi_object *obj;
+   int err;
+
+   in.length = sizeof(u64);
+   in.pointer = &priv->debug.arg;
+
+   err = huawei_wmi_call(&priv->pdev->dev, &in, &out);
+   if (err)
+   return err;
+
+   obj = out.pointer;
+   if (!obj) {
+   err = -EIO;
+   goto fail_debugfs_call;
+   }
+
+   huawei_wmi_debugfs_call_dump(m, priv, obj);
+
+fail_debugfs_call:
+   kfree(out.pointer);
+   return err;
+}
+
+DEFINE_SHOW_ATTRIBUTE(huawei_wmi_debugfs_call);
+
+static void huawei_wmi_debugfs_exit(struct device *dev)
+{
+   struct huawei_wmi_priv *priv = dev_get_drvdata(dev);
+
+   debugfs_remove_recursive(priv->debug.root);
+}
+
+static void huawei_wmi_debugfs_setup(struct device *dev)
+{
+   struct huawei_wmi_priv *priv = dev_get_drvdata(dev);
+
+   priv->debug.root = debugfs_create_dir("huawei-wmi", NULL);
+
+   debugfs_create_x64("arg", 0644, priv->debug.root,
+   &priv->debug.arg);
+   debugfs_create_file("call", 0400,
+   priv->debug.root, priv, &huawei_wmi_debugfs_call_fops);
+}
+
 /* Input */
 
 static void huawei_wmi_process_key(struct input_dev *idev, int code)
@@ -589,6 +684,8 @@ static int huawei_wmi_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Failed to create sysfs 
interface\n");
return err;
}
+
+   huawei_wmi_debugfs_setup(&pdev->dev);
}
 
return 0;
@@ -603,6 +700,7 @@ static int huawei_wmi_remove(struct platform_device *pdev)
wmi_remove_notify_handler(HWMI_EVENT_GUID);
 
if (wmi_has_guid(HWMI_METHOD_GUID)) {
+   huawei_wmi_debugfs_exit(&pdev->dev);
sysfs_remove_groups(&pdev->dev.kobj, huawei_wmi_groups);
}
 
-- 
2.20.1



[RFC 6/9] platform/x86: huawei-wmi: Add battery charging thresholds

2019-06-29 Thread Ayman Bagabas
Implement battery charging thresholds functionality to be used with
sysfs interface support that is implemented in this series.

Setting battery charging thresholds can introduce a race condition where
two are trying to read/write values from/to EC memory. Even though a
mutex is used, this doesn't guarantee that these values will make it to
EC so a blocking sleep is necessary.

This sleep is enforced on the MACH-WX9 model since turning off battery
protection using (0,100) doesn't change the values in the EC memory,
instead it only disables the protection. So a workaround is to set the
thresholds to (0,0) before setting it to (0,100). This will ensure
that we update these values in EC memory and then turning it off with
(0,100). Thus, the msleep(1000).

Signed-off-by: Ayman Bagabas 
---
In huawei_wmi_battery_get and later in huawei_wmi_fn_lock_get, 256 bytes
buffer is allocated dynamically on the stack. Now I know this is
discouraged, should it be changed to use kmalloc?

 drivers/platform/x86/huawei-wmi.c | 54 +++
 1 file changed, 54 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 9013a05d2832..da3986cd0428 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -6,6 +6,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -46,6 +47,7 @@ static struct quirk_entry *quirks;
 struct huawei_wmi_priv {
struct input_dev *idev[2];
struct led_classdev cdev;
+   struct mutex battery_lock;
struct mutex wmi_lock;
struct platform_device *pdev;
 };
@@ -298,6 +300,57 @@ static int huawei_wmi_leds_setup(struct device *dev)
return devm_led_classdev_register(dev, &priv->cdev);
 }
 
+/* Battery protection */
+
+static int huawei_wmi_battery_get(struct device *dev, int *low, int *high)
+{
+   struct huawei_wmi_priv *priv = dev_get_drvdata(dev);
+   u8 ret[0x100];
+   int err, i;
+
+   mutex_lock(&priv->battery_lock);
+   err = huawei_wmi_cmd(dev, BATTERY_THRESH_GET, ret, 0x100);
+   mutex_unlock(&priv->battery_lock);
+   if (err)
+   return err;
+
+   /* Find the last two non-zero values. Return status is ignored. */
+   i = 0x100;
+   do {
+   *low = ret[i-1];
+   *high = ret[i];
+   } while (i > 2 && !ret[i--]);
+
+   return 0;
+}
+
+static int huawei_wmi_battery_set(struct device *dev, int low, int high)
+{
+   struct huawei_wmi_priv *priv = dev_get_drvdata(dev);
+   u8 arg[8];
+   int err;
+
+   *(u64 *)arg = BATTERY_THRESH_SET;
+   arg[2] = low;
+   arg[3] = high;
+
+   /* This is an edge case were some models turn battery protection
+* off without changing their thresholds values. We clear the
+* values before turning off protection. Sometimes we need a sleep 
delay to
+* make sure these values make their way to EC memory.
+*/
+   if (low == 0 && high == 100)
+   huawei_wmi_battery_set(dev, 0, 0);
+
+   mutex_lock(&priv->battery_lock);
+   err = huawei_wmi_cmd(dev, *(u64 *)arg, NULL, NULL);
+   if (quirks && quirks->battery_sleep)
+   msleep(1000);
+   mutex_unlock(&priv->battery_lock);
+
+   return err;
+}
+
 /* Input */
 
 static void huawei_wmi_process_key(struct input_dev *idev, int code)
@@ -418,6 +471,7 @@ static int huawei_wmi_probe(struct platform_device *pdev)
 
if (wmi_has_guid(HWMI_METHOD_GUID)) {
mutex_init(&priv->wmi_lock);
+   mutex_init(&priv->battery_lock);
 
err = huawei_wmi_leds_setup(&pdev->dev);
if (err) {
-- 
2.20.1



[RFC 5/9] platform/x86: huawei-wmi: Control micmute led through wmi interface

2019-06-29 Thread Ayman Bagabas
Now that huawei WMI management interface is implemented, micmute LED can
be controlled easily through this interface. Exception is the Matebook X
(2017) which continue to uses ACPI EC method to control the LED. This
model can control the LED through the legacy WMI interface which is not
implemented ATM.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 86 ++-
 1 file changed, 49 insertions(+), 37 deletions(-)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 8f918138053a..9013a05d2832 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -46,8 +46,6 @@ static struct quirk_entry *quirks;
 struct huawei_wmi_priv {
struct input_dev *idev[2];
struct led_classdev cdev;
-   acpi_handle handle;
-   char *acpi_method;
struct mutex wmi_lock;
struct platform_device *pdev;
 };
@@ -238,49 +236,57 @@ static int huawei_wmi_cmd(struct device *dev, u64 arg, u8 
*buf, size_t buflen)
 static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
 {
-   struct huawei_wmi_priv *priv = dev_get_drvdata(led_cdev->dev->parent);
-   acpi_status status;
-   union acpi_object args[3];
-   struct acpi_object_list arg_list = {
-   .pointer = args,
-   .count = ARRAY_SIZE(args),
-   };
-
-   args[0].type = args[1].type = args[2].type = ACPI_TYPE_INTEGER;
-   args[1].integer.value = 0x04;
-
-   if (strcmp(priv->acpi_method, "SPIN") == 0) {
-   args[0].integer.value = 0;
-   args[2].integer.value = brightness ? 1 : 0;
-   } else if (strcmp(priv->acpi_method, "WPIN") == 0) {
-   args[0].integer.value = 1;
-   args[2].integer.value = brightness ? 0 : 1;
+   /* This is a workaround until the "legacy" interface is implemented. */
+   if (quirks && quirks->ec_micmute) {
+   char *acpi_method;
+   acpi_handle handle;
+   acpi_status status;
+   union acpi_object args[3];
+   struct acpi_object_list arg_list = {
+   .pointer = args,
+   .count = ARRAY_SIZE(args),
+   };
+
+   handle = ec_get_handle();
+   if (!handle) {
+   dev_err(led_cdev->dev->parent, "Failed to get EC 
handle\n");
+   return -ENODEV;
+   }
+
+   args[0].type = args[1].type = args[2].type = ACPI_TYPE_INTEGER;
+   args[1].integer.value = 0x04;
+
+   if (acpi_has_method(handle, "SPIN")) {
+   acpi_method = "SPIN";
+   args[0].integer.value = 0;
+   args[2].integer.value = brightness ? 1 : 0;
+   } else if (acpi_has_method(handle, "WPIN")) {
+   acpi_method = "WPIN";
+   args[0].integer.value = 1;
+   args[2].integer.value = brightness ? 0 : 1;
+   } else {
+   return -ENODEV;
+   }
+
+   status = acpi_evaluate_object(handle, acpi_method, &arg_list, 
NULL);
+   if (ACPI_FAILURE(status))
+   return -ENODEV;
+
+   return 0;
} else {
-   return -EINVAL;
-   }
+   u8 arg[8];
 
-   status = acpi_evaluate_object(priv->handle, priv->acpi_method, 
&arg_list, NULL);
-   if (ACPI_FAILURE(status))
-   return -ENXIO;
+   *(u64 *)arg = MICMUTE_LED_SET;
+   arg[2] = brightness;
 
-   return 0;
+   return huawei_wmi_cmd(led_cdev->dev->parent, *(u64 *)arg, NULL, 
NULL);
+   }
 }
 
 static int huawei_wmi_leds_setup(struct device *dev)
 {
struct huawei_wmi_priv *priv = dev_get_drvdata(dev);
 
-   priv->handle = ec_get_handle();
-   if (!priv->handle)
-   return 0;
-
-   if (acpi_has_method(priv->handle, "SPIN"))
-   priv->acpi_method = "SPIN";
-   else if (acpi_has_method(priv->handle, "WPIN"))
-   priv->acpi_method = "WPIN";
-   else
-   return 0;
-
priv->cdev.name = "platform::micmute";
priv->cdev.max_brightness = 1;
priv->cdev.brightness_set_blocking = huawei_wmi_micmute_led_set;
@@ -412,9 +418,15 @@ static int huawei_wmi_probe(struct platform_device *pdev)
 
if (wmi_has_guid(HWMI_METHOD_GUID)) {
mutex_init(&priv->wmi_lock);
+
+   err = huawei_wmi_leds_setup(&pdev->dev);
+   if (err) {
+   dev_err(&pdev->dev, "Failed to setup leds\n");
+   return err;
+   }
}
 
-   return huawei_wmi_leds_setup(&pdev->dev);
+   return 0;
 }
 
 static int huawei_wmi_remove(struct platform_device *pdev)
-- 
2.20.1



[RFC 0/9] platform/x86: Huawei WMI laptop extras driver

2019-06-29 Thread Ayman Bagabas
This patch series introduce changes to huawei-wmi driver that includes:
* Move to platform driver
* Implement WMI management interface
* Add micmute LED support through WMI
* Add battery charging protection support through WMI
* Add fn-lock support through WMI
* Implement driver quirks and parameters
* Add a debugfs interface to WMI

# Move to platform driver

The current driver offers hotkeys and micmute led support only. With
these changes, a platform driver makes more sense since it handles these
changes pretty nicely.

# Implement WMI management interface

Huawei Matebook laptops come with two WMI interfaces. The first being
WMI0 which is considered "legacy" and AFAIK only found on the Matebook X
released in 2017. The second has a UID of "HWMI" and is found in pretty
much all models with a slight difference in implementation except for
the Matebook X (2017). Since this model has two interfaces, some aspects
are controlled through the legacy interface and some through the other
interface. Currently, the legacy interface is not fully implemented and
is only used for hotkeys and further debugging has to be done.

The WMI interface takes a 64 bit integer, although uses 32 bits most of
the time, and returns a 256-260 bytes buffer consists of either one ACPI
buffer of 260 bytes, in the case of Matebook X (2017), or one ACPI
package of two buffers, one with 4 bytes, and the other with 256 bytes.
We only care about the latter 256 buffer in both cases since the 4 bytes
always return zeros. The first byte of this 256 buffer always has the
return status where 1 indicated error. Some models require calling the
WMI interface twice to execute a command.

# Add micmute LED support through WMI

After implementing the WMI interface, micmute LED can be controlled
easily. Models with the legacy interface fall back to ACPI EC method
control since the legacy interface is not implemented.

# Add battery charging protection support through WMI

Most models, that has the WMI interface, are capable of battery
protection where it can control battery charging thresholds and limits
charging the battery to certain values.

# Add fn-lock support through WMI

The behavior of hotkeys is not the same among all models. Some models
require fn-lock to do things like `Ctrl-Ins` or `Alt-PrtSc`. By default,
hotkeys behave as special keys (media keys, Ins, etc), but if a modifier
is used (ctrl, alt, shift) these keys behave as F1-F12 keys. If the Fn
key is toggled on, the hotkeys with or without a modifier, behave as
F1-F12 keys. This makes it impossible to use a modifier and `PrtSc` or
`Ins`.

Now, some models fix this by excluding `PrtSc` and `Ins` keys from being
treated as F11 and F12 keys with the use of a modifier. However, some
models do not, and fixes this by the so called fn-lock.

Fn-lock inverts the behavior of the top row from special keys to F1-F12
keys. So a modifier and a special key would be possible which make
things like `Alt-Ins` possible. Now, with fn-lock we would have 4 modes:

* Fn-key off & fn-lock off - hotkeys treated as special keys using a
  modifier gives F1-F12 keys.
* Fn-key on & fn-lock off - hotkeys treated as F1-F12 keys and using a
  modifier gives F1-F12.
* Fn-key off & fn-lock on - hotkeys are treated as F1-F12 keys and using
  a modifier gives special keys.
* Fn-key on & fn-lock on - hotkeys are treated as special keys and using
  a modifier gives special keys.

# Implement driver quirks and parameters

The driver introduces 3 quirks and 2 parameters that can change the
driver's behavior. These quirks being as:
1. Fixes reporting brightness keys twice since it's already handled by
   acpi-video.
2. Some models need a short delay when setting battery thresholds to
   prevent a race condition when two processes read/write.
3. Matebook X (2017) handles micmute led through the "legacy" interface
   which is not currently implemented. Use ACPI EC method to control
   this led.

and the 2 parameters can enforce the behavior of quirk 1 & 2.

# Add a debugfs interface to WMI

An interface to the WMI management interface that allows easier
debugging.

Ayman Bagabas (9):
  platform/x86: huawei-wmi: rename guid and driver name
  platform/x86: huawei-wmi: move to platform driver
  platform/x86: huawei-wmi: implement huawei wmi management interface
  platform/x86: huawei-wmi: add quirks and module parameters
  platform/x86: huawei-wmi: control micmute led through wmi interface
  platform/x86: huawei-wmi: add battery charging thresholds
  platform/x86: huawei-wmi: add fn-lock support
  platform/x86: huawei-wmi: add sysfs interface support
  platform/x86: huawei-wmi: add debugfs support

 drivers/platform/x86/huawei-wmi.c | 710 ++
 1 file changed, 629 insertions(+), 81 deletions(-)

-- 
2.20.1



[RFC 2/9] platform/x86: huawei-wmi: Move to platform driver

2019-06-29 Thread Ayman Bagabas
Move from WMI driver to platform driver. This move is necessary since
the driver is no longer a hotkeys driver only. Platform driver makes it
easier for users to access sysfs attributes under (i.e.
/sys/devices/platform/huawei-wmi) compared to wmi driver.

Signed-off-by: Ayman Bagabas 
---
Do you think we should stick to wmi driver especially after
"platform/x86: wmi: add context pointer field to struct wmi_device_id"
https://patchwork.kernel.org/patch/10963421/ which is basically was my
issue with wmi driver where there wasn't a way to distinguish what wmi
device is associated with which guid.

I believe that platform driver is more suitable for this since it has
grown from just a hotkeys driver and users can access sysfs of this
easily with platform driver. What do you think?

 drivers/platform/x86/huawei-wmi.c | 152 +-
 1 file changed, 110 insertions(+), 42 deletions(-)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index a1159850a16c..12cae450fc1f 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -10,6 +10,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 
 /*
@@ -23,12 +24,15 @@
 
 
 struct huawei_wmi_priv {
-   struct input_dev *idev;
+   struct input_dev *idev[2];
struct led_classdev cdev;
acpi_handle handle;
char *acpi_method;
+   struct platform_device *pdev;
 };
 
+struct platform_device *huawei_wmi_pdev;
+
 static const struct key_entry huawei_wmi_keymap[] = {
{ KE_KEY,0x281, { KEY_BRIGHTNESSDOWN } },
{ KE_KEY,0x282, { KEY_BRIGHTNESSUP } },
@@ -46,6 +50,8 @@ static const struct key_entry huawei_wmi_keymap[] = {
{ KE_END,0 }
 };
 
+/* LEDs */
+
 static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
 {
@@ -77,9 +83,9 @@ static int huawei_wmi_micmute_led_set(struct led_classdev 
*led_cdev,
return 0;
 }
 
-static int huawei_wmi_leds_setup(struct wmi_device *wdev)
+static int huawei_wmi_leds_setup(struct device *dev)
 {
-   struct huawei_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
+   struct huawei_wmi_priv *priv = dev_get_drvdata(dev);
 
priv->handle = ec_get_handle();
if (!priv->handle)
@@ -97,15 +103,16 @@ static int huawei_wmi_leds_setup(struct wmi_device *wdev)
priv->cdev.brightness_set_blocking = huawei_wmi_micmute_led_set;
priv->cdev.default_trigger = "audio-micmute";
priv->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
-   priv->cdev.dev = &wdev->dev;
+   priv->cdev.dev = dev;
priv->cdev.flags = LED_CORE_SUSPENDRESUME;
 
-   return devm_led_classdev_register(&wdev->dev, &priv->cdev);
+   return devm_led_classdev_register(dev, &priv->cdev);
 }
 
-static void huawei_wmi_process_key(struct wmi_device *wdev, int code)
+/* Input */
+
+static void huawei_wmi_process_key(struct input_dev *idev, int code)
 {
-   struct huawei_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
const struct key_entry *key;
 
/*
@@ -129,81 +136,142 @@ static void huawei_wmi_process_key(struct wmi_device 
*wdev, int code)
kfree(response.pointer);
}
 
-   key = sparse_keymap_entry_from_scancode(priv->idev, code);
+   key = sparse_keymap_entry_from_scancode(idev, code);
if (!key) {
-   dev_info(&wdev->dev, "Unknown key pressed, code: 0x%04x\n", 
code);
+   dev_info(&idev->dev, "Unknown key pressed, code: 0x%04x\n", 
code);
return;
}
 
-   sparse_keymap_report_entry(priv->idev, key, 1, true);
+   sparse_keymap_report_entry(idev, key, 1, true);
 }
 
-static void huawei_wmi_notify(struct wmi_device *wdev,
-   union acpi_object *obj)
+static void huawei_wmi_input_notify(u32 value, void *context)
 {
-   if (obj->type == ACPI_TYPE_INTEGER)
-   huawei_wmi_process_key(wdev, obj->integer.value);
+   struct input_dev *idev = (struct input_dev *)context;
+   struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
+   union acpi_object *obj;
+   acpi_status status;
+
+   status = wmi_get_event_data(value, &response);
+   if (ACPI_FAILURE(status)) {
+   dev_err(&idev->dev, "Unable to get event data\n");
+   return;
+   }
+
+   obj = (union acpi_object *)response.pointer;
+   if (obj && obj->type == ACPI_TYPE_INTEGER)
+   huawei_wmi_process_key(idev, obj->integer.value);
else
-   dev_info(&wdev->dev, "Bad response type %d\n", obj->type);
+   dev_err(&idev->dev, "Bad response type\n");
+
+   kfree(response.pointer);
 }
 
-static int hua

[RFC 3/9] platform/x86: huawei-wmi: Implement huawei wmi management interface

2019-06-29 Thread Ayman Bagabas
Huawei Matebook laptops come with a WMI management interface that can
control various aspects of the device. This interface is also found on
the old Matebook X released in 2017.

Implement basic functionality of this interface along with supported
command IDs that are implemented in this series.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 130 ++
 1 file changed, 130 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 12cae450fc1f..27520b0f8956 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -10,24 +10,36 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 
 /*
  * Huawei WMI GUIDs
  */
+#define HWMI_METHOD_GUID "ABBC0F5B-8EA1-11D1-A000-C9062910"
 #define HWMI_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
 
 /* Legacy GUIDs */
 #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
 #define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
 
+/* HWMI commands */
+
+enum {
+   BATTERY_THRESH_GET  = 0x1103, /* \GBTT */
+   BATTERY_THRESH_SET  = 0x1003, /* \SBTT */
+   FN_LOCK_GET = 0x0604, /* \GFRS */
+   FN_LOCK_SET = 0x0704, /* \SFRS */
+   MICMUTE_LED_SET = 0x0b04, /* \SMLS */
+};
 
 struct huawei_wmi_priv {
struct input_dev *idev[2];
struct led_classdev cdev;
acpi_handle handle;
char *acpi_method;
+   struct mutex wmi_lock;
struct platform_device *pdev;
 };
 
@@ -50,6 +62,116 @@ static const struct key_entry huawei_wmi_keymap[] = {
{ KE_END,0 }
 };
 
+/* Utils */
+
+static int huawei_wmi_call(struct device *dev, struct acpi_buffer *in,
+   struct acpi_buffer *out)
+{
+   struct huawei_wmi_priv *priv = dev_get_drvdata(dev);
+   acpi_status status;
+
+   mutex_lock(&priv->wmi_lock);
+   status = wmi_evaluate_method(HWMI_METHOD_GUID, 0, 1, in, out);
+   mutex_unlock(&priv->wmi_lock);
+   if (ACPI_FAILURE(status)) {
+   dev_err(dev, "Failed to evaluate wmi method\n");
+   return -ENODEV;
+   }
+
+   return 0;
+}
+
+/* HWMI takes a 64 bit input and returns either a package with 2 buffers, one 
of
+ * 4 bytes and the other of 256 bytes, or one buffer of size 0x104 (260) bytes.
+ * The first 4 bytes are ignored, we ignore the first 4 bytes buffer if we got 
a
+ * package, or skip the first 4 if a buffer of 0x104 is used. The first byte of
+ * the remaining 0x100 sized buffer has the return status of every call. In 
case
+ * the return status is non-zero, we return -ENODEV but still copy the returned
+ * buffer to the given buffer parameter (buf).
+ */
+static int huawei_wmi_cmd(struct device *dev, u64 arg, u8 *buf, size_t buflen)
+{
+   struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
+   struct acpi_buffer in;
+   union acpi_object *obj;
+   size_t len;
+   int err, i;
+
+   in.length = sizeof(arg);
+   in.pointer = &arg;
+
+   /* Some models require calling HWMI twice to execute a command. We 
evaluate
+* HWMI and if we get a non-zero return status we evaluate it again.
+*/
+   for (i = 0; i < 2; i++) {
+   err = huawei_wmi_call(dev,  &in, &out);
+   if (err)
+   goto fail_cmd;
+
+   obj = out.pointer;
+   if (!obj) {
+   err = -EIO;
+   goto fail_cmd;
+   }
+
+   switch (obj->type) {
+   /* Models that implement both "legacy" and HWMI tend to return 
a 0x104
+* sized buffer instead of a package of 0x4 and 0x100 buffers.
+*/
+   case ACPI_TYPE_BUFFER:
+   if (obj->buffer.length == 0x104) {
+   // Skip the first 4 bytes.
+   obj->buffer.pointer += 4;
+   len = 0x100;
+   } else {
+   dev_err(dev, "Bad buffer length, got %d\n", 
obj->buffer.length);
+   err = -EIO;
+   goto fail_cmd;
+   }
+
+   break;
+   /* HWMI returns a package with 2 buffer elements, one of 4 
bytes and the
+* other is 256 bytes.
+*/
+   case ACPI_TYPE_PACKAGE:
+   if (obj->package.count != 2) {
+   dev_err(dev, "Bad package count, got %d\n", 
obj->package.count);
+   err = -EIO;
+   goto fail_cmd;
+  

[RFC 4/9] platform/x86: huawei-wmi: Add quirks and module parameters

2019-06-29 Thread Ayman Bagabas
Introduce quirks and module parameters. 3 quirks are added:
1. Fixes reporting brightness keys twice since it's already handled by
   acpi-video.
2. Some models need a short delay when setting battery thresholds to
   prevent a race condition when two processes read/write.
3. Matebook X (2017) handles micmute led through the "legacy" interface
   which is not currently implemented. Use ACPI EC method to control
   this led.

2 module parameters are added to enable this short delay and/or report
  brightness keys through this driver.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 71 +++
 1 file changed, 71 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 27520b0f8956..8f918138053a 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -6,6 +6,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -34,6 +35,14 @@ enum {
MICMUTE_LED_SET = 0x0b04, /* \SMLS */
 };
 
+struct quirk_entry {
+   bool battery_sleep;
+   bool ec_micmute;
+   bool report_brightness;
+};
+
+static struct quirk_entry *quirks;
+
 struct huawei_wmi_priv {
struct input_dev *idev[2];
struct led_classdev cdev;
@@ -62,6 +71,58 @@ static const struct key_entry huawei_wmi_keymap[] = {
{ KE_END,0 }
 };
 
+static bool battery_sleep;
+static bool report_brightness;
+
+module_param(battery_sleep, bool, 0444);
+MODULE_PARM_DESC(battery_sleep,
+   "Delay after setting battery charging thresholds.");
+module_param(report_brightness, bool, 0444);
+MODULE_PARM_DESC(report_brightness,
+   "Report brightness keys.");
+
+/* Quirks */
+
+static int __init dmi_matched(const struct dmi_system_id *dmi)
+{
+   quirks = dmi->driver_data;
+   return 1;
+}
+
+static struct quirk_entry quirk_unknown = {
+};
+
+static struct quirk_entry quirk_battery_sleep = {
+   .battery_sleep = true,
+};
+
+static struct quirk_entry quirk_matebook_x = {
+   .ec_micmute = true,
+   .report_brightness = true,
+};
+
+static const struct dmi_system_id huawei_quirks[] = {
+   {
+   .callback = dmi_matched,
+   .ident = "Huawei MACH-WX9",
+   .matches = {
+   DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
+   DMI_MATCH(DMI_PRODUCT_NAME, "MACH-WX9"),
+   },
+   .driver_data = &quirk_battery_sleep
+   },
+   {
+   .callback = dmi_matched,
+   .ident = "Huawei MateBook X",
+   .matches = {
+   DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
+   DMI_MATCH(DMI_PRODUCT_NAME, "HUAWEI MateBook X")
+   },
+   .driver_data = &quirk_matebook_x
+   },
+   {  }
+};
+
 /* Utils */
 
 static int huawei_wmi_call(struct device *dev, struct acpi_buffer *in,
@@ -264,6 +325,11 @@ static void huawei_wmi_process_key(struct input_dev *idev, 
int code)
return;
}
 
+   if (quirks && !quirks->report_brightness &&
+   (key->sw.code == KEY_BRIGHTNESSDOWN ||
+   key->sw.code == KEY_BRIGHTNESSUP))
+   return;
+
sparse_keymap_report_entry(idev, key, 1, true);
 }
 
@@ -377,6 +443,11 @@ static __init int huawei_wmi_init(void)
 {
int err;
 
+   quirks = &quirk_unknown;
+   dmi_check_system(huawei_quirks);
+   quirks->battery_sleep |= battery_sleep;
+   quirks->report_brightness |= report_brightness;
+
err = platform_driver_register(&huawei_wmi_driver);
if (err)
return err;
-- 
2.20.1



[RFC 1/9] platform/x86: huawei-wmi: Rename guid and driver name

2019-06-29 Thread Ayman Bagabas
Use WMI device UID, AMW0 has a UID of HWMI. WMI0 is the device name
and doesn't have a UID so keep it as it is.

Change module description to reflect the upcoming changes.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 14 --
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 52fcac5b393a..a1159850a16c 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- *  Huawei WMI hotkeys
+ *  Huawei WMI laptop extras driver
  *
  *  Copyright (C) 2018   Ayman Bagabas 
  */
@@ -15,10 +15,12 @@
 /*
  * Huawei WMI GUIDs
  */
-#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
-#define AMW0_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
+#define HWMI_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
 
+/* Legacy GUIDs */
 #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
+#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
+
 
 struct huawei_wmi_priv {
struct input_dev *idev;
@@ -37,7 +39,7 @@ static const struct key_entry huawei_wmi_keymap[] = {
{ KE_KEY,0x289, { KEY_WLAN } },
// Huawei |M| key
{ KE_KEY,0x28a, { KEY_CONFIG } },
-   // Keyboard backlight
+   // Keyboard backlit
{ KE_IGNORE, 0x293, { KEY_KBDILLUMTOGGLE } },
{ KE_IGNORE, 0x294, { KEY_KBDILLUMUP } },
{ KE_IGNORE, 0x295, { KEY_KBDILLUMUP } },
@@ -186,7 +188,7 @@ static int huawei_wmi_probe(struct wmi_device *wdev)
 
 static const struct wmi_device_id huawei_wmi_id_table[] = {
{ .guid_string = WMI0_EVENT_GUID },
-   { .guid_string = AMW0_EVENT_GUID },
+   { .guid_string = HWMI_EVENT_GUID },
{  }
 };
 
@@ -203,5 +205,5 @@ module_wmi_driver(huawei_wmi_driver);
 
 MODULE_DEVICE_TABLE(wmi, huawei_wmi_id_table);
 MODULE_AUTHOR("Ayman Bagabas ");
-MODULE_DESCRIPTION("Huawei WMI hotkeys");
+MODULE_DESCRIPTION("Huawei WMI laptop extras driver");
 MODULE_LICENSE("GPL v2");
-- 
2.20.1



[PATCH v2 2/8] platform/x86: huawei-wmi: implement WMI management interface

2019-06-13 Thread Ayman Bagabas
The patch introduces a WMI BIOS interface that can control various
device features like micmute LED, battery charging thresholds, and
fn-lock. This interface, with device UID of HWMI, is found on recent and
old models including MateBook X released in 2017. This model is kind of
"special" since it has two WMI interfaces, this interface and what we
call it the "legacy" interface. Due to lack of hardware and testers,
this "legacy" interface is not "fully" implemented yet. This "legacy"
interface supports setting the micmute LED for MateBook X (2017).

This device, HWMI, has only one method that takes a 64 bit argument and
returns a package with two elements, the first is 4 bytes and the second
is 256 bytes. The first 4 bytes are always skipped since they return
zero all the time. MateBook X (2017) is a bit different
where it takes 64 bit argument but returns one 260 byte buffer (265+4).
Right now, this interface doesn't offer any usability for MateBook X
(2017) except for fn-lock and debugfs.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 127 ++
 1 file changed, 127 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 4a9e14d3b705..37b09d497f5e 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -10,22 +10,35 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 
 /*
  * Huawei WMI GUIDs
  */
+#define HWMI_METHOD_GUID "ABBC0F5B-8EA1-11D1-A000-C9062910"
 #define HWMI_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
 
 /* Legacy GUIDs */
 #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
 #define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
 
+/* HWMI_commands */
+
+enum {
+   BATTERY_THRESH_GET  = 0x1103, /* \GBTT */
+   BATTERY_THRESH_SET  = 0x1003, /* \SBTT */
+   FAN_SPEED_GET   = 0x0802, /* \GFNS */
+   FN_LOCK_GET = 0x0604, /* \GFRS */
+   FN_LOCK_SET = 0x0704, /* \SFRS */
+   MICMUTE_LED_SET = 0x0b04, /* \SMLS */
+};
 
 struct huawei_wmi {
struct led_classdev cdev;
struct input_dev *idev[2];
+   struct mutex wmi_lock;
struct platform_device *pdev;
 };
 
@@ -48,6 +61,118 @@ static const struct key_entry huawei_wmi_keymap[] = {
{ KE_END,0 }
 };
 
+/* Utils */
+
+static int huawei_wmi_call(struct device *dev, struct acpi_buffer *in,
+   struct acpi_buffer *out)
+{
+   struct huawei_wmi *huawei = dev_get_drvdata(dev);
+   acpi_status status;
+
+   mutex_lock(&huawei->wmi_lock);
+   status = wmi_evaluate_method(HWMI_METHOD_GUID, 0, 1, in, out);
+   mutex_unlock(&huawei->wmi_lock);
+   if (ACPI_FAILURE(status)) {
+   dev_err(dev, "Failed to evaluate wmi method\n");
+   return -ENODEV;
+   }
+
+   return 0;
+}
+
+/* HWMI takes a 64 bit input and returns either a package with 2 buffers, one 
of
+ * 4 bytes and the other of 256 bytes, or one buffer of size 0x104 (260) bytes.
+ * The first 4 bytes are ignored, we ignore the first 4 bytes buffer if we got 
a
+ * package, or skip the first 4 if a buffer of 0x104 is used. The first byte of
+ * the remaining 0x100 sized buffer has the return status of every call. In 
case
+ * the return status is non-zero, we return -ENODEV but still copy the returned
+ * buffer to the given buffer parameter (buf).
+ */
+static int huawei_wmi_cmd(struct device *dev, u64 arg, u8 *buf, size_t buflen)
+{
+   struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
+   struct acpi_buffer in;
+   union acpi_object *obj;
+   size_t len;
+   int err, i;
+
+   in.length = sizeof(u64);
+   in.pointer = &arg;
+
+   /* Some models require calling HWMI twice to execute a command. We 
evaluate
+* HWMI and if we get a non-zero return status we evaluate it again.
+*/
+   for (i = 0; i < 2; i++) {
+   err = huawei_wmi_call(dev,  &in, &out);
+   if (err) {
+   goto fail_cmd;
+   }
+
+   obj = out.pointer;
+   if (!obj) {
+   err = -EIO;
+   goto fail_cmd;
+   }
+
+   switch (obj->type) {
+   /* Models that implement both "legacy" and HWMI tend to return 
a 0x104
+* sized buffer instead of a package of 0x4 and 0x100 buffers.
+*/
+   case ACPI_TYPE_BUFFER:
+   if (obj->buffer.length == 0x104) {
+   // Skip the first 4 bytes.
+   obj->buffer.pointer += 4;
+   

[PATCH v2 0/8] platform/x86: Huawei WMI laptop extras driver

2019-06-13 Thread Ayman Bagabas
Changes from v1:
* introducing debugfs
* code reformatting

This patch series introduce new features to the driver and also moves the
driver from wmi_driver to platform_driver. This move is necessary because the
driver is no longer only a hotkeys driver and platform_driver offers easier
future extensibility.

The patch series introduces a WMI BIOS interface that brings on new features
and enables controlling micmute LED through this interface on supported models.
It also enables controlling battery charging thresholds and fn-lock state.
These features are controlled through the HWMI WMI device present in most of
these laptops.

Currently, micmute LED is controlled through an ACPI method under EC.
This method ("SPIN", "WPIN") is specific to some models and wouldn't
work on all Huawei laptops. Controlling this LED through the interface provides
a better unified method to control the LED on models that implements this
feature.

The behavior of hotkeys is not the same among all models. Some models
require fn-lock to do things like `Ctrl-Ins` or `Alt-PrtSc`. Fn-lock inverts the
behavior of the top row from special keys to F1-F12 keys.

A debugfs interface is also implemented to support unrepresented features and to
provide debugging feedback from users.

Ayman Bagabas (8):
  platform/x86: huawei-wmi: move to platform driver
  platform/x86: huawei-wmi: implement WMI management interface
  platform/x86: huawei-wmi: use quirks and module parameters
  platform/x86: huawei-wmi: control micmute LED through WMI interface
  platform/x86: huawei-wmi: add battery charging protection support
  platform/x86: huawei-wmi: add fn-lock support
  platform/x86: huawei-wmi: add sysfs interface support
  platform/x86: huawei-wmi: add debugfs files support

 drivers/platform/x86/huawei-wmi.c | 754 ++
 1 file changed, 665 insertions(+), 89 deletions(-)

-- 
2.20.1



[PATCH v2 1/8] platform/x86: huawei-wmi: move to platform driver

2019-06-13 Thread Ayman Bagabas
This move is necessary because the driver is no longer only a hotkeys
driver and platform_driver offers easier future extensibility. Along
with that, some renames have been made to identify the WMI device in ASL
code. AMW0 -> HWMI which is the UID of this WMI device found on
supported laptops. WMI0 is the device name and has no UID, therefore,
left as it is.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 231 --
 1 file changed, 154 insertions(+), 77 deletions(-)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 52fcac5b393a..4a9e14d3b705 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- *  Huawei WMI hotkeys
+ *  Huawei WMI laptop extras driver
  *
  *  Copyright (C) 2018   Ayman Bagabas 
  */
@@ -10,23 +10,27 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 
 /*
  * Huawei WMI GUIDs
  */
-#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
-#define AMW0_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
+#define HWMI_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
 
+/* Legacy GUIDs */
 #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
+#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
 
-struct huawei_wmi_priv {
-   struct input_dev *idev;
+
+struct huawei_wmi {
struct led_classdev cdev;
-   acpi_handle handle;
-   char *acpi_method;
+   struct input_dev *idev[2];
+   struct platform_device *pdev;
 };
 
+struct platform_device *huawei_wmi_pdev;
+
 static const struct key_entry huawei_wmi_keymap[] = {
{ KE_KEY,0x281, { KEY_BRIGHTNESSDOWN } },
{ KE_KEY,0x282, { KEY_BRIGHTNESSUP } },
@@ -37,17 +41,20 @@ static const struct key_entry huawei_wmi_keymap[] = {
{ KE_KEY,0x289, { KEY_WLAN } },
// Huawei |M| key
{ KE_KEY,0x28a, { KEY_CONFIG } },
-   // Keyboard backlight
+   // Keyboard backlit
{ KE_IGNORE, 0x293, { KEY_KBDILLUMTOGGLE } },
{ KE_IGNORE, 0x294, { KEY_KBDILLUMUP } },
{ KE_IGNORE, 0x295, { KEY_KBDILLUMUP } },
{ KE_END,0 }
 };
 
+/* LEDs */
+
 static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
 {
-   struct huawei_wmi_priv *priv = dev_get_drvdata(led_cdev->dev->parent);
+   char *acpi_method;
+   acpi_handle handle;
acpi_status status;
union acpi_object args[3];
struct acpi_object_list arg_list = {
@@ -55,55 +62,54 @@ static int huawei_wmi_micmute_led_set(struct led_classdev 
*led_cdev,
.count = ARRAY_SIZE(args),
};
 
+   handle = ec_get_handle();
+   if (!handle) {
+   dev_err(led_cdev->dev->parent, "Failed to get EC handle\n");
+   return -ENODEV;
+   }
+
args[0].type = args[1].type = args[2].type = ACPI_TYPE_INTEGER;
args[1].integer.value = 0x04;
 
-   if (strcmp(priv->acpi_method, "SPIN") == 0) {
+   if (acpi_has_method(handle, "SPIN")) {
+   acpi_method = "SPIN";
args[0].integer.value = 0;
args[2].integer.value = brightness ? 1 : 0;
-   } else if (strcmp(priv->acpi_method, "WPIN") == 0) {
+   } else if (acpi_has_method(handle, "WPIN")) {
+   acpi_method = "WPIN";
args[0].integer.value = 1;
args[2].integer.value = brightness ? 0 : 1;
} else {
-   return -EINVAL;
+   return -ENODEV;
}
 
-   status = acpi_evaluate_object(priv->handle, priv->acpi_method, 
&arg_list, NULL);
-   if (ACPI_FAILURE(status))
-   return -ENXIO;
+   status = acpi_evaluate_object(handle, acpi_method, &arg_list, NULL);
+   if (ACPI_FAILURE(status)) {
+   return -ENODEV;
+   }
 
return 0;
 }
 
-static int huawei_wmi_leds_setup(struct wmi_device *wdev)
+static int huawei_wmi_leds_setup(struct device *dev)
 {
-   struct huawei_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
+   struct huawei_wmi *huawei = dev_get_drvdata(dev);
 
-   priv->handle = ec_get_handle();
-   if (!priv->handle)
-   return 0;
+   huawei->cdev.name = "platform::micmute";
+   huawei->cdev.max_brightness = 1;
+   huawei->cdev.brightness_set_blocking = huawei_wmi_micmute_led_set;
+   huawei->cdev.default_trigger = "audio-micmute";
+   huawei->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
+   huawei->cdev.dev = dev->parent;
+   huawei->cdev.flags = LED_CORE_SUSPENDRESUME;
 
-   if (acpi_has_method(priv->handle, "SPIN"))
-   

[PATCH v1] platform/x86: Huawei laptop extras driver

2019-06-13 Thread Ayman Bagabas
This patch introduces new features to the driver and also moves the
driver from wmi_driver to platform_driver.

This move is necessary because the driver is no longer only a hotkeys
driver and platform_driver offers easier future extensibility. The patch
introduces a WMI BIOS interface that brings on new features and enables
controlling micmute LED through this interface on supported models. New
features are controlling battery charging thresholds and fn-lock state
among with module parameters and quirks check.

Currently, micmute LED is controlled through an ACPI method under EC.
This method ("SPIN", "WPIN") is specific to some models and wouldn't
work on all Huawei laptops.

Using this interface, controlling this LED should work with any model.
Except `MateBook X` from 2017, this one doesn't provide controlling the
LED through this interface instead it uses another "legacy" interface
that is not "fully" implemented yet due to lack of hardware. Currently,
this "legacy" interface is used for hotkeys on this specific model. A
quirk is set to use ACPI method to control micmute LED on this model.

Some models that implement the new WMI BIOS interface can control
battery charging thresholds where it limits charging the battery once it
reaches certain thresholds.

The behavior of hotkeys is not the same among all models. Some models
require fn-lock to do things like `Ctrl-Ins` or `Alt-PrtSc`. By default,
hotkeys behave as special keys (media keys, Ins, etc), but if a modifier
is used (ctrl, alt, shift) these keys behave as F1-F12 keys. If the Fn
key is toggled on, the hotkeys with or without a modifier, behave as
F1-F12 keys. This makes it impossible to use a modifier and `PrtSc` or
`Ins`.

Now, some models fix this by excluding `PrtSc` and `Ins` keys from
being treated as F11 and F12 keys with the use of a modifier. However,
some models do not, and fixes this by the so called fn-lock.

Fn-lock inverts the behavior of the top row from special keys to F1-F12
keys. So a modifier and a special key would be possible which make
things like `Alt-Ins` possible. Now, with fn-lock we would have 4 modes:
* Fn-key off & fn-lock off - hotkeys treated as special keys using a
  modifier gives F1-F12 keys.
* Fn-key on & fn-lock off - hotkeys treated as F1-F12 keys and using a
  modifier gives F1-F12.
* Fn-key off & fn-lock on - hotkeys are treated as F1-F12 keys and using
  a modifier gives special keys.
* Fn-key on & fn-lock on - hotkeys are treated as special keys and using
  a modifier gives special keys.

The driver introduces two parameters to force reporting brightness keys
and sleeping after setting a threshold value.

All newer models that "fully" implement the new interface report
brightness key events twice, once through WMI and once through
acpi-video. Older models, such as `MateBook X`, don't report brightness
events using WMI. This is implemented as a quirk and can be forced using
module parameters.

Some models don't allow setting thresholds to (0, 100), due to bad ASL
code, which indicates reset values, instead, it only turns off battery
charging protection. This would return the previously set values even
though battery protection is off which doesn't make sense. A sane value
like (0, 100) indicates no charging protection, but since it's not
possible to set such values, (0, 0) is set before turning protection
off with (0, 100). This requires a delay after setting (0, 0) and after
(0, 100) so that these values make their way to EC memory.

These parameters are implemented as quirks along with `ec_micmute` quirk
which controls the micmute LED through ACPI EC interface.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 630 +-
 1 file changed, 541 insertions(+), 89 deletions(-)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 52fcac5b393a..3f945b4cf115 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -1,32 +1,65 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- *  Huawei WMI hotkeys
+ *  Huawei WMI laptop extras driver
  *
  *  Copyright (C) 2018   Ayman Bagabas 
  */
 
 #include 
+#include 
+#include 
 #include 
 #include 
 #include 
 #include 
+#include 
+#include 
+#include 
 #include 
 
 /*
  * Huawei WMI GUIDs
  */
-#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
+#define AMW0_METHOD_GUID "ABBC0F5B-8EA1-11D1-A000-C9062910"
 #define AMW0_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
 
+/* Legacy GUIDs */
 #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
+#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
+
+/* AMW0_commands */
+
+enum wmaa_cmd {
+   BATTERY_GET, /* \GBTT 0x1103 */
+   BATTERY_SET, /* \SBTT 0xXXYY1003 */
+   FN_LOCK_GET, /* \

[PATCH v2 3/8] platform/x86: huawei-wmi: use quirks and module parameters

2019-06-13 Thread Ayman Bagabas
The patch introduces two parameters to force reporting brightness keys
and sleeping after setting battery thresholds value.
These parameters are implemented as quirks along with `ec_micmute` quirk
which controls the micmute LED through ACPI EC interface.

All newer models that "fully" implement the new interface report
brightness key events twice, once through WMI and once through
acpi-video. Older models, such as `MateBook X`, don't report brightness
events using WMI. This is implemented as a quirk and can be forced using
module parameters.

Some models don't allow setting thresholds to (0, 100), due to bad ASL
code, which indicates reset values, instead, it only turns off battery
charging protection. This would return the currently set values even
though battery protection is off which doesn't make sense. A sane value
like (0, 100) indicates no charging protection, but since it's not
possible to set such values, (0, 0) is set before turning protection
off with (0, 100). This requires a delay after setting (0, 0) and after
(0, 100) so that these values make their way to EC memory.

Method (SBTT, 1, NotSerialized)
{
Name (BUFF, Buffer (0x0100){})
Local0 = Arg0
CreateByteField (Arg0, 0x02, STCP)
CreateByteField (Arg0, 0x03, SOCP)
CreateByteField (BUFF, Zero, STAT)
If (((STCP == Zero) && (SOCP == 0x64)))
{
\_SB.PCI0.LPCB.EC0.ECXT (0xC7, Zero, Zero, Zero, Zero, Zero)
}
Else
{
\_SB.PCI0.LPCB.EC0.ECXT (0xC7, One, STCP, SOCP, Zero, Zero)
}// ^^ ^
 // || |
STAT = Zero  // on   low   high
Return (BUFF) /* \SBTT.BUFF */   // bit  thresh  thresh
}

ASL code taken from MateBook X Pro (MACH-WX9) showing how it turns off
protection without changing values.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 71 +++
 1 file changed, 71 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 37b09d497f5e..647c5a6c8ab3 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -6,6 +6,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -35,6 +36,14 @@ enum {
MICMUTE_LED_SET = 0x0b04, /* \SMLS */
 };
 
+struct quirk_entry {
+   bool battery_sleep;
+   bool ec_micmute;
+   bool report_brightness;
+};
+
+static struct quirk_entry *quirks;
+
 struct huawei_wmi {
struct led_classdev cdev;
struct input_dev *idev[2];
@@ -61,6 +70,58 @@ static const struct key_entry huawei_wmi_keymap[] = {
{ KE_END,0 }
 };
 
+static bool battery_sleep;
+static bool report_brightness;
+
+module_param(battery_sleep, bool, 0444);
+MODULE_PARM_DESC(battery_sleep,
+   "Delay after setting battery charging thresholds.");
+module_param(report_brightness, bool, 0444);
+MODULE_PARM_DESC(report_brightness,
+   "Report brightness key events.");
+
+/* Quirks */
+
+static int __init dmi_matched(const struct dmi_system_id *dmi)
+{
+   quirks = dmi->driver_data;
+   return 1;
+}
+
+static struct quirk_entry quirk_unknown = {
+};
+
+static struct quirk_entry quirk_battery_sleep = {
+   .battery_sleep = true,
+};
+
+static struct quirk_entry quirk_matebook_x = {
+   .ec_micmute = true,
+   .report_brightness = true,
+};
+
+static const struct dmi_system_id huawei_quirks[] = {
+   {
+   .callback = dmi_matched,
+   .ident = "Huawei MACH-WX9",
+   .matches = {
+   DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
+   DMI_MATCH(DMI_PRODUCT_NAME, "MACH-WX9"),
+   },
+   .driver_data = &quirk_battery_sleep
+   },
+   {
+   .callback = dmi_matched,
+   .ident = "Huawei MateBook X",
+   .matches = {
+   DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
+   DMI_MATCH(DMI_PRODUCT_NAME, "HUAWEI MateBook X")
+   },
+   .driver_data = &quirk_matebook_x
+   },
+   {  }
+};
+
 /* Utils */
 
 static int huawei_wmi_call(struct device *dev, struct acpi_buffer *in,
@@ -266,6 +327,11 @@ static void huawei_wmi_process_key(struct input_dev *idev, 
int code)
return;
}
 
+   if (quirks && !quirks->report_brightness &&
+   (key->sw.code == KEY_BRIGHTNESSDOWN ||
+   key->sw.code == KEY_BRIGHTNESSUP))
+   return;
+
sparse_keymap_report_entry(idev, key, 1, true);
 }
 
@@ -378,6 +444,11 @@ static __init int huawei_wmi_init(void)
 {
int err;
 
+

[PATCH v2 5/8] platform/x86: huawei-wmi: add battery charging protection support

2019-06-13 Thread Ayman Bagabas
Some models that implement the new WMI management interface can control
battery charging thresholds where it limits charging the battery once it
reaches certain thresholds. This feature is not present in MateBook X
(2017).

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 56 +++
 1 file changed, 56 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 358d9d168300..06d83e613504 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -6,6 +6,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -48,6 +49,7 @@ struct huawei_wmi {
struct led_classdev cdev;
struct input_dev *idev[2];
struct mutex wmi_lock;
+   struct mutex battery_lock;
struct platform_device *pdev;
 };
 
@@ -302,6 +304,59 @@ static int huawei_wmi_leds_setup(struct device *dev)
return devm_led_classdev_register(dev, &huawei->cdev);
 }
 
+/* Battery protection */
+
+static int huawei_wmi_battery_get(struct device *dev, int *low, int *high)
+{
+   struct huawei_wmi *huawei = dev_get_drvdata(dev);
+   u8 ret[0x100];
+   int err, i;
+
+   mutex_lock(&huawei->battery_lock);
+   err = huawei_wmi_cmd(dev, BATTERY_THRESH_GET, ret, 0x100);
+   mutex_unlock(&huawei->battery_lock);
+   if (err) {
+   return err;
+   }
+
+   /* Find the last two non-zero values. Return status is ignored. */
+   i = 0x100;
+   do {
+   *low = ret[i-1];
+   *high = ret[i];
+   } while (i > 2 && !ret[i--]);
+
+   return 0;
+}
+
+static int huawei_wmi_battery_set(struct device *dev, int low, int high)
+{
+   struct huawei_wmi *huawei = dev_get_drvdata(dev);
+   u8 arg[8];
+   int err;
+
+   *(u64 *)arg = BATTERY_THRESH_SET;
+   arg[2] = low;
+   arg[3] = high;
+
+   /* This is an edge case were some models turn battery protection
+* off without changing their thresholds values. We clear the
+* values before turning off protection. Sometimes we need a sleep 
delay to
+* make sure these values make their way to EC memory.
+*/
+   if (low == 0 && high == 100) {
+   huawei_wmi_battery_set(dev, 0, 0);
+   }
+
+   mutex_lock(&huawei->battery_lock);
+   err = huawei_wmi_cmd(dev, *(u64 *)arg, NULL, NULL);
+   if (quirks && quirks->battery_sleep)
+   msleep(1000);
+   mutex_unlock(&huawei->battery_lock);
+
+   return err;
+}
+
 /* Input */
 
 static void huawei_wmi_process_key(struct input_dev *idev, int code)
@@ -424,6 +479,7 @@ static int huawei_wmi_probe(struct platform_device *pdev)
 
if (wmi_has_guid(HWMI_METHOD_GUID)) {
mutex_init(&huawei->wmi_lock);
+   mutex_init(&huawei->battery_lock);
err = huawei_wmi_leds_setup(&pdev->dev);
if (err)
dev_err(&pdev->dev, "Failed to setup leds\n");
-- 
2.20.1



[PATCH v2 4/8] platform/x86: huawei-wmi: control micmute LED through WMI interface

2019-06-13 Thread Ayman Bagabas
Using HWMI interface, micmute LED can be controlled on supported models.
`MateBook X` from 2017, doesn't provide controlling the micmute
LED through this interface instead it uses another "legacy" interface
that is not "fully" implemented yet.
Currently, this "legacy" interface is used for hotkeys on this specific
model. A quirk is set to use ACPI method to control micmute LED on
MateBook X (2017).

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 74 ++-
 1 file changed, 42 insertions(+), 32 deletions(-)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 647c5a6c8ab3..358d9d168300 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -239,42 +239,52 @@ static int huawei_wmi_cmd(struct device *dev, u64 arg, u8 
*buf, size_t buflen)
 static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
 {
-   char *acpi_method;
-   acpi_handle handle;
-   acpi_status status;
-   union acpi_object args[3];
-   struct acpi_object_list arg_list = {
-   .pointer = args,
-   .count = ARRAY_SIZE(args),
-   };
-
-   handle = ec_get_handle();
-   if (!handle) {
-   dev_err(led_cdev->dev->parent, "Failed to get EC handle\n");
-   return -ENODEV;
-   }
+   /* This is a workaround until the "legacy" interface is implemented. */
+   if (quirks && quirks->ec_micmute) {
+   char *acpi_method;
+   acpi_handle handle;
+   acpi_status status;
+   union acpi_object args[3];
+   struct acpi_object_list arg_list = {
+   .pointer = args,
+   .count = ARRAY_SIZE(args),
+   };
+
+   handle = ec_get_handle();
+   if (!handle) {
+   dev_err(led_cdev->dev->parent, "Failed to get EC 
handle\n");
+   return -ENODEV;
+   }
 
-   args[0].type = args[1].type = args[2].type = ACPI_TYPE_INTEGER;
-   args[1].integer.value = 0x04;
-
-   if (acpi_has_method(handle, "SPIN")) {
-   acpi_method = "SPIN";
-   args[0].integer.value = 0;
-   args[2].integer.value = brightness ? 1 : 0;
-   } else if (acpi_has_method(handle, "WPIN")) {
-   acpi_method = "WPIN";
-   args[0].integer.value = 1;
-   args[2].integer.value = brightness ? 0 : 1;
+   args[0].type = args[1].type = args[2].type = ACPI_TYPE_INTEGER;
+   args[1].integer.value = 0x04;
+
+   if (acpi_has_method(handle, "SPIN")) {
+   acpi_method = "SPIN";
+   args[0].integer.value = 0;
+   args[2].integer.value = brightness ? 1 : 0;
+   } else if (acpi_has_method(handle, "WPIN")) {
+   acpi_method = "WPIN";
+   args[0].integer.value = 1;
+   args[2].integer.value = brightness ? 0 : 1;
+   } else {
+   return -ENODEV;
+   }
+
+   status = acpi_evaluate_object(handle, acpi_method, &arg_list, 
NULL);
+   if (ACPI_FAILURE(status)) {
+   return -ENODEV;
+   }
+
+   return 0;
} else {
-   return -ENODEV;
-   }
+   u8 arg[8];
 
-   status = acpi_evaluate_object(handle, acpi_method, &arg_list, NULL);
-   if (ACPI_FAILURE(status)) {
-   return -ENODEV;
-   }
+   *(u64 *)arg = MICMUTE_LED_SET;
+   arg[2] = brightness;
 
-   return 0;
+   return huawei_wmi_cmd(led_cdev->dev->parent, *(u64 *)arg, NULL, 
NULL);
+   }
 }
 
 static int huawei_wmi_leds_setup(struct device *dev)
-- 
2.20.1



[PATCH v2 6/8] platform/x86: huawei-wmi: add fn-lock support

2019-06-13 Thread Ayman Bagabas
The behavior of hotkeys is not the same among all models. Some models
require fn-lock to do things like `Ctrl-Ins` or `Alt-PrtSc`. By default,
hotkeys behave as special keys (media keys, Ins, etc), but if a modifier
is used (ctrl, alt, shift) these keys behave as F1-F12 keys. If the Fn
key is toggled on, the hotkeys with or without a modifier, behave as
F1-F12 keys. This makes it impossible to use a modifier and `PrtSc` or
`Ins`.

Now, some models fix this by excluding `PrtSc` and `Ins` keys from
being treated as F11 and F12 keys with the use of a modifier. However,
some models do not, and fixes this by the so called fn-lock.

Fn-lock inverts the behavior of the top row from special keys to F1-F12
keys. So a modifier and a special key would be possible which make
things like `Alt-Ins` possible. Now, with fn-lock we would have 4 modes:
* Fn-key off & fn-lock off - hotkeys treated as special keys using a
  modifier gives F1-F12 keys.
* Fn-key on & fn-lock off - hotkeys treated as F1-F12 keys and using a
  modifier gives F1-F12.
* Fn-key off & fn-lock on - hotkeys are treated as F1-F12 keys and using
  a modifier gives special keys.
* Fn-key on & fn-lock on - hotkeys are treated as special keys and using
  a modifier gives special keys.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 31 +++
 1 file changed, 31 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 06d83e613504..aac9b80f9976 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -357,6 +357,37 @@ static int huawei_wmi_battery_set(struct device *dev, int 
low, int high)
return err;
 }
 
+/* Fn lock */
+
+static int huawei_wmi_fn_lock_get(struct device *dev, int *on)
+{
+   u8 ret[0x100] = { 0 };
+   int err, i;
+
+   err = huawei_wmi_cmd(dev, FN_LOCK_GET, ret, 0x100);
+   if (err) {
+   return err;
+   }
+
+   /* Find the first non-zero value. Return status is ignored. */
+   i = 1;
+   do {
+   *on = ret[i] - 1; // -1 undefined, 0 off, 1 on.
+   } while (i < 0x100 && !ret[i++]);
+
+   return 0;
+}
+
+static int huawei_wmi_fn_lock_set(struct device *dev, int on)
+{
+   u8 arg[8];
+
+   *(u64 *)arg = FN_LOCK_SET;
+   arg[2] = on + 1; // 0 undefined, 1 off, 2 on.
+
+   return huawei_wmi_cmd(dev, *(u64 *)arg, NULL, NULL);
+}
+
 /* Input */
 
 static void huawei_wmi_process_key(struct input_dev *idev, int code)
-- 
2.20.1



[PATCH v2 8/8] platform/x86: huawei-wmi: add debugfs files support

2019-06-13 Thread Ayman Bagabas
A debugfs interface that creates two attributes `arg` and `call` to set
an argument to be called by the WMI device and show the results
returned. This argument is a 64 bit long which complies the properties
of the HWMI interface.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 117 ++
 1 file changed, 117 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index cc6745ff1bad..a74ddd9adb47 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -6,6 +6,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -46,8 +47,14 @@ struct quirk_entry {
 
 static struct quirk_entry *quirks;
 
+struct huawei_wmi_debug {
+   struct dentry *root;
+   u64 arg;
+};
+
 struct huawei_wmi {
struct led_classdev cdev;
+   struct huawei_wmi_debug debug;
struct input_dev *idev[2];
struct mutex wmi_lock;
struct mutex battery_lock;
@@ -463,6 +470,110 @@ static struct attribute *huawei_wmi_attrs[] = {
 
 ATTRIBUTE_GROUPS(huawei_wmi);
 
+/* debugfs */
+
+static void huawei_wmi_debugfs_call_dump(struct seq_file *m, void *data,
+   union acpi_object *obj)
+{
+   struct huawei_wmi *huawei = m->private;
+   int i;
+
+   switch (obj->type) {
+   case ACPI_TYPE_INTEGER:
+   seq_printf(m, "0x%llx", obj->integer.value);
+   break;
+   case ACPI_TYPE_STRING:
+   seq_printf(m, "\"%*s\"", obj->string.length, 
obj->string.pointer);
+   break;
+   case ACPI_TYPE_BUFFER:
+   seq_printf(m, "{");
+   for (i = 0; i < obj->buffer.length; i++) {
+   seq_printf(m, "0x%02x", obj->buffer.pointer[i]);
+   if (i < obj->buffer.length - 1)
+   seq_printf(m, ",");
+   }
+   seq_printf(m, "}");
+   break;
+   case ACPI_TYPE_PACKAGE:
+   seq_printf(m, "[");
+   for (i = 0; i < obj->package.count; i++) {
+   huawei_wmi_debugfs_call_dump(m, huawei, 
&obj->package.elements[i]);
+   if (i < obj->package.count - 1)
+   seq_printf(m, ",");
+   }
+   seq_printf(m, "]");
+   break;
+   default:
+   dev_err(&huawei->pdev->dev, "Unexpected obj type, got %d\n", 
obj->type);
+   return;
+   }
+}
+
+static int huawei_wmi_debugfs_call_show(struct seq_file *m, void *data)
+{
+   struct huawei_wmi *huawei = m->private;
+   struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
+   struct acpi_buffer in;
+   union acpi_object *obj;
+   int err;
+
+   in.length = sizeof(u64);
+   in.pointer = &huawei->debug.arg;
+
+   err = huawei_wmi_call(&huawei->pdev->dev, &in, &out);
+   if (err)
+   return err;
+
+   obj = out.pointer;
+   if (!obj) {
+   err = -EIO;
+   goto fail_debugfs_call;
+   }
+
+   huawei_wmi_debugfs_call_dump(m, huawei, obj);
+
+fail_debugfs_call:
+   kfree(out.pointer);
+   return err;
+}
+
+DEFINE_SHOW_ATTRIBUTE(huawei_wmi_debugfs_call);
+
+static void huawei_wmi_debugfs_exit(struct device *dev)
+{
+   struct huawei_wmi *huawei = dev_get_drvdata(dev);
+
+   debugfs_remove_recursive(huawei->debug.root);
+}
+
+static int huawei_wmi_debugfs_init(struct device *dev)
+{
+   struct huawei_wmi *huawei = dev_get_drvdata(dev);
+   struct dentry *dent;
+
+   huawei->debug.root = debugfs_create_dir("huawei-wmi", NULL);
+   if (!huawei->debug.root) {
+   dev_err(dev, "Failed to create debugfs directory\n");
+   goto fail_debugfs;
+   }
+
+   dent = debugfs_create_x64("arg", S_IRUGO | S_IWUSR, huawei->debug.root,
+   &huawei->debug.arg);
+   if (!dent)
+   goto fail_debugfs;
+
+   dent = debugfs_create_file("call", S_IFREG | S_IRUSR,
+   huawei->debug.root, huawei, 
&huawei_wmi_debugfs_call_fops);
+   if (!dent)
+   goto fail_debugfs;
+
+   return 0;
+
+fail_debugfs:
+   huawei_wmi_debugfs_exit(dev);
+   return -ENOMEM;
+}
+
 /* Input */
 
 static void huawei_wmi_process_key(struct input_dev *idev, int code)
@@ -597,7 +708,12 @@ static int huawei_wmi_probe(struct platform_device *pdev)
err = huawei_wmi_leds_setup(&pdev->dev);
if (err)
dev_err(&pdev->dev, "Failed to setup leds\n");
+
+   err = huawei_wmi_debugfs_init(&pde

[PATCH v2 7/8] platform/x86: huawei-wmi: add sysfs interface support

2019-06-13 Thread Ayman Bagabas
Battery charging thresholds and fn-lock are implemented as sysfs
attributes. Both have R/W permissions and set with root permission.
Although using Huawei Management Software in Windows gives access to
these features without admin privileges, user could use something like a
udev rule to change writing permissions of these attributes.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 87 +++
 1 file changed, 87 insertions(+)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index aac9b80f9976..cc6745ff1bad 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -14,6 +14,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 
 /*
@@ -388,6 +389,80 @@ static int huawei_wmi_fn_lock_set(struct device *dev, int 
on)
return huawei_wmi_cmd(dev, *(u64 *)arg, NULL, NULL);
 }
 
+/* sysfs */
+
+static ssize_t charge_thresholds_show(struct device *dev,
+   struct device_attribute *attr,
+   char *buf)
+{
+   int err, low, high;
+
+   err = huawei_wmi_battery_get(dev, &low, &high);
+   if (err)
+   return err;
+
+   return sprintf(buf, "%d %d\n", low, high);
+}
+
+static ssize_t charge_thresholds_store(struct device *dev,
+   struct device_attribute *attr,
+   const char *buf, size_t size)
+{
+   int low, high, err;
+
+   if (sscanf(buf, "%d %d", &low, &high) != 2 ||
+   low < 0 || high > 100 ||
+   low > high)
+   return -EINVAL;
+
+   err = huawei_wmi_battery_set(dev, low, high);
+   if (err)
+   return err;
+
+   return size;
+}
+
+static ssize_t fn_lock_state_show(struct device *dev,
+   struct device_attribute *attr,
+   char *buf)
+{
+   int err, on;
+
+   err = huawei_wmi_fn_lock_get(dev, &on);
+   if (err)
+   return err;
+
+   return sprintf(buf, "%d\n", on);
+}
+
+static ssize_t fn_lock_state_store(struct device *dev,
+   struct device_attribute *attr,
+   const char *buf, size_t size)
+{
+   int on, err;
+
+   if (kstrtoint(buf, 10, &on) ||
+   on < 0 || on > 1)
+   return -EINVAL;
+
+   err = huawei_wmi_fn_lock_set(dev, on);
+   if (err)
+   return err;
+
+   return size;
+}
+
+static DEVICE_ATTR_RW(charge_thresholds);
+static DEVICE_ATTR_RW(fn_lock_state);
+
+static struct attribute *huawei_wmi_attrs[] = {
+   &dev_attr_charge_thresholds.attr,
+   &dev_attr_fn_lock_state.attr,
+   NULL
+};
+
+ATTRIBUTE_GROUPS(huawei_wmi);
+
 /* Input */
 
 static void huawei_wmi_process_key(struct input_dev *idev, int code)
@@ -509,8 +584,16 @@ static int huawei_wmi_probe(struct platform_device *pdev)
}
 
if (wmi_has_guid(HWMI_METHOD_GUID)) {
+
mutex_init(&huawei->wmi_lock);
mutex_init(&huawei->battery_lock);
+
+   err = sysfs_create_group(&pdev->dev.kobj, &huawei_wmi_group);
+   if (err) {
+   dev_err(&pdev->dev, "Failed to create sysfs 
interface\n");
+   return err;
+   }
+
err = huawei_wmi_leds_setup(&pdev->dev);
if (err)
dev_err(&pdev->dev, "Failed to setup leds\n");
@@ -526,6 +609,10 @@ static int huawei_wmi_remove(struct platform_device *pdev)
if (wmi_has_guid(HWMI_EVENT_GUID))
wmi_remove_notify_handler(HWMI_EVENT_GUID);
 
+   if (wmi_has_guid(HWMI_METHOD_GUID)) {
+   sysfs_remove_group(&pdev->dev.kobj, &huawei_wmi_group);
+   }
+
return 0;
 }
 
-- 
2.20.1



Re: [PATCH v1] platform/x86: Huawei laptop extras driver

2019-05-23 Thread ayman . bagabas
I'm really sorry, I just realized the changes are a lot and unreadable.
Changes should be splitted into a series and resubmitted.

On Thu, 2019-05-23 at 05:31 -0400, Ayman Bagabas wrote:
> This patch introduces new features to the driver and also moves the
> driver from wmi_driver to platform_driver.
> 
> This move is necessary because the driver is no longer only a hotkeys
> driver and platform_driver offers easier future extensibility. The
> patch
> introduces a WMI BIOS interface that brings on new features and
> enables
> controlling micmute LED through this interface on supported models.
> New
> features are controlling battery charging thresholds and fn-lock
> state
> among with module parameters and quirks check.
> 
> Currently, micmute LED is controlled through an ACPI method under EC.
> This method ("SPIN", "WPIN") is specific to some models and wouldn't
> work on all Huawei laptops.
> 
> Using this interface, controlling this LED should work with any
> model.
> Except `MateBook X` from 2017, this one doesn't provide controlling
> the
> LED through this interface instead it uses another "legacy" interface
> that is not "fully" implemented yet due to lack of hardware.
> Currently,
> this "legacy" interface is used for hotkeys on this specific model. A
> quirk is set to use ACPI method to control micmute LED on this model.
> 
> Some models that implement the new WMI BIOS interface can control
> battery charging thresholds where it limits charging the battery once
> it
> reaches certain thresholds.
> 
> The behavior of hotkeys is not the same among all models. Some models
> require fn-lock to do things like `Ctrl-Ins` or `Alt-PrtSc`. By
> default,
> hotkeys behave as special keys (media keys, Ins, etc), but if a
> modifier
> is used (ctrl, alt, shift) these keys behave as F1-F12 keys. If the
> Fn
> key is toggled on, the hotkeys with or without a modifier, behave as
> F1-F12 keys. This makes it impossible to use a modifier and `PrtSc`
> or
> `Ins`.
> 
> Now, some models fix this by excluding `PrtSc` and `Ins` keys from
> being treated as F11 and F12 keys with the use of a modifier.
> However,
> some models do not, and fixes this by the so called fn-lock.
> 
> Fn-lock inverts the behavior of the top row from special keys to F1-
> F12
> keys. So a modifier and a special key would be possible which make
> things like `Alt-Ins` possible. Now, with fn-lock we would have 4
> modes:
> * Fn-key off & fn-lock off - hotkeys treated as special keys using a
>   modifier gives F1-F12 keys.
> * Fn-key on & fn-lock off - hotkeys treated as F1-F12 keys and using
> a
>   modifier gives F1-F12.
> * Fn-key off & fn-lock on - hotkeys are treated as F1-F12 keys and
> using
>   a modifier gives special keys.
> * Fn-key on & fn-lock on - hotkeys are treated as special keys and
> using
>   a modifier gives special keys.
> 
> The driver introduces two parameters to force reporting brightness
> keys
> and sleeping after setting a threshold value.
> 
> All newer models that "fully" implement the new interface report
> brightness key events twice, once through WMI and once through
> acpi-video. Older models, such as `MateBook X`, don't report
> brightness
> events using WMI. This is implemented as a quirk and can be forced
> using
> module parameters.
> 
> Some models don't allow setting thresholds to (0, 100), due to bad
> ASL
> code, which indicates reset values, instead, it only turns off
> battery
> charging protection. This would return the previously set values even
> though battery protection is off which doesn't make sense. A sane
> value
> like (0, 100) indicates no charging protection, but since it's not
> possible to set such values, (0, 0) is set before turning protection
> off with (0, 100). This requires a delay after setting (0, 0) and
> after
> (0, 100) so that these values make their way to EC memory.
> 
> These parameters are implemented as quirks along with `ec_micmute`
> quirk
> which controls the micmute LED through ACPI EC interface.
> 
> Signed-off-by: Ayman Bagabas 
> ---
>  drivers/platform/x86/huawei-wmi.c | 630 +---
> --
>  1 file changed, 541 insertions(+), 89 deletions(-)
> 
> diff --git a/drivers/platform/x86/huawei-wmi.c
> b/drivers/platform/x86/huawei-wmi.c
> index 52fcac5b393a..3f945b4cf115 100644
> --- a/drivers/platform/x86/huawei-wmi.c
> +++ b/drivers/platform/x86/huawei-wmi.c
> @@ -1,32 +1,65 @@
>  // SPDX-License-Identifier: GPL-2.0
>  /*
> - *  Huawei WMI hotkeys
> + *  Huawei WMI laptop extras driver
>   *
>   * 

[PATCH v1] platform/x86: Huawei laptop extras driver

2019-05-23 Thread Ayman Bagabas
This patch introduces new features to the driver and also moves the
driver from wmi_driver to platform_driver.

This move is necessary because the driver is no longer only a hotkeys
driver and platform_driver offers easier future extensibility. The patch
introduces a WMI BIOS interface that brings on new features and enables
controlling micmute LED through this interface on supported models. New
features are controlling battery charging thresholds and fn-lock state
among with module parameters and quirks check.

Currently, micmute LED is controlled through an ACPI method under EC.
This method ("SPIN", "WPIN") is specific to some models and wouldn't
work on all Huawei laptops.

Using this interface, controlling this LED should work with any model.
Except `MateBook X` from 2017, this one doesn't provide controlling the
LED through this interface instead it uses another "legacy" interface
that is not "fully" implemented yet due to lack of hardware. Currently,
this "legacy" interface is used for hotkeys on this specific model. A
quirk is set to use ACPI method to control micmute LED on this model.

Some models that implement the new WMI BIOS interface can control
battery charging thresholds where it limits charging the battery once it
reaches certain thresholds.

The behavior of hotkeys is not the same among all models. Some models
require fn-lock to do things like `Ctrl-Ins` or `Alt-PrtSc`. By default,
hotkeys behave as special keys (media keys, Ins, etc), but if a modifier
is used (ctrl, alt, shift) these keys behave as F1-F12 keys. If the Fn
key is toggled on, the hotkeys with or without a modifier, behave as
F1-F12 keys. This makes it impossible to use a modifier and `PrtSc` or
`Ins`.

Now, some models fix this by excluding `PrtSc` and `Ins` keys from
being treated as F11 and F12 keys with the use of a modifier. However,
some models do not, and fixes this by the so called fn-lock.

Fn-lock inverts the behavior of the top row from special keys to F1-F12
keys. So a modifier and a special key would be possible which make
things like `Alt-Ins` possible. Now, with fn-lock we would have 4 modes:
* Fn-key off & fn-lock off - hotkeys treated as special keys using a
  modifier gives F1-F12 keys.
* Fn-key on & fn-lock off - hotkeys treated as F1-F12 keys and using a
  modifier gives F1-F12.
* Fn-key off & fn-lock on - hotkeys are treated as F1-F12 keys and using
  a modifier gives special keys.
* Fn-key on & fn-lock on - hotkeys are treated as special keys and using
  a modifier gives special keys.

The driver introduces two parameters to force reporting brightness keys
and sleeping after setting a threshold value.

All newer models that "fully" implement the new interface report
brightness key events twice, once through WMI and once through
acpi-video. Older models, such as `MateBook X`, don't report brightness
events using WMI. This is implemented as a quirk and can be forced using
module parameters.

Some models don't allow setting thresholds to (0, 100), due to bad ASL
code, which indicates reset values, instead, it only turns off battery
charging protection. This would return the previously set values even
though battery protection is off which doesn't make sense. A sane value
like (0, 100) indicates no charging protection, but since it's not
possible to set such values, (0, 0) is set before turning protection
off with (0, 100). This requires a delay after setting (0, 0) and after
(0, 100) so that these values make their way to EC memory.

These parameters are implemented as quirks along with `ec_micmute` quirk
which controls the micmute LED through ACPI EC interface.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/huawei-wmi.c | 630 +-
 1 file changed, 541 insertions(+), 89 deletions(-)

diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 52fcac5b393a..3f945b4cf115 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -1,32 +1,65 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- *  Huawei WMI hotkeys
+ *  Huawei WMI laptop extras driver
  *
  *  Copyright (C) 2018   Ayman Bagabas 
  */
 
 #include 
+#include 
+#include 
 #include 
 #include 
 #include 
 #include 
+#include 
+#include 
+#include 
 #include 
 
 /*
  * Huawei WMI GUIDs
  */
-#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
+#define AMW0_METHOD_GUID "ABBC0F5B-8EA1-11D1-A000-C9062910"
 #define AMW0_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
 
+/* Legacy GUIDs */
 #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
+#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
+
+/* AMW0_commands */
+
+enum wmaa_cmd {
+   BATTERY_GET, /* \GBTT 0x1103 */
+   BATTERY_SET, /* \SBTT 0xXXYY1003 */
+   FN_LOCK_GET, /* \

[PATCH] ALSA: Enable micmute LED for Huawei laptops

2019-05-23 Thread Ayman Bagabas
Since this LED is found on all Huawei laptops, we can hook it to
huawei-wmi platform driver to control it.

Also, some renames have been made to use product name instead of common
name to avoid confusions.

Signed-off-by: Ayman Bagabas 
---
 sound/pci/hda/patch_realtek.c | 9 -
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index f83f21d64dd4..ab6a856ec614 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -5747,7 +5747,7 @@ enum {
ALC298_FIXUP_TPT470_DOCK,
ALC255_FIXUP_DUMMY_LINEOUT_VERB,
ALC255_FIXUP_DELL_HEADSET_MIC,
-   ALC256_FIXUP_HUAWEI_MBXP_PINS,
+   ALC256_FIXUP_HUAWEI_MACH_WX9_PINS,
ALC295_FIXUP_HP_X360,
ALC221_FIXUP_HP_HEADSET_MIC,
ALC285_FIXUP_LENOVO_HEADPHONE_NOISE,
@@ -6038,7 +6038,7 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC269_FIXUP_HEADSET_MIC
},
-   [ALC256_FIXUP_HUAWEI_MBXP_PINS] = {
+   [ALC256_FIXUP_HUAWEI_MACH_WX9_PINS] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
{0x12, 0x90a60130},
@@ -7063,9 +7063,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x511f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
-   SND_PCI_QUIRK(0x19e5, 0x3200, "Huawei MBX", ALC255_FIXUP_MIC_MUTE_LED),
-   SND_PCI_QUIRK(0x19e5, 0x3201, "Huawei MBX", ALC255_FIXUP_MIC_MUTE_LED),
-   SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MBXP", 
ALC256_FIXUP_HUAWEI_MBXP_PINS),
+   SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MACH-WX9", 
ALC256_FIXUP_HUAWEI_MACH_WX9_PINS),
SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", 
ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */
 
 #if 0
@@ -7124,6 +7122,7 @@ static const struct snd_pci_quirk 
alc269_fixup_vendor_tbl[] = {
SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad", ALC269_FIXUP_THINKPAD_ACPI),
+   SND_PCI_QUIRK_VENDOR(0x19e5, "Huawei Matebook", 
ALC255_FIXUP_MIC_MUTE_LED),
{}
 };
 
-- 
2.20.1



Re: [PATCH v1 1/2] platform/x86: Huawei WMI laptop extras driver update

2019-05-15 Thread ayman . bagabas
On Wed, 2019-05-15 at 15:10 +0200, Takashi Iwai wrote:
> On Mon, 13 May 2019 22:30:05 +0200,
> Ayman Bagabas wrote:
> > This update brings on the use of WMI BIOS management interface
> > found on
> > Huawei laptops. This interface can control the micmute LED found on
> > most
> > of these laptops, control charging thresholds values, and control
> > fn-lock feature.
> > 
> > Signed-off-by: Ayman Bagabas 
> 
> You need to put the actual change (e.g. moving to the platform
> driver)
> here as well, not only in the cover letter.

I will put a detailed explanation in the second version.

> 
> But I wonder what the actual motivation to move to the platform
> driver...
> 

This patch adds another platform driver with the same name,
"huawei-wmi", along with the existing wmi input driver. So the driver
registers two drivers platform and wmi. The reason behind this is
that they both use ACPI WMI and two drivers with the same type/bus
cannot have the same name.

The way this works is, input driver hooks to wmi event guids
while the other uses wmi method guid. Most existing x86 drivers with
similar structure use only one platform driver. At first, my
approach was combining all these together in one wmi driver.
However, I couldn't find a way to differentiate which wmi device
belong to which guid in wmi driver probe method. I could've moved
everything to the platform driver but the wmi driver handles input
very neatly.

So with this "new" platform driver, micmute led is now controlled
through wmi interface instead of ACPI EC which should work with
all huawei laptops. In addition, two features were added, setting
charging thresholds and fn-lock.

I know having two drivers in one file might not be a good idea,
should
it be one platform driver instead?

Thank you,
Ayman

> 
> thanks,
> 
> Takashi
> 
> > ---
> >  drivers/platform/x86/Kconfig  |   8 +-
> >  drivers/platform/x86/huawei-wmi.c | 578 +-
> > 
> >  2 files changed, 500 insertions(+), 86 deletions(-)
> > 
> > diff --git a/drivers/platform/x86/Kconfig
> > b/drivers/platform/x86/Kconfig
> > index a1ed13183559..e46261b6def5 100644
> > --- a/drivers/platform/x86/Kconfig
> > +++ b/drivers/platform/x86/Kconfig
> > @@ -1287,7 +1287,7 @@ config INTEL_ATOMISP2_PM
> >   will be called intel_atomisp2_pm.
> >  
> >  config HUAWEI_WMI
> > -   tristate "Huawei WMI hotkeys driver"
> > +   tristate "Huawei WMI laptop extras driver"
> > depends on ACPI_WMI
> > depends on INPUT
> > select INPUT_SPARSEKMAP
> > @@ -1296,9 +1296,9 @@ config HUAWEI_WMI
> > select LEDS_TRIGGER_AUDIO
> > select NEW_LEDS
> > help
> > - This driver provides support for Huawei WMI hotkeys.
> > - It enables the missing keys and adds support to the micmute
> > - LED found on some of these laptops.
> > + This driver provides support for some extra features found on
> > Huawei
> > + laptops that are controlled through WMI. These features are
> > keyboard
> > + hotkeys, micmute LED, charging thresholds, and fn-lock state.
> >  
> >   To compile this driver as a module, choose M here: the module
> >   will be called huawei-wmi.
> > diff --git a/drivers/platform/x86/huawei-wmi.c
> > b/drivers/platform/x86/huawei-wmi.c
> > index 52fcac5b393a..4ec04196f386 100644
> > --- a/drivers/platform/x86/huawei-wmi.c
> > +++ b/drivers/platform/x86/huawei-wmi.c
> > @@ -1,32 +1,63 @@
> >  // SPDX-License-Identifier: GPL-2.0
> >  /*
> > - *  Huawei WMI hotkeys
> > + *  Huawei WMI laptop extras driver
> >   *
> >   *  Copyright (C) 2018   Ayman Bagabas <
> > ayman.baga...@gmail.com>
> >   */
> >  
> >  #include 
> > +#include 
> > +#include 
> >  #include 
> >  #include 
> >  #include 
> >  #include 
> > +#include 
> > +#include 
> > +#include 
> >  #include 
> >  
> >  /*
> >   * Huawei WMI GUIDs
> >   */
> > -#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
> > +#define AMW0_METHOD_GUID "ABBC0F5B-8EA1-11D1-A000-C9062910"
> >  #define AMW0_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
> >  
> > +/* Legacy GUIDs */
> >  #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
> > +#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
> >  
> > -struct huawei_wmi_priv {
> > -   struct input_dev *idev;
> > +/* AMW0_commands */
> > +
&

Re: [PATCH v1 2/2] sound: Enable micmute led for Huawei laptops

2019-05-15 Thread ayman . bagabas
On Wed, 2019-05-15 at 15:11 +0200, Takashi Iwai wrote:
> On Mon, 13 May 2019 22:30:06 +0200,
> Ayman Bagabas wrote:
> > Since this LED is found on huawei laptops, we can hook it to
> > huawei-wmi platform driver which uses the common WMI interface
> > present
> > in these laptops to control the LED.
> > 
> > I've also made some renames and used product name instead of common
> > name
> > to avoid confusion.
> > 
> > Signed-off-by: Ayman Bagabas 
> 
> This looks applicable independently from the patch 1?
> If so, it can go via sound git tree while another via x86-platform
> tree.
> 

Yes it is. Will send another one.

> 
> thanks,
> 
> Takashi
> 



[PATCH v1 1/2] platform/x86: Huawei WMI laptop extras driver update

2019-05-13 Thread Ayman Bagabas
This update brings on the use of WMI BIOS management interface found on
Huawei laptops. This interface can control the micmute LED found on most
of these laptops, control charging thresholds values, and control
fn-lock feature.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/Kconfig  |   8 +-
 drivers/platform/x86/huawei-wmi.c | 578 +-
 2 files changed, 500 insertions(+), 86 deletions(-)

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index a1ed13183559..e46261b6def5 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1287,7 +1287,7 @@ config INTEL_ATOMISP2_PM
  will be called intel_atomisp2_pm.
 
 config HUAWEI_WMI
-   tristate "Huawei WMI hotkeys driver"
+   tristate "Huawei WMI laptop extras driver"
depends on ACPI_WMI
depends on INPUT
select INPUT_SPARSEKMAP
@@ -1296,9 +1296,9 @@ config HUAWEI_WMI
select LEDS_TRIGGER_AUDIO
select NEW_LEDS
help
- This driver provides support for Huawei WMI hotkeys.
- It enables the missing keys and adds support to the micmute
- LED found on some of these laptops.
+ This driver provides support for some extra features found on Huawei
+ laptops that are controlled through WMI. These features are keyboard
+ hotkeys, micmute LED, charging thresholds, and fn-lock state.
 
  To compile this driver as a module, choose M here: the module
  will be called huawei-wmi.
diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
index 52fcac5b393a..4ec04196f386 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -1,32 +1,63 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- *  Huawei WMI hotkeys
+ *  Huawei WMI laptop extras driver
  *
  *  Copyright (C) 2018   Ayman Bagabas 
  */
 
 #include 
+#include 
+#include 
 #include 
 #include 
 #include 
 #include 
+#include 
+#include 
+#include 
 #include 
 
 /*
  * Huawei WMI GUIDs
  */
-#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
+#define AMW0_METHOD_GUID "ABBC0F5B-8EA1-11D1-A000-C9062910"
 #define AMW0_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
 
+/* Legacy GUIDs */
 #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
+#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
 
-struct huawei_wmi_priv {
-   struct input_dev *idev;
+/* AMW0_commands */
+
+enum wmaa_cmd {
+   BATTERY_GET, /* \GBTT 0x1103 */
+   BATTERY_SET, /* \SBTT 0xXXYY1003 */
+   FN_LOCK_GET, /* \GFRS 0x0604 */
+   FN_LOCK_SET, /* \SFRS 0x000X0704 */
+   MICMUTE_LED, /* \SMLS 0x000X0b04 */
+};
+
+enum fn_state {
+   FN_LOCK_OFF = 0x01,
+   FN_LOCK_ON = 0x02,
+};
+
+struct quirk_entry {
+   bool battery_reset;
+   bool ec_micmute;
+};
+
+static struct quirk_entry *quirks;
+
+struct huawei_wmi {
struct led_classdev cdev;
-   acpi_handle handle;
-   char *acpi_method;
+   struct mutex wmi_lock;
+   struct mutex battery_lock;
+   struct platform_device *pdev;
 };
 
+struct platform_device *huawei_wmi_pdev;
+
 static const struct key_entry huawei_wmi_keymap[] = {
{ KE_KEY,0x281, { KEY_BRIGHTNESSDOWN } },
{ KE_KEY,0x282, { KEY_BRIGHTNESSUP } },
@@ -37,73 +68,169 @@ static const struct key_entry huawei_wmi_keymap[] = {
{ KE_KEY,0x289, { KEY_WLAN } },
// Huawei |M| key
{ KE_KEY,0x28a, { KEY_CONFIG } },
-   // Keyboard backlight
+   // Keyboard backlit
{ KE_IGNORE, 0x293, { KEY_KBDILLUMTOGGLE } },
{ KE_IGNORE, 0x294, { KEY_KBDILLUMUP } },
{ KE_IGNORE, 0x295, { KEY_KBDILLUMUP } },
{ KE_END,0 }
 };
 
-static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
-   enum led_brightness brightness)
+/* Quirks */
+
+static int __init dmi_matched(const struct dmi_system_id *dmi)
 {
-   struct huawei_wmi_priv *priv = dev_get_drvdata(led_cdev->dev->parent);
+   quirks = dmi->driver_data;
+   return 1;
+}
+
+static struct quirk_entry quirk_battery_reset = {
+   .battery_reset = true,
+};
+
+static struct quirk_entry quirk_ec_micmute = {
+   .ec_micmute = true,
+};
+
+static const struct dmi_system_id huawei_quirks[] = {
+   {
+   .callback = dmi_matched,
+   .ident = "Huawei MACH-WX9",
+   .matches = {
+   DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
+   DMI_MATCH(DMI_PRODUCT_NAME, "MACH-WX9"),
+   },
+   .driver_data = &quirk_battery_reset
+   },
+   {
+   .callback = dmi_matched,
+   .ident = "Huawei MateBook X",
+   .matches = {
+   

[PATCH v1 2/2] sound: Enable micmute led for Huawei laptops

2019-05-13 Thread Ayman Bagabas
Since this LED is found on huawei laptops, we can hook it to
huawei-wmi platform driver which uses the common WMI interface present
in these laptops to control the LED.

I've also made some renames and used product name instead of common name
to avoid confusion.

Signed-off-by: Ayman Bagabas 
---
 sound/pci/hda/patch_realtek.c | 9 -
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 42cd3945e0de..3661470766ba 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -5684,7 +5684,7 @@ enum {
ALC298_FIXUP_TPT470_DOCK,
ALC255_FIXUP_DUMMY_LINEOUT_VERB,
ALC255_FIXUP_DELL_HEADSET_MIC,
-   ALC256_FIXUP_HUAWEI_MBXP_PINS,
+   ALC256_FIXUP_HUAWEI_MACH_WX9_PINS,
ALC295_FIXUP_HP_X360,
ALC221_FIXUP_HP_HEADSET_MIC,
ALC285_FIXUP_LENOVO_HEADPHONE_NOISE,
@@ -5975,7 +5975,7 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC269_FIXUP_HEADSET_MIC
},
-   [ALC256_FIXUP_HUAWEI_MBXP_PINS] = {
+   [ALC256_FIXUP_HUAWEI_MACH_WX9_PINS] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
{0x12, 0x90a60130},
@@ -6996,9 +6996,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x511f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
-   SND_PCI_QUIRK(0x19e5, 0x3200, "Huawei MBX", ALC255_FIXUP_MIC_MUTE_LED),
-   SND_PCI_QUIRK(0x19e5, 0x3201, "Huawei MBX", ALC255_FIXUP_MIC_MUTE_LED),
-   SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MBXP", 
ALC256_FIXUP_HUAWEI_MBXP_PINS),
+   SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MACH-WX9", 
ALC256_FIXUP_HUAWEI_MACH_WX9_PINS),
SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", 
ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */
 
 #if 0
@@ -7057,6 +7055,7 @@ static const struct snd_pci_quirk 
alc269_fixup_vendor_tbl[] = {
SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad", ALC269_FIXUP_THINKPAD_ACPI),
+   SND_PCI_QUIRK_VENDOR(0x19e5, "Huawei Matebook", 
ALC255_FIXUP_MIC_MUTE_LED),
{}
 };
 
-- 
2.20.1



[PATCH v1 0/2] Huawei WMI laptop extras driver

2019-05-13 Thread Ayman Bagabas
Using the WMI BIOS management interface found on Huawei laptops, extra features
such as micmute LED, charging thresholds, and fn-lock can be controlled.

A platform driver has been implemented along with the existing WMI driver. The
platform driver controls the LED, battery charging thresholds, and fn-lock.

We enable sound micmute hooks to platform driver on Huawei laptops to get the
LED to work.

Ayman Bagabas (2):
  platform/x86: Huawei WMI laptop extras driver update
  sound: Enable micmute led for Huawei laptops

 drivers/platform/x86/Kconfig  |   8 +-
 drivers/platform/x86/huawei-wmi.c | 578 +-
 sound/pci/hda/patch_realtek.c |   9 +-
 3 files changed, 504 insertions(+), 91 deletions(-)

-- 
2.20.1



[PATCH v11 1/3] ALSA: hda: fix front speakers on Huawei MBXP.

2018-12-12 Thread Ayman Bagabas
This patch solves bug 200501 'Only 2 of 4 speakers playing sound.'
https://bugzilla.kernel.org/show_bug.cgi?id=200501
It enables the front speakers on Huawei Matebook X Pro laptops.
These laptops come with Dolby Atmos sound system and these pins
configuration enables the front speakers.

Reviewed-by: Andy Shevchenko 
Reviewed-by: Takashi Iwai 
Signed-off-by: Ayman Bagabas 
---
 sound/pci/hda/patch_realtek.c | 18 ++
 1 file changed, 18 insertions(+)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 993d34c141c2..77ee471a67e5 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -5490,6 +5490,7 @@ enum {
ALC298_FIXUP_TPT470_DOCK,
ALC255_FIXUP_DUMMY_LINEOUT_VERB,
ALC255_FIXUP_DELL_HEADSET_MIC,
+   ALC256_FIXUP_HUAWEI_MBXP_PINS,
ALC295_FIXUP_HP_X360,
ALC221_FIXUP_HP_HEADSET_MIC,
 };
@@ -5761,6 +5762,22 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC269_FIXUP_HEADSET_MIC
},
+   [ALC256_FIXUP_HUAWEI_MBXP_PINS] = {
+   .type = HDA_FIXUP_PINS,
+   .v.pins = (const struct hda_pintbl[]) {
+   {0x12, 0x90a60130},
+   {0x13, 0x4000},
+   {0x14, 0x90170110},
+   {0x18, 0x41f0},
+   {0x19, 0x04a11040},
+   {0x1a, 0x41f0},
+   {0x1b, 0x90170112},
+   {0x1d, 0x40759a05},
+   {0x1e, 0x41f0},
+   {0x21, 0x04211020},
+   { }
+   },
+   },
[ALC269_FIXUP_ASUS_X101_FUNC] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc269_fixup_x101_headset_mic,
@@ -6591,6 +6608,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x511f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
+   SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MBXP", 
ALC256_FIXUP_HUAWEI_MBXP_PINS),
SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", 
ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */
 
 #if 0
-- 
2.19.2



[PATCH v11 2/3] x86: add support for Huawei WMI hotkeys.

2018-12-12 Thread Ayman Bagabas
This driver adds support for missing hotkeys on some Huawei laptops.
Laptops such as the Matebook X have non functioning hotkeys. Whereas
newer laptops such as the Matebook X Pro come with working hotkeys out
of the box.

Old laptops, such as the Matebook X, report hotkey events through ACPI
device "\WMI0". However, new laptops, such as the Matebook X Pro,
does not have this WMI device.

All the hotkeys on the Matebook X Pro work fine
without this patch except (micmute, wlan, and huawei key). These keys
and the brightness keys report events to "\AMW0" ACPI device. One
problem is that brightness keys on the Matebook X Pro work without this
patch. This results in reporting two brightness key press
events one is captured by ACPI and another by this driver.

A solution would be to check if such event came from the "\AMW0" WMI driver
then skip reporting event. Another solution would be to leave this to
user-space to handle. Which can be achieved by using "hwdb" tables and
remap those keys to "unknown". This solution seems more natural to me
because it leaves the decision to user-space.

Acked-by: Andy Shevchenko 
Reviewed-by: Takashi Iwai 
Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/Kconfig  |  17 +++
 drivers/platform/x86/Makefile |   1 +
 drivers/platform/x86/huawei-wmi.c | 208 ++
 3 files changed, 226 insertions(+)
 create mode 100644 drivers/platform/x86/huawei-wmi.c

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 87f70e8f4dd0..45ef4d22f14c 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1292,6 +1292,23 @@ config INTEL_ATOMISP2_PM
  To compile this driver as a module, choose M here: the module
  will be called intel_atomisp2_pm.
 
+config HUAWEI_WMI
+   tristate "Huawei WMI hotkeys driver"
+   depends on ACPI_WMI
+   depends on INPUT
+   select INPUT_SPARSEKMAP
+   select LEDS_CLASS
+   select LEDS_TRIGGERS
+   select LEDS_TRIGGER_AUDIO
+   select NEW_LEDS
+   help
+ This driver provides support for Huawei WMI hotkeys.
+ It enables the missing keys and adds support to the micmute
+ LED found on some of these laptops.
+
+ To compile this driver as a module, choose M here: the module
+ will be called huawei-wmi.
+
 endif # X86_PLATFORM_DEVICES
 
 config PMC_ATOM
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 39ae94135406..d841c550e3cc 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_ACERHDF) += acerhdf.o
 obj-$(CONFIG_HP_ACCEL) += hp_accel.o
 obj-$(CONFIG_HP_WIRELESS)  += hp-wireless.o
 obj-$(CONFIG_HP_WMI)   += hp-wmi.o
+obj-$(CONFIG_HUAWEI_WMI)   += huawei-wmi.o
 obj-$(CONFIG_AMILO_RFKILL) += amilo-rfkill.o
 obj-$(CONFIG_GPD_POCKET_FAN)   += gpd-pocket-fan.o
 obj-$(CONFIG_TC1100_WMI)   += tc1100-wmi.o
diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
new file mode 100644
index ..59872f87b741
--- /dev/null
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Huawei WMI hotkeys
+ *
+ *  Copyright (C) 2018   Ayman Bagabas 
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+/*
+ * Huawei WMI GUIDs
+ */
+#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
+#define AMW0_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
+
+#define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
+
+struct huawei_wmi_priv {
+   struct input_dev *idev;
+   struct led_classdev cdev;
+   acpi_handle handle;
+   char *acpi_method;
+};
+
+static const struct key_entry huawei_wmi_keymap[] = {
+   { KE_KEY,0x281, { KEY_BRIGHTNESSDOWN } },
+   { KE_KEY,0x282, { KEY_BRIGHTNESSUP } },
+   { KE_KEY,0x284, { KEY_MUTE } },
+   { KE_KEY,0x285, { KEY_VOLUMEDOWN } },
+   { KE_KEY,0x286, { KEY_VOLUMEUP } },
+   { KE_KEY,0x287, { KEY_MICMUTE } },
+   { KE_KEY,0x289, { KEY_WLAN } },
+   // Huawei |M| key
+   { KE_KEY,0x28a, { KEY_CONFIG } },
+   // Keyboard backlight
+   { KE_IGNORE, 0x293, { KEY_KBDILLUMTOGGLE } },
+   { KE_IGNORE, 0x294, { KEY_KBDILLUMUP } },
+   { KE_IGNORE, 0x295, { KEY_KBDILLUMUP } },
+   { KE_END,0 }
+};
+
+static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
+   enum led_brightness brightness)
+{
+   struct huawei_wmi_priv *priv = dev_get_drvdata(led_cdev->dev->parent);
+   acpi_status status;
+   union acpi_object args[3];
+   struct acpi_object_list arg_list = {
+   .pointer = args,
+   .count = ARRAY_SIZE(args),
+   };
+
+   args[0].type = args[1].type = a

[PATCH v11 3/3] ALSA: hda: add support for Huawei WMI micmute LED

2018-12-12 Thread Ayman Bagabas
Some of Huawei laptops come with a LED in the micmute key. This patch
enables the use of micmute LED for these devices:
1. Matebook X (19e5:3200), (19e5:3201)
2. Matebook X Pro (19e5:3204)

Reviewed-by: Andy Shevchenko 
Reviewed-by: Takashi Iwai 
Signed-off-by: Ayman Bagabas 
---
 sound/pci/hda/patch_realtek.c | 4 
 1 file changed, 4 insertions(+)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 77ee471a67e5..9766fd249bdf 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -5777,6 +5777,8 @@ static const struct hda_fixup alc269_fixups[] = {
{0x21, 0x04211020},
{ }
},
+   .chained = true,
+   .chain_id = ALC255_FIXUP_MIC_MUTE_LED
},
[ALC269_FIXUP_ASUS_X101_FUNC] = {
.type = HDA_FIXUP_FUNC,
@@ -6608,6 +6610,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x511f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
+   SND_PCI_QUIRK(0x19e5, 0x3200, "Huawei MBX", ALC255_FIXUP_MIC_MUTE_LED),
+   SND_PCI_QUIRK(0x19e5, 0x3201, "Huawei MBX", ALC255_FIXUP_MIC_MUTE_LED),
SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MBXP", 
ALC256_FIXUP_HUAWEI_MBXP_PINS),
SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", 
ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */
 
-- 
2.19.2



[PATCH v11 0/3] Huawei laptops

2018-12-12 Thread Ayman Bagabas
This patch set is based on the new audio LED triggers in topic/leds-trigger 
branch from
git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git

Changes in v11:
* Minor code changes

Changes in v10:
* Use ec_get_handle instead of acpi_get_handle since we are using the ec device
* Switch to WMI0_EXPENSIVE_GUID and wmi_query_block to fetch keycode when WMI0 
is used

Changes in v9:
* Explicitly use NULL in acpi_get_handle
* Drop __initconst from huawei_wmi_keymap

Changes in v8:
* Switch to wmi_driver
* Use devm to allocate and manage devices
* Skip registering LED subsystem if a ACPI controller method was not found

Changes in v7:
* Use audio LED triggers patch set
* Use KEY_CONFIG (XF86Tools) instead of KEY_PROG1.
 In Windows, the key is used to launch Huawei PC manager. XF86Tools is
 used by default on most desktop environments i.e. Gnome.

Changes in v6:
* Review tags

Changes in v5:
* Consistency in file names
* How module would be enabled (Kconfig)
* Match license in SPDX and MODULE_LICENSE 

Changes in v4:
* Code formatting

Changes in v3:
* Support for Huawei MBX
* Style and formatting issues

Ayman Bagabas (3):
  ALSA: hda: fix front speakers on Huawei MBXP.
  x86: add support for Huawei WMI hotkeys.
  ALSA: hda: add support for Huawei WMI micmute LED

 drivers/platform/x86/Kconfig  |  17 +++
 drivers/platform/x86/Makefile |   1 +
 drivers/platform/x86/huawei-wmi.c | 208 ++
 sound/pci/hda/patch_realtek.c |  22 
 4 files changed, 248 insertions(+)
 create mode 100644 drivers/platform/x86/huawei-wmi.c

-- 
2.19.2



Re: [PATCH v10 2/3] x86: add support for Huawei WMI hotkeys.

2018-12-11 Thread ayman . bagabas
On Tue, 2018-12-11 at 12:14 +0200, Andy Shevchenko wrote:
> On Tue, Dec 11, 2018 at 8:02 AM Ayman Bagabas <
> ayman.baga...@gmail.com> wrote:
> > This driver adds support for missing hotkeys on some Huawei
> > laptops.
> > Laptops such as the Matebook X have non functioning hotkeys.
> > Whereas
> > newer laptops such as the Matebook X Pro come with working hotkeys
> > out
> > of the box.
> > 
> > Old laptops, such as the Matebook X, report hotkey events through
> > ACPI
> > device "\WMI0". However, new laptops, such as the Matebook X Pro,
> > does not have this WMI device.
> > 
> > All the hotkeys on the Matebook X Pro work fine
> > without this patch except (micmute, wlan, and huawei key). These
> > keys
> > and the brightness keys report events to "\AMW0" ACPI device. One
> > problem is that brightness keys on the Matebook X Pro work without
> > this
> > patch. This results in reporting two brightness key press
> > events one is captured by ACPI and another by this driver.
> > 
> > A solution would be to check if such event came from the "\AMW0"
> > WMI driver
> > then skip reporting event. Another solution would be to leave this
> > to
> > user-space to handle. Which can be achieved by using "hwdb" tables
> > and
> > remap those keys to "unknown". This solution seems more natural to
> > me
> > because it leaves the decision to user-space.
> > 
> > Reviewed-by: Takashi Iwai 
> > Signed-off-by: Ayman Bagabas 
> > ---
> >  drivers/platform/x86/Kconfig  |  17 +++
> >  drivers/platform/x86/Makefile |   1 +
> >  drivers/platform/x86/huawei-wmi.c | 220
> > ++
> >  3 files changed, 238 insertions(+)
> >  create mode 100644 drivers/platform/x86/huawei-wmi.c
> > 
> > diff --git a/drivers/platform/x86/Kconfig
> > b/drivers/platform/x86/Kconfig
> > index 87f70e8f4dd0..45ef4d22f14c 100644
> > --- a/drivers/platform/x86/Kconfig
> > +++ b/drivers/platform/x86/Kconfig
> > @@ -1292,6 +1292,23 @@ config INTEL_ATOMISP2_PM
> >   To compile this driver as a module, choose M here: the
> > module
> >   will be called intel_atomisp2_pm.
> > 
> > +config HUAWEI_WMI
> > +   tristate "Huawei WMI hotkeys driver"
> > +   depends on ACPI_WMI
> > +   depends on INPUT
> > +   select INPUT_SPARSEKMAP
> > +   select LEDS_CLASS
> > +   select LEDS_TRIGGERS
> > +   select LEDS_TRIGGER_AUDIO
> > +   select NEW_LEDS
> > +   help
> > + This driver provides support for Huawei WMI hotkeys.
> > + It enables the missing keys and adds support to the
> > micmute
> > + LED found on some of these laptops.
> > +
> > + To compile this driver as a module, choose M here: the
> > module
> > + will be called huawei-wmi.
> > +
> >  endif # X86_PLATFORM_DEVICES
> > 
> >  config PMC_ATOM
> > diff --git a/drivers/platform/x86/Makefile
> > b/drivers/platform/x86/Makefile
> > index 39ae94135406..d841c550e3cc 100644
> > --- a/drivers/platform/x86/Makefile
> > +++ b/drivers/platform/x86/Makefile
> > @@ -32,6 +32,7 @@ obj-$(CONFIG_ACERHDF) += acerhdf.o
> >  obj-$(CONFIG_HP_ACCEL) += hp_accel.o
> >  obj-$(CONFIG_HP_WIRELESS)  += hp-wireless.o
> >  obj-$(CONFIG_HP_WMI)   += hp-wmi.o
> > +obj-$(CONFIG_HUAWEI_WMI)   += huawei-wmi.o
> >  obj-$(CONFIG_AMILO_RFKILL) += amilo-rfkill.o
> >  obj-$(CONFIG_GPD_POCKET_FAN)   += gpd-pocket-fan.o
> >  obj-$(CONFIG_TC1100_WMI)   += tc1100-wmi.o
> > diff --git a/drivers/platform/x86/huawei-wmi.c
> > b/drivers/platform/x86/huawei-wmi.c
> > new file mode 100644
> > index ..89ba7ea33499
> > --- /dev/null
> > +++ b/drivers/platform/x86/huawei-wmi.c
> > @@ -0,0 +1,220 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + *  Huawei WMI hotkeys
> > + *
> > + *  Copyright (C) 2018   Ayman Bagabas <
> > ayman.baga...@gmail.com>
> > + */
> > +
> > +#include 
> > +#include 
> > +#include 
> > +#include 
> > +#include 
> > +#include 
> > +
> > +/*
> > + * Huawei WMI GUIDs
> > + */
> > +#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
> > +#define AMW0_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
> > +
> > +#define WMI0_EXPENSIVE_GUID &q

[PATCH v10 3/3] ALSA: hda: add support for Huawei WMI micmute LED

2018-12-10 Thread Ayman Bagabas
Some of Huawei laptops come with a LED in the micmute key. This patch
enables the use of micmute LED for these devices:
1. Matebook X (19e5:3200), (19e5:3201)
2. Matebook X Pro (19e5:3204)

Reviewed-by: Takashi Iwai 
Signed-off-by: Ayman Bagabas 
---
 sound/pci/hda/patch_realtek.c | 6 +-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 1326f32f4574..9766fd249bdf 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -5776,7 +5776,9 @@ static const struct hda_fixup alc269_fixups[] = {
{0x1e, 0x41f0},
{0x21, 0x04211020},
{ }
-   }
+   },
+   .chained = true,
+   .chain_id = ALC255_FIXUP_MIC_MUTE_LED
},
[ALC269_FIXUP_ASUS_X101_FUNC] = {
.type = HDA_FIXUP_FUNC,
@@ -6608,6 +6610,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x511f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
+   SND_PCI_QUIRK(0x19e5, 0x3200, "Huawei MBX", ALC255_FIXUP_MIC_MUTE_LED),
+   SND_PCI_QUIRK(0x19e5, 0x3201, "Huawei MBX", ALC255_FIXUP_MIC_MUTE_LED),
SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MBXP", 
ALC256_FIXUP_HUAWEI_MBXP_PINS),
SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", 
ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */
 
-- 
2.19.2



[PATCH v10 2/3] x86: add support for Huawei WMI hotkeys.

2018-12-10 Thread Ayman Bagabas
This driver adds support for missing hotkeys on some Huawei laptops.
Laptops such as the Matebook X have non functioning hotkeys. Whereas
newer laptops such as the Matebook X Pro come with working hotkeys out
of the box.

Old laptops, such as the Matebook X, report hotkey events through ACPI
device "\WMI0". However, new laptops, such as the Matebook X Pro,
does not have this WMI device.

All the hotkeys on the Matebook X Pro work fine
without this patch except (micmute, wlan, and huawei key). These keys
and the brightness keys report events to "\AMW0" ACPI device. One
problem is that brightness keys on the Matebook X Pro work without this
patch. This results in reporting two brightness key press
events one is captured by ACPI and another by this driver.

A solution would be to check if such event came from the "\AMW0" WMI driver
then skip reporting event. Another solution would be to leave this to
user-space to handle. Which can be achieved by using "hwdb" tables and
remap those keys to "unknown". This solution seems more natural to me
because it leaves the decision to user-space.

Reviewed-by: Takashi Iwai 
Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/Kconfig  |  17 +++
 drivers/platform/x86/Makefile |   1 +
 drivers/platform/x86/huawei-wmi.c | 220 ++
 3 files changed, 238 insertions(+)
 create mode 100644 drivers/platform/x86/huawei-wmi.c

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 87f70e8f4dd0..45ef4d22f14c 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1292,6 +1292,23 @@ config INTEL_ATOMISP2_PM
  To compile this driver as a module, choose M here: the module
  will be called intel_atomisp2_pm.
 
+config HUAWEI_WMI
+   tristate "Huawei WMI hotkeys driver"
+   depends on ACPI_WMI
+   depends on INPUT
+   select INPUT_SPARSEKMAP
+   select LEDS_CLASS
+   select LEDS_TRIGGERS
+   select LEDS_TRIGGER_AUDIO
+   select NEW_LEDS
+   help
+ This driver provides support for Huawei WMI hotkeys.
+ It enables the missing keys and adds support to the micmute
+ LED found on some of these laptops.
+
+ To compile this driver as a module, choose M here: the module
+ will be called huawei-wmi.
+
 endif # X86_PLATFORM_DEVICES
 
 config PMC_ATOM
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 39ae94135406..d841c550e3cc 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_ACERHDF) += acerhdf.o
 obj-$(CONFIG_HP_ACCEL) += hp_accel.o
 obj-$(CONFIG_HP_WIRELESS)  += hp-wireless.o
 obj-$(CONFIG_HP_WMI)   += hp-wmi.o
+obj-$(CONFIG_HUAWEI_WMI)   += huawei-wmi.o
 obj-$(CONFIG_AMILO_RFKILL) += amilo-rfkill.o
 obj-$(CONFIG_GPD_POCKET_FAN)   += gpd-pocket-fan.o
 obj-$(CONFIG_TC1100_WMI)   += tc1100-wmi.o
diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
new file mode 100644
index ..89ba7ea33499
--- /dev/null
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Huawei WMI hotkeys
+ *
+ *  Copyright (C) 2018   Ayman Bagabas 
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+/*
+ * Huawei WMI GUIDs
+ */
+#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
+#define AMW0_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
+
+#define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
+
+struct huawei_wmi_priv {
+   struct input_dev *idev;
+   struct led_classdev cdev;
+   acpi_handle handle;
+   char *acpi_method;
+};
+
+static const struct key_entry huawei_wmi_keymap[] = {
+   { KE_KEY,0x281, { KEY_BRIGHTNESSDOWN } },
+   { KE_KEY,0x282, { KEY_BRIGHTNESSUP } },
+   { KE_KEY,0x284, { KEY_MUTE } },
+   { KE_KEY,0x285, { KEY_VOLUMEDOWN } },
+   { KE_KEY,0x286, { KEY_VOLUMEUP } },
+   { KE_KEY,0x287, { KEY_MICMUTE } },
+   { KE_KEY,0x289, { KEY_WLAN } },
+   // Huawei |M| key
+   { KE_KEY,0x28a, { KEY_CONFIG } },
+   // Keyboard backlight
+   { KE_IGNORE, 0x293, { KEY_KBDILLUMTOGGLE } },
+   { KE_IGNORE, 0x294, { KEY_KBDILLUMUP } },
+   { KE_IGNORE, 0x295, { KEY_KBDILLUMUP } },
+   { KE_END,0 }
+};
+
+static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
+   enum led_brightness brightness)
+{
+   struct huawei_wmi_priv *priv = dev_get_drvdata(led_cdev->dev->parent);
+   acpi_status status;
+   union acpi_object args[3];
+   struct acpi_object_list arg_list = {
+   .pointer = args,
+   .count = ARRAY_SIZE(args),
+   };
+
+   args[0].type = args[1].type = args[2].type = ACPI_TYPE_INT

[PATCH v10 1/3] ALSA: hda: fix front speakers on Huawei MBXP.

2018-12-10 Thread Ayman Bagabas
This patch solves bug 200501 'Only 2 of 4 speakers playing sound.'
https://bugzilla.kernel.org/show_bug.cgi?id=200501
It enables the front speakers on Huawei Matebook X Pro laptops.
These laptops come with Dolby Atmos sound system and these pins
configuration enables the front speakers.

Reviewed-by: Takashi Iwai 
Signed-off-by: Ayman Bagabas 
---
 sound/pci/hda/patch_realtek.c | 18 ++
 1 file changed, 18 insertions(+)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 993d34c141c2..1326f32f4574 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -5490,6 +5490,7 @@ enum {
ALC298_FIXUP_TPT470_DOCK,
ALC255_FIXUP_DUMMY_LINEOUT_VERB,
ALC255_FIXUP_DELL_HEADSET_MIC,
+   ALC256_FIXUP_HUAWEI_MBXP_PINS,
ALC295_FIXUP_HP_X360,
ALC221_FIXUP_HP_HEADSET_MIC,
 };
@@ -5761,6 +5762,22 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC269_FIXUP_HEADSET_MIC
},
+   [ALC256_FIXUP_HUAWEI_MBXP_PINS] = {
+   .type = HDA_FIXUP_PINS,
+   .v.pins = (const struct hda_pintbl[]) {
+   {0x12, 0x90a60130},
+   {0x13, 0x4000},
+   {0x14, 0x90170110},
+   {0x18, 0x41f0},
+   {0x19, 0x04a11040},
+   {0x1a, 0x41f0},
+   {0x1b, 0x90170112},
+   {0x1d, 0x40759a05},
+   {0x1e, 0x41f0},
+   {0x21, 0x04211020},
+   { }
+   }
+   },
[ALC269_FIXUP_ASUS_X101_FUNC] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc269_fixup_x101_headset_mic,
@@ -6591,6 +6608,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x511f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
+   SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MBXP", 
ALC256_FIXUP_HUAWEI_MBXP_PINS),
SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", 
ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */
 
 #if 0
-- 
2.19.2



[PATCH v10 0/3] Huawei laptops

2018-12-10 Thread Ayman Bagabas
This patch set is based on the new audio LED triggers in topic/leds-trigger 
branch from
git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git

Changes in v10:
* Use ec_get_handle instead of acpi_get_handle since we are using the ec device
* Switch to WMI0_EXPENSIVE_GUID and wmi_query_block to fetch keycode when WMI0 
is used

Changes in v9:
* Explicitly use NULL in acpi_get_handle
* Drop __initconst from huawei_wmi_keymap

Changes in v8:
* Switch to wmi_driver
* Use devm to allocate and manage devices
* Skip registering LED subsystem if a ACPI controller method was not found

Changes in v7:
* Use audio LED triggers patch set
* Use KEY_CONFIG (XF86Tools) instead of KEY_PROG1.
 In Windows, the key is used to launch Huawei PC manager. XF86Tools is
 used by default on most desktop environments i.e. Gnome.

Changes in v6:
* Review tags

Changes in v5:
* Consistency in file names
* How module would be enabled (Kconfig)
* Match license in SPDX and MODULE_LICENSE 

Changes in v4:
* Code formatting

Changes in v3:
* Support for Huawei MBX
* Style and formatting issues

Ayman Bagabas (3):
  ALSA: hda: fix front speakers on Huawei MBXP.
  x86: add support for Huawei WMI hotkeys.
  ALSA: hda: add support for Huawei WMI micmute LED

 drivers/platform/x86/Kconfig  |  17 +++
 drivers/platform/x86/Makefile |   1 +
 drivers/platform/x86/huawei-wmi.c | 220 ++
 sound/pci/hda/patch_realtek.c |  22 +++
 4 files changed, 260 insertions(+)
 create mode 100644 drivers/platform/x86/huawei-wmi.c

-- 
2.19.2



Re: [PATCH v9 2/3] x86: add support for Huawei WMI hotkeys.

2018-12-07 Thread ayman . bagabas
On Fri, 2018-12-07 at 00:52 -0500, ayman.baga...@gmail.com wrote:
On Mon, 2018-12-03 at 21:17 +0200, Andy Shevchenko wrote:
> On Mon, Dec 3, 2018 at 9:04 PM Takashi Iwai  wrote:
> > On Mon, 03 Dec 2018 19:53:39 +0100,
> > Ayman Bagabas wrote:
> > > + if (code == 0x80) {
> > > + acpi_status status;
> > > + acpi_handle handle;
> > > + unsigned long long result;
> > > + union acpi_object args[1];
> > > + struct acpi_object_list arg_list = {
> > > + .pointer = args,
> > > + .count = ARRAY_SIZE(args),
> > > + };
> > > +
> > > + args[0].type = ACPI_TYPE_INTEGER;
> > > + args[0].integer.value = 0;
> > > +
> > > + status = acpi_get_handle(NULL, "\\WMI0", &handle);
> > > + if (ACPI_FAILURE(status)) {
> > > + dev_err(&wdev->dev, "Unable to get ACPI
> > > handle\n");
> > > + return;
> > > + }
> > > +
> > > + status = acpi_evaluate_integer(NULL, "WQ00",
> > > &arg_list, &result);
> > 
> > I guess you need to pass handle here?  In the earlier version, you
> > passed \\WMI0.WQ00, so it worked with NULL handle.  But now it's no
> > longer so...
> 
> I think in this case we don't need to have a separate call to get
> handle and try to get integer directly. In either we will have an
> error case if method  / namespace / etc is not found.
> 
> 

I was digging further into the DSDT table of the laptop that has WQ00
method and it turned out that this method is a ACPI_WMI_EXPENSIVE. It
has its own GUID of "39142400-C6A3-40fa-BADB-8A2652834100". I believe
wmi_query_block, which is deprecated BTW, can be used to access this
method instead of acpi_evaluate. However, I don't have a machine to
test it on. Any ideas?




Re: [PATCH v9 2/3] x86: add support for Huawei WMI hotkeys.

2018-12-04 Thread ayman . bagabas
On Mon, 2018-12-03 at 21:17 +0200, Andy Shevchenko wrote:
> On Mon, Dec 3, 2018 at 9:04 PM Takashi Iwai  wrote:
> > On Mon, 03 Dec 2018 19:53:39 +0100,
> > Ayman Bagabas wrote:
> > > + if (code == 0x80) {
> > > + acpi_status status;
> > > + acpi_handle handle;
> > > + unsigned long long result;
> > > + union acpi_object args[1];
> > > + struct acpi_object_list arg_list = {
> > > + .pointer = args,
> > > + .count = ARRAY_SIZE(args),
> > > + };
> > > +
> > > + args[0].type = ACPI_TYPE_INTEGER;
> > > + args[0].integer.value = 0;
> > > +
> > > + status = acpi_get_handle(NULL, "\\WMI0", &handle);
> > > + if (ACPI_FAILURE(status)) {
> > > + dev_err(&wdev->dev, "Unable to get ACPI
> > > handle\n");
> > > + return;
> > > + }
> > > +
> > > + status = acpi_evaluate_integer(NULL, "WQ00",
> > > &arg_list, &result);
> > 
> > I guess you need to pass handle here?  In the earlier version, you
> > passed \\WMI0.WQ00, so it worked with NULL handle.  But now it's no
> > longer so...
> 
> I think in this case we don't need to have a separate call to get
> handle and try to get integer directly. In either we will have an
> error case if method  / namespace / etc is not found.

Agree.

> 
> > Other than that, it looks OK to me.
> >   Reviewed-by: Takashi Iwai 

Thanks.

> > 
> > But, you don't need to rush too much.  Instead, better to test your
> > own patches and make sure that they really work before submitting
> > the
> > final version.
> 
> Yes, I will wait couple of days for v10 in hope it will be tested
> carefully.

You're right, I will test it more and make sure everything work as
expected.

> 



Re: [PATCH v9 2/3] x86: add support for Huawei WMI hotkeys.

2018-12-04 Thread ayman . bagabas
On Mon, 2018-12-03 at 11:41 -0800, Randy Dunlap wrote:
> On 12/3/18 10:53 AM, Ayman Bagabas wrote:
> > This driver adds support for missing hotkeys on some Huawei
> > laptops.
> > Laptops such as the Matebook X have non functioning hotkeys.
> > Whereas
> > newer laptops such as the Matebook X Pro come with working hotkeys
> > out
> > of the box.
> > 
> > Old laptops, such as the Matebook X, report hotkey events through
> > ACPI
> > device "\WMI0". However, new laptops, such as the Matebook X Pro,
> > does not have this WMI device.
> > 
> > All the hotkeys on the Matebook X Pro work fine
> > without this patch except (micmute, wlan, and huawei key). These
> > keys
> > and the brightness keys report events to "\AMW0" ACPI device. One
> > problem is that brightness keys on the Matebook X Pro work without
> > this
> > patch. This results in reporting two brightness key press
> > events one is captured by ACPI and another by this driver.
> > 
> > A solution would be to check if such event came from the "\AMW0"
> > WMI driver
> > then skip reporting event. Another solution would be to leave this
> > to
> > user-space to handle. Which can be achieved by using "hwdb" tables
> > and
> > remap those keys to "unknown". This solution seems more natural to
> > me
> > because it leaves the decision to user-space.
> > 
> > Signed-off-by: Ayman Bagabas 
> > ---
> >  drivers/platform/x86/Kconfig  |  16 +++
> >  drivers/platform/x86/Makefile |   1 +
> >  drivers/platform/x86/huawei-wmi.c | 226
> > ++
> >  3 files changed, 243 insertions(+)
> >  create mode 100644 drivers/platform/x86/huawei-wmi.c
> > 
> > diff --git a/drivers/platform/x86/Kconfig
> > b/drivers/platform/x86/Kconfig
> > index 87f70e8f4dd0..27db3ce7a1e1 100644
> > --- a/drivers/platform/x86/Kconfig
> > +++ b/drivers/platform/x86/Kconfig
> > @@ -1292,6 +1292,22 @@ config INTEL_ATOMISP2_PM
> >   To compile this driver as a module, choose M here: the module
> >   will be called intel_atomisp2_pm.
> >  
> > +config HUAWEI_WMI
> > +   tristate "Huawei WMI hotkeys driver"
> > +   depends on ACPI_WMI
> > +   depends on INPUT
> > +   select INPUT_SPARSEKMAP
> > +   select LEDS_CLASS
> 
> but LEDS_CLASS is only available if NEW_LEDS is set/enabled, so it
> either
> depends on LEDS_CLASS... or it needs to select NEW_LEDS, which is
> what other
> drivers in this Kconfig file do, even though our b.d. has said a few
> times
> that any one driver should not "select" => enable an entire
> subsystem,
> so they all should be "depends on NEW_LEDS" IMHO.

Noted, I'll select NEW_LEDS just like other drivers in Kconfig.

> 
> > +   select LEDS_TRIGGERS
> > +   select LEDS_TRIGGER_AUDIO
> > +   help
> > + This driver provides support for Huawei WMI hotkeys.
> > + It enables the missing keys and adds support to the micmute
> > + LED found on some of these laptops.
> > +
> > + To compile this driver as a module, choose M here: the module
> > + will be called huawei-wmi.
> > +
> >  endif # X86_PLATFORM_DEVICES
> >  
> >  config PMC_ATOM
> 
> 



[PATCH v9 2/3] x86: add support for Huawei WMI hotkeys.

2018-12-03 Thread Ayman Bagabas
This driver adds support for missing hotkeys on some Huawei laptops.
Laptops such as the Matebook X have non functioning hotkeys. Whereas
newer laptops such as the Matebook X Pro come with working hotkeys out
of the box.

Old laptops, such as the Matebook X, report hotkey events through ACPI
device "\WMI0". However, new laptops, such as the Matebook X Pro,
does not have this WMI device.

All the hotkeys on the Matebook X Pro work fine
without this patch except (micmute, wlan, and huawei key). These keys
and the brightness keys report events to "\AMW0" ACPI device. One
problem is that brightness keys on the Matebook X Pro work without this
patch. This results in reporting two brightness key press
events one is captured by ACPI and another by this driver.

A solution would be to check if such event came from the "\AMW0" WMI driver
then skip reporting event. Another solution would be to leave this to
user-space to handle. Which can be achieved by using "hwdb" tables and
remap those keys to "unknown". This solution seems more natural to me
because it leaves the decision to user-space.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/Kconfig  |  16 +++
 drivers/platform/x86/Makefile |   1 +
 drivers/platform/x86/huawei-wmi.c | 226 ++
 3 files changed, 243 insertions(+)
 create mode 100644 drivers/platform/x86/huawei-wmi.c

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 87f70e8f4dd0..27db3ce7a1e1 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1292,6 +1292,22 @@ config INTEL_ATOMISP2_PM
  To compile this driver as a module, choose M here: the module
  will be called intel_atomisp2_pm.
 
+config HUAWEI_WMI
+   tristate "Huawei WMI hotkeys driver"
+   depends on ACPI_WMI
+   depends on INPUT
+   select INPUT_SPARSEKMAP
+   select LEDS_CLASS
+   select LEDS_TRIGGERS
+   select LEDS_TRIGGER_AUDIO
+   help
+ This driver provides support for Huawei WMI hotkeys.
+ It enables the missing keys and adds support to the micmute
+ LED found on some of these laptops.
+
+ To compile this driver as a module, choose M here: the module
+ will be called huawei-wmi.
+
 endif # X86_PLATFORM_DEVICES
 
 config PMC_ATOM
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 39ae94135406..d841c550e3cc 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_ACERHDF) += acerhdf.o
 obj-$(CONFIG_HP_ACCEL) += hp_accel.o
 obj-$(CONFIG_HP_WIRELESS)  += hp-wireless.o
 obj-$(CONFIG_HP_WMI)   += hp-wmi.o
+obj-$(CONFIG_HUAWEI_WMI)   += huawei-wmi.o
 obj-$(CONFIG_AMILO_RFKILL) += amilo-rfkill.o
 obj-$(CONFIG_GPD_POCKET_FAN)   += gpd-pocket-fan.o
 obj-$(CONFIG_TC1100_WMI)   += tc1100-wmi.o
diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
new file mode 100644
index ..afa030fd1285
--- /dev/null
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Huawei WMI hotkeys
+ *
+ *  Copyright (C) 2018   Ayman Bagabas 
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+/*
+ * Huawei WMI Events GUIDs
+ */
+#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
+#define AMW0_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
+
+struct huawei_wmi_priv {
+   struct input_dev *idev;
+   struct led_classdev cdev;
+   acpi_handle handle;
+   char *acpi_method;
+};
+
+static const struct key_entry huawei_wmi_keymap[] = {
+   { KE_KEY,0x281, { KEY_BRIGHTNESSDOWN } },
+   { KE_KEY,0x282, { KEY_BRIGHTNESSUP } },
+   { KE_KEY,0x284, { KEY_MUTE } },
+   { KE_KEY,0x285, { KEY_VOLUMEDOWN } },
+   { KE_KEY,0x286, { KEY_VOLUMEUP } },
+   { KE_KEY,0x287, { KEY_MICMUTE } },
+   { KE_KEY,0x289, { KEY_WLAN } },
+   // Huawei |M| button
+   { KE_KEY,0x28a, { KEY_CONFIG } },
+   // Keyboard light
+   { KE_IGNORE, 0x293, { KEY_KBDILLUMTOGGLE } },
+   { KE_IGNORE, 0x294, { KEY_KBDILLUMUP } },
+   { KE_IGNORE, 0x295, { KEY_KBDILLUMUP } },
+   { KE_END,0 }
+};
+
+static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
+   enum led_brightness brightness)
+{
+   struct huawei_wmi_priv *priv = dev_get_drvdata(led_cdev->dev->parent);
+   acpi_status status;
+   union acpi_object args[3];
+   struct acpi_object_list arg_list = {
+   .pointer = args,
+   .count = ARRAY_SIZE(args),
+   };
+
+   args[0].type = args[1].type = args[2].type = ACPI_TYPE_INTEGER;
+   args[1].integer.value = 0x04;
+
+   if (strcmp(priv->acpi_method, "SPIN") == 0) {
+ 

[PATCH v9 3/3] ALSA: hda: add support for Huawei WMI micmute LED

2018-12-03 Thread Ayman Bagabas
Some of Huawei laptops come with a LED in the micmute key. This patch
enables the use of micmute LED for these devices:
1. Matebook X (19e5:3200), (19e5:3201)
2. Matebook X Pro (19e5:3204)

Reviewed-by: Takashi Iwai 
Signed-off-by: Ayman Bagabas 
---
 sound/pci/hda/patch_realtek.c | 6 +-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 1326f32f4574..9766fd249bdf 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -5776,7 +5776,9 @@ static const struct hda_fixup alc269_fixups[] = {
{0x1e, 0x41f0},
{0x21, 0x04211020},
{ }
-   }
+   },
+   .chained = true,
+   .chain_id = ALC255_FIXUP_MIC_MUTE_LED
},
[ALC269_FIXUP_ASUS_X101_FUNC] = {
.type = HDA_FIXUP_FUNC,
@@ -6608,6 +6610,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x511f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
+   SND_PCI_QUIRK(0x19e5, 0x3200, "Huawei MBX", ALC255_FIXUP_MIC_MUTE_LED),
+   SND_PCI_QUIRK(0x19e5, 0x3201, "Huawei MBX", ALC255_FIXUP_MIC_MUTE_LED),
SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MBXP", 
ALC256_FIXUP_HUAWEI_MBXP_PINS),
SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", 
ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */
 
-- 
2.19.2



[PATCH v9 0/3] Huawei laptops

2018-12-03 Thread Ayman Bagabas
This patch set is based on new audio LED triggers in branch topic/leds-trigger 
from
git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git

Changes from v8:
* Explicitly use NULL in acpi_get_handle
* Drop __initconst from huawei_wmi_keymap

Changes from v7:
* Switch to wmi_driver
* Use devm to allocate and manage devices
* Skip registering LED subsystem if a ACPI controller method was not found

Changes from v6:
* Use audio LED triggers patch set
* Use KEY_CONFIG (XF86Tools) instead of KEY_PROG1.
 In Windows, the key is used to launch Huawei PC manager. XF86Tools is
 used by default on most desktop environments i.e. Gnome.

Changes from v5:
* Review tags

Changes from v4:
* Consistency in file names
* How module would be enabled (Kconfig)
* Match license in SPDX and MODULE_LICENSE 

Changes from v3:
* Code formatting

Changes from v2:
* Support for Huawei MBX
* Style and formatting issues

Ayman Bagabas (3):
  ALSA: hda: fix front speakers on Huawei MBXP.
  x86: add support for Huawei WMI hotkeys.
  ALSA: hda: add support for Huawei WMI micmute LED

 drivers/platform/x86/Kconfig  |  16 +++
 drivers/platform/x86/Makefile |   1 +
 drivers/platform/x86/huawei-wmi.c | 226 ++
 sound/pci/hda/patch_realtek.c |  22 +++
 4 files changed, 265 insertions(+)
 create mode 100644 drivers/platform/x86/huawei-wmi.c

-- 
2.19.2



[PATCH v9 1/3] ALSA: hda: fix front speakers on Huawei MBXP.

2018-12-03 Thread Ayman Bagabas
This patch solves bug 200501 'Only 2 of 4 speakers playing sound.'
https://bugzilla.kernel.org/show_bug.cgi?id=200501
It enables the front speakers on Huawei Matebook X Pro laptops.
These laptops come with Dolby Atmos sound system and these pins
configuration enables the front speakers.

Reviewed-by: Takashi Iwai 
Signed-off-by: Ayman Bagabas 
---
 sound/pci/hda/patch_realtek.c | 18 ++
 1 file changed, 18 insertions(+)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 993d34c141c2..1326f32f4574 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -5490,6 +5490,7 @@ enum {
ALC298_FIXUP_TPT470_DOCK,
ALC255_FIXUP_DUMMY_LINEOUT_VERB,
ALC255_FIXUP_DELL_HEADSET_MIC,
+   ALC256_FIXUP_HUAWEI_MBXP_PINS,
ALC295_FIXUP_HP_X360,
ALC221_FIXUP_HP_HEADSET_MIC,
 };
@@ -5761,6 +5762,22 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC269_FIXUP_HEADSET_MIC
},
+   [ALC256_FIXUP_HUAWEI_MBXP_PINS] = {
+   .type = HDA_FIXUP_PINS,
+   .v.pins = (const struct hda_pintbl[]) {
+   {0x12, 0x90a60130},
+   {0x13, 0x4000},
+   {0x14, 0x90170110},
+   {0x18, 0x41f0},
+   {0x19, 0x04a11040},
+   {0x1a, 0x41f0},
+   {0x1b, 0x90170112},
+   {0x1d, 0x40759a05},
+   {0x1e, 0x41f0},
+   {0x21, 0x04211020},
+   { }
+   }
+   },
[ALC269_FIXUP_ASUS_X101_FUNC] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc269_fixup_x101_headset_mic,
@@ -6591,6 +6608,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x511f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
+   SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MBXP", 
ALC256_FIXUP_HUAWEI_MBXP_PINS),
SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", 
ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */
 
 #if 0
-- 
2.19.2



Re: [PATCH v8 3/3] ALSA: hda: add support for Huawei WMI micmute LED

2018-12-03 Thread ayman . bagabas
On Mon, 2018-12-03 at 13:00 +0100, Takashi Iwai wrote:
> On Fri, 30 Nov 2018 00:57:38 +0100,
> Ayman Bagabas wrote:
> > Some of Huawei laptops come with a LED in the micmute key. This
> > patch
> > enables the use of micmute LED for these devices:
> > 1. Matebook X (19e5:3200), (19e5:3201)
> > 2. Matebook X Pro (19e5:3204)
> > 
> > Signed-off-by: Ayman Bagabas 
> 
> Feel free to take my ack
>   Reviewed-by: Takashi Iwai 

Thanks, i'll include that in next submission.

> 
> 
> thanks,
> 
> Takashi



Re: [PATCH v8 2/3] x86: add support for Huawei WMI hotkeys.

2018-12-03 Thread ayman . bagabas
On Mon, 2018-12-03 at 13:00 +0100, Takashi Iwai wrote:
> On Fri, 30 Nov 2018 00:57:37 +0100,
> Ayman Bagabas wrote:
> > This driver adds support for missing hotkeys on some Huawei
> > laptops.
> > Laptops such as the Matebook X have non functioning hotkeys.
> > Whereas
> > newer laptops such as the Matebook X Pro come with working hotkeys
> > out
> > of the box.
> > 
> > Old laptops, such as the Matebook X, report hotkey events through
> > ACPI
> > device "\WMI0". However, new laptops, such as the Matebook X Pro,
> > does not have this WMI device.
> > 
> > All the hotkeys on the Matebook X Pro work fine
> > without this patch except (micmute, wlan, and huawei key). These
> > keys
> > and the brightness keys report events to "\AMW0" ACPI device. One
> > problem is that brightness keys on the Matebook X Pro work without
> > this
> > patch. This results in reporting two brightness key press
> > events one is captured by ACPI and another by this driver.
> > 
> > A solution would be to check if such event came from the "\AMW0"
> > WMI driver
> > then skip reporting event. Another solution would be to leave this
> > to user-space to handle. Which
> > can be achieved by using "hwdb" tables and remap those keys to
> > "unknown".
> > This solution seems more natural to me because it leaves the
> > decision to
> > user-space.
> > 
> > Signed-off-by: Ayman Bagabas 
> 
> The new patch looks much better than the previous one, thanks for
> working on it.
> 
> Just a few comments:
> 
> > +struct huawei_wmi_priv {
> > +   struct input_dev *idev;
> > +   struct led_classdev cdev;
> > +   acpi_handle handle;
> 
> Is this handle set in anywhere?  I couldn't see it in your patch.
> If it's supposed to be NULL, passing NULL explicitly makes your
> intention clearer.
> 
> 
> > +static int huawei_wmi_leds_setup(struct wmi_device *wdev)
> > +{
> > +   struct huawei_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
> > +   acpi_status status;
> > +
> > +   // Skip registering LED subsystem if no ACPI method was found.
> > +   status = acpi_get_handle(priv->handle, "\\_SB.PCI0.LPCB.EC0",
> > &priv->handle);
> > +   if (ACPI_FAILURE(status))
> > +   return 0;
> > +
> > +   if (acpi_has_method(priv->handle, "SPIN"))
> > +   priv->acpi_method = "SPIN";
> > +   else if (acpi_has_method(priv->handle, "WPIN"))
> > +   priv->acpi_method = "WPIN";
> > +   else
> > +   return 0;
> > +
> > +   priv->cdev.name = "platform::micmute";
> > +   priv->cdev.max_brightness = 1;
> > +   priv->cdev.brightness_set_blocking =
> > huawei_wmi_micmute_led_set;
> > +   priv->cdev.default_trigger = "audio-micmute";
> > +   priv->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
> > +   priv->cdev.dev = &wdev->dev;
> 
> What about suspend/resume?
> When the driver is bound wit HD-audio, the HD-audio will restore the
> state at resume, so it would work.  But, by providing the LED class
> device, it is supposed to work even without HD-audio, so it might
> make
> sense to pass LED_CORE_SUSPENDRESUME, too.

Besides that, is there anything needed for wmi_device suspend/resume?

> 
> 
> > +static int __init huawei_wmi_init(void)
> > +{
> > +   if (!(wmi_has_guid(WMI0_EVENT_GUID) ||
> > wmi_has_guid(AMW0_EVENT_GUID))) {
> > +   pr_debug("Compatible WMI GUID not found\n");
> > +   return -ENODEV;
> > +   }
> 
> This is superfluous when you implement with wmi_driver.
> In theory, the supported GUID can be added dynamically via sysfs,
> too.
> 

I left it that way so it doesn't insert the module if these GUIDs were
not found. Should I drop that and use module_wmi_driver instead?

> 
> thanks,
> 
> Takashi



[PATCH v8 2/3] x86: add support for Huawei WMI hotkeys.

2018-11-29 Thread Ayman Bagabas
This driver adds support for missing hotkeys on some Huawei laptops.
Laptops such as the Matebook X have non functioning hotkeys. Whereas
newer laptops such as the Matebook X Pro come with working hotkeys out
of the box.

Old laptops, such as the Matebook X, report hotkey events through ACPI
device "\WMI0". However, new laptops, such as the Matebook X Pro,
does not have this WMI device.

All the hotkeys on the Matebook X Pro work fine
without this patch except (micmute, wlan, and huawei key). These keys
and the brightness keys report events to "\AMW0" ACPI device. One
problem is that brightness keys on the Matebook X Pro work without this
patch. This results in reporting two brightness key press
events one is captured by ACPI and another by this driver.

A solution would be to check if such event came from the "\AMW0" WMI driver
then skip reporting event. Another solution would be to leave this to 
user-space to handle. Which
can be achieved by using "hwdb" tables and remap those keys to "unknown".
This solution seems more natural to me because it leaves the decision to
user-space.

Signed-off-by: Ayman Bagabas 
---
 drivers/platform/x86/Kconfig  |  16 ++
 drivers/platform/x86/Makefile |   1 +
 drivers/platform/x86/huawei-wmi.c | 238 ++
 3 files changed, 255 insertions(+)
 create mode 100644 drivers/platform/x86/huawei-wmi.c

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 87f70e8f4dd0..27db3ce7a1e1 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1292,6 +1292,22 @@ config INTEL_ATOMISP2_PM
  To compile this driver as a module, choose M here: the module
  will be called intel_atomisp2_pm.
 
+config HUAWEI_WMI
+   tristate "Huawei WMI hotkeys driver"
+   depends on ACPI_WMI
+   depends on INPUT
+   select INPUT_SPARSEKMAP
+   select LEDS_CLASS
+   select LEDS_TRIGGERS
+   select LEDS_TRIGGER_AUDIO
+   help
+ This driver provides support for Huawei WMI hotkeys.
+ It enables the missing keys and adds support to the micmute
+ LED found on some of these laptops.
+
+ To compile this driver as a module, choose M here: the module
+ will be called huawei-wmi.
+
 endif # X86_PLATFORM_DEVICES
 
 config PMC_ATOM
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 39ae94135406..d841c550e3cc 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_ACERHDF) += acerhdf.o
 obj-$(CONFIG_HP_ACCEL) += hp_accel.o
 obj-$(CONFIG_HP_WIRELESS)  += hp-wireless.o
 obj-$(CONFIG_HP_WMI)   += hp-wmi.o
+obj-$(CONFIG_HUAWEI_WMI)   += huawei-wmi.o
 obj-$(CONFIG_AMILO_RFKILL) += amilo-rfkill.o
 obj-$(CONFIG_GPD_POCKET_FAN)   += gpd-pocket-fan.o
 obj-$(CONFIG_TC1100_WMI)   += tc1100-wmi.o
diff --git a/drivers/platform/x86/huawei-wmi.c 
b/drivers/platform/x86/huawei-wmi.c
new file mode 100644
index ..5371c8e2ded7
--- /dev/null
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -0,0 +1,238 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Huawei WMI hotkeys
+ *
+ *  Copyright (C) 2018   Ayman Bagabas 
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+/*
+ * Huawei WMI Events GUIDs
+ */
+#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
+#define AMW0_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C9062910"
+
+struct huawei_wmi_priv {
+   struct input_dev *idev;
+   struct led_classdev cdev;
+   acpi_handle handle;
+   char *acpi_method;
+};
+
+static const struct key_entry huawei_wmi_keymap[] __initconst = {
+   { KE_KEY,0x281, { KEY_BRIGHTNESSDOWN } },
+   { KE_KEY,0x282, { KEY_BRIGHTNESSUP } },
+   { KE_KEY,0x284, { KEY_MUTE } },
+   { KE_KEY,0x285, { KEY_VOLUMEDOWN } },
+   { KE_KEY,0x286, { KEY_VOLUMEUP } },
+   { KE_KEY,0x287, { KEY_MICMUTE } },
+   { KE_KEY,0x289, { KEY_WLAN } },
+   // Huawei |M| button
+   { KE_KEY,0x28a, { KEY_CONFIG } },
+   // Keyboard light
+   { KE_IGNORE, 0x293, { KEY_KBDILLUMTOGGLE } },
+   { KE_IGNORE, 0x294, { KEY_KBDILLUMUP } },
+   { KE_IGNORE, 0x295, { KEY_KBDILLUMUP } },
+   { KE_END,0 }
+};
+
+static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
+   enum led_brightness brightness)
+{
+   struct huawei_wmi_priv *priv = dev_get_drvdata(led_cdev->dev->parent);
+   acpi_status status;
+   union acpi_object args[3];
+   struct acpi_object_list arg_list = {
+   .pointer = args,
+   .count = ARRAY_SIZE(args),
+   };
+
+   args[0].type = args[1].type = args[2].type = ACPI_TYPE_INTEGER;
+   args[0].integer.value = 0;
+   args[1].integer.value = 0x04;
+   args[2].in

[PATCH v8 1/3] ALSA: hda: fix front speakers on Huawei MBXP.

2018-11-29 Thread Ayman Bagabas
This patch solves bug 200501 'Only 2 of 4 speakers playing sound.'
https://bugzilla.kernel.org/show_bug.cgi?id=200501
It enables the front speakers on Huawei Matebook X Pro laptops.
These laptops come with Dolby Atmos sound system and these pins
configuration enables the front speakers.

Reviewed-by: Takashi Iwai 
Signed-off-by: Ayman Bagabas 
---
 sound/pci/hda/patch_realtek.c | 18 ++
 1 file changed, 18 insertions(+)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 993d34c141c2..1326f32f4574 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -5490,6 +5490,7 @@ enum {
ALC298_FIXUP_TPT470_DOCK,
ALC255_FIXUP_DUMMY_LINEOUT_VERB,
ALC255_FIXUP_DELL_HEADSET_MIC,
+   ALC256_FIXUP_HUAWEI_MBXP_PINS,
ALC295_FIXUP_HP_X360,
ALC221_FIXUP_HP_HEADSET_MIC,
 };
@@ -5761,6 +5762,22 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC269_FIXUP_HEADSET_MIC
},
+   [ALC256_FIXUP_HUAWEI_MBXP_PINS] = {
+   .type = HDA_FIXUP_PINS,
+   .v.pins = (const struct hda_pintbl[]) {
+   {0x12, 0x90a60130},
+   {0x13, 0x4000},
+   {0x14, 0x90170110},
+   {0x18, 0x41f0},
+   {0x19, 0x04a11040},
+   {0x1a, 0x41f0},
+   {0x1b, 0x90170112},
+   {0x1d, 0x40759a05},
+   {0x1e, 0x41f0},
+   {0x21, 0x04211020},
+   { }
+   }
+   },
[ALC269_FIXUP_ASUS_X101_FUNC] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc269_fixup_x101_headset_mic,
@@ -6591,6 +6608,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x511f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
+   SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MBXP", 
ALC256_FIXUP_HUAWEI_MBXP_PINS),
SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", 
ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */
 
 #if 0
-- 
2.19.1



  1   2   >