2024-03-13 21:19:55

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ v1] input/device: Force UHID_DESTROY on error

From: Luiz Augusto von Dentz <[email protected]>

If we cannot sent a message back to the device destroy the UHID device
since it is likely that the driver is trying to reach the remote device
which is no longer connected.

Fixes: https://github.com/bluez/bluez/issues/777
---
profiles/input/device.c | 60 ++++++++++++++++++++++-------------------
1 file changed, 32 insertions(+), 28 deletions(-)

diff --git a/profiles/input/device.c b/profiles/input/device.c
index c4f75c7442e8..8184d7c9ff7b 100644
--- a/profiles/input/device.c
+++ b/profiles/input/device.c
@@ -105,7 +105,6 @@ bool input_get_classic_bonded_only(void)

static void input_device_enter_reconnect_mode(struct input_device *idev);
static int connection_disconnect(struct input_device *idev, uint32_t flags);
-static int uhid_disconnect(struct input_device *idev);

static bool input_device_bonded(struct input_device *idev)
{
@@ -314,6 +313,28 @@ static bool hidp_recv_intr_data(GIOChannel *chan, struct input_device *idev)
return true;
}

+static int uhid_disconnect(struct input_device *idev, bool force)
+{
+ int err;
+
+ if (!bt_uhid_created(idev->uhid))
+ return 0;
+
+ /* Only destroy the node if virtual cable unplug flag has been set */
+ if (!idev->virtual_cable_unplug && !force)
+ return 0;
+
+ bt_uhid_unregister_all(idev->uhid);
+
+ err = bt_uhid_destroy(idev->uhid);
+ if (err < 0) {
+ error("bt_uhid_destroy: %s", strerror(-err));
+ return err;
+ }
+
+ return err;
+}
+
static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
{
struct input_device *idev = data;
@@ -356,8 +377,7 @@ static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data
virtual_cable_unplug(idev);

/* If connection abruptly ended, uhid might be not yet disconnected */
- if (bt_uhid_created(idev->uhid))
- uhid_disconnect(idev);
+ uhid_disconnect(idev, false);

return FALSE;
}
@@ -654,8 +674,11 @@ static void hidp_send_set_report(struct uhid_event *ev, void *user_data)
timeout_add_seconds(REPORT_REQ_TIMEOUT,
hidp_report_req_timeout, idev, NULL);
idev->report_rsp_id = ev->u.set_report.id;
- } else
+ } else {
uhid_send_set_report_reply(idev, ev->u.set_report.id, EIO);
+ /* Force UHID_DESTROY on error */
+ uhid_disconnect(idev, true);
+ }
}

static void hidp_send_get_report(struct uhid_event *ev, void *user_data)
@@ -698,9 +721,12 @@ static void hidp_send_get_report(struct uhid_event *ev, void *user_data)
hidp_report_req_timeout, idev,
NULL);
idev->report_rsp_id = ev->u.get_report.id;
- } else
+ } else {
uhid_send_get_report_reply(idev, NULL, 0, ev->u.get_report.id,
EIO);
+ /* Force UHID_DESTROY on error */
+ uhid_disconnect(idev, true);
+ }
}

static void epox_endian_quirk(unsigned char *data, int size)
@@ -934,28 +960,6 @@ static int uhid_connadd(struct input_device *idev, struct hidp_connadd_req *req)
return err;
}

-static int uhid_disconnect(struct input_device *idev)
-{
- int err;
-
- if (!bt_uhid_created(idev->uhid))
- return 0;
-
- /* Only destroy the node if virtual cable unplug flag has been set */
- if (!idev->virtual_cable_unplug)
- return 0;
-
- bt_uhid_unregister_all(idev->uhid);
-
- err = bt_uhid_destroy(idev->uhid);
- if (err < 0) {
- error("bt_uhid_destroy: %s", strerror(-err));
- return err;
- }
-
- return err;
-}
-
static gboolean encrypt_notify(GIOChannel *io, GIOCondition condition,
gpointer data)
{
@@ -1087,7 +1091,7 @@ static int connection_disconnect(struct input_device *idev, uint32_t flags)
idev->virtual_cable_unplug = true;

if (idev->uhid)
- return uhid_disconnect(idev);
+ return uhid_disconnect(idev, false);
else
return ioctl_disconnect(idev, flags);
}
--
2.43.0