Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

USB-Host mouse events work for X/Y moves and buttons, but not for wheel-scrolling #10159

Open
GeoffreyPlitt opened this issue Mar 21, 2025 · 2 comments
Labels
support issues that involve helping a user accomplish a task usb
Milestone

Comments

@GeoffreyPlitt
Copy link

GeoffreyPlitt commented Mar 21, 2025

CircuitPython version and board name

Adafruit Feather RP2040 with USB Type A Host
CircuitPython 9.2.5

Code/REPL

device.read(ENDPOINT_ADDRESS, buf, timeout=10)

Behavior

See below

Description

I bought an Adafruit Feather RP2040 with USB Type A Host and installed the special version of CircuitPython for it (https://circuitpython.org/board/adafruit_feather_rp2040_usb_host/) as well as the latest (as of today) related libraries.

I can read mouse events! I see X/Y moves and button up/downs. But wheel-scrolling does not seem to get detected.

I suspect the issue is that the Adafruit library uses "boot protocol" which lacks wheel-scrolling.

Would it be much work to switch to "report protocol" which adds a byte to the reports to include wheel-scrolling?

Additional information

No response

@dhalbert dhalbert added the usb label Mar 25, 2025
@dhalbert dhalbert added this to the 9.x.x milestone Mar 25, 2025
@tannewt tannewt added support issues that involve helping a user accomplish a task and removed bug labels Mar 27, 2025
@tannewt tannewt modified the milestones: 9.x.x, Support Mar 27, 2025
@tannewt
Copy link
Member

tannewt commented Mar 27, 2025

I don't think this is a core issue. Instead, we could add an example that shows how to read the scroll wheel. So, moving to support.

@Neradoc
Copy link

Neradoc commented Mar 28, 2025

I found it a little hard to find information on that, but I managed to request the "report protocol" from the mouse. Based on the code from the Feather USB Host guide I added the following. You could use wValue=0 to set to boot protocol I believe.

REQDIR_HOSTTODEVICE = 0
REQTYPE_CLASS = 1 << 5
REQREC_INTERFACE = 1 << 0
HID_REQ_SetProtocol = 0x0B

bmRequestType = (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE)
bRequest = HID_REQ_SetProtocol
wValue = 1
wIndex = 0

try:
    buf = bytearray(1)
    num = device.ctrl_transfer(bmRequestType, bRequest, wValue, wIndex, buf)
except usb.core.USBError:
    print("TRANSFER CONTROL ERROR")

FYI I based this on this code snippet which I found linked from a question in a forum post about switching to boot mode.
https://github.com/abcminiuser/lufa/blob/c503aa724e6c7b15d273d884473d7afb67bdb116/LUFA/Drivers/USB/Class/Host/HIDClassHost.c#L366-L381

Full mouse pass-through example.

# SPDX-FileCopyrightText: Copyright (c) 2025 Tim Cocks for Adafruit Industries
# SPDX-FileCopyrightText: Copyright (c) 2025 Neradoc, https://neradoc.me
#
# SPDX-License-Identifier: MIT
"""
Read a mouse movements and clicks, and pass through to the host.
Mouse wheel supported !
"""
import array
import time
import usb.core
import adafruit_usb_host_descriptors

from adafruit_hid.mouse import Mouse
import usb_hid
import struct

mouse = Mouse(usb_hid.devices)
mouse.release_all()

DIR_IN = 0x80
devices = []

for device in usb.core.find(find_all=True):
    IN_EP = []
    for conf_num in range(10):
        try:
            config_descriptor = adafruit_usb_host_descriptors.get_configuration_descriptor(
                device, conf_num
            )
        except usb.core.USBError:
            break

        i = 0
        while i < len(config_descriptor):
            descriptor_len = config_descriptor[i]
            descriptor_type = config_descriptor[i + 1]
            if descriptor_type == adafruit_usb_host_descriptors.DESC_CONFIGURATION:
                config_value = config_descriptor[i + 5]
            elif descriptor_type == adafruit_usb_host_descriptors.DESC_INTERFACE:
                interface_number = config_descriptor[i + 2]
                interface_class = config_descriptor[i + 5]
                interface_subclass = config_descriptor[i + 6]
            elif descriptor_type == adafruit_usb_host_descriptors.DESC_ENDPOINT:
                endpoint_address = config_descriptor[i + 2]
                if endpoint_address & DIR_IN:
                    IN_EP.append(endpoint_address)
                else:
                    pass
            i += descriptor_len
    devices.append((device, IN_EP))

# get the first device found
device, IN_EP = devices[0]

# Test to see if the kernel is using the device and detach it.
if device.is_kernel_driver_active(0):
    device.detach_kernel_driver(0)

DIR_OUT = 0
REQTYPE_CLASS = 1 << 5
REQREC_INTERFACE = 1 << 0
HID_REQ_SetProtocol = 0x0B

bmRequestType = (DIR_OUT | REQTYPE_CLASS | REQREC_INTERFACE)
bRequest = HID_REQ_SetProtocol
wValue = 1
wIndex = 0
wLength = 0

try:
    buf = bytearray(1)
    num = device.ctrl_transfer(bmRequestType, bRequest, wValue, wIndex, buf)
except usb.core.USBError:
    print("TRANSFER CONTROL ERROR")

# set configuration so we can read data from it
device.set_configuration()
print(f"configuration set for {device.manufacturer}, {device.product}, {device.serial_number}")

buf = array.array("B", [0] * 4)

while True:
    try:
        count = device.read(IN_EP[0], buf)
        if count:
            print(buf)
    except usb.core.USBError:
        count = 0

    if count:
        buttons, x, y, wheel = struct.unpack("Bbbb", buf[:4])

        if buttons & 1:
            mouse.press(Mouse.LEFT_BUTTON)
        else:
            mouse.release(Mouse.LEFT_BUTTON)

        if buttons & 2:
            mouse.press(Mouse.RIGHT_BUTTON)
        else:
            mouse.release(Mouse.RIGHT_BUTTON)

        if buttons & 4:
            mouse.press(Mouse.MIDDLE_BUTTON)
        else:
            mouse.release(Mouse.MIDDLE_BUTTON)

        mouse.move(x, y, wheel)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
support issues that involve helping a user accomplish a task usb
Projects
None yet
Development

No branches or pull requests

4 participants