All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/5] Add high-resolution 2D mouse wheel support to USB HID
@ 2026-03-31 17:08 Soumyajyotii Ssarkar
  2026-03-31 17:08 ` [RFC PATCH 1/5] input/hid: add horizontal pan to pointer event path Soumyajyotii Ssarkar
                   ` (4 more replies)
  0 siblings, 5 replies; 14+ messages in thread
From: Soumyajyotii Ssarkar @ 2026-03-31 17:08 UTC (permalink / raw)
  To: qemu-devel
  Cc: Richard Henderson, Paolo Bonzini, Soumyajyotii Ssarkar,
	Michael Tokarev, Thomas Lambertz, Philippe Mathieu-Daudé,
	Alex Golovko, ShengYi Hung, Soumyajyotii Ssarkar

This RFC series adds high resolution vertical and horizontal wheel support
to QEMU's USB HID mouse device.

The current USB HID mouse implementation only reports fixed wheel deltas and
does not support horizontal scrolling.
Modern touchpads and trackpoints can generate finer grained wheel movement
and simultaneous horizontal & vertical scrolling,
however guests currently cannot make use of these capabilities.

Windows Vista onward their has been support for
Microsoft's high resolution wheel protocol,  including resolution multipliers
and horizontal scrolling via AC Pan usage.

This series updates the USB HID mouse descriptor and report format to support:
- High-resolution vertical wheel events
- Horizontal wheel / AC Pan events
- Resolution multiplier feature reports
- Simultaneous vertical and horizontal scrolling
- Boot protocol compatibility
The enhanced report protocol uses Report ID 1 for pointer input reports and
Report ID 2 for wheel resolution feature reports. Moreover, vertical wheel scaling and
horizontal pan scaling are preserved across VM migration using residual state.

With this change, guests can receive smaller wheel deltas
such as 25 units instead of only legacy 120-unit wheel notches,
resulting in smoother scrolling behavior in Windows and Linux guests.

Testing was performed using Windows 10 guest and Microsoft's wheel event viewer:
https://domeventviewer.com/wheel-event-viewer.html

Observed behavior:
- Without patch: wheel delta values appear as coarse 120-unit steps
- With patch: wheel delta values appear as smaller increments
- Horizontal scrolling events are reported correctly

Reference documentation:
https://download.microsoft.com/download/b/d/1/bd1f7ef4-7d72-419e-bc5c-9f79ad7bb66e/wheel.docx

Signed-off-by: Soumyajyotii Ssarkar <soumyajyotisarkar23@gmail.com>

Soumyajyotii Ssarkar (5):
  input/hid: add horizontal pan to pointer event path
  usb/hid: add enhanced wheel mouse report descriptor and packet size
  input/hid: emit protocol aware mouse reports with pan support
  usb/hid: handle mouse feature report for wheel/pan multipliers
  input/hid: apply multiplier scaling with residual accumulation

 hw/input/hid.c         | 117 ++++++++++++++++++++++---
 hw/usb/dev-hid.c       | 191 ++++++++++++++++++++++++++++++++---------
 include/hw/input/hid.h |   5 ++
 3 files changed, 262 insertions(+), 51 deletions(-)

--
2.53.0



^ permalink raw reply	[flat|nested] 14+ messages in thread

* [RFC PATCH 1/5] input/hid: add horizontal pan to pointer event path
  2026-03-31 17:08 [RFC PATCH 0/5] Add high-resolution 2D mouse wheel support to USB HID Soumyajyotii Ssarkar
@ 2026-03-31 17:08 ` Soumyajyotii Ssarkar
  2026-09-27 17:06   ` Helge Deller
  2026-03-31 17:08 ` [RFC PATCH 2/5] usb/hid: add enhanced wheel mouse report descriptor and packet size Soumyajyotii Ssarkar
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 14+ messages in thread
From: Soumyajyotii Ssarkar @ 2026-03-31 17:08 UTC (permalink / raw)
  To: qemu-devel
  Cc: Richard Henderson, Paolo Bonzini, Soumyajyotii Ssarkar,
	Michael Tokarev, Thomas Lambertz, Philippe Mathieu-Daudé,
	Alex Golovko, ShengYi Hung, Soumyajyotii Ssarkar

Add support for horizontal wheel scrolling (pan) in the HID mouse
implementation. Extend the HIDPointerEvent structure with "pan" field
to track horizontal scroll deltas, similar to the existing 'dz' field
for vertical scrolling.

Map INPUT_BUTTON_WHEEL_LEFT and INPUT_BUTTON_WHEEL_RIGHT to pan delta
changes in the event handler. Integrate pan field handling into event
compression logic and queue management to ensure consistent behavior with
vertical wheel events.

This changes enables subsequent patches to implement
multiplier based scaling and protocol aware reporting for horizontal
scrolling.

Signed-off-by: Soumyajyotii Ssarkar <soumyajyotisarkar23@gmail.com>
---
 hw/input/hid.c         | 13 ++++++++++++-
 include/hw/input/hid.h |  1 +
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/hw/input/hid.c b/hw/input/hid.c
index de24cd0ef0..457dbb0096 100644
--- a/hw/input/hid.c
+++ b/hw/input/hid.c
@@ -152,6 +152,10 @@ static void hid_pointer_event(DeviceState *dev, QemuConsole *src,
                 e->dz--;
             } else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) {
                 e->dz++;
+            } else if (btn->button == INPUT_BUTTON_WHEEL_LEFT) {
+                e->pan--;
+            } else if (btn->button == INPUT_BUTTON_WHEEL_RIGHT) {
+                e->pan++;
             }
         } else {
             e->buttons_state &= ~bmap[btn->button];
@@ -208,6 +212,8 @@ static void hid_pointer_sync(DeviceState *dev)
         }
         prev->dz += curr->dz;
         curr->dz = 0;
+        prev->pan += curr->pan;
+        curr->pan = 0;
     } else {
         /* prepare next (clear rel, copy abs + btns) */
         if (hs->kind == HID_MOUSE) {
@@ -218,6 +224,7 @@ static void hid_pointer_sync(DeviceState *dev)
             next->ydy = curr->ydy;
         }
         next->dz = 0;
+        next->pan = 0;
         next->buttons_state = curr->buttons_state;
         /* make current guest visible, notify guest */
         hs->n++;
@@ -357,7 +364,7 @@ void hid_pointer_activate(HIDState *hs)

 int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
 {
-    int dx, dy, dz, l;
+    int dx, dy, dz, pan, l;
     int index;
     HIDPointerEvent *e;

@@ -381,9 +388,13 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
     }
     dz = int_clamp(e->dz, -127, 127);
     e->dz -= dz;
+    pan = int_clamp(e->pan, -127, 127);
+    e->pan -= pan;
+

     if (hs->n &&
         !e->dz &&
+        !e->pan &&
         (hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) {
         /* that deals with this event */
         QUEUE_INCR(hs->head);
diff --git a/include/hw/input/hid.h b/include/hw/input/hid.h
index 6a9d7bf466..1bb1778ee7 100644
--- a/include/hw/input/hid.h
+++ b/include/hw/input/hid.h
@@ -10,6 +10,7 @@
 typedef struct HIDPointerEvent {
     int32_t xdx, ydy; /* relative iff it's a mouse, otherwise absolute */
     int32_t dz, buttons_state;
+    int32_t pan;
 } HIDPointerEvent;

 #define QUEUE_LENGTH    16 /* should be enough for a triple-click */
--
2.53.0



^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH 2/5] usb/hid: add enhanced wheel mouse report descriptor and packet size
  2026-03-31 17:08 [RFC PATCH 0/5] Add high-resolution 2D mouse wheel support to USB HID Soumyajyotii Ssarkar
  2026-03-31 17:08 ` [RFC PATCH 1/5] input/hid: add horizontal pan to pointer event path Soumyajyotii Ssarkar
@ 2026-03-31 17:08 ` Soumyajyotii Ssarkar
  2026-09-27 17:11   ` Helge Deller
  2026-09-28  8:31   ` Alex Bennée
  2026-03-31 17:08 ` [RFC PATCH 3/5] input/hid: emit protocol aware mouse reports with pan support Soumyajyotii Ssarkar
                   ` (2 subsequent siblings)
  4 siblings, 2 replies; 14+ messages in thread
From: Soumyajyotii Ssarkar @ 2026-03-31 17:08 UTC (permalink / raw)
  To: qemu-devel
  Cc: Richard Henderson, Paolo Bonzini, Soumyajyotii Ssarkar,
	Michael Tokarev, Thomas Lambertz, Philippe Mathieu-Daudé,
	Alex Golovko, ShengYi Hung, Soumyajyotii Ssarkar

Build on top of the simple mouse HID report descriptor with an enhanced version
that supports report IDs and feature reports for high resolution mouse
wheel control. As per the documentation structure.

Check: https://download.microsoft.com/download/b/d/1/bd1f7ef4-7d72-419e-bc5c-9f79ad7bb66e/wheel.docx

Changes:
- Increase HID descriptor length from 52 to 144 bytes
- Add report IDs (0x01 input, 0x02 feature)
- Implement feature reports for wheel/pan resolution multipliers
- Add AC Pan (horizontal scroll) usage
- Increase endpoint packet size from 4 to 6 bytes for pan byte

This enables guests to configure resolution multipliers (+-1, +-4, +-8, +-16)
to control scroll acceleration and precision.

Signed-off-by: Soumyajyotii Ssarkar <soumyajyotisarkar23@gmail.com>
---
 hw/usb/dev-hid.c | 126 +++++++++++++++++++++++++++++++++++------------
 1 file changed, 95 insertions(+), 31 deletions(-)

diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c
index ae19d60203..49e23c2319 100644
--- a/hw/usb/dev-hid.c
+++ b/hw/usb/dev-hid.c
@@ -93,7 +93,7 @@ static const USBDescIface desc_iface_mouse = {
                 0x00,          /*  u8  country_code */
                 0x01,          /*  u8  num_descriptors */
                 USB_DT_REPORT, /*  u8  type: Report */
-                52, 0,         /*  u16 len */
+                144, 0,        /*  u16 len */
             },
         },
     },
@@ -101,7 +101,7 @@ static const USBDescIface desc_iface_mouse = {
         {
             .bEndpointAddress      = USB_DIR_IN | 0x01,
             .bmAttributes          = USB_ENDPOINT_XFER_INT,
-            .wMaxPacketSize        = 4,
+            .wMaxPacketSize        = 6,
             .bInterval             = 0x0a,
         },
     },
@@ -124,7 +124,7 @@ static const USBDescIface desc_iface_mouse2 = {
                 0x00,          /*  u8  country_code */
                 0x01,          /*  u8  num_descriptors */
                 USB_DT_REPORT, /*  u8  type: Report */
-                52, 0,         /*  u16 len */
+                144, 0,        /*  u16 len */
             },
         },
     },
@@ -132,7 +132,7 @@ static const USBDescIface desc_iface_mouse2 = {
         {
             .bEndpointAddress      = USB_DIR_IN | 0x01,
             .bmAttributes          = USB_ENDPOINT_XFER_INT,
-            .wMaxPacketSize        = 4,
+            .wMaxPacketSize        = 6,
             .bInterval             = 7, /* 2 ^ (8-1) * 125 usecs = 8 ms */
         },
     },
@@ -454,33 +454,97 @@ static const USBDesc desc_keyboard2 = {
 };

 static const uint8_t qemu_mouse_hid_report_descriptor[] = {
-    0x05, 0x01,		/* Usage Page (Generic Desktop) */
-    0x09, 0x02,		/* Usage (Mouse) */
-    0xa1, 0x01,		/* Collection (Application) */
-    0x09, 0x01,		/*   Usage (Pointer) */
-    0xa1, 0x00,		/*   Collection (Physical) */
-    0x05, 0x09,		/*     Usage Page (Button) */
-    0x19, 0x01,		/*     Usage Minimum (1) */
-    0x29, 0x05,		/*     Usage Maximum (5) */
-    0x15, 0x00,		/*     Logical Minimum (0) */
-    0x25, 0x01,		/*     Logical Maximum (1) */
-    0x95, 0x05,		/*     Report Count (5) */
-    0x75, 0x01,		/*     Report Size (1) */
-    0x81, 0x02,		/*     Input (Data, Variable, Absolute) */
-    0x95, 0x01,		/*     Report Count (1) */
-    0x75, 0x03,		/*     Report Size (3) */
-    0x81, 0x01,		/*     Input (Constant) */
-    0x05, 0x01,		/*     Usage Page (Generic Desktop) */
-    0x09, 0x30,		/*     Usage (X) */
-    0x09, 0x31,		/*     Usage (Y) */
-    0x09, 0x38,		/*     Usage (Wheel) */
-    0x15, 0x81,		/*     Logical Minimum (-0x7f) */
-    0x25, 0x7f,		/*     Logical Maximum (0x7f) */
-    0x75, 0x08,		/*     Report Size (8) */
-    0x95, 0x03,		/*     Report Count (3) */
-    0x81, 0x06,		/*     Input (Data, Variable, Relative) */
-    0xc0,		/*   End Collection */
-    0xc0,		/* End Collection */
+    0x05, 0x01,     /* Usage Page (Generic Desktop) */
+    0x09, 0x02,     /* Usage (Mouse) */
+    0xA1, 0x01,     /* Collection (Application) */
+
+    0x09, 0x02,     /* Usage (Mouse) */
+    0xA1, 0x02,     /* Collection (Logical) */
+
+    0x85, 0x01,     /* Report ID (0x01) */
+    0x09, 0x01,     /* Usage (Pointer) */
+    0xA1, 0x00,     /* Collection (Physical) */
+
+    0x05, 0x09,     /* Usage Page (Button) */
+    0x19, 0x01,     /* Usage Minimum (1) */
+    0x29, 0x05,     /* Usage Maximum (5) */
+    0x15, 0x00,     /* Logical Minimum (0) */
+    0x25, 0x01,     /* Logical Maximum (1) */
+    0x95, 0x05,     /* Report Count (5) */
+    0x75, 0x01,     /* Report Size (1) */
+    0x81, 0x02,     /* Input (Data,Var,Abs) */
+
+    0x95, 0x01,     /*  Report Count (1) */
+    0x75, 0x03,     /*  Report Size (3) */
+    0x81, 0x01,     /*  Input (Const) */
+
+    0x05, 0x01,     /*  Usage Page (Generic Desktop) */
+    0x09, 0x30,     /*  Usage (X) */
+    0x09, 0x31,     /*  Usage (Y) */
+    0x95, 0x02,     /*  Report Count (2) */
+    0x75, 0x08,     /*  Report Size (8) */
+    0x15, 0x81,     /*  Logical Minimum (-127) */
+    0x25, 0x7F,     /*  Logical Maximum (127) */
+    0x81, 0x06,     /*  Input (Data,Var,Rel) */
+
+    /* Vertical wheel logical collection */
+    0xA1, 0x02,     /*  Collection (Logical) */
+
+    0x85, 0x02,     /*  Report ID (0x02) */
+    0x09, 0x48,     /*  Usage (Resolution Multiplier) */
+    0x15, 0x00,     /*  Logical Minimum (0) */
+    0x25, 0x01,     /*  Logical Maximum (1) */
+    0x35, 0x01,     /*  Physical Minimum (1) */
+    0x45, 0x04,     /*  Physical Maximum (4) */
+    0x75, 0x02,     /*  Report Size (2) */
+    0x95, 0x01,     /*  Report Count (1) */
+    0xB1, 0x02,     /*  Feature (Data,Var,Abs) */
+
+    0x85, 0x01,     /*  Report ID (0x01) */
+    0x09, 0x38,     /*  Usage (Wheel) */
+    0x35, 0x00,     /*  Physical Minimum (0) */
+    0x45, 0x00,     /*  Physical Maximum (0) */
+    0x15, 0x81,     /*  Logical Minimum (-0x7f) */
+    0x25, 0x7F,     /*  Logical Maximum (0x7f) */
+    0x75, 0x08,     /*  Report Size (8) */
+    0x95, 0x01,     /*  Report Count (1) */
+    0x81, 0x06,     /*  Input (Data,Var,Rel) */
+
+    0xC0,           /*  End Collection */
+
+    /* Horizontal wheel logical collection */
+    0xA1, 0x02,     /*  Collection (Logical) */
+
+    0x85, 0x02,     /*  Report ID (0x02) */
+    0x09, 0x48,     /*  Usage (Resolution Multiplier) */
+    0x15, 0x00,     /*  Logical Minimum (0) */
+    0x25, 0x01,     /*  Logical Maximum (1) */
+    0x35, 0x01,     /*  Physical Minimum (1) */
+    0x45, 0x04,     /*  Physical Maximum (4) */
+    0x75, 0x02,     /*  Report Size (2) */
+    0x95, 0x01,     /*  Report Count (1) */
+    0xB1, 0x02,     /*  Feature (Data,Var,Abs) */
+
+    0x35, 0x00,     /*  Physical Minimum (0) */
+    0x45, 0x00,     /*  Physical Maximum (0) */
+    0x75, 0x04,     /*  Report Size (4) */
+    0x95, 0x01,     /*  Report Count (1) */
+    0xB1, 0x01,     /*  Feature (Const) */
+
+    0x85, 0x01,     /*  Report ID (0x01) */
+    0x05, 0x0C,     /*  Usage Page (Consumer Devices) */
+    0x0A, 0x38, 0x02,   /*  Usage (AC Pan) */
+    0x15, 0x81,     /*  Logical Minimum (-0x7f) */
+    0x25, 0x7F,     /*  Logical Maximum (0x7f) */
+    0x75, 0x08,     /*  Report Size (8) */
+    0x95, 0x01,     /*  Report Count (1) */
+    0x81, 0x06,     /*  Input (Data,Var,Rel) */
+
+    0xC0,           /*  End Collection */
+
+    0xC0,           /*  End Collection (Physical) */
+    0xC0,           /*  End Collection (Logical) */
+    0xC0            /*  End Collection (Application) */
 };

 static const uint8_t qemu_tablet_hid_report_descriptor[] = {
--
2.53.0



^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH 3/5] input/hid: emit protocol aware mouse reports with pan support
  2026-03-31 17:08 [RFC PATCH 0/5] Add high-resolution 2D mouse wheel support to USB HID Soumyajyotii Ssarkar
  2026-03-31 17:08 ` [RFC PATCH 1/5] input/hid: add horizontal pan to pointer event path Soumyajyotii Ssarkar
  2026-03-31 17:08 ` [RFC PATCH 2/5] usb/hid: add enhanced wheel mouse report descriptor and packet size Soumyajyotii Ssarkar
@ 2026-03-31 17:08 ` Soumyajyotii Ssarkar
  2026-09-27 17:14   ` Helge Deller
  2026-03-31 17:08 ` [RFC PATCH 4/5] usb/hid: handle mouse feature report for wheel/pan multipliers Soumyajyotii Ssarkar
  2026-03-31 17:08 ` [RFC PATCH 5/5] input/hid: apply multiplier scaling with residual accumulation Soumyajyotii Ssarkar
  4 siblings, 1 reply; 14+ messages in thread
From: Soumyajyotii Ssarkar @ 2026-03-31 17:08 UTC (permalink / raw)
  To: qemu-devel
  Cc: Richard Henderson, Paolo Bonzini, Soumyajyotii Ssarkar,
	Michael Tokarev, Thomas Lambertz, Philippe Mathieu-Daudé,
	Alex Golovko, ShengYi Hung, Soumyajyotii Ssarkar

Implement protocol-aware mouse report output to support both boot
protocol (protocol 0) and report protocol (protocol 1).

Boot protocol: 4-byte reports (button, dx, dy, dz)
Report protocol: 6-byte reports with report ID 0x01 + pan byte

Both modes now support the 'pan' field for horizontal scrolling. The
distinction ensures correct behavior across different guest operating
systems and their HID implementations.

Signed-off-by: Soumyajyotii Ssarkar <soumyajyotisarkar23@gmail.com>
---
 hw/input/hid.c | 44 +++++++++++++++++++++++++++++++++-----------
 1 file changed, 33 insertions(+), 11 deletions(-)

diff --git a/hw/input/hid.c b/hw/input/hid.c
index 457dbb0096..d0b7af7c13 100644
--- a/hw/input/hid.c
+++ b/hw/input/hid.c
@@ -403,20 +403,42 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)

     /* Appears we have to invert the wheel direction */
     dz = 0 - dz;
+    pan = 0 - pan;
     l = 0;
     switch (hs->kind) {
     case HID_MOUSE:
-        if (len > l) {
-            buf[l++] = e->buttons_state;
-        }
-        if (len > l) {
-            buf[l++] = dx;
-        }
-        if (len > l) {
-            buf[l++] = dy;
-        }
-        if (len > l) {
-            buf[l++] = dz;
+        if (hs->protocol == 0) {
+            if (len > l) {
+                buf[l++] = e->buttons_state;
+            }
+            if (len > l) {
+                buf[l++] = dx;
+            }
+            if (len > l) {
+                buf[l++] = dy;
+            }
+            if (len > l) {
+                buf[l++] = dz;
+            }
+        } else {
+            if (len > l) {
+                buf[l++] = 0x01;
+            }
+            if (len > l) {
+                buf[l++] = e->buttons_state;
+            }
+            if (len > l) {
+                buf[l++] = dx;
+            }
+            if (len > l) {
+                buf[l++] = dy;
+            }
+            if (len > l) {
+                buf[l++] = dz;
+            }
+            if (len > l) {
+                buf[l++] = pan;
+            }
         }
         break;

--
2.53.0



^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH 4/5] usb/hid: handle mouse feature report for wheel/pan multipliers
  2026-03-31 17:08 [RFC PATCH 0/5] Add high-resolution 2D mouse wheel support to USB HID Soumyajyotii Ssarkar
                   ` (2 preceding siblings ...)
  2026-03-31 17:08 ` [RFC PATCH 3/5] input/hid: emit protocol aware mouse reports with pan support Soumyajyotii Ssarkar
@ 2026-03-31 17:08 ` Soumyajyotii Ssarkar
  2026-09-27 19:58   ` Helge Deller
  2026-03-31 17:08 ` [RFC PATCH 5/5] input/hid: apply multiplier scaling with residual accumulation Soumyajyotii Ssarkar
  4 siblings, 1 reply; 14+ messages in thread
From: Soumyajyotii Ssarkar @ 2026-03-31 17:08 UTC (permalink / raw)
  To: qemu-devel
  Cc: Richard Henderson, Paolo Bonzini, Soumyajyotii Ssarkar,
	Michael Tokarev, Thomas Lambertz, Philippe Mathieu-Daudé,
	Alex Golovko, ShengYi Hung, Soumyajyotii Ssarkar

Implement HID feature report handlers (GET_REPORT/SET_REPORT) for
report ID 0x02 to allow guests to configure wheel/pan resolution
multipliers.

Multiplier encoding:
- Bits [1:0]: wheel_multiplier (0=+-1, 1=+-4, 2=+-8, 3=+-16)
- Bits [3:2]: pan_multiplier (0=+-1, 1=+-4, 2=+-8, 3=+-16)

Supports flexible payload formats (with/without report ID byte).
Allows Windows guests to dynamically adjust scroll sensitivity without
driver installation.

Signed-off-by: Soumyajyotii Ssarkar <soumyajyotisarkar23@gmail.com>
---
 hw/usb/dev-hid.c       | 65 ++++++++++++++++++++++++++++++++++++------
 include/hw/input/hid.h |  2 ++
 2 files changed, 59 insertions(+), 8 deletions(-)

diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c
index 49e23c2319..cdcf2a4a0e 100644
--- a/hw/usb/dev-hid.c
+++ b/hw/usb/dev-hid.c
@@ -672,17 +672,66 @@ static void usb_hid_handle_control(USBDevice *dev, USBPacket *p,
         }
         break;
     case HID_GET_REPORT:
-        if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
-            p->actual_length = hid_pointer_poll(hs, data, length);
-        } else if (hs->kind == HID_KEYBOARD) {
-            p->actual_length = hid_keyboard_poll(hs, data, length);
+        {
+            uint8_t mult;
+            uint8_t report_type = (value >> 8) & 0xFF;
+            uint8_t report_id = value & 0xFF;
+
+            if (report_type == 0x03 && report_id == 0x02 &&
+                hs->kind == HID_MOUSE) {
+
+                if (length < 1) {
+                    goto fail;
+                }
+
+                mult = ((hs->ptr.pan_multiplier & 0x03) << 2) |
+                       (hs->ptr.wheel_multiplier & 0x03);
+                if (length >= 2) {
+                    data[0] = 0x02;
+                    data[1] = mult;
+                    p->actual_length = 2;
+                } else {
+                    data[0] = mult;
+                    p->actual_length = 1;
+                }
+
+            } else if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
+                p->actual_length = hid_pointer_poll(hs, data, length);
+            } else if (hs->kind == HID_KEYBOARD) {
+                p->actual_length = hid_keyboard_poll(hs, data, length);
+            } else {
+                goto fail;
+            }
         }
         break;
     case HID_SET_REPORT:
-        if (hs->kind == HID_KEYBOARD) {
-            p->actual_length = hid_keyboard_write(hs, data, length);
-        } else {
-            goto fail;
+        {
+            uint8_t mult;
+            uint8_t report_type = (value >> 8) & 0xFF;
+            uint8_t report_id = value & 0xFF;
+
+            if (report_type == 0x03 && report_id == 0x02 &&
+                hs->kind == HID_MOUSE) {
+
+                if (length < 1) {
+                    goto fail;
+                }
+
+                if (length >= 2 && data[0] == 0x02) {
+                    mult = data[1];
+                } else {
+                    mult = data[0];
+                }
+
+                hs->ptr.wheel_multiplier = mult & 0x03;
+                hs->ptr.pan_multiplier = (mult >> 2) & 0x03;
+                p->actual_length = length;
+
+            } else if (hs->kind == HID_KEYBOARD) {
+                p->actual_length = hid_keyboard_write(hs, data, length);
+            } else {
+                goto fail;
+            }
         }
         break;
     case HID_GET_PROTOCOL:
diff --git a/include/hw/input/hid.h b/include/hw/input/hid.h
index 1bb1778ee7..1a1941d371 100644
--- a/include/hw/input/hid.h
+++ b/include/hw/input/hid.h
@@ -23,6 +23,8 @@ typedef void (*HIDEventFunc)(HIDState *s);
 typedef struct HIDMouseState {
     HIDPointerEvent queue[QUEUE_LENGTH];
     int mouse_grabbed;
+    uint8_t wheel_multiplier;
+    uint8_t pan_multiplier;
 } HIDMouseState;

 typedef struct HIDKeyboardState {
--
2.53.0



^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH 5/5] input/hid: apply multiplier scaling with residual accumulation
  2026-03-31 17:08 [RFC PATCH 0/5] Add high-resolution 2D mouse wheel support to USB HID Soumyajyotii Ssarkar
                   ` (3 preceding siblings ...)
  2026-03-31 17:08 ` [RFC PATCH 4/5] usb/hid: handle mouse feature report for wheel/pan multipliers Soumyajyotii Ssarkar
@ 2026-03-31 17:08 ` Soumyajyotii Ssarkar
  2026-09-27 20:21   ` Helge Deller
  2026-09-28 14:00   ` Alex Bennée
  4 siblings, 2 replies; 14+ messages in thread
From: Soumyajyotii Ssarkar @ 2026-03-31 17:08 UTC (permalink / raw)
  To: qemu-devel
  Cc: Richard Henderson, Paolo Bonzini, Soumyajyotii Ssarkar,
	Michael Tokarev, Thomas Lambertz, Philippe Mathieu-Daudé,
	Alex Golovko, ShengYi Hung, Soumyajyotii Ssarkar

Implement multiplier-based scaling with residual accumulation for
fine-grained mouse wheel and pan sensitivity control in report mode.

Applies divisor based scaling (1, 4, 8, 16) based on negotiated
multiplier values.
Then uses residual based approach to prevent precision loss:

  residual += delta
  output = residual / divisor
  residual -= output * divisor

Residual state preserved across VM migrations via VMState.

Signed-off-by: Soumyajyotii Ssarkar <soumyajyotisarkar23@gmail.com>
---
 hw/input/hid.c         | 60 ++++++++++++++++++++++++++++++++++++++++++
 include/hw/input/hid.h |  2 ++
 2 files changed, 62 insertions(+)

diff --git a/hw/input/hid.c b/hw/input/hid.c
index d0b7af7c13..9b800faeb0 100644
--- a/hw/input/hid.c
+++ b/hw/input/hid.c
@@ -365,6 +365,7 @@ void hid_pointer_activate(HIDState *hs)
 int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
 {
     int dx, dy, dz, pan, l;
+    int wheel_divisor, pan_divisor;
     int index;
     HIDPointerEvent *e;

@@ -392,6 +393,54 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
     e->pan -= pan;


+    if (hs->kind == HID_MOUSE && hs->protocol != 0) {
+        switch (hs->ptr.wheel_multiplier & 0x03) {
+        case 0:
+            wheel_divisor = 1;
+            break;
+        case 1:
+            wheel_divisor = 4;
+            break;
+        case 2:
+            wheel_divisor = 8;
+            break;
+        default:
+            wheel_divisor = 16;
+            break;
+        }
+
+        switch (hs->ptr.pan_multiplier & 0x03) {
+        case 0:
+            pan_divisor = 1;
+            break;
+        case 1:
+            pan_divisor = 4;
+            break;
+        case 2:
+            pan_divisor = 8;
+            break;
+        default:
+            pan_divisor = 16;
+            break;
+        }
+
+        if (wheel_divisor > 1) {
+            hs->ptr.wheel_residual += dz;
+            dz = hs->ptr.wheel_residual / wheel_divisor;
+            hs->ptr.wheel_residual -= dz * wheel_divisor;
+        } else {
+            hs->ptr.wheel_residual = 0;
+        }
+
+        if (pan_divisor > 1) {
+            hs->ptr.pan_residual += pan;
+            pan = hs->ptr.pan_residual / pan_divisor;
+            hs->ptr.pan_residual -= pan * pan_divisor;
+        } else {
+            hs->ptr.pan_residual = 0;
+        }
+    }
+
     if (hs->n &&
         !e->dz &&
         !e->pan &&
@@ -405,6 +454,7 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
     dz = 0 - dz;
     pan = 0 - pan;
     l = 0;
+
     switch (hs->kind) {
     case HID_MOUSE:
         if (hs->protocol == 0) {
@@ -528,6 +578,11 @@ void hid_reset(HIDState *hs)
     case HID_MOUSE:
     case HID_TABLET:
         memset(hs->ptr.queue, 0, sizeof(hs->ptr.queue));
+        hs->ptr.mouse_grabbed = 0;
+        hs->ptr.wheel_multiplier = 0;
+        hs->ptr.pan_multiplier = 0;
+        hs->ptr.wheel_residual = 0;
+        hs->ptr.pan_residual = 0;
         break;
     }
     hs->head = 0;
@@ -619,6 +674,7 @@ static const VMStateDescription vmstate_hid_ptr_queue = {
         VMSTATE_INT32(xdx, HIDPointerEvent),
         VMSTATE_INT32(ydy, HIDPointerEvent),
         VMSTATE_INT32(dz, HIDPointerEvent),
+        VMSTATE_INT32(pan, HIDPointerEvent),
         VMSTATE_INT32(buttons_state, HIDPointerEvent),
         VMSTATE_END_OF_LIST()
     }
@@ -636,6 +692,10 @@ const VMStateDescription vmstate_hid_ptr_device = {
         VMSTATE_UINT32(n, HIDState),
         VMSTATE_INT32(protocol, HIDState),
         VMSTATE_UINT8(idle, HIDState),
+        VMSTATE_UINT8(ptr.wheel_multiplier, HIDState),
+        VMSTATE_UINT8(ptr.pan_multiplier, HIDState),
+        VMSTATE_INT32(ptr.wheel_residual, HIDState),
+        VMSTATE_INT32(ptr.pan_residual, HIDState),
         VMSTATE_END_OF_LIST(),
     }
 };
diff --git a/include/hw/input/hid.h b/include/hw/input/hid.h
index 1a1941d371..db902fa023 100644
--- a/include/hw/input/hid.h
+++ b/include/hw/input/hid.h
@@ -25,6 +25,8 @@ typedef struct HIDMouseState {
     int mouse_grabbed;
     uint8_t wheel_multiplier;
     uint8_t pan_multiplier;
+    int32_t wheel_residual;
+    int32_t pan_residual;
 } HIDMouseState;

 typedef struct HIDKeyboardState {
--
2.53.0



^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH 1/5] input/hid: add horizontal pan to pointer event path
  2026-03-31 17:08 ` [RFC PATCH 1/5] input/hid: add horizontal pan to pointer event path Soumyajyotii Ssarkar
@ 2026-09-27 17:06   ` Helge Deller
  0 siblings, 0 replies; 14+ messages in thread
From: Helge Deller @ 2026-09-27 17:06 UTC (permalink / raw)
  To: Soumyajyotii Ssarkar, qemu-devel
  Cc: Richard Henderson, Paolo Bonzini, Soumyajyotii Ssarkar,
	Michael Tokarev, Thomas Lambertz, Philippe Mathieu-Daudé,
	Alex Golovko, ShengYi Hung

On 3/31/26 19:08, Soumyajyotii Ssarkar wrote:
> Add support for horizontal wheel scrolling (pan) in the HID mouse
> implementation. Extend the HIDPointerEvent structure with "pan" field
> to track horizontal scroll deltas, similar to the existing 'dz' field
> for vertical scrolling.
> 
> Map INPUT_BUTTON_WHEEL_LEFT and INPUT_BUTTON_WHEEL_RIGHT to pan delta
> changes in the event handler. Integrate pan field handling into event
> compression logic and queue management to ensure consistent behavior with
> vertical wheel events.
> 
> This changes enables subsequent patches to implement
> multiplier based scaling and protocol aware reporting for horizontal
> scrolling.
> 
> Signed-off-by: Soumyajyotii Ssarkar <soumyajyotisarkar23@gmail.com>

Reviewed-by: Helge Deller <deller@gmx.de>


> ---
>   hw/input/hid.c         | 13 ++++++++++++-
>   include/hw/input/hid.h |  1 +
>   2 files changed, 13 insertions(+), 1 deletion(-)
> 
> diff --git a/hw/input/hid.c b/hw/input/hid.c
> index de24cd0ef0..457dbb0096 100644
> --- a/hw/input/hid.c
> +++ b/hw/input/hid.c
> @@ -152,6 +152,10 @@ static void hid_pointer_event(DeviceState *dev, QemuConsole *src,
>                   e->dz--;
>               } else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) {
>                   e->dz++;
> +            } else if (btn->button == INPUT_BUTTON_WHEEL_LEFT) {
> +                e->pan--;
> +            } else if (btn->button == INPUT_BUTTON_WHEEL_RIGHT) {
> +                e->pan++;
>               }
>           } else {
>               e->buttons_state &= ~bmap[btn->button];
> @@ -208,6 +212,8 @@ static void hid_pointer_sync(DeviceState *dev)
>           }
>           prev->dz += curr->dz;
>           curr->dz = 0;
> +        prev->pan += curr->pan;
> +        curr->pan = 0;
>       } else {
>           /* prepare next (clear rel, copy abs + btns) */
>           if (hs->kind == HID_MOUSE) {
> @@ -218,6 +224,7 @@ static void hid_pointer_sync(DeviceState *dev)
>               next->ydy = curr->ydy;
>           }
>           next->dz = 0;
> +        next->pan = 0;
>           next->buttons_state = curr->buttons_state;
>           /* make current guest visible, notify guest */
>           hs->n++;
> @@ -357,7 +364,7 @@ void hid_pointer_activate(HIDState *hs)
> 
>   int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
>   {
> -    int dx, dy, dz, l;
> +    int dx, dy, dz, pan, l;
>       int index;
>       HIDPointerEvent *e;
> 
> @@ -381,9 +388,13 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
>       }
>       dz = int_clamp(e->dz, -127, 127);
>       e->dz -= dz;
> +    pan = int_clamp(e->pan, -127, 127);
> +    e->pan -= pan;
> +
> 
>       if (hs->n &&
>           !e->dz &&
> +        !e->pan &&
>           (hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) {
>           /* that deals with this event */
>           QUEUE_INCR(hs->head);
> diff --git a/include/hw/input/hid.h b/include/hw/input/hid.h
> index 6a9d7bf466..1bb1778ee7 100644
> --- a/include/hw/input/hid.h
> +++ b/include/hw/input/hid.h
> @@ -10,6 +10,7 @@
>   typedef struct HIDPointerEvent {
>       int32_t xdx, ydy; /* relative iff it's a mouse, otherwise absolute */
>       int32_t dz, buttons_state;
> +    int32_t pan;
>   } HIDPointerEvent;
> 
>   #define QUEUE_LENGTH    16 /* should be enough for a triple-click */
> --
> 2.53.0



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH 2/5] usb/hid: add enhanced wheel mouse report descriptor and packet size
  2026-03-31 17:08 ` [RFC PATCH 2/5] usb/hid: add enhanced wheel mouse report descriptor and packet size Soumyajyotii Ssarkar
@ 2026-09-27 17:11   ` Helge Deller
  2026-09-28  8:31   ` Alex Bennée
  1 sibling, 0 replies; 14+ messages in thread
From: Helge Deller @ 2026-09-27 17:11 UTC (permalink / raw)
  To: Soumyajyotii Ssarkar, qemu-devel
  Cc: Richard Henderson, Paolo Bonzini, Soumyajyotii Ssarkar,
	Michael Tokarev, Thomas Lambertz, Philippe Mathieu-Daudé,
	Alex Golovko, ShengYi Hung

On 3/31/26 19:08, Soumyajyotii Ssarkar wrote:
> Build on top of the simple mouse HID report descriptor with an enhanced version
> that supports report IDs and feature reports for high resolution mouse
> wheel control. As per the documentation structure.
> 
> Check: https://download.microsoft.com/download/b/d/1/bd1f7ef4-7d72-419e-bc5c-9f79ad7bb66e/wheel.docx
> 
> Changes:
> - Increase HID descriptor length from 52 to 144 bytes
> - Add report IDs (0x01 input, 0x02 feature)
> - Implement feature reports for wheel/pan resolution multipliers
> - Add AC Pan (horizontal scroll) usage
> - Increase endpoint packet size from 4 to 6 bytes for pan byte
> 
> This enables guests to configure resolution multipliers (+-1, +-4, +-8, +-16)
> to control scroll acceleration and precision.
> 
> Signed-off-by: Soumyajyotii Ssarkar <soumyajyotisarkar23@gmail.com>
> ---
>   hw/usb/dev-hid.c | 126 +++++++++++++++++++++++++++++++++++------------
>   1 file changed, 95 insertions(+), 31 deletions(-)
> 
> diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c
> index ae19d60203..49e23c2319 100644
> --- a/hw/usb/dev-hid.c
> +++ b/hw/usb/dev-hid.c
> @@ -93,7 +93,7 @@ static const USBDescIface desc_iface_mouse = {
>                   0x00,          /*  u8  country_code */
>                   0x01,          /*  u8  num_descriptors */
>                   USB_DT_REPORT, /*  u8  type: Report */
> -                52, 0,         /*  u16 len */
> +                144, 0,        /*  u16 len */
>               },
>           },
>       },
> @@ -101,7 +101,7 @@ static const USBDescIface desc_iface_mouse = {
>           {
>               .bEndpointAddress      = USB_DIR_IN | 0x01,
>               .bmAttributes          = USB_ENDPOINT_XFER_INT,
> -            .wMaxPacketSize        = 4,
> +            .wMaxPacketSize        = 6,
>               .bInterval             = 0x0a,
>           },
>       },
> @@ -124,7 +124,7 @@ static const USBDescIface desc_iface_mouse2 = {
>                   0x00,          /*  u8  country_code */
>                   0x01,          /*  u8  num_descriptors */
>                   USB_DT_REPORT, /*  u8  type: Report */
> -                52, 0,         /*  u16 len */
> +                144, 0,        /*  u16 len */
>               },
>           },
>       },
> @@ -132,7 +132,7 @@ static const USBDescIface desc_iface_mouse2 = {
>           {
>               .bEndpointAddress      = USB_DIR_IN | 0x01,
>               .bmAttributes          = USB_ENDPOINT_XFER_INT,
> -            .wMaxPacketSize        = 4,
> +            .wMaxPacketSize        = 6,
>               .bInterval             = 7, /* 2 ^ (8-1) * 125 usecs = 8 ms */
>           },
>       },
> @@ -454,33 +454,97 @@ static const USBDesc desc_keyboard2 = {
>   };
> 
>   static const uint8_t qemu_mouse_hid_report_descriptor[] = {
> -    0x05, 0x01,		/* Usage Page (Generic Desktop) */
> -    0x09, 0x02,		/* Usage (Mouse) */
> -    0xa1, 0x01,		/* Collection (Application) */
> -    0x09, 0x01,		/*   Usage (Pointer) */
> -    0xa1, 0x00,		/*   Collection (Physical) */
> -    0x05, 0x09,		/*     Usage Page (Button) */
> -    0x19, 0x01,		/*     Usage Minimum (1) */
> -    0x29, 0x05,		/*     Usage Maximum (5) */
> -    0x15, 0x00,		/*     Logical Minimum (0) */
> -    0x25, 0x01,		/*     Logical Maximum (1) */
> -    0x95, 0x05,		/*     Report Count (5) */
> -    0x75, 0x01,		/*     Report Size (1) */
> -    0x81, 0x02,		/*     Input (Data, Variable, Absolute) */
> -    0x95, 0x01,		/*     Report Count (1) */
> -    0x75, 0x03,		/*     Report Size (3) */
> -    0x81, 0x01,		/*     Input (Constant) */
> -    0x05, 0x01,		/*     Usage Page (Generic Desktop) */
> -    0x09, 0x30,		/*     Usage (X) */
> -    0x09, 0x31,		/*     Usage (Y) */
> -    0x09, 0x38,		/*     Usage (Wheel) */
> -    0x15, 0x81,		/*     Logical Minimum (-0x7f) */
> -    0x25, 0x7f,		/*     Logical Maximum (0x7f) */
> -    0x75, 0x08,		/*     Report Size (8) */
> -    0x95, 0x03,		/*     Report Count (3) */
> -    0x81, 0x06,		/*     Input (Data, Variable, Relative) */
> -    0xc0,		/*   End Collection */
> -    0xc0,		/* End Collection */
> +    0x05, 0x01,     /* Usage Page (Generic Desktop) */

You are reformatting the lines here by moving the comments.
Please do that in an own patch, or keep the position of the comments for your code as well.

Otherwise the patch looks good.

Helge

> +    0x09, 0x02,     /* Usage (Mouse) */
> +    0xA1, 0x01,     /* Collection (Application) */
> +
> +    0x09, 0x02,     /* Usage (Mouse) */
> +    0xA1, 0x02,     /* Collection (Logical) */
> +
> +    0x85, 0x01,     /* Report ID (0x01) */
> +    0x09, 0x01,     /* Usage (Pointer) */
> +    0xA1, 0x00,     /* Collection (Physical) */
> +
> +    0x05, 0x09,     /* Usage Page (Button) */
> +    0x19, 0x01,     /* Usage Minimum (1) */
> +    0x29, 0x05,     /* Usage Maximum (5) */
> +    0x15, 0x00,     /* Logical Minimum (0) */
> +    0x25, 0x01,     /* Logical Maximum (1) */
> +    0x95, 0x05,     /* Report Count (5) */
> +    0x75, 0x01,     /* Report Size (1) */
> +    0x81, 0x02,     /* Input (Data,Var,Abs) */
> +
> +    0x95, 0x01,     /*  Report Count (1) */
> +    0x75, 0x03,     /*  Report Size (3) */
> +    0x81, 0x01,     /*  Input (Const) */
> +
> +    0x05, 0x01,     /*  Usage Page (Generic Desktop) */
> +    0x09, 0x30,     /*  Usage (X) */
> +    0x09, 0x31,     /*  Usage (Y) */
> +    0x95, 0x02,     /*  Report Count (2) */
> +    0x75, 0x08,     /*  Report Size (8) */
> +    0x15, 0x81,     /*  Logical Minimum (-127) */
> +    0x25, 0x7F,     /*  Logical Maximum (127) */
> +    0x81, 0x06,     /*  Input (Data,Var,Rel) */
> +
> +    /* Vertical wheel logical collection */
> +    0xA1, 0x02,     /*  Collection (Logical) */
> +
> +    0x85, 0x02,     /*  Report ID (0x02) */
> +    0x09, 0x48,     /*  Usage (Resolution Multiplier) */
> +    0x15, 0x00,     /*  Logical Minimum (0) */
> +    0x25, 0x01,     /*  Logical Maximum (1) */
> +    0x35, 0x01,     /*  Physical Minimum (1) */
> +    0x45, 0x04,     /*  Physical Maximum (4) */
> +    0x75, 0x02,     /*  Report Size (2) */
> +    0x95, 0x01,     /*  Report Count (1) */
> +    0xB1, 0x02,     /*  Feature (Data,Var,Abs) */
> +
> +    0x85, 0x01,     /*  Report ID (0x01) */
> +    0x09, 0x38,     /*  Usage (Wheel) */
> +    0x35, 0x00,     /*  Physical Minimum (0) */
> +    0x45, 0x00,     /*  Physical Maximum (0) */
> +    0x15, 0x81,     /*  Logical Minimum (-0x7f) */
> +    0x25, 0x7F,     /*  Logical Maximum (0x7f) */
> +    0x75, 0x08,     /*  Report Size (8) */
> +    0x95, 0x01,     /*  Report Count (1) */
> +    0x81, 0x06,     /*  Input (Data,Var,Rel) */
> +
> +    0xC0,           /*  End Collection */
> +
> +    /* Horizontal wheel logical collection */
> +    0xA1, 0x02,     /*  Collection (Logical) */
> +
> +    0x85, 0x02,     /*  Report ID (0x02) */
> +    0x09, 0x48,     /*  Usage (Resolution Multiplier) */
> +    0x15, 0x00,     /*  Logical Minimum (0) */
> +    0x25, 0x01,     /*  Logical Maximum (1) */
> +    0x35, 0x01,     /*  Physical Minimum (1) */
> +    0x45, 0x04,     /*  Physical Maximum (4) */
> +    0x75, 0x02,     /*  Report Size (2) */
> +    0x95, 0x01,     /*  Report Count (1) */
> +    0xB1, 0x02,     /*  Feature (Data,Var,Abs) */
> +
> +    0x35, 0x00,     /*  Physical Minimum (0) */
> +    0x45, 0x00,     /*  Physical Maximum (0) */
> +    0x75, 0x04,     /*  Report Size (4) */
> +    0x95, 0x01,     /*  Report Count (1) */
> +    0xB1, 0x01,     /*  Feature (Const) */
> +
> +    0x85, 0x01,     /*  Report ID (0x01) */
> +    0x05, 0x0C,     /*  Usage Page (Consumer Devices) */
> +    0x0A, 0x38, 0x02,   /*  Usage (AC Pan) */
> +    0x15, 0x81,     /*  Logical Minimum (-0x7f) */
> +    0x25, 0x7F,     /*  Logical Maximum (0x7f) */
> +    0x75, 0x08,     /*  Report Size (8) */
> +    0x95, 0x01,     /*  Report Count (1) */
> +    0x81, 0x06,     /*  Input (Data,Var,Rel) */
> +
> +    0xC0,           /*  End Collection */
> +
> +    0xC0,           /*  End Collection (Physical) */
> +    0xC0,           /*  End Collection (Logical) */
> +    0xC0            /*  End Collection (Application) */
>   };
> 
>   static const uint8_t qemu_tablet_hid_report_descriptor[] = {
> --
> 2.53.0



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH 3/5] input/hid: emit protocol aware mouse reports with pan support
  2026-03-31 17:08 ` [RFC PATCH 3/5] input/hid: emit protocol aware mouse reports with pan support Soumyajyotii Ssarkar
@ 2026-09-27 17:14   ` Helge Deller
  0 siblings, 0 replies; 14+ messages in thread
From: Helge Deller @ 2026-09-27 17:14 UTC (permalink / raw)
  To: Soumyajyotii Ssarkar, qemu-devel
  Cc: Richard Henderson, Paolo Bonzini, Soumyajyotii Ssarkar,
	Michael Tokarev, Thomas Lambertz, Philippe Mathieu-Daudé,
	Alex Golovko, ShengYi Hung

On 3/31/26 19:08, Soumyajyotii Ssarkar wrote:
> Implement protocol-aware mouse report output to support both boot
> protocol (protocol 0) and report protocol (protocol 1).
> 
> Boot protocol: 4-byte reports (button, dx, dy, dz)
> Report protocol: 6-byte reports with report ID 0x01 + pan byte

pan bytes ? (with "s"?)

> Both modes now support the 'pan' field for horizontal scrolling. The
> distinction ensures correct behavior across different guest operating
> systems and their HID implementations.
> 
> Signed-off-by: Soumyajyotii Ssarkar <soumyajyotisarkar23@gmail.com>
> ---
>   hw/input/hid.c | 44 +++++++++++++++++++++++++++++++++-----------
>   1 file changed, 33 insertions(+), 11 deletions(-)
> 
> diff --git a/hw/input/hid.c b/hw/input/hid.c
> index 457dbb0096..d0b7af7c13 100644
> --- a/hw/input/hid.c
> +++ b/hw/input/hid.c
> @@ -403,20 +403,42 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
> 
>       /* Appears we have to invert the wheel direction */
>       dz = 0 - dz;
> +    pan = 0 - pan;
>       l = 0;
>       switch (hs->kind) {
>       case HID_MOUSE:
> -        if (len > l) {
> -            buf[l++] = e->buttons_state;
> -        }
> -        if (len > l) {
> -            buf[l++] = dx;
> -        }
> -        if (len > l) {
> -            buf[l++] = dy;
> -        }
> -        if (len > l) {
> -            buf[l++] = dz;
> +        if (hs->protocol == 0) {

I suggest to add a comment here, e.g. "/* boot protocol (0) */"

> +            if (len > l) {
> +                buf[l++] = e->buttons_state;
> +            }
> +            if (len > l) {
> +                buf[l++] = dx;
> +            }
> +            if (len > l) {
> +                buf[l++] = dy;
> +            }
> +            if (len > l) {
> +                buf[l++] = dz;
> +            }
> +        } else {

and e.g. "/* report protocol (1) */"

Looks good otherwise.

Helge


> +            if (len > l) {
> +                buf[l++] = 0x01;
> +            }
> +            if (len > l) {
> +                buf[l++] = e->buttons_state;
> +            }
> +            if (len > l) {
> +                buf[l++] = dx;
> +            }
> +            if (len > l) {
> +                buf[l++] = dy;
> +            }
> +            if (len > l) {
> +                buf[l++] = dz;
> +            }
> +            if (len > l) {
> +                buf[l++] = pan;
> +            }
>           }
>           break;
> 
> --
> 2.53.0



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH 4/5] usb/hid: handle mouse feature report for wheel/pan multipliers
  2026-03-31 17:08 ` [RFC PATCH 4/5] usb/hid: handle mouse feature report for wheel/pan multipliers Soumyajyotii Ssarkar
@ 2026-09-27 19:58   ` Helge Deller
  0 siblings, 0 replies; 14+ messages in thread
From: Helge Deller @ 2026-09-27 19:58 UTC (permalink / raw)
  To: Soumyajyotii Ssarkar, qemu-devel
  Cc: Richard Henderson, Paolo Bonzini, Soumyajyotii Ssarkar,
	Michael Tokarev, Thomas Lambertz, Philippe Mathieu-Daudé,
	Alex Golovko, ShengYi Hung

On 3/31/26 19:08, Soumyajyotii Ssarkar wrote:
> Implement HID feature report handlers (GET_REPORT/SET_REPORT) for
> report ID 0x02 to allow guests to configure wheel/pan resolution
> multipliers.
> 
> Multiplier encoding:
> - Bits [1:0]: wheel_multiplier (0=+-1, 1=+-4, 2=+-8, 3=+-16)
> - Bits [3:2]: pan_multiplier (0=+-1, 1=+-4, 2=+-8, 3=+-16)
> 
> Supports flexible payload formats (with/without report ID byte).
> Allows Windows guests to dynamically adjust scroll sensitivity without
> driver installation.
> 
> Signed-off-by: Soumyajyotii Ssarkar <soumyajyotisarkar23@gmail.com>
> ---
>   hw/usb/dev-hid.c       | 65 ++++++++++++++++++++++++++++++++++++------
>   include/hw/input/hid.h |  2 ++
>   2 files changed, 59 insertions(+), 8 deletions(-)
LGTM,

Reviewed-by: Helge Deller <deller@gmx.de>


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH 5/5] input/hid: apply multiplier scaling with residual accumulation
  2026-03-31 17:08 ` [RFC PATCH 5/5] input/hid: apply multiplier scaling with residual accumulation Soumyajyotii Ssarkar
@ 2026-09-27 20:21   ` Helge Deller
  2026-09-28 14:00   ` Alex Bennée
  1 sibling, 0 replies; 14+ messages in thread
From: Helge Deller @ 2026-09-27 20:21 UTC (permalink / raw)
  To: Soumyajyotii Ssarkar, qemu-devel
  Cc: Richard Henderson, Paolo Bonzini, Soumyajyotii Ssarkar,
	Michael Tokarev, Thomas Lambertz, Philippe Mathieu-Daudé,
	Alex Golovko, ShengYi Hung

On 3/31/26 19:08, Soumyajyotii Ssarkar wrote:
> Implement multiplier-based scaling with residual accumulation for
> fine-grained mouse wheel and pan sensitivity control in report mode.
> 
> Applies divisor based scaling (1, 4, 8, 16) based on negotiated
> multiplier values.
> Then uses residual based approach to prevent precision loss:
> 
>    residual += delta
>    output = residual / divisor
>    residual -= output * divisor
> 
> Residual state preserved across VM migrations via VMState.
> 
> Signed-off-by: Soumyajyotii Ssarkar <soumyajyotisarkar23@gmail.com>
> ---
>   hw/input/hid.c         | 60 ++++++++++++++++++++++++++++++++++++++++++
>   include/hw/input/hid.h |  2 ++
>   2 files changed, 62 insertions(+)
> 
> diff --git a/hw/input/hid.c b/hw/input/hid.c
> index d0b7af7c13..9b800faeb0 100644
> --- a/hw/input/hid.c
> +++ b/hw/input/hid.c
> @@ -365,6 +365,7 @@ void hid_pointer_activate(HIDState *hs)
>   int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
>   {
>       int dx, dy, dz, pan, l;
> +    int wheel_divisor, pan_divisor;
>       int index;
>       HIDPointerEvent *e;
> 
> @@ -392,6 +393,54 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
>       e->pan -= pan;
> 
> 
> +    if (hs->kind == HID_MOUSE && hs->protocol != 0) {

shouldn't this better be "hs->protocol == 1" ?


> +        switch (hs->ptr.wheel_multiplier & 0x03) {
> +        case 0:
> +            wheel_divisor = 1;
> +            break;
> +        case 1:
> +            wheel_divisor = 4;
> +            break;
> +        case 2:
> +            wheel_divisor = 8;
> +            break;
> +        default:
> +            wheel_divisor = 16;
> +            break;
> +        }
> +
> +        switch (hs->ptr.pan_multiplier & 0x03) {
> +        case 0:
> +            pan_divisor = 1;
> +            break;
> +        case 1:
> +            pan_divisor = 4;
> +            break;
> +        case 2:
> +            pan_divisor = 8;
> +            break;
> +        default:
> +            pan_divisor = 16;
> +            break;
> +        }
> +
> +        if (wheel_divisor > 1) {
> +            hs->ptr.wheel_residual += dz;
> +            dz = hs->ptr.wheel_residual / wheel_divisor;
> +            hs->ptr.wheel_residual -= dz * wheel_divisor;
> +        } else {
> +            hs->ptr.wheel_residual = 0;
> +        }

maybe instead of the whole above: ?

	if (hs->ptr.wheel_multiplier & 0x03) {
             wheel_divisor = 2 << (hs->ptr.wheel_multiplier & 0x03);
             hs->ptr.wheel_residual += dz;
             dz = hs->ptr.wheel_residual / wheel_divisor;
             hs->ptr.wheel_residual -= dz * wheel_divisor;
         } else {
             hs->ptr.wheel_residual = 0;
         }

> +
> +        if (pan_divisor > 1) {
> +            hs->ptr.pan_residual += pan;
> +            pan = hs->ptr.pan_residual / pan_divisor;
> +            hs->ptr.pan_residual -= pan * pan_divisor;
> +        } else {
> +            hs->ptr.pan_residual = 0;
> +        }

same here.


> +    }
> +
>       if (hs->n &&
>           !e->dz &&
>           !e->pan &&
> @@ -405,6 +454,7 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
>       dz = 0 - dz;
>       pan = 0 - pan;
>       l = 0;
> +

drop this change.

>       switch (hs->kind) {
>       case HID_MOUSE:
>           if (hs->protocol == 0) {
> @@ -528,6 +578,11 @@ void hid_reset(HIDState *hs)
>       case HID_MOUSE:
>       case HID_TABLET:
>           memset(hs->ptr.queue, 0, sizeof(hs->ptr.queue));
> +        hs->ptr.mouse_grabbed = 0;
> +        hs->ptr.wheel_multiplier = 0;
> +        hs->ptr.pan_multiplier = 0;
> +        hs->ptr.wheel_residual = 0;
> +        hs->ptr.pan_residual = 0;

and move that in an own patch.

Otherwise the patch seems ok.
I suggest you resend the series with the proposed changes and
make sure it applies against git head.

Thanks!
Helge


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH 2/5] usb/hid: add enhanced wheel mouse report descriptor and packet size
  2026-03-31 17:08 ` [RFC PATCH 2/5] usb/hid: add enhanced wheel mouse report descriptor and packet size Soumyajyotii Ssarkar
  2026-09-27 17:11   ` Helge Deller
@ 2026-09-28  8:31   ` Alex Bennée
  2026-10-08 15:30     ` Soumyajyotii Ssarkar
  1 sibling, 1 reply; 14+ messages in thread
From: Alex Bennée @ 2026-09-28  8:31 UTC (permalink / raw)
  To: Soumyajyotii Ssarkar
  Cc: qemu-devel, Richard Henderson, Paolo Bonzini,
	Soumyajyotii Ssarkar, Michael Tokarev, Thomas Lambertz,
	Philippe Mathieu-Daudé, Alex Golovko, ShengYi Hung

Soumyajyotii Ssarkar <soumyajyotisarkar23@gmail.com> writes:

> Build on top of the simple mouse HID report descriptor with an enhanced version
> that supports report IDs and feature reports for high resolution mouse
> wheel control. As per the documentation structure.
>
> Check: https://download.microsoft.com/download/b/d/1/bd1f7ef4-7d72-419e-bc5c-9f79ad7bb66e/wheel.docx
>
> Changes:
> - Increase HID descriptor length from 52 to 144 bytes
> - Add report IDs (0x01 input, 0x02 feature)
> - Implement feature reports for wheel/pan resolution multipliers
> - Add AC Pan (horizontal scroll) usage
> - Increase endpoint packet size from 4 to 6 bytes for pan byte
>
> This enables guests to configure resolution multipliers (+-1, +-4, +-8, +-16)
> to control scroll acceleration and precision.
>
> Signed-off-by: Soumyajyotii Ssarkar <soumyajyotisarkar23@gmail.com>
> ---
>  hw/usb/dev-hid.c | 126 +++++++++++++++++++++++++++++++++++------------
>  1 file changed, 95 insertions(+), 31 deletions(-)
>
> diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c
> index ae19d60203..49e23c2319 100644
> --- a/hw/usb/dev-hid.c
> +++ b/hw/usb/dev-hid.c
> @@ -93,7 +93,7 @@ static const USBDescIface desc_iface_mouse = {
>                  0x00,          /*  u8  country_code */
>                  0x01,          /*  u8  num_descriptors */
>                  USB_DT_REPORT, /*  u8  type: Report */
> -                52, 0,         /*  u16 len */
> +                144, 0,        /*  u16 len */

Is this effectively just sizeof(qemu_mouse_hid_report_descriptor)? Could
we use that instead of hard coding constants in the descriptor? Are
there a limited number of sizes or are these a flexible stream of bytes?

>              },
>          },
>      },
> @@ -101,7 +101,7 @@ static const USBDescIface desc_iface_mouse = {
>          {
>              .bEndpointAddress      = USB_DIR_IN | 0x01,
>              .bmAttributes          = USB_ENDPOINT_XFER_INT,
> -            .wMaxPacketSize        = 4,
> +            .wMaxPacketSize        = 6,
>              .bInterval             = 0x0a,
>          },
>      },
> @@ -124,7 +124,7 @@ static const USBDescIface desc_iface_mouse2 = {
>                  0x00,          /*  u8  country_code */
>                  0x01,          /*  u8  num_descriptors */
>                  USB_DT_REPORT, /*  u8  type: Report */
> -                52, 0,         /*  u16 len */
> +                144, 0,        /*  u16 len */
>              },
>          },
>      },
> @@ -132,7 +132,7 @@ static const USBDescIface desc_iface_mouse2 = {
>          {
>              .bEndpointAddress      = USB_DIR_IN | 0x01,
>              .bmAttributes          = USB_ENDPOINT_XFER_INT,
> -            .wMaxPacketSize        = 4,
> +            .wMaxPacketSize        = 6,
>              .bInterval             = 7, /* 2 ^ (8-1) * 125 usecs = 8 ms */
>          },
>      },
> @@ -454,33 +454,97 @@ static const USBDesc desc_keyboard2 = {
>  };
>
>  static const uint8_t qemu_mouse_hid_report_descriptor[] = {
> -    0x05, 0x01,		/* Usage Page (Generic Desktop) */
> -    0x09, 0x02,		/* Usage (Mouse) */
> -    0xa1, 0x01,		/* Collection (Application) */
> -    0x09, 0x01,		/*   Usage (Pointer) */
> -    0xa1, 0x00,		/*   Collection (Physical) */
> -    0x05, 0x09,		/*     Usage Page (Button) */
> -    0x19, 0x01,		/*     Usage Minimum (1) */
> -    0x29, 0x05,		/*     Usage Maximum (5) */
> -    0x15, 0x00,		/*     Logical Minimum (0) */
> -    0x25, 0x01,		/*     Logical Maximum (1) */
> -    0x95, 0x05,		/*     Report Count (5) */
> -    0x75, 0x01,		/*     Report Size (1) */
> -    0x81, 0x02,		/*     Input (Data, Variable, Absolute) */
> -    0x95, 0x01,		/*     Report Count (1) */
> -    0x75, 0x03,		/*     Report Size (3) */
> -    0x81, 0x01,		/*     Input (Constant) */
> -    0x05, 0x01,		/*     Usage Page (Generic Desktop) */
> -    0x09, 0x30,		/*     Usage (X) */
> -    0x09, 0x31,		/*     Usage (Y) */
> -    0x09, 0x38,		/*     Usage (Wheel) */
> -    0x15, 0x81,		/*     Logical Minimum (-0x7f) */
> -    0x25, 0x7f,		/*     Logical Maximum (0x7f) */
> -    0x75, 0x08,		/*     Report Size (8) */
> -    0x95, 0x03,		/*     Report Count (3) */
> -    0x81, 0x06,		/*     Input (Data, Variable, Relative) */
> -    0xc0,		/*   End Collection */
> -    0xc0,		/* End Collection */
> +    0x05, 0x01,     /* Usage Page (Generic Desktop) */
> +    0x09, 0x02,     /* Usage (Mouse) */
> +    0xA1, 0x01,     /* Collection (Application) */
> +
> +    0x09, 0x02,     /* Usage (Mouse) */
> +    0xA1, 0x02,     /* Collection (Logical) */
> +
> +    0x85, 0x01,     /* Report ID (0x01) */
> +    0x09, 0x01,     /* Usage (Pointer) */
> +    0xA1, 0x00,     /* Collection (Physical) */
> +
> +    0x05, 0x09,     /* Usage Page (Button) */
> +    0x19, 0x01,     /* Usage Minimum (1) */
> +    0x29, 0x05,     /* Usage Maximum (5) */
> +    0x15, 0x00,     /* Logical Minimum (0) */
> +    0x25, 0x01,     /* Logical Maximum (1) */
> +    0x95, 0x05,     /* Report Count (5) */
> +    0x75, 0x01,     /* Report Size (1) */
> +    0x81, 0x02,     /* Input (Data,Var,Abs) */
> +
> +    0x95, 0x01,     /*  Report Count (1) */
> +    0x75, 0x03,     /*  Report Size (3) */
> +    0x81, 0x01,     /*  Input (Const) */
> +
> +    0x05, 0x01,     /*  Usage Page (Generic Desktop) */
> +    0x09, 0x30,     /*  Usage (X) */
> +    0x09, 0x31,     /*  Usage (Y) */
> +    0x95, 0x02,     /*  Report Count (2) */
> +    0x75, 0x08,     /*  Report Size (8) */
> +    0x15, 0x81,     /*  Logical Minimum (-127) */
> +    0x25, 0x7F,     /*  Logical Maximum (127) */
> +    0x81, 0x06,     /*  Input (Data,Var,Rel) */
> +
> +    /* Vertical wheel logical collection */
> +    0xA1, 0x02,     /*  Collection (Logical) */
> +
> +    0x85, 0x02,     /*  Report ID (0x02) */
> +    0x09, 0x48,     /*  Usage (Resolution Multiplier) */
> +    0x15, 0x00,     /*  Logical Minimum (0) */
> +    0x25, 0x01,     /*  Logical Maximum (1) */
> +    0x35, 0x01,     /*  Physical Minimum (1) */
> +    0x45, 0x04,     /*  Physical Maximum (4) */
> +    0x75, 0x02,     /*  Report Size (2) */
> +    0x95, 0x01,     /*  Report Count (1) */
> +    0xB1, 0x02,     /*  Feature (Data,Var,Abs) */
> +
> +    0x85, 0x01,     /*  Report ID (0x01) */
> +    0x09, 0x38,     /*  Usage (Wheel) */
> +    0x35, 0x00,     /*  Physical Minimum (0) */
> +    0x45, 0x00,     /*  Physical Maximum (0) */
> +    0x15, 0x81,     /*  Logical Minimum (-0x7f) */
> +    0x25, 0x7F,     /*  Logical Maximum (0x7f) */
> +    0x75, 0x08,     /*  Report Size (8) */
> +    0x95, 0x01,     /*  Report Count (1) */
> +    0x81, 0x06,     /*  Input (Data,Var,Rel) */
> +
> +    0xC0,           /*  End Collection */
> +
> +    /* Horizontal wheel logical collection */
> +    0xA1, 0x02,     /*  Collection (Logical) */
> +
> +    0x85, 0x02,     /*  Report ID (0x02) */
> +    0x09, 0x48,     /*  Usage (Resolution Multiplier) */
> +    0x15, 0x00,     /*  Logical Minimum (0) */
> +    0x25, 0x01,     /*  Logical Maximum (1) */
> +    0x35, 0x01,     /*  Physical Minimum (1) */
> +    0x45, 0x04,     /*  Physical Maximum (4) */
> +    0x75, 0x02,     /*  Report Size (2) */
> +    0x95, 0x01,     /*  Report Count (1) */
> +    0xB1, 0x02,     /*  Feature (Data,Var,Abs) */
> +
> +    0x35, 0x00,     /*  Physical Minimum (0) */
> +    0x45, 0x00,     /*  Physical Maximum (0) */
> +    0x75, 0x04,     /*  Report Size (4) */
> +    0x95, 0x01,     /*  Report Count (1) */
> +    0xB1, 0x01,     /*  Feature (Const) */
> +
> +    0x85, 0x01,     /*  Report ID (0x01) */
> +    0x05, 0x0C,     /*  Usage Page (Consumer Devices) */
> +    0x0A, 0x38, 0x02,   /*  Usage (AC Pan) */
> +    0x15, 0x81,     /*  Logical Minimum (-0x7f) */
> +    0x25, 0x7F,     /*  Logical Maximum (0x7f) */
> +    0x75, 0x08,     /*  Report Size (8) */
> +    0x95, 0x01,     /*  Report Count (1) */
> +    0x81, 0x06,     /*  Input (Data,Var,Rel) */
> +
> +    0xC0,           /*  End Collection */
> +
> +    0xC0,           /*  End Collection (Physical) */
> +    0xC0,           /*  End Collection (Logical) */
> +    0xC0            /*  End Collection (Application) */
>  };
>
>  static const uint8_t qemu_tablet_hid_report_descriptor[] = {

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH 5/5] input/hid: apply multiplier scaling with residual accumulation
  2026-03-31 17:08 ` [RFC PATCH 5/5] input/hid: apply multiplier scaling with residual accumulation Soumyajyotii Ssarkar
  2026-09-27 20:21   ` Helge Deller
@ 2026-09-28 14:00   ` Alex Bennée
  1 sibling, 0 replies; 14+ messages in thread
From: Alex Bennée @ 2026-09-28 14:00 UTC (permalink / raw)
  To: Soumyajyotii Ssarkar
  Cc: qemu-devel, Richard Henderson, Paolo Bonzini,
	Soumyajyotii Ssarkar, Michael Tokarev, Thomas Lambertz,
	Philippe Mathieu-Daudé, Alex Golovko, ShengYi Hung

Soumyajyotii Ssarkar <soumyajyotisarkar23@gmail.com> writes:

> Implement multiplier-based scaling with residual accumulation for
> fine-grained mouse wheel and pan sensitivity control in report mode.
>
> Applies divisor based scaling (1, 4, 8, 16) based on negotiated
> multiplier values.
> Then uses residual based approach to prevent precision loss:
>
>   residual += delta
>   output = residual / divisor
>   residual -= output * divisor
>
> Residual state preserved across VM migrations via VMState.
>
> Signed-off-by: Soumyajyotii Ssarkar <soumyajyotisarkar23@gmail.com>
> ---
>  hw/input/hid.c         | 60 ++++++++++++++++++++++++++++++++++++++++++
>  include/hw/input/hid.h |  2 ++
>  2 files changed, 62 insertions(+)
>
> diff --git a/hw/input/hid.c b/hw/input/hid.c
> index d0b7af7c13..9b800faeb0 100644
> --- a/hw/input/hid.c
> +++ b/hw/input/hid.c
> @@ -365,6 +365,7 @@ void hid_pointer_activate(HIDState *hs)
>  int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
>  {
>      int dx, dy, dz, pan, l;
> +    int wheel_divisor, pan_divisor;
>      int index;
>      HIDPointerEvent *e;
>
> @@ -392,6 +393,54 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
>      e->pan -= pan;
>
>
> +    if (hs->kind == HID_MOUSE && hs->protocol != 0) {
> +        switch (hs->ptr.wheel_multiplier & 0x03) {
> +        case 0:
> +            wheel_divisor = 1;
> +            break;
> +        case 1:
> +            wheel_divisor = 4;
> +            break;
> +        case 2:
> +            wheel_divisor = 8;
> +            break;
> +        default:
> +            wheel_divisor = 16;
> +            break;
> +        }
> +
> +        switch (hs->ptr.pan_multiplier & 0x03) {
> +        case 0:
> +            pan_divisor = 1;
> +            break;
> +        case 1:
> +            pan_divisor = 4;
> +            break;
> +        case 2:
> +            pan_divisor = 8;
> +            break;
> +        default:
> +            pan_divisor = 16;
> +            break;
> +        }
> +
> +        if (wheel_divisor > 1) {
> +            hs->ptr.wheel_residual += dz;
> +            dz = hs->ptr.wheel_residual / wheel_divisor;
> +            hs->ptr.wheel_residual -= dz * wheel_divisor;
> +        } else {
> +            hs->ptr.wheel_residual = 0;
> +        }
> +
> +        if (pan_divisor > 1) {
> +            hs->ptr.pan_residual += pan;
> +            pan = hs->ptr.pan_residual / pan_divisor;
> +            hs->ptr.pan_residual -= pan * pan_divisor;
> +        } else {
> +            hs->ptr.pan_residual = 0;
> +        }
> +    }
> +
>      if (hs->n &&
>          !e->dz &&
>          !e->pan &&
> @@ -405,6 +454,7 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
>      dz = 0 - dz;
>      pan = 0 - pan;
>      l = 0;
> +
>      switch (hs->kind) {
>      case HID_MOUSE:
>          if (hs->protocol == 0) {
> @@ -528,6 +578,11 @@ void hid_reset(HIDState *hs)
>      case HID_MOUSE:
>      case HID_TABLET:
>          memset(hs->ptr.queue, 0, sizeof(hs->ptr.queue));
> +        hs->ptr.mouse_grabbed = 0;
> +        hs->ptr.wheel_multiplier = 0;
> +        hs->ptr.pan_multiplier = 0;
> +        hs->ptr.wheel_residual = 0;
> +        hs->ptr.pan_residual = 0;
>          break;
>      }
>      hs->head = 0;
> @@ -619,6 +674,7 @@ static const VMStateDescription vmstate_hid_ptr_queue = {
>          VMSTATE_INT32(xdx, HIDPointerEvent),
>          VMSTATE_INT32(ydy, HIDPointerEvent),
>          VMSTATE_INT32(dz, HIDPointerEvent),
> +        VMSTATE_INT32(pan, HIDPointerEvent),
>          VMSTATE_INT32(buttons_state, HIDPointerEvent),
>          VMSTATE_END_OF_LIST()
>      }
> @@ -636,6 +692,10 @@ const VMStateDescription vmstate_hid_ptr_device = {
>          VMSTATE_UINT32(n, HIDState),
>          VMSTATE_INT32(protocol, HIDState),
>          VMSTATE_UINT8(idle, HIDState),
> +        VMSTATE_UINT8(ptr.wheel_multiplier, HIDState),
> +        VMSTATE_UINT8(ptr.pan_multiplier, HIDState),
> +        VMSTATE_INT32(ptr.wheel_residual, HIDState),
> +        VMSTATE_INT32(ptr.pan_residual, HIDState),
>          VMSTATE_END_OF_LIST(),

If you update migration state you also need to ensure that older systems
can be migrated to newer ones. There are some docs in docs/devel that
give some guidance on handling these cases. 

>      }
>  };
> diff --git a/include/hw/input/hid.h b/include/hw/input/hid.h
> index 1a1941d371..db902fa023 100644
> --- a/include/hw/input/hid.h
> +++ b/include/hw/input/hid.h
> @@ -25,6 +25,8 @@ typedef struct HIDMouseState {
>      int mouse_grabbed;
>      uint8_t wheel_multiplier;
>      uint8_t pan_multiplier;
> +    int32_t wheel_residual;
> +    int32_t pan_residual;
>  } HIDMouseState;
>
>  typedef struct HIDKeyboardState {

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH 2/5] usb/hid: add enhanced wheel mouse report descriptor and packet size
  2026-09-28  8:31   ` Alex Bennée
@ 2026-10-08 15:30     ` Soumyajyotii Ssarkar
  0 siblings, 0 replies; 14+ messages in thread
From: Soumyajyotii Ssarkar @ 2026-10-08 15:30 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Richard Henderson, Paolo Bonzini,
	Soumyajyotii Ssarkar, Michael Tokarev, Thomas Lambertz,
	Philippe Mathieu-Daudé, Alex Golovko, ShengYi Hung

[-- Attachment #1: Type: text/plain, Size: 10146 bytes --]

On Mon, Sep 28, 2026 at 2:01 PM Alex Bennée <alex.bennee@linaro.org> wrote:

> Soumyajyotii Ssarkar <soumyajyotisarkar23@gmail.com> writes:
>
> > Build on top of the simple mouse HID report descriptor with an enhanced
> version
> > that supports report IDs and feature reports for high resolution mouse
> > wheel control. As per the documentation structure.
> >
> > Check:
> https://download.microsoft.com/download/b/d/1/bd1f7ef4-7d72-419e-bc5c-9f79ad7bb66e/wheel.docx
> >
> > Changes:
> > - Increase HID descriptor length from 52 to 144 bytes
> > - Add report IDs (0x01 input, 0x02 feature)
> > - Implement feature reports for wheel/pan resolution multipliers
> > - Add AC Pan (horizontal scroll) usage
> > - Increase endpoint packet size from 4 to 6 bytes for pan byte
> >
> > This enables guests to configure resolution multipliers (+-1, +-4, +-8,
> +-16)
> > to control scroll acceleration and precision.
> >
> > Signed-off-by: Soumyajyotii Ssarkar <soumyajyotisarkar23@gmail.com>
> > ---
> >  hw/usb/dev-hid.c | 126 +++++++++++++++++++++++++++++++++++------------
> >  1 file changed, 95 insertions(+), 31 deletions(-)
> >
> > diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c
> > index ae19d60203..49e23c2319 100644
> > --- a/hw/usb/dev-hid.c
> > +++ b/hw/usb/dev-hid.c
> > @@ -93,7 +93,7 @@ static const USBDescIface desc_iface_mouse = {
> >                  0x00,          /*  u8  country_code */
> >                  0x01,          /*  u8  num_descriptors */
> >                  USB_DT_REPORT, /*  u8  type: Report */
> > -                52, 0,         /*  u16 len */
> > +                144, 0,        /*  u16 len */
>
>



> Is this effectively just sizeof(qemu_mouse_hid_report_descriptor)? Could
> we use that instead of hard coding constants in the descriptor? Are
> there a limited number of sizes or are these a flexible stream of bytes?
>

Yes, that is an option, before the valiue was 52 (hardcoded) then I
modified it to 144.
But the thing is the qemu_mouse_hid_report_descriptor is quite sensitive.
Like, from my testing: windows was being too sensitive.
Like when testing with other sizes by modifying the descriptor,
with the slightest of change it was not sensing
the mouse anymore... So to avoid the hassle, a hardcoded descriptor
would be a more viable option design wise, in my opinion.
This would help provide a proper cap and value for
further changes in the descriptor size.

Perhaps, I can do this in the actual patch: I could add a comment
regarding this descriptor size sensitivity and adopt the
sizeof(qemu_mouse_hid_report_descriptor) mentioning about
the 52->144 size change and the windows issue with it.

Would this be a viable approach?



> >              },
> >          },
> >      },
> > @@ -101,7 +101,7 @@ static const USBDescIface desc_iface_mouse = {
> >          {
> >              .bEndpointAddress      = USB_DIR_IN | 0x01,
> >              .bmAttributes          = USB_ENDPOINT_XFER_INT,
> > -            .wMaxPacketSize        = 4,
> > +            .wMaxPacketSize        = 6,
> >              .bInterval             = 0x0a,
> >          },
> >      },
> > @@ -124,7 +124,7 @@ static const USBDescIface desc_iface_mouse2 = {
> >                  0x00,          /*  u8  country_code */
> >                  0x01,          /*  u8  num_descriptors */
> >                  USB_DT_REPORT, /*  u8  type: Report */
> > -                52, 0,         /*  u16 len */
> > +                144, 0,        /*  u16 len */
> >              },
> >          },
> >      },
> > @@ -132,7 +132,7 @@ static const USBDescIface desc_iface_mouse2 = {
> >          {
> >              .bEndpointAddress      = USB_DIR_IN | 0x01,
> >              .bmAttributes          = USB_ENDPOINT_XFER_INT,
> > -            .wMaxPacketSize        = 4,
> > +            .wMaxPacketSize        = 6,
> >              .bInterval             = 7, /* 2 ^ (8-1) * 125 usecs = 8 ms
> */
> >          },
> >      },
> > @@ -454,33 +454,97 @@ static const USBDesc desc_keyboard2 = {
> >  };
> >
> >  static const uint8_t qemu_mouse_hid_report_descriptor[] = {
> > -    0x05, 0x01,              /* Usage Page (Generic Desktop) */
> > -    0x09, 0x02,              /* Usage (Mouse) */
> > -    0xa1, 0x01,              /* Collection (Application) */
> > -    0x09, 0x01,              /*   Usage (Pointer) */
> > -    0xa1, 0x00,              /*   Collection (Physical) */
> > -    0x05, 0x09,              /*     Usage Page (Button) */
> > -    0x19, 0x01,              /*     Usage Minimum (1) */
> > -    0x29, 0x05,              /*     Usage Maximum (5) */
> > -    0x15, 0x00,              /*     Logical Minimum (0) */
> > -    0x25, 0x01,              /*     Logical Maximum (1) */
> > -    0x95, 0x05,              /*     Report Count (5) */
> > -    0x75, 0x01,              /*     Report Size (1) */
> > -    0x81, 0x02,              /*     Input (Data, Variable, Absolute) */
> > -    0x95, 0x01,              /*     Report Count (1) */
> > -    0x75, 0x03,              /*     Report Size (3) */
> > -    0x81, 0x01,              /*     Input (Constant) */
> > -    0x05, 0x01,              /*     Usage Page (Generic Desktop) */
> > -    0x09, 0x30,              /*     Usage (X) */
> > -    0x09, 0x31,              /*     Usage (Y) */
> > -    0x09, 0x38,              /*     Usage (Wheel) */
> > -    0x15, 0x81,              /*     Logical Minimum (-0x7f) */
> > -    0x25, 0x7f,              /*     Logical Maximum (0x7f) */
> > -    0x75, 0x08,              /*     Report Size (8) */
> > -    0x95, 0x03,              /*     Report Count (3) */
> > -    0x81, 0x06,              /*     Input (Data, Variable, Relative) */
> > -    0xc0,            /*   End Collection */
> > -    0xc0,            /* End Collection */
> > +    0x05, 0x01,     /* Usage Page (Generic Desktop) */
> > +    0x09, 0x02,     /* Usage (Mouse) */
> > +    0xA1, 0x01,     /* Collection (Application) */
> > +
> > +    0x09, 0x02,     /* Usage (Mouse) */
> > +    0xA1, 0x02,     /* Collection (Logical) */
> > +
> > +    0x85, 0x01,     /* Report ID (0x01) */
> > +    0x09, 0x01,     /* Usage (Pointer) */
> > +    0xA1, 0x00,     /* Collection (Physical) */
> > +
> > +    0x05, 0x09,     /* Usage Page (Button) */
> > +    0x19, 0x01,     /* Usage Minimum (1) */
> > +    0x29, 0x05,     /* Usage Maximum (5) */
> > +    0x15, 0x00,     /* Logical Minimum (0) */
> > +    0x25, 0x01,     /* Logical Maximum (1) */
> > +    0x95, 0x05,     /* Report Count (5) */
> > +    0x75, 0x01,     /* Report Size (1) */
> > +    0x81, 0x02,     /* Input (Data,Var,Abs) */
> > +
> > +    0x95, 0x01,     /*  Report Count (1) */
> > +    0x75, 0x03,     /*  Report Size (3) */
> > +    0x81, 0x01,     /*  Input (Const) */
> > +
> > +    0x05, 0x01,     /*  Usage Page (Generic Desktop) */
> > +    0x09, 0x30,     /*  Usage (X) */
> > +    0x09, 0x31,     /*  Usage (Y) */
> > +    0x95, 0x02,     /*  Report Count (2) */
> > +    0x75, 0x08,     /*  Report Size (8) */
> > +    0x15, 0x81,     /*  Logical Minimum (-127) */
> > +    0x25, 0x7F,     /*  Logical Maximum (127) */
> > +    0x81, 0x06,     /*  Input (Data,Var,Rel) */
> > +
> > +    /* Vertical wheel logical collection */
> > +    0xA1, 0x02,     /*  Collection (Logical) */
> > +
> > +    0x85, 0x02,     /*  Report ID (0x02) */
> > +    0x09, 0x48,     /*  Usage (Resolution Multiplier) */
> > +    0x15, 0x00,     /*  Logical Minimum (0) */
> > +    0x25, 0x01,     /*  Logical Maximum (1) */
> > +    0x35, 0x01,     /*  Physical Minimum (1) */
> > +    0x45, 0x04,     /*  Physical Maximum (4) */
> > +    0x75, 0x02,     /*  Report Size (2) */
> > +    0x95, 0x01,     /*  Report Count (1) */
> > +    0xB1, 0x02,     /*  Feature (Data,Var,Abs) */
> > +
> > +    0x85, 0x01,     /*  Report ID (0x01) */
> > +    0x09, 0x38,     /*  Usage (Wheel) */
> > +    0x35, 0x00,     /*  Physical Minimum (0) */
> > +    0x45, 0x00,     /*  Physical Maximum (0) */
> > +    0x15, 0x81,     /*  Logical Minimum (-0x7f) */
> > +    0x25, 0x7F,     /*  Logical Maximum (0x7f) */
> > +    0x75, 0x08,     /*  Report Size (8) */
> > +    0x95, 0x01,     /*  Report Count (1) */
> > +    0x81, 0x06,     /*  Input (Data,Var,Rel) */
> > +
> > +    0xC0,           /*  End Collection */
> > +
> > +    /* Horizontal wheel logical collection */
> > +    0xA1, 0x02,     /*  Collection (Logical) */
> > +
> > +    0x85, 0x02,     /*  Report ID (0x02) */
> > +    0x09, 0x48,     /*  Usage (Resolution Multiplier) */
> > +    0x15, 0x00,     /*  Logical Minimum (0) */
> > +    0x25, 0x01,     /*  Logical Maximum (1) */
> > +    0x35, 0x01,     /*  Physical Minimum (1) */
> > +    0x45, 0x04,     /*  Physical Maximum (4) */
> > +    0x75, 0x02,     /*  Report Size (2) */
> > +    0x95, 0x01,     /*  Report Count (1) */
> > +    0xB1, 0x02,     /*  Feature (Data,Var,Abs) */
> > +
> > +    0x35, 0x00,     /*  Physical Minimum (0) */
> > +    0x45, 0x00,     /*  Physical Maximum (0) */
> > +    0x75, 0x04,     /*  Report Size (4) */
> > +    0x95, 0x01,     /*  Report Count (1) */
> > +    0xB1, 0x01,     /*  Feature (Const) */
> > +
> > +    0x85, 0x01,     /*  Report ID (0x01) */
> > +    0x05, 0x0C,     /*  Usage Page (Consumer Devices) */
> > +    0x0A, 0x38, 0x02,   /*  Usage (AC Pan) */
> > +    0x15, 0x81,     /*  Logical Minimum (-0x7f) */
> > +    0x25, 0x7F,     /*  Logical Maximum (0x7f) */
> > +    0x75, 0x08,     /*  Report Size (8) */
> > +    0x95, 0x01,     /*  Report Count (1) */
> > +    0x81, 0x06,     /*  Input (Data,Var,Rel) */
> > +
> > +    0xC0,           /*  End Collection */
> > +
> > +    0xC0,           /*  End Collection (Physical) */
> > +    0xC0,           /*  End Collection (Logical) */
> > +    0xC0            /*  End Collection (Application) */
> >  };
> >
> >  static const uint8_t qemu_tablet_hid_report_descriptor[] = {
>
> --
> Alex Bennée
> Virtualisation Tech Lead @ Linaro
>

Thank you,
Soumyajyotii Ssarkar

[-- Attachment #2: Type: text/html, Size: 14254 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2026-10-08 15:32 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-31 17:08 [RFC PATCH 0/5] Add high-resolution 2D mouse wheel support to USB HID Soumyajyotii Ssarkar
2026-03-31 17:08 ` [RFC PATCH 1/5] input/hid: add horizontal pan to pointer event path Soumyajyotii Ssarkar
2026-09-27 17:06   ` Helge Deller
2026-03-31 17:08 ` [RFC PATCH 2/5] usb/hid: add enhanced wheel mouse report descriptor and packet size Soumyajyotii Ssarkar
2026-09-27 17:11   ` Helge Deller
2026-09-28  8:31   ` Alex Bennée
2026-10-08 15:30     ` Soumyajyotii Ssarkar
2026-03-31 17:08 ` [RFC PATCH 3/5] input/hid: emit protocol aware mouse reports with pan support Soumyajyotii Ssarkar
2026-09-27 17:14   ` Helge Deller
2026-03-31 17:08 ` [RFC PATCH 4/5] usb/hid: handle mouse feature report for wheel/pan multipliers Soumyajyotii Ssarkar
2026-09-27 19:58   ` Helge Deller
2026-03-31 17:08 ` [RFC PATCH 5/5] input/hid: apply multiplier scaling with residual accumulation Soumyajyotii Ssarkar
2026-09-27 20:21   ` Helge Deller
2026-09-28 14:00   ` Alex Bennée

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.