Comment 59 for bug 1366421

Revision history for this message
Ritesh Raj Sarraf (rrs) wrote :

@marmuta. Can you please share your fix in revision 2217 ?

I thought of looking at ideapad-laptop driver today. I kinda have a hacky solution working locally. I would appreciate if you share revision 2217 so that I can do a full test. If it looks okay, I may try pushing it to the Linux kernel. It is a hacky trick but maybe they'll accept it.

00:37:35.527 INFO AutoShow can_hide_keyboard: locks=[]
00:37:35.527 DEBUG HardwareSensorTracker Opening '/sys/devices/platform/hp-wmi/tablet' failed: [Errno 2] No such file or directory: '/sys/devices/platform/hp-wmi/tablet'
00:37:35.528 INFO HardwareSensorTracker read tablet_mode=False from '/sys/bus/platform/devices/VPC2004:00/tablet_mode' with pattern '1'
00:37:35.528 INFO AutoShow can_show_keyboard: locks=[] tablet_mode=False
00:37:35.528 DEBUG AutoShow request_keyboard_visible(False): lock_visible=False can_hide=True can_show=False
00:37:36.496 INFO HardwareSensorTracker global key press 23 received
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/Onboard/XInput.py", line 395, in _on_device_event
    callback(ev)
  File "/usr/lib/python3/dist-packages/Onboard/GlobalKeyListener.py", line 175, in _on_device_event
    self.emit("key-press", event)
  File "/usr/lib/python3/dist-packages/Onboard/utils.py", line 1314, in emit
    callback(*args, **kwargs)
  File "/usr/lib/python3/dist-packages/Onboard/AutoHide.py", line 67, in _on_key_press
    s = self._key_listener.get_key_event_string(event)
AttributeError: 'GlobalKeyListener' object has no attribute 'get_key_event_string'
00:37:36.581 DEBUG OnboardGtk keyboard state changed to 0x0
00:37:36.581 DEBUG Onboard.Keyboard set_modifiers(0) False None False
00:37:36.738 INFO AutoShow can_hide_keyboard: locks=[]
00:37:36.738 DEBUG HardwareSensorTracker Opening '/sys/devices/platform/hp-wmi/tablet' failed: [Errno 2] No such file or directory: '/sys/devices/platform/hp-wmi/tablet'
00:37:36.739 INFO HardwareSensorTracker read tablet_mode=False from '/sys/bus/platform/devices/VPC2004:00/tablet_mode' with pattern '1'
00:37:36.739 INFO AutoShow can_show_keyboard: locks=[] tablet_mode=False
00:37:36.740 DEBUG AutoShow request_keyboard_visible(True): lock_visible=False can_hide=True can_show=False

I'm also not sure if that sysfs path would apply to the entire Yoga series. But that's something to think about later.

root@learner:/sys/kernel/debug/ideapad# cat status
Backlight max: 16
Backlight now: 3
BL power value: On
=====================
Radio status: Off(0)
Wifi status: Off(0)
BT status: Off(0)
3G status: Off(0)
=====================
Touchpad status:Off(0)
SW_TABLET_MODE: On(1)
Camera status: On(1)

root@learner:/sys/kernel/debug/ideapad# cat status
Backlight max: 16
Backlight now: 3
BL power value: On
=====================
Radio status: Off(0)
Wifi status: Off(0)
BT status: Off(0)
3G status: Off(0)
=====================
Touchpad status:On(1)
SW_TABLET_MODE: Off(0)
Camera status: On(1)

rrs@learner:~$ cat /sys/bus/platform/devices/VPC2004:00/tablet_mode
0
2017-01-29 / 00:44:22 ♒♒♒ ☺

rrs@learner:~/rrs-home/Community/linux-upstream_GIT (tempsquash)$ cat /var/tmp/0001-Add-sysfs-interface-for-tablet-mode.patch
From a2856f7cfbac5e63731feadc6510ba10c3ac040a Mon Sep 17 00:00:00 2001
From: Ritesh Raj Sarraf <email address hidden>
Date: Sun, 29 Jan 2017 00:19:41 +0530
Subject: [PATCH] Add sysfs interface for tablet mode

Also add tablet mode status in debugfs
Only enable tablet mode interface through module paramters

Background

Lenovo Yoga 2 13 (and may be more Yoga variants) ideappad-laptop driver
does not seem to provide a standard way to determine if the hardware is
in Laptop Mode or Tablet Mode Orientation.

This patch adds a hacky possibility to enable such state by relying on
the touchpad status.

Note: There's one false case. If the user, in Laptop Mode, decides to
disable Touchpad, then the tablet_mode status listing will be incorrect

Signed-off-by: Ritesh Raj Sarraf <email address hidden>
---
 drivers/platform/x86/ideapad-laptop.c | 62 ++++++++++++++++++++++++++++++++++-
 1 file changed, 61 insertions(+), 1 deletion(-)

diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c
index a7614fc542b5..ddf1b0958c0b 100644
--- a/drivers/platform/x86/ideapad-laptop.c
+++ b/drivers/platform/x86/ideapad-laptop.c
@@ -96,6 +96,7 @@ struct ideapad_private {
  struct dentry *debug;
  unsigned long cfg;
  bool has_hw_rfkill_switch;
+ bool sw_tablet_mode;
  const char *fnesc_guid;
 };

@@ -103,6 +104,10 @@ static bool no_bt_rfkill;
 module_param(no_bt_rfkill, bool, 0444);
 MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth.");

+static bool tablet_mode_hack;
+module_param(tablet_mode_hack, bool, 0444);
+MODULE_PARM_DESC(tablet_mode_hack, "Enable Tablet-Mode Hack, using touchpad status");
+
 /*
  * ACPI Helpers
  */
@@ -247,6 +252,14 @@ static int debugfs_status_show(struct seq_file *s, void *data)
  if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value))
   seq_printf(s, "Touchpad status:%s(%lu)\n",
       value ? "On" : "Off", value);
+ /* Flip values to determine tablet mode, because Yoga doesn't seem to
+ * provide anoter interface, otherwise. */
+ if (tablet_mode_hack &&
+ (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value))) {
+ value = ((value)?0:1);
+ seq_printf(s, "SW_TABLET_MODE:\t%s(%lu)\n",
+ value ? "On" : "Off", value);
+ }
  if (!read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &value))
   seq_printf(s, "Camera status:\t%s(%lu)\n",
       value ? "On" : "Off", value);
@@ -423,9 +436,51 @@ static ssize_t store_ideapad_fan(struct device *dev,

 static DEVICE_ATTR(fan_mode, 0644, show_ideapad_fan, store_ideapad_fan);

+static ssize_t show_tablet_state(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ unsigned long result;
+ struct ideapad_private *priv = dev_get_drvdata(dev);
+
+ if (!tablet_mode_hack)
+ return -1;
+
+ if (read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &result))
+ return sprintf(buf, "-1\n");
+
+ result = ((result)?0:1);
+ return sprintf(buf, "%lu\n", result);
+}
+
+static ssize_t store_tablet_state(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret, state;
+ struct ideapad_private *priv = dev_get_drvdata(dev);
+
+ if (!tablet_mode_hack)
+ return -1;
+
+ if (!count)
+ return 0;
+ if (sscanf(buf, "%i", &state) != 1)
+ return -EINVAL;
+ if (state < 0 || state > 4 || state == 3)
+ return -EINVAL;
+ ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_TOUCHPAD, state);
+ if (ret < 0)
+ return -EIO;
+ return count;
+}
+
+static DEVICE_ATTR(tablet_mode, 0644, show_tablet_state, store_tablet_state);
+
 static struct attribute *ideapad_attributes[] = {
  &dev_attr_camera_power.attr,
  &dev_attr_fan_mode.attr,
+ &dev_attr_tablet_mode.attr,
  NULL
 };

@@ -443,7 +498,10 @@ static umode_t ideapad_is_visible(struct kobject *kobj,
   unsigned long value;
   supported = !read_ec_data(priv->adev->handle, VPCCMD_R_FAN,
        &value);
- } else
+ } else if (attr == &dev_attr_tablet_mode.attr) {
+ unsigned long value;
+ supported = !read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value);
+ } else
   supported = true;

  return supported ? attr->mode : 0;
@@ -956,6 +1014,7 @@ static int ideapad_acpi_add(struct platform_device *pdev)
  int cfg;
  struct ideapad_private *priv;
  struct acpi_device *adev;
+ unsigned long value;

  ret = acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev);
  if (ret)
@@ -973,6 +1032,7 @@ static int ideapad_acpi_add(struct platform_device *pdev)
  priv->adev = adev;
  priv->platform_device = pdev;
  priv->has_hw_rfkill_switch = !dmi_check_system(no_hw_rfkill_list);
+ priv->sw_tablet_mode = !read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value);

  ret = ideapad_sysfs_init(priv);
  if (ret)
--
2.11.0

2017-01-29 / 00:41:02 ♒♒♒ ☺