From patchwork Wed Jan 18 14:30:50 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Phil Dennis-Jordan X-Patchwork-Id: 716680 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3v3Tv11lQ8z9sR9 for ; Thu, 19 Jan 2017 01:31:55 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=philjordan-eu.20150623.gappssmtp.com header.i=@philjordan-eu.20150623.gappssmtp.com header.b="useihQPm"; dkim-atps=neutral Received: from localhost ([::1]:41802 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cTrHE-0007dI-AM for incoming@patchwork.ozlabs.org; Wed, 18 Jan 2017 09:31:52 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:42276) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cTrGZ-0007JH-78 for qemu-devel@nongnu.org; Wed, 18 Jan 2017 09:31:12 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1cTrGU-0006Ka-GK for qemu-devel@nongnu.org; Wed, 18 Jan 2017 09:31:11 -0500 Received: from mail-wm0-x244.google.com ([2a00:1450:400c:c09::244]:36242) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1cTrGU-0006J5-5n for qemu-devel@nongnu.org; Wed, 18 Jan 2017 09:31:06 -0500 Received: by mail-wm0-x244.google.com with SMTP id r126so4475011wmr.3 for ; Wed, 18 Jan 2017 06:31:05 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=philjordan-eu.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id; bh=W6BUxwNSiyl/8OFPBp2VFuguCj0GMlKX4vwz4mWXnqU=; b=useihQPmdm4J2t3JEqk9eB7hR/vLjSm7U2tvuejCX8vLkyxKZBLTmpc0DQxJKAAH/7 vZQNMMcO/6UkGeSeHfNRvvyPi48a21+8PsR9phn1Ch9Icsmu01Y/DW4geg16nWMzU3jr dC3Hhe7iH6O78qpRfdUt1wQQsKmwT0fowDel+A1FXJjpoHXlOdx5Q6Dm5TfA31i6yGhp zMu0XddV6Vs2nLWNN5aDIGIsmxXcuNMzzr9UfNNoUe0ezsAIhzihp/CsMJbIiRwv2eRW /0Siul1EsQYHwdhd2ZfSUnPRj9x3youB4N0+eTzRLSpddg2ItF3eVo4wJAWk36A5acVL z4Cg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=W6BUxwNSiyl/8OFPBp2VFuguCj0GMlKX4vwz4mWXnqU=; b=Xf9OVfTuwjA7Vb/735iiXdw1xbj/Q9HInEEz5bzQEuhSt+TnPtFPiRtAn5HxQ+W3Lx HrLGKCp36FPywBH6ORBBdQAcbnQPy4xbRqHu2tM7nW+1nQZifYAqGzcTIo+tAszkPc5t 4co3HxwMXPpTo82CKI+cZyooHgbHDSioCyxh/jDX46e559uecjrBQjmh1mNDcfBXekWb ze5iGSNjQJHph/9Zn29rHG4K5wXKRCjDJhwNxZBSf5DblsdmmoJVsdTXrTe4kIEmpbP1 11ldgzlIbp8AeTD28Bpw1yut+tM/s+x2mf8ElArWCf5kE1dakCqoRplJmWTyUeANXJ1X rShQ== X-Gm-Message-State: AIkVDXK0qpgRj6kbWfwoXPjWWc3jriQyXqZDmM6WZeHXzi7aymaZHR6E7nv6lI4+m5DRlw== X-Received: by 10.223.171.65 with SMTP id r1mr3086645wrc.38.1484749864878; Wed, 18 Jan 2017 06:31:04 -0800 (PST) Received: from Phils-MacBook-Pro-57025.local.eu (37-186-10-86.ip.as39912.net. [37.186.10.86]) by smtp.gmail.com with ESMTPSA id u78sm45734900wma.11.2017.01.18.06.31.02 (version=TLS1 cipher=AES128-SHA bits=128/128); Wed, 18 Jan 2017 06:31:03 -0800 (PST) From: Phil Dennis-Jordan To: qemu-devel@nongnu.org Date: Wed, 18 Jan 2017 15:30:50 +0100 Message-Id: <1484749850-87206-1-git-send-email-phil@philjordan.eu> X-Mailer: git-send-email 2.3.2 (Apple Git-55) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 2a00:1450:400c:c09::244 Subject: [Qemu-devel] [PATCH] hw/usb/dev-hid: add a Mac guest compatibility option to usb-tablet X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Phil Dennis-Jordan , Gerd Hoffmann Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" Darwin/OS X/macOS's HID driver stack does not correctly drive Qemu's simulated USB Tablet. This adds a boolean option "mac_compat" which subtly changes the device so it behaves in a way that Mac guests can handle. The specific incompatibilities with the regular Qemu USB tablet are: 1. Absolute pointing devices with HID Report Descriptor usage page of 0x01 (pointing) are handled by the macOS HID driver as analog sticks, so the movement of the cursor ends up being the cumulative deviance from the centre position. 2. The bInterfaceProtocol of 0x02 enables a particular macOS HID driver mode which only works properly with mice (relative motion) not absolute pointing devices, so spurious events with relative coordinates are generated in addition to absolute ones. This manifests as a very jittery cursor. The workaround is to report a usage page of 0x02 (mouse) and a bInterfaceProtocol value of 0x00. Signed-off-by: Phil Dennis-Jordan --- hw/usb/dev-hid.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 2 deletions(-) diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c index 24d05f7..0f5b796 100644 --- a/hw/usb/dev-hid.c +++ b/hw/usb/dev-hid.c @@ -51,6 +51,7 @@ typedef struct USBHIDState { uint32_t usb_version; char *display; uint32_t head; + bool mac_compat; } USBHIDState; #define TYPE_USB_HID "usb-hid" @@ -200,6 +201,66 @@ static const USBDescIface desc_iface_tablet2 = { }, }; +static const USBDescIface desc_iface_tablet_mac_compat = { + .bInterfaceNumber = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceProtocol = 0x00, /* OSX/macOS can't handle 2 here */ + .ndesc = 1, + .descs = (USBDescOther[]) { + { + /* HID descriptor */ + .data = (uint8_t[]) { + 0x09, /* u8 bLength */ + USB_DT_HID, /* u8 bDescriptorType */ + 0x01, 0x00, /* u16 HID_class */ + 0x00, /* u8 country_code */ + 0x01, /* u8 num_descriptors */ + USB_DT_REPORT, /* u8 type: Report */ + 74, 0, /* u16 len */ + }, + }, + }, + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | 0x01, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 8, + .bInterval = 0x0a, + }, + }, +}; + +static const USBDescIface desc_iface_tablet_mac_compat2 = { + .bInterfaceNumber = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceProtocol = 0x00, /* OSX/macOS can't handle 2 here */ + .ndesc = 1, + .descs = (USBDescOther[]) { + { + /* HID descriptor */ + .data = (uint8_t[]) { + 0x09, /* u8 bLength */ + USB_DT_HID, /* u8 bDescriptorType */ + 0x01, 0x00, /* u16 HID_class */ + 0x00, /* u8 country_code */ + 0x01, /* u8 num_descriptors */ + USB_DT_REPORT, /* u8 type: Report */ + 74, 0, /* u16 len */ + }, + }, + }, + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | 0x01, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 8, + .bInterval = 4, /* 2 ^ (4-1) * 125 usecs = 1 ms */ + }, + }, +}; + static const USBDescIface desc_iface_keyboard = { .bInterfaceNumber = 0, .bNumEndpoints = 1, @@ -330,6 +391,40 @@ static const USBDescDevice desc_device_tablet2 = { }, }; +static const USBDescDevice desc_device_tablet_mac_compat = { + .bcdUSB = 0x0100, + .bMaxPacketSize0 = 8, + .bNumConfigurations = 1, + .confs = (USBDescConfig[]) { + { + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = STR_CONFIG_TABLET, + .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, + .bMaxPower = 50, + .nif = 1, + .ifs = &desc_iface_tablet_mac_compat, + }, + }, +}; + +static const USBDescDevice desc_device_tablet_mac_compat2 = { + .bcdUSB = 0x0200, + .bMaxPacketSize0 = 64, + .bNumConfigurations = 1, + .confs = (USBDescConfig[]) { + { + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = STR_CONFIG_TABLET, + .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, + .bMaxPower = 50, + .nif = 1, + .ifs = &desc_iface_tablet_mac_compat2, + }, + }, +}; + static const USBDescDevice desc_device_keyboard = { .bcdUSB = 0x0100, .bMaxPacketSize0 = 8, @@ -426,6 +521,35 @@ static const USBDesc desc_tablet2 = { .msos = &desc_msos_suspend, }; +static const USBDesc desc_tablet_mac_compat = { + .id = { + .idVendor = 0x0627, + .idProduct = 0x0002, + .bcdDevice = 0, + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT_TABLET, + .iSerialNumber = STR_SERIALNUMBER, + }, + .full = &desc_device_tablet_mac_compat, + .str = desc_strings, + .msos = &desc_msos_suspend, +}; + +static const USBDesc desc_tablet_mac_compat2 = { + .id = { + .idVendor = 0x0627, + .idProduct = 0x0002, + .bcdDevice = 0, + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT_TABLET, + .iSerialNumber = STR_SERIALNUMBER, + }, + .full = &desc_device_tablet_mac_compat, + .high = &desc_device_tablet_mac_compat2, + .str = desc_strings, + .msos = &desc_msos_suspend, +}; + static const USBDesc desc_keyboard = { .id = { .idVendor = 0x0627, @@ -599,6 +723,9 @@ static void usb_hid_handle_control(USBDevice *dev, USBPacket *p, memcpy(data, qemu_tablet_hid_report_descriptor, sizeof(qemu_tablet_hid_report_descriptor)); p->actual_length = sizeof(qemu_tablet_hid_report_descriptor); + if (us->mac_compat) { + data[3] = 0x02; /* Set usage to mouse, not pointing (1) */ + } } else if (hs->kind == HID_KEYBOARD) { memcpy(data, qemu_keyboard_hid_report_descriptor, sizeof(qemu_keyboard_hid_report_descriptor)); @@ -731,8 +858,14 @@ static void usb_hid_initfn(USBDevice *dev, int kind, static void usb_tablet_realize(USBDevice *dev, Error **errp) { - - usb_hid_initfn(dev, HID_TABLET, &desc_tablet, &desc_tablet2, errp); + USBHIDState *us = USB_HID(dev); + if (us->mac_compat) { + usb_hid_initfn( + dev, HID_TABLET, + &desc_tablet_mac_compat, &desc_tablet_mac_compat2, errp); + } else { + usb_hid_initfn(dev, HID_TABLET, &desc_tablet, &desc_tablet2, errp); + } } static void usb_mouse_realize(USBDevice *dev, Error **errp) @@ -801,6 +934,7 @@ static Property usb_tablet_properties[] = { DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2), DEFINE_PROP_STRING("display", USBHIDState, display), DEFINE_PROP_UINT32("head", USBHIDState, head, 0), + DEFINE_PROP_BOOL("mac_compat", USBHIDState, mac_compat, false), DEFINE_PROP_END_OF_LIST(), };