@@ -84,6 +84,7 @@ struct input_device {
unsigned int report_req_timer;
uint32_t report_rsp_id;
bool virtual_cable_unplug;
+ uint8_t type;
unsigned int idle_timer;
};
@@ -174,18 +175,19 @@ static int uhid_disconnect(struct input_device *idev, bool force)
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;
+ /* Force destroy the node if virtual cable unplug flag has been set */
+ if (idev->virtual_cable_unplug && !force)
+ force = true;
- bt_uhid_unregister_all(idev->uhid);
-
- err = bt_uhid_destroy(idev->uhid);
+ err = bt_uhid_destroy(idev->uhid, force);
if (err < 0) {
error("bt_uhid_destroy: %s", strerror(-err));
return err;
}
+ if (!bt_uhid_created(idev->uhid))
+ bt_uhid_unregister_all(idev->uhid);
+
return err;
}
@@ -989,7 +991,8 @@ static int uhid_connadd(struct input_device *idev, struct hidp_connadd_req *req)
err = bt_uhid_create(idev->uhid, req->name, &idev->src, &idev->dst,
req->vendor, req->product, req->version,
- req->country, req->rd_data, req->rd_size);
+ req->country, idev->type,
+ req->rd_data, req->rd_size);
if (err < 0) {
error("bt_uhid_create: %s", strerror(-err));
return err;
@@ -1078,7 +1081,7 @@ static int hidp_add_connection(struct input_device *idev)
/* Some platforms may choose to require encryption for all devices */
/* Note that this only matters for pre 2.1 devices as otherwise the */
/* device is encrypted by default by the lower layers */
- if (classic_bonded_only || req->subclass & 0x40) {
+ if (classic_bonded_only || idev->type == BT_UHID_KEYBOARD) {
if (!bt_io_set(idev->intr_io, &gerr,
BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
BT_IO_OPT_INVALID)) {
@@ -1468,6 +1471,7 @@ static struct input_device *input_device_new(struct btd_service *service)
idev->service = btd_service_ref(service);
idev->device = btd_device_ref(device);
idev->path = g_strdup(path);
+ idev->type = bt_uhid_icon_to_type(btd_device_get_icon(device));
input_device_update_rec(idev);
@@ -73,6 +73,7 @@ struct bt_hog {
uint16_t vendor;
uint16_t product;
uint16_t version;
+ uint8_t type;
struct gatt_db_attribute *attr;
struct gatt_primary *primary;
GAttrib *attrib;
@@ -825,17 +826,18 @@ static void set_report_cb(guint8 status, const guint8 *pdu,
error("bt_uhid_set_report_reply: %s", strerror(-err));
}
-static void uhid_destroy(struct bt_hog *hog)
+static void uhid_destroy(struct bt_hog *hog, bool force)
{
int err;
- bt_uhid_unregister_all(hog->uhid);
-
- err = bt_uhid_destroy(hog->uhid);
+ err = bt_uhid_destroy(hog->uhid, force);
if (err < 0) {
error("bt_uhid_destroy: %s", strerror(-err));
return;
}
+
+ if (bt_uhid_created(hog->uhid))
+ bt_uhid_unregister_all(hog->uhid);
}
static void set_report(struct uhid_event *ev, void *user_data)
@@ -850,7 +852,7 @@ static void set_report(struct uhid_event *ev, void *user_data)
* while disconnected.
*/
if (hog->attrib == NULL) {
- uhid_destroy(hog);
+ uhid_destroy(hog, true);
return;
}
@@ -948,7 +950,7 @@ static void get_report(struct uhid_event *ev, void *user_data)
* while disconnected.
*/
if (hog->attrib == NULL) {
- uhid_destroy(hog);
+ uhid_destroy(hog, true);
return;
}
@@ -1003,7 +1005,7 @@ static void uhid_create(struct bt_hog *hog, uint8_t *report_map,
err = bt_uhid_create(hog->uhid, hog->name, &src, &dst,
hog->vendor, hog->product, hog->version,
- hog->bcountrycode, value, vlen);
+ hog->bcountrycode, hog->type, value, vlen);
if (err < 0) {
error("bt_uhid_create: %s", strerror(-err));
return;
@@ -1233,7 +1235,7 @@ static void hog_free(void *data)
struct bt_hog *hog = data;
bt_hog_detach(hog, true);
- uhid_destroy(hog);
+ uhid_destroy(hog, true);
queue_destroy(hog->bas, (void *) bt_bas_unref);
g_slist_free_full(hog->instances, hog_free);
@@ -1252,9 +1254,9 @@ static void hog_free(void *data)
struct bt_hog *bt_hog_new_default(const char *name, uint16_t vendor,
uint16_t product, uint16_t version,
- struct gatt_db *db)
+ uint8_t type, struct gatt_db *db)
{
- return bt_hog_new(-1, name, vendor, product, version, db);
+ return bt_hog_new(-1, name, vendor, product, version, type, db);
}
static void foreach_hog_report(struct gatt_db_attribute *attr, void *user_data)
@@ -1414,6 +1416,7 @@ static void foreach_hog_chrc(struct gatt_db_attribute *attr, void *user_data)
static struct bt_hog *hog_new(int fd, const char *name, uint16_t vendor,
uint16_t product, uint16_t version,
+ uint8_t type,
struct gatt_db_attribute *attr)
{
struct bt_hog *hog;
@@ -1441,6 +1444,7 @@ static struct bt_hog *hog_new(int fd, const char *name, uint16_t vendor,
hog->vendor = vendor;
hog->product = product;
hog->version = version;
+ hog->type = type;
hog->attr = attr;
return hog;
@@ -1456,8 +1460,8 @@ static void hog_attach_instance(struct bt_hog *hog,
return;
}
- instance = hog_new(hog->uhid_fd, hog->name, hog->vendor,
- hog->product, hog->version, attr);
+ instance = hog_new(hog->uhid_fd, hog->name, hog->vendor, hog->product,
+ hog->version, hog->type, attr);
if (!instance)
return;
@@ -1493,11 +1497,11 @@ static void dis_notify(uint8_t source, uint16_t vendor, uint16_t product,
struct bt_hog *bt_hog_new(int fd, const char *name, uint16_t vendor,
uint16_t product, uint16_t version,
- struct gatt_db *db)
+ uint8_t type, struct gatt_db *db)
{
struct bt_hog *hog;
- hog = hog_new(fd, name, vendor, product, version, NULL);
+ hog = hog_new(fd, name, vendor, product, version, type, NULL);
if (!hog)
return NULL;
@@ -1620,7 +1624,7 @@ static void hog_attach_hog(struct bt_hog *hog, struct gatt_primary *primary)
instance = bt_hog_new(hog->uhid_fd, hog->name, hog->vendor,
hog->product, hog->version,
- hog->gatt_db);
+ hog->type, hog->gatt_db);
if (!instance)
return;
@@ -1751,9 +1755,12 @@ void bt_hog_detach(struct bt_hog *hog, bool force)
{
GSList *l;
- if (!hog->attrib)
+ if (!hog)
return;
+ if (!hog->attrib)
+ goto done;
+
queue_foreach(hog->bas, (void *) bt_bas_detach, NULL);
for (l = hog->instances; l; l = l->next) {
@@ -1781,8 +1788,8 @@ void bt_hog_detach(struct bt_hog *hog, bool force)
g_attrib_unref(hog->attrib);
hog->attrib = NULL;
- if (force)
- uhid_destroy(hog);
+done:
+ uhid_destroy(hog, force);
}
int bt_hog_set_control_point(struct bt_hog *hog, bool suspend)
@@ -12,11 +12,11 @@ struct bt_hog;
struct bt_hog *bt_hog_new_default(const char *name, uint16_t vendor,
uint16_t product, uint16_t version,
- struct gatt_db *db);
+ uint8_t type, struct gatt_db *db);
struct bt_hog *bt_hog_new(int fd, const char *name, uint16_t vendor,
uint16_t product, uint16_t version,
- struct gatt_db *db);
+ uint8_t type, struct gatt_db *db);
struct bt_hog *bt_hog_ref(struct bt_hog *hog);
void bt_hog_unref(struct bt_hog *hog);
@@ -50,6 +50,7 @@
struct hog_device {
struct btd_device *device;
struct bt_hog *hog;
+ uint8_t type;
};
static gboolean suspend_supported = FALSE;
@@ -64,7 +65,7 @@ void input_set_auto_sec(bool state)
static void hog_device_accept(struct hog_device *dev, struct gatt_db *db)
{
char name[248];
- uint16_t vendor, product, version;
+ uint16_t vendor, product, version, type;
if (dev->hog)
return;
@@ -77,11 +78,12 @@ static void hog_device_accept(struct hog_device *dev, struct gatt_db *db)
vendor = btd_device_get_vendor(dev->device);
product = btd_device_get_product(dev->device);
version = btd_device_get_version(dev->device);
+ type = bt_uhid_icon_to_type(btd_device_get_icon(dev->device));
DBG("name=%s vendor=0x%X, product=0x%X, version=0x%X", name, vendor,
product, version);
- dev->hog = bt_hog_new_default(name, vendor, product, version, db);
+ dev->hog = bt_hog_new_default(name, vendor, product, version, type, db);
}
static struct hog_device *hog_device_new(struct btd_device *device)
@@ -44,6 +44,7 @@ struct bt_uhid {
unsigned int notify_id;
struct queue *notify_list;
struct queue *input;
+ uint8_t type;
bool created;
bool started;
struct uhid_replay *replay;
@@ -338,8 +339,8 @@ static void uhid_start(struct uhid_event *ev, void *user_data)
int bt_uhid_create(struct bt_uhid *uhid, const char *name, bdaddr_t *src,
bdaddr_t *dst, uint32_t vendor, uint32_t product,
- uint32_t version, uint32_t country, void *rd_data,
- size_t rd_size)
+ uint32_t version, uint32_t country, uint8_t type,
+ void *rd_data, size_t rd_size)
{
struct uhid_event ev;
int err;
@@ -381,6 +382,7 @@ int bt_uhid_create(struct bt_uhid *uhid, const char *name, bdaddr_t *src,
uhid->created = true;
uhid->started = false;
+ uhid->type = type;
return 0;
}
@@ -487,7 +489,7 @@ done:
return bt_uhid_send(uhid, &ev);
}
-int bt_uhid_destroy(struct bt_uhid *uhid)
+int bt_uhid_destroy(struct bt_uhid *uhid, bool force)
{
struct uhid_event ev;
int err;
@@ -495,7 +497,14 @@ int bt_uhid_destroy(struct bt_uhid *uhid)
if (!uhid)
return -EINVAL;
- if (!uhid->created)
+ /* Force destroy for non-keyboard devices - keyboards are not destroyed
+ * on disconnect since they can glitch on reconnection losing
+ * keypresses.
+ */
+ if (!force && uhid->type != BT_UHID_KEYBOARD)
+ force = true;
+
+ if (!uhid->created || !force)
return 0;
memset(&ev, 0, sizeof(ev));
@@ -15,6 +15,31 @@
struct bt_uhid;
+enum {
+ BT_UHID_NONE = 0,
+ BT_UHID_KEYBOARD,
+ BT_UHID_MOUSE,
+ BT_UHID_GAMING,
+ BT_UHID_TABLET
+};
+
+static inline uint8_t bt_uhid_icon_to_type(const char *icon)
+{
+ if (!icon)
+ return BT_UHID_NONE;
+
+ if (!strcmp(icon, "input-keyboard"))
+ return BT_UHID_KEYBOARD;
+ else if (!strcmp(icon, "input-mouse"))
+ return BT_UHID_MOUSE;
+ else if (!strcmp(icon, "input-gaming"))
+ return BT_UHID_GAMING;
+ else if (!strcmp(icon, "input-tablet"))
+ return BT_UHID_TABLET;
+ else
+ return BT_UHID_NONE;
+}
+
struct bt_uhid *bt_uhid_new_default(void);
struct bt_uhid *bt_uhid_new(int fd);
@@ -32,8 +57,8 @@ bool bt_uhid_unregister_all(struct bt_uhid *uhid);
int bt_uhid_send(struct bt_uhid *uhid, const struct uhid_event *ev);
int bt_uhid_create(struct bt_uhid *uhid, const char *name, bdaddr_t *src,
bdaddr_t *dst, uint32_t vendor, uint32_t product,
- uint32_t version, uint32_t country, void *rd_data,
- size_t rd_size);
+ uint32_t version, uint32_t country, uint8_t type,
+ void *rd_data, size_t rd_size);
bool bt_uhid_created(struct bt_uhid *uhid);
bool bt_uhid_started(struct bt_uhid *uhid);
int bt_uhid_input(struct bt_uhid *uhid, uint8_t number, const void *data,
@@ -41,5 +66,5 @@ int bt_uhid_input(struct bt_uhid *uhid, uint8_t number, const void *data,
int bt_uhid_set_report_reply(struct bt_uhid *uhid, uint8_t id, uint8_t status);
int bt_uhid_get_report_reply(struct bt_uhid *uhid, uint8_t id, uint8_t number,
uint8_t status, const void *data, size_t size);
-int bt_uhid_destroy(struct bt_uhid *uhid);
+int bt_uhid_destroy(struct bt_uhid *uhid, bool force);
int bt_uhid_replay(struct bt_uhid *uhid);
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> This adds special handling for non-keyboards so they are destroyed when disconnected rather than keeping their device node around. Fixes: https://github.com/bluez/bluez/issues/848 --- profiles/input/device.c | 20 +++++++++++-------- profiles/input/hog-lib.c | 43 +++++++++++++++++++++++----------------- profiles/input/hog-lib.h | 4 ++-- profiles/input/hog.c | 6 ++++-- src/shared/uhid.c | 17 ++++++++++++---- src/shared/uhid.h | 31 ++++++++++++++++++++++++++--- 6 files changed, 84 insertions(+), 37 deletions(-)