summaryrefslogtreecommitdiff
path: root/drivers/hid/hid-uclogic-params.c
diff options
context:
space:
mode:
authorJosé Expósito <jose.exposito89@gmail.com>2022-12-26 15:54:52 +0300
committerJiri Kosina <jkosina@suse.cz>2023-01-18 11:44:57 +0300
commita251d6576d2a29fc0806ef4775719e3b6e672d91 (patch)
treedc01207f77f9300469c0ca9f7e8916a3e7bfd178 /drivers/hid/hid-uclogic-params.c
parentbd85c131b2e3edf2cf1a498652e8c3a066a338e7 (diff)
downloadlinux-a251d6576d2a29fc0806ef4775719e3b6e672d91.tar.xz
HID: uclogic: Handle wireless device reconnection
UGEEv2 tablets with battery can be connected using a USB cable or a USB Bluetooth dongle. When the Bluetooth dongle is used, the connection to that tablet can be lost because the tablet is out of the range of the receiver or because it was switched off using the switch placed in the back of the tablet's frame. After losing connection, the tablet is able to reconnect automatically and its firmware sends a special packet indicating that the device was reconnected. In response to this packet, the tablet needs to receive the same array of magic data it expects on probe to enable its interfaces. This patch implements a generic mechanism to hook raw events and schedule a work to perform any custom action. Tested-by: Mia Kanashi <chad@redpilled.dev> Tested-by: Andreas Grosse <andig.mail@t-online.de> Signed-off-by: José Expósito <jose.exposito89@gmail.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid/hid-uclogic-params.c')
-rw-r--r--drivers/hid/hid-uclogic-params.c99
1 files changed, 99 insertions, 0 deletions
diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c
index b6a515973942..86702c994c57 100644
--- a/drivers/hid/hid-uclogic-params.c
+++ b/drivers/hid/hid-uclogic-params.c
@@ -616,6 +616,31 @@ cleanup:
}
/**
+ * uclogic_params_cleanup_event_hooks - free resources used by the list of raw
+ * event hooks.
+ * Can be called repeatedly.
+ *
+ * @params: Input parameters to cleanup. Cannot be NULL.
+ */
+static void uclogic_params_cleanup_event_hooks(struct uclogic_params *params)
+{
+ struct uclogic_raw_event_hook *curr, *n;
+
+ if (!params || !params->event_hooks)
+ return;
+
+ list_for_each_entry_safe(curr, n, &params->event_hooks->list, list) {
+ cancel_work_sync(&curr->work);
+ list_del(&curr->list);
+ kfree(curr->event);
+ kfree(curr);
+ }
+
+ kfree(params->event_hooks);
+ params->event_hooks = NULL;
+}
+
+/**
* uclogic_params_cleanup - free resources used by struct uclogic_params
* (tablet interface's parameters).
* Can be called repeatedly.
@@ -631,6 +656,7 @@ void uclogic_params_cleanup(struct uclogic_params *params)
for (i = 0; i < ARRAY_SIZE(params->frame_list); i++)
uclogic_params_frame_cleanup(&params->frame_list[i]);
+ uclogic_params_cleanup_event_hooks(params);
memset(params, 0, sizeof(*params));
}
}
@@ -1281,6 +1307,72 @@ static int uclogic_params_ugee_v2_init_battery(struct hid_device *hdev,
}
/**
+ * uclogic_params_ugee_v2_reconnect_work() - When a wireless tablet looses
+ * connection to the USB dongle and reconnects, either because of its physical
+ * distance or because it was switches off and on using the frame's switch,
+ * uclogic_probe_interface() needs to be called again to enable the tablet.
+ *
+ * @work: The work that triggered this function.
+ */
+static void uclogic_params_ugee_v2_reconnect_work(struct work_struct *work)
+{
+ struct uclogic_raw_event_hook *event_hook;
+
+ event_hook = container_of(work, struct uclogic_raw_event_hook, work);
+ uclogic_probe_interface(event_hook->hdev, uclogic_ugee_v2_probe_arr,
+ uclogic_ugee_v2_probe_size,
+ uclogic_ugee_v2_probe_endpoint);
+}
+
+/**
+ * uclogic_params_ugee_v2_init_event_hooks() - initialize the list of events
+ * to be hooked for UGEE v2 devices.
+ * @hdev: The HID device of the tablet interface to initialize and get
+ * parameters from.
+ * @p: Parameters to fill in, cannot be NULL.
+ *
+ * Returns:
+ * Zero, if successful. A negative errno code on error.
+ */
+static int uclogic_params_ugee_v2_init_event_hooks(struct hid_device *hdev,
+ struct uclogic_params *p)
+{
+ struct uclogic_raw_event_hook *event_hook;
+ __u8 reconnect_event[] = {
+ /* Event received on wireless tablet reconnection */
+ 0x02, 0xF8, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ if (!p)
+ return -EINVAL;
+
+ /* The reconnection event is only received if the tablet has battery */
+ if (!uclogic_params_ugee_v2_has_battery(hdev))
+ return 0;
+
+ p->event_hooks = kzalloc(sizeof(*p->event_hooks), GFP_KERNEL);
+ if (!p->event_hooks)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&p->event_hooks->list);
+
+ event_hook = kzalloc(sizeof(*event_hook), GFP_KERNEL);
+ if (!event_hook)
+ return -ENOMEM;
+
+ INIT_WORK(&event_hook->work, uclogic_params_ugee_v2_reconnect_work);
+ event_hook->hdev = hdev;
+ event_hook->size = ARRAY_SIZE(reconnect_event);
+ event_hook->event = kmemdup(reconnect_event, event_hook->size, GFP_KERNEL);
+ if (!event_hook->event)
+ return -ENOMEM;
+
+ list_add_tail(&event_hook->list, &p->event_hooks->list);
+
+ return 0;
+}
+
+/**
* uclogic_params_ugee_v2_init() - initialize a UGEE graphics tablets by
* discovering their parameters.
*
@@ -1416,6 +1508,13 @@ static int uclogic_params_ugee_v2_init(struct uclogic_params *params,
}
}
+ /* Create a list of raw events to be ignored */
+ rc = uclogic_params_ugee_v2_init_event_hooks(hdev, &p);
+ if (rc) {
+ hid_err(hdev, "error initializing event hook list: %d\n", rc);
+ goto cleanup;
+ }
+
output:
/* Output parameters */
memcpy(params, &p, sizeof(*params));