From patchwork Wed Apr 10 14:54:45 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans de Goede X-Patchwork-Id: 10894135 X-Patchwork-Delegate: jikos@jikos.cz Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id ED94F17E1 for ; Wed, 10 Apr 2019 14:55:50 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D7D6828A44 for ; Wed, 10 Apr 2019 14:55:50 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id CBE0628970; Wed, 10 Apr 2019 14:55:50 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3242728A44 for ; Wed, 10 Apr 2019 14:55:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732947AbfDJOzt (ORCPT ); Wed, 10 Apr 2019 10:55:49 -0400 Received: from mx1.redhat.com ([209.132.183.28]:36166 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1732870AbfDJOzt (ORCPT ); Wed, 10 Apr 2019 10:55:49 -0400 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 0373181E18; Wed, 10 Apr 2019 14:55:49 +0000 (UTC) Received: from shalem.localdomain.com (ovpn-116-249.ams2.redhat.com [10.36.116.249]) by smtp.corp.redhat.com (Postfix) with ESMTP id DDE3D61102; Wed, 10 Apr 2019 14:55:46 +0000 (UTC) From: Hans de Goede To: Jiri Kosina , Benjamin Tissoires Cc: Hans de Goede , Nestor Lopez Casado , linux-input@vger.kernel.org Subject: [PATCH v2 23/37] HID: logitech-dj: add support for Logitech Bluetooth Mini-Receiver Date: Wed, 10 Apr 2019 16:54:45 +0200 Message-Id: <20190410145459.11430-24-hdegoede@redhat.com> In-Reply-To: <20190410145459.11430-1-hdegoede@redhat.com> References: <20190410145459.11430-1-hdegoede@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.25]); Wed, 10 Apr 2019 14:55:49 +0000 (UTC) Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add support for the Logitech Bluetooth Mini-Receiver in HID proxy mode This requires some special handing in dj_find_receiver_dev because the BT Mini-Receiver contains a built-in hub and has separate USB-devices for the keyboard and mouse interfaces, rather then using 2 interfaces on a single USB device. Otherwise this receiver works identical to the standard non-unifying nano receivers. Signed-off-by: Hans de Goede --- drivers/hid/hid-logitech-dj.c | 91 ++++++++++++++++++++++++++++++++--- 1 file changed, 85 insertions(+), 6 deletions(-) diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 826e2079b9ae..8bb36333233a 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -112,6 +112,7 @@ #define HIDPP_PARAM_27MHZ_DEVID 0x03 #define HIDPP_DEVICE_TYPE_MASK GENMASK(3, 0) #define HIDPP_LINK_STATUS_MASK BIT(6) +#define HIDPP_MANUFACTURER_MASK BIT(7) #define HIDPP_DEVICE_TYPE_KEYBOARD 1 #define HIDPP_DEVICE_TYPE_MOUSE 2 @@ -128,6 +129,7 @@ enum recvr_type { recvr_type_hidpp, recvr_type_gaming_hidpp, recvr_type_27mhz, + recvr_type_bluetooth, }; struct dj_report { @@ -295,6 +297,55 @@ static const char mse_27mhz_descriptor[] = { 0xC0, /* END_COLLECTION */ }; +/* Mouse descriptor (2) for Bluetooth receiver, low-res hwheel, 12 buttons */ +static const char mse_bluetooth_descriptor[] = { + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ + 0x09, 0x02, /* USAGE (Mouse) */ + 0xA1, 0x01, /* COLLECTION (Application) */ + 0x85, 0x02, /* REPORT_ID = 2 */ + 0x09, 0x01, /* USAGE (pointer) */ + 0xA1, 0x00, /* COLLECTION (physical) */ + 0x05, 0x09, /* USAGE_PAGE (buttons) */ + 0x19, 0x01, /* USAGE_MIN (1) */ + 0x29, 0x08, /* USAGE_MAX (8) */ + 0x15, 0x00, /* LOGICAL_MIN (0) */ + 0x25, 0x01, /* LOGICAL_MAX (1) */ + 0x95, 0x08, /* REPORT_COUNT (8) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x81, 0x02, /* INPUT (data var abs) */ + 0x05, 0x01, /* USAGE_PAGE (generic desktop) */ + 0x16, 0x01, 0xF8, /* LOGICAL_MIN (-2047) */ + 0x26, 0xFF, 0x07, /* LOGICAL_MAX (2047) */ + 0x75, 0x0C, /* REPORT_SIZE (12) */ + 0x95, 0x02, /* REPORT_COUNT (2) */ + 0x09, 0x30, /* USAGE (X) */ + 0x09, 0x31, /* USAGE (Y) */ + 0x81, 0x06, /* INPUT */ + 0x15, 0x81, /* LOGICAL_MIN (-127) */ + 0x25, 0x7F, /* LOGICAL_MAX (127) */ + 0x75, 0x08, /* REPORT_SIZE (8) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x09, 0x38, /* USAGE (wheel) */ + 0x81, 0x06, /* INPUT */ + 0x05, 0x0C, /* USAGE_PAGE(consumer) */ + 0x0A, 0x38, 0x02, /* USAGE(AC Pan) */ + 0x15, 0xF9, /* LOGICAL_MIN (-7) */ + 0x25, 0x07, /* LOGICAL_MAX (7) */ + 0x75, 0x04, /* REPORT_SIZE (4) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x81, 0x06, /* INPUT */ + 0x05, 0x09, /* USAGE_PAGE (buttons) */ + 0x19, 0x09, /* USAGE_MIN (9) */ + 0x29, 0x0C, /* USAGE_MAX (12) */ + 0x15, 0x00, /* LOGICAL_MIN (0) */ + 0x25, 0x01, /* LOGICAL_MAX (1) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x95, 0x04, /* REPORT_COUNT (4) */ + 0x81, 0x06, /* INPUT */ + 0xC0, /* END_COLLECTION */ + 0xC0, /* END_COLLECTION */ +}; + /* Gaming Mouse descriptor (2) */ static const char mse_high_res_descriptor[] = { 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ @@ -441,7 +492,7 @@ static const char hidpp_descriptor[] = { /* Make sure all descriptors are present here */ #define MAX_RDESC_SIZE \ (sizeof(kbd_descriptor) + \ - sizeof(mse_descriptor) + \ + sizeof(mse_bluetooth_descriptor) + \ sizeof(consumer_descriptor) + \ sizeof(syscontrol_descriptor) + \ sizeof(media_descriptor) + \ @@ -486,24 +537,32 @@ static DEFINE_MUTEX(dj_hdev_list_lock); * to create a single struct dj_receiver_dev for all interfaces belonging to * a single USB-device / receiver. */ -static struct dj_receiver_dev *dj_find_receiver_dev(struct hid_device *hdev) +static struct dj_receiver_dev *dj_find_receiver_dev(struct hid_device *hdev, + enum recvr_type type) { struct dj_receiver_dev *djrcv_dev; + char sep; + + /* + * The bluetooth receiver contains a built-in hub and has separate + * USB-devices for the keyboard and mouse interfaces. + */ + sep = (type == recvr_type_bluetooth) ? '.' : '/'; /* Try to find an already-probed interface from the same device */ list_for_each_entry(djrcv_dev, &dj_hdev_list, list) { if (djrcv_dev->mouse && - hid_compare_device_paths(hdev, djrcv_dev->mouse, '/')) { + hid_compare_device_paths(hdev, djrcv_dev->mouse, sep)) { kref_get(&djrcv_dev->kref); return djrcv_dev; } if (djrcv_dev->keyboard && - hid_compare_device_paths(hdev, djrcv_dev->keyboard, '/')) { + hid_compare_device_paths(hdev, djrcv_dev->keyboard, sep)) { kref_get(&djrcv_dev->kref); return djrcv_dev; } if (djrcv_dev->hidpp && - hid_compare_device_paths(hdev, djrcv_dev->hidpp, '/')) { + hid_compare_device_paths(hdev, djrcv_dev->hidpp, sep)) { kref_get(&djrcv_dev->kref); return djrcv_dev; } @@ -548,7 +607,7 @@ static struct dj_receiver_dev *dj_get_receiver_dev(struct hid_device *hdev, mutex_lock(&dj_hdev_list_lock); - djrcv_dev = dj_find_receiver_dev(hdev); + djrcv_dev = dj_find_receiver_dev(hdev, type); if (!djrcv_dev) { djrcv_dev = kzalloc(sizeof(*djrcv_dev), GFP_KERNEL); if (!djrcv_dev) @@ -878,6 +937,14 @@ static void logi_hidpp_recv_queue_notif(struct hid_device *hdev, switch (hidpp_report->params[HIDPP_PARAM_PROTO_TYPE]) { case 0x01: device_type = "Bluetooth"; + /* Bluetooth connect packet contents is the same as (e)QUAD */ + logi_hidpp_dev_conn_notif_equad(hidpp_report, &workitem); + if (!(hidpp_report->params[HIDPP_PARAM_DEVICE_INFO] & + HIDPP_MANUFACTURER_MASK)) { + hid_info(hdev, "Non Logitech device connected on slot %d\n", + hidpp_report->device_index); + workitem.reports_supported &= ~HIDPP; + } break; case 0x02: device_type = "27 Mhz"; @@ -1267,6 +1334,9 @@ static int logi_dj_ll_parse(struct hid_device *hid) else if (djdev->dj_receiver_dev->type == recvr_type_27mhz) rdcat(rdesc, &rsize, mse_27mhz_descriptor, sizeof(mse_27mhz_descriptor)); + else if (djdev->dj_receiver_dev->type == recvr_type_bluetooth) + rdcat(rdesc, &rsize, mse_bluetooth_descriptor, + sizeof(mse_bluetooth_descriptor)); else rdcat(rdesc, &rsize, mse_descriptor, sizeof(mse_descriptor)); @@ -1579,6 +1649,7 @@ static int logi_dj_probe(struct hid_device *hdev, case recvr_type_hidpp: no_dj_interfaces = 2; break; case recvr_type_gaming_hidpp: no_dj_interfaces = 3; break; case recvr_type_27mhz: no_dj_interfaces = 2; break; + case recvr_type_bluetooth: no_dj_interfaces = 2; break; } if (hid_is_using_ll_driver(hdev, &usb_hid_driver) && (intf = to_usb_interface(hdev->dev.parent)) && @@ -1766,6 +1837,14 @@ static const struct hid_device_id logi_dj_receivers[] = { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_27MHZ_MOUSE_RECEIVER), .driver_data = recvr_type_27mhz}, + { /* Logitech MX5000 HID++ / bluetooth receiver keyboard intf. */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, + 0xc70e), + .driver_data = recvr_type_bluetooth}, + { /* Logitech MX5000 HID++ / bluetooth receiver mouse intf. */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, + 0xc70a), + .driver_data = recvr_type_bluetooth}, {} };