Subject: upower: bluetooth HID keyboard shows up as laptop battery
Package: upower
Version: 0.99.1-3.1
Severity: normal

Dear Maintainer,

The UPower battery detection system incorrectly assumes that Bluetooth
keyboards have an
"input" node immediately underneath the "bluetooth" node.  With HID
devices, the "input"
node exists under a subsequent "hid" node.

Because of this, Bluetooth HID batteries will appear as full laptop
batteries, and upower will
incorrectly assume main power is dying when the laptop keyboard runs low
on power.

This bug has been reported upstream, but no action has been taken:
https://bugs.freedesktop.org/show_bug.cgi?id=86510

The attached patch solves the problem.


-- System Information:
Debian Release: jessie/sid
  APT prefers testing
  APT policy: (500, 'testing')
Architecture: armhf (armv7l)

Kernel: Linux 3.17.0-rc5-00226-gdcb0c37 (SMP w/4 CPU cores; PREEMPT)
Locale: LANG=en_SG.UTF-8, LC_CTYPE=en_SG.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash

Versions of packages upower depends on:
ii  dbus               1.8.12-1
ii  libc6              2.19-13
ii  libdbus-1-3        1.8.12-1
ii  libdbus-glib-1-2   0.102-1
ii  libglib2.0-0       2.42.0-2
ii  libgudev-1.0-0     215-7
ii  libimobiledevice4  1.1.6+dfsg-3.1
ii  libplist2          1.11-3
ii  libupower-glib3    0.99.1-3.1
ii  libusb-1.0-0       2:1.0.19-1
ii  udev               215-7

Versions of packages upower recommends:
ii  policykit-1  0.105-8

upower suggests no packages.

-- no debconf information

Description: Add support for batteries on HID devices
 HID keyboards can be attached via Bluetooth or USB, but are in a separate class
 from both.  Upower will reach too far up the ownership chain, and won't be
 able to detect the input device.  As a result, keyboards will show up as
 the main battery.
 .
 This patch adds support for creating a chain of classes to look for input data,
 and places "hid" in front of "bluetooth".
 .
 upower (0.99.1-3.1) unstable; urgency=medium
 .
   * Non-maintainer upload.
   * Add support for HID keyboards
Author: Sean Cross <x...@kosagi.com>

--- upower-0.99.1.orig/src/linux/up-device-supply.c
+++ upower-0.99.1/src/linux/up-device-supply.c
@@ -902,6 +902,39 @@ up_device_supply_poll_unknown_battery (U
 	return FALSE;
 }
 
+static gchar *
+up_device_get_input_path (GUdevDevice *native, const gchar *type)
+{
+	GUdevDevice *dev;
+	gchar *input_path = NULL;
+	const gchar *device_path = NULL;
+	GError *error = NULL;
+	GDir *dir = NULL;
+	const gchar *file;
+
+	/* Detect if the battery comes from bluetooth keyboard or mouse. */
+	dev = g_udev_device_get_parent_with_subsystem (native, type, NULL);
+	if (dev != NULL) {
+		device_path = g_udev_device_get_sysfs_path (dev);
+		if ((dir = g_dir_open (device_path, 0, &error))) {
+			while ((file = g_dir_read_name (dir))) {
+				/* Check if it is an input device. */
+				if (g_str_has_prefix (file, "input")) {
+					input_path = g_build_filename (device_path, file, NULL);
+					break;
+				}
+			}
+			g_dir_close (dir);
+		} else {
+			g_warning ("Can not open folder %s: %s", device_path, error->message);
+			g_error_free (error);
+		}
+		g_object_unref (dev);
+	}
+
+	return input_path;
+}
+
 /**
  * up_device_supply_coldplug:
  *
@@ -912,10 +945,8 @@ up_device_supply_coldplug (UpDevice *dev
 {
 	UpDeviceSupply *supply = UP_DEVICE_SUPPLY (device);
 	gboolean ret = FALSE;
-	GUdevDevice *bluetooth;
 	GUdevDevice *native;
 	const gchar *file;
-	const gchar *device_path = NULL;
 	const gchar *native_path;
 	const gchar *scope;
 	gchar *device_type = NULL;
@@ -959,43 +990,32 @@ up_device_supply_coldplug (UpDevice *dev
 			type = UP_DEVICE_KIND_LINE_POWER;
 		} else if (g_ascii_strcasecmp (device_type, "battery") == 0) {
 
-			/* Detect if the battery comes from bluetooth keyboard or mouse. */
-			bluetooth = g_udev_device_get_parent_with_subsystem (native, "bluetooth", NULL);
-			if (bluetooth != NULL) {
-				device_path = g_udev_device_get_sysfs_path (bluetooth);
-				if ((dir = g_dir_open (device_path, 0, &error))) {
-					while ((file = g_dir_read_name (dir))) {
-						/* Check if it is an input device. */
-						if (g_str_has_prefix (file, "input")) {
-							input_path = g_build_filename (device_path, file, NULL);
-							break;
+			int i;
+			const char *classes[] = {"hid", "bluetooth"};
+			int num_classes = sizeof(classes) / sizeof(*classes);
+
+			for (i = 0; i < num_classes && type == UP_DEVICE_KIND_UNKNOWN; i++) {
+
+				input_path = up_device_get_input_path (native, classes[i]);
+
+				if (input_path != NULL) {
+					if ((dir = g_dir_open (input_path, 0, &error))) {
+						while ((file = g_dir_read_name (dir))) {
+							/* Check if it is a mouse device. */
+							if (g_str_has_prefix (file, "mouse")) {
+								type = UP_DEVICE_KIND_MOUSE;
+								break;
+							}
 						}
+						g_dir_close (dir);
+					} else {
+						g_warning ("Can not open folder %s: %s", input_path, error->message);
+						g_error_free (error);
 					}
-					g_dir_close (dir);
-				} else {
-					g_warning ("Can not open folder %s: %s", device_path, error->message);
-					g_error_free (error);
-				}
-				g_object_unref (bluetooth);
-			}
-
-			if (input_path != NULL) {
-				if ((dir = g_dir_open (input_path, 0, &error))) {
-					while ((file = g_dir_read_name (dir))) {
-						/* Check if it is a mouse device. */
-						if (g_str_has_prefix (file, "mouse")) {
-							type = UP_DEVICE_KIND_MOUSE;
-							break;
-						}
+					g_free (input_path);
+					if (type == UP_DEVICE_KIND_UNKNOWN) {
+						type = UP_DEVICE_KIND_KEYBOARD;
 					}
-					g_dir_close (dir);
-				} else {
-					g_warning ("Can not open folder %s: %s", input_path, error->message);
-					g_error_free (error);
-				}
-				g_free (input_path);
-				if (type == UP_DEVICE_KIND_UNKNOWN) {
-					type = UP_DEVICE_KIND_KEYBOARD;
 				}
 			}
 

Reply via email to