On Sun, May 19, 2019 at 7:46 PM Alexander Schremmer <alex@xxxxxxxxxxxxxxx> wrote: > > From 6bfe30cae2be3f4fbe9f9990a4e83302569ff7e9 Mon Sep 17 00:00:00 2001 > From: Alexander Schremmer <alex@xxxxxxxxxxxxxxx> > Date: Sun, 19 May 2019 18:13:05 +0200 > Subject: [PATCH] platform/x86: Add Lenovo ThinkPad PrivacyGuard. > > This feature is found optionally in T480s, T490, T490s. > > The feature is called lcdshadow and visible via > /proc/acpi/ibm/lcdshadow. > > The ACPI methods \_SB.PCI0.LPCB.EC.HKEY.{GSSS,SSSS,TSSS,CSSS} are > available in these machines. They get, set, toggle or change the state > apparently. > > The patch was tested on a 5.0 series kernel on a T480s. While being in my review and testing queue it doesn't produce any warnings the ABI extension lacks of documentation part. > > Signed-off-by: Alexander Schremmer <alex@xxxxxxxxxxxxxxx> > --- > drivers/platform/x86/thinkpad_acpi.c | 108 +++++++++++++++++++++++++++ > 1 file changed, 108 insertions(+) > > diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c > index 71cfaf26efd1..f2603643b067 100644 > --- a/drivers/platform/x86/thinkpad_acpi.c > +++ b/drivers/platform/x86/thinkpad_acpi.c > @@ -9729,6 +9729,110 @@ static struct ibm_struct battery_driver_data = { > .exit = tpacpi_battery_exit, > }; > > +/************************************************************************* > + * LCD Shadow subdriver, for the Lenovo PrivacyGuard feature > + */ > + > + > +static int lcdshadow_state; > + > +static int lcdshadow_on_off(bool state) > +{ > + acpi_handle set_shadow_handle; > + int output; > + > + if (ACPI_FAILURE(acpi_get_handle( > + hkey_handle, > + "SSSS", > + &set_shadow_handle))) { > + pr_warn("Thinkpad ACPI has no %s interface.\n", "SSSS"); > + return -EIO; > + } > + > + if (!acpi_evalf(set_shadow_handle, &output, NULL, "dd", (int)state)) > + return -EIO; > + > + lcdshadow_state = state; > + return 0; > +} > + > +static int lcdshadow_set(bool on) > +{ > + if (lcdshadow_state < 0 || lcdshadow_state == on) > + return lcdshadow_state; > + return lcdshadow_on_off(on); > +} > + > +static int tpacpi_lcdshadow_init(struct ibm_init_struct *iibm) > +{ > + acpi_handle get_shadow_handle; > + int output; > + > + if (ACPI_FAILURE(acpi_get_handle( > + hkey_handle, > + "GSSS", > + &get_shadow_handle))) { > + lcdshadow_state = -ENODEV; > + return 0; > + } > + > + if (!acpi_evalf(get_shadow_handle, &output, NULL, "dd", 0)) > + return -EIO; > + if (!(output & 0x10000)) { > + lcdshadow_state = -ENODEV; > + return 0; > + } > + lcdshadow_state = output & 0x1; > + > + return 0; > +} > + > +static void lcdshadow_resume(void) > +{ > + if (lcdshadow_state >= 0) > + lcdshadow_on_off(lcdshadow_state); > +} > + > +static int lcdshadow_read(struct seq_file *m) > +{ > + if (lcdshadow_state < 0) { > + seq_puts(m, "status:\t\tnot supported\n"); > + } else { > + seq_printf(m, "status:\t\t%d\n", lcdshadow_state); > + seq_puts(m, "commands:\t0, 1\n"); > + } > + > + return 0; > +} > + > +static int lcdshadow_write(char *buf) > +{ > + char *cmd; > + int state = -1; > + > + if (lcdshadow_state < 0) > + return -ENODEV; > + > + while ((cmd = next_cmd(&buf))) { > + if (strlencmp(cmd, "0") == 0) > + state = 0; > + else if (strlencmp(cmd, "1") == 0) > + state = 1; > + } > + > + if (state == -1) > + return -EINVAL; > + > + return lcdshadow_set(state); > +} > + > +static struct ibm_struct lcdshadow_driver_data = { > + .name = "lcdshadow", > + .resume = lcdshadow_resume, > + .read = lcdshadow_read, > + .write = lcdshadow_write, > +}; > + > /**************************************************************************** > **************************************************************************** > * > @@ -10210,6 +10314,10 @@ static struct ibm_init_struct ibms_init[] __initdata = { > .init = tpacpi_battery_init, > .data = &battery_driver_data, > }, > + { > + .init = tpacpi_lcdshadow_init, > + .data = &lcdshadow_driver_data, > + }, > }; > > static int __init set_ibm_param(const char *val, const struct kernel_param *kp) > -- > 2.20.1 > -- With Best Regards, Andy Shevchenko