diff mbox series

[BlueZ,v1] input: Add support for UserspaceHID=persist

Message ID 20241025193011.3035565-1-luiz.dentz@gmail.com
State New
Headers show
Series [BlueZ,v1] input: Add support for UserspaceHID=persist | expand

Commit Message

Luiz Augusto von Dentz Oct. 25, 2024, 7:30 p.m. UTC
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This adds support supportfor persist mode via input.conf:UserspaceHID
but don't default to it since this appears to create more problems than
it solves.

Fixes: https://github.com/bluez/bluez/issues/983
Fixes: https://github.com/bluez/bluez/issues/977
Fixes: https://github.com/bluez/bluez/issues/949
---
 profiles/input/device.c   | 27 ++++++++++++++++++++++-----
 profiles/input/device.h   |  9 ++++++++-
 profiles/input/hog.c      |  5 ++++-
 profiles/input/input.conf |  6 +++++-
 profiles/input/manager.c  | 11 ++++++-----
 5 files changed, 45 insertions(+), 13 deletions(-)
diff mbox series

Patch

diff --git a/profiles/input/device.c b/profiles/input/device.c
index b162c0bc7572..8cf8e5ea78e1 100644
--- a/profiles/input/device.c
+++ b/profiles/input/device.c
@@ -89,7 +89,7 @@  struct input_device {
 };
 
 static int idle_timeout = 0;
-static bool uhid_enabled = true;
+static uhid_state_t uhid_state = UHID_ENABLED;
 static bool classic_bonded_only = true;
 
 void input_set_idle_timeout(int timeout)
@@ -97,9 +97,23 @@  void input_set_idle_timeout(int timeout)
 	idle_timeout = timeout;
 }
 
-void input_enable_userspace_hid(bool state)
+void input_set_userspace_hid(char *state)
 {
-	uhid_enabled = state;
+	if (!strcasecmp(state, "false") || !strcasecmp(state, "no") ||
+			!strcasecmp(state, "off"))
+		uhid_state = UHID_DISABLED;
+	else if (!strcasecmp(state, "true") || !strcasecmp(state, "yes") ||
+			!strcasecmp(state, "on"))
+		uhid_state = UHID_ENABLED;
+	else if (!strcasecmp(state, "persist"))
+		uhid_state = UHID_PERSIST;
+	else
+		error("Unknown value '%s'", state);
+}
+
+uint8_t input_get_userspace_hid(void)
+{
+	return uhid_state;
 }
 
 void input_set_classic_bonded_only(bool state)
@@ -179,6 +193,9 @@  static int uhid_disconnect(struct input_device *idev, bool force)
 	if (idev->virtual_cable_unplug && !force)
 		force = true;
 
+	if (!force && uhid_state != UHID_PERSIST)
+		force = true;
+
 	err = bt_uhid_destroy(idev->uhid, force);
 	if (err < 0) {
 		error("bt_uhid_destroy: %s", strerror(-err));
@@ -1507,7 +1524,7 @@  int input_device_register(struct btd_service *service)
 	if (!idev)
 		return -EINVAL;
 
-	if (uhid_enabled) {
+	if (uhid_state) {
 		idev->uhid = bt_uhid_new_default();
 		if (!idev->uhid) {
 			error("bt_uhid_new_default: failed");
@@ -1605,7 +1622,7 @@  int input_device_set_channel(const bdaddr_t *src, const bdaddr_t *dst, int psm,
 	if (!idev)
 		return -ENOENT;
 
-	if (uhid_enabled)
+	if (uhid_state)
 		cond |= G_IO_IN;
 
 	switch (psm) {
diff --git a/profiles/input/device.h b/profiles/input/device.h
index cf03894178f7..7b87ce590337 100644
--- a/profiles/input/device.h
+++ b/profiles/input/device.h
@@ -11,11 +11,18 @@ 
 #define L2CAP_PSM_HIDP_CTRL	0x11
 #define L2CAP_PSM_HIDP_INTR	0x13
 
+typedef enum {
+	UHID_DISABLED = 0,
+	UHID_ENABLED,
+	UHID_PERSIST
+} uhid_state_t;
+
 struct input_device;
 struct input_conn;
 
 void input_set_idle_timeout(int timeout);
-void input_enable_userspace_hid(bool state);
+void input_set_userspace_hid(char *state);
+uint8_t input_get_userspace_hid(void);
 void input_set_classic_bonded_only(bool state);
 bool input_get_classic_bonded_only(void);
 void input_set_auto_sec(bool state);
diff --git a/profiles/input/hog.c b/profiles/input/hog.c
index bc1b89e57fbd..017e320f0781 100644
--- a/profiles/input/hog.c
+++ b/profiles/input/hog.c
@@ -208,7 +208,10 @@  static int hog_disconnect(struct btd_service *service)
 {
 	struct hog_device *dev = btd_service_get_user_data(service);
 
-	bt_hog_detach(dev->hog, false);
+	if (input_get_userspace_hid() == UHID_PERSIST)
+		bt_hog_detach(dev->hog, false);
+	else
+		bt_hog_detach(dev->hog, true);
 
 	btd_service_disconnecting_complete(service, 0);
 
diff --git a/profiles/input/input.conf b/profiles/input/input.conf
index 273e15bc9c80..d555d423e43c 100644
--- a/profiles/input/input.conf
+++ b/profiles/input/input.conf
@@ -10,7 +10,11 @@ 
 #IdleTimeout=0
 
 # Enable HID protocol handling in userspace input profile
-# Defaults to true (Use UHID instead of kernel HIDP)
+# Possible values:
+#  - persist: Use UHID in persistent mode (keyboard only)
+#  - true: Use UHID instead
+#  - false: User kernel HIDP
+# Defaults to true
 #UserspaceHID=true
 
 # Limit HID connections to bonded devices
diff --git a/profiles/input/manager.c b/profiles/input/manager.c
index f4598bcd4e47..1c4442ef847f 100644
--- a/profiles/input/manager.c
+++ b/profiles/input/manager.c
@@ -83,7 +83,8 @@  static int input_init(void)
 	config = load_config_file(CONFIGDIR "/input.conf");
 	if (config) {
 		int idle_timeout;
-		gboolean uhid_enabled, classic_bonded_only, auto_sec;
+		gboolean classic_bonded_only, auto_sec;
+		char *uhid_enabled;
 
 		idle_timeout = g_key_file_get_integer(config, "General",
 							"IdleTimeout", &err);
@@ -93,12 +94,12 @@  static int input_init(void)
 		} else
 			g_clear_error(&err);
 
-		uhid_enabled = g_key_file_get_boolean(config, "General",
+		uhid_enabled = g_key_file_get_string(config, "General",
 							"UserspaceHID", &err);
 		if (!err) {
-			DBG("input.conf: UserspaceHID=%s", uhid_enabled ?
-							"true" : "false");
-			input_enable_userspace_hid(uhid_enabled);
+			DBG("input.conf: UserspaceHID=%s", uhid_enabled);
+			input_set_userspace_hid(uhid_enabled);
+			free(uhid_enabled);
 		} else
 			g_clear_error(&err);