Opened 8 months ago

Last modified 8 months ago

#18888 new bug

USB mouse - no buttons

Reported by: donn Owned by: nobody
Priority: normal Milestone: Unscheduled
Component: Drivers/Input/HID/USB Version: R1/beta4
Keywords: Cc:
Blocked By: Blocking:
Platform: All

Description

USB mouse & keyboard, wireless with "dongle", Cherry "Stream", on Lenovo Thinkpad T450s.

Input devices: Trackpoint 1, AT Keyboard 1, PS/2 Touchpad 1

USB Keyboard 3, USB keyboard 2, USB Keyboard 1, USB Mouse 1

Mouse pointer works, but no buttons. (Buttons do work on another computer.)

Same if the keyboard is switched off.

Curiously, if I disable the Trackpoint and Touchpad in BIOS, they still work. The Trackpoint doesn't appear in the [Preferences] Input list, but its buttons continue to function.

Files in /tmp - whether or not keyboard is switched on: usb_hid_report_descriptor_046a_0115_0.bin usb_hid_report_descriptor_046a_0115_1.bin usb_hid_report_descriptor_046a_0115_2.bin

Attachments (8)

syslog (129.7 KB ) - added by donn 8 months ago.
listdev (2.8 KB ) - added by donn 8 months ago.
usb_hid_report_descriptor_046a_0115_0.bin (65 bytes ) - added by donn 8 months ago.
usb_hid_report_descriptor_046a_0115_1.bin (115 bytes ) - added by donn 8 months ago.
usb_hid_report_descriptor_046a_0115_2.bin (85 bytes ) - added by donn 8 months ago.
listusb (33.7 KB ) - added by donn 8 months ago.
listusb -v
syslog3 (292.4 KB ) - added by donn 8 months ago.
some 1st button clicks, then move to select restart
tworeport.diff (18.6 KB ) - added by donn 8 months ago.

Download all attachments as: .zip

Change History (34)

by donn, 8 months ago

Attachment: syslog added

by donn, 8 months ago

Attachment: listdev added

comment:1 by pulkomandy, 8 months ago

Since the mouse and keyboard are connected to the same dongle, there is a single USB device which is both a keyboard and a mouse. It's this one in the listdev output:

22	      device Human Interface Device (No Subclass, None) [3|0|0]
23	        vendor 046a: Cherry GmbH
24	        device 0115: Unknown

Please attach the 3 HID descriptors from /tmp, they tell us in which format the mouse and keyboard events are sent, and this information is required to do anything about it.

The output of listusb -v could also be useful to get more details about that device.

by donn, 8 months ago

Attachment: listusb added

listusb -v

comment:2 by waddlesplash, 8 months ago

Component: - GeneralDrivers/Input/HID/USB

comment:3 by donn, 8 months ago

For what it's worth, with extra tracing in usb_hid so I get control(...) traces, the mouse generates them with movement, but buttons go unnoticed at this level.

comment:4 by donn, 8 months ago

I see "KERN: usb_hid: keyboard device unhandled control 0x00002710" in syslog, which I think means the (shared) driver doesn't understand KB_GET_KEYBOARD_ID. Not that this should matter, it's just a note on related circumstances.

comment:5 by pulkomandy, 8 months ago

So, we have a single USB device with 3 interfaces.

Each interface has an HID descriptor. The decoded descriptors (I use http://eleccelerator.com/usbdescreqparser/ to decode them):

0x05, 0x01,        // Usage Page (Generic Desktop Ctrls)
0x09, 0x06,        // Usage (Keyboard)
0xA1, 0x01,        // Collection (Application)
0x05, 0x07,        //   Usage Page (Kbrd/Keypad)
0x19, 0xE0,        //   Usage Minimum (0xE0)
0x29, 0xE7,        //   Usage Maximum (0xE7)
0x15, 0x00,        //   Logical Minimum (0)
0x25, 0x01,        //   Logical Maximum (1)
0x75, 0x01,        //   Report Size (1)
0x95, 0x08,        //   Report Count (8)
0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x01,        //   Report Count (1)
0x75, 0x08,        //   Report Size (8)
0x81, 0x01,        //   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x05,        //   Report Count (5)
0x75, 0x01,        //   Report Size (1)
0x05, 0x08,        //   Usage Page (LEDs)
0x19, 0x01,        //   Usage Minimum (Num Lock)
0x29, 0x05,        //   Usage Maximum (Kana)
0x91, 0x02,        //   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x95, 0x01,        //   Report Count (1)
0x75, 0x03,        //   Report Size (3)
0x91, 0x01,        //   Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x95, 0x06,        //   Report Count (6)
0x75, 0x08,        //   Report Size (8)
0x15, 0x00,        //   Logical Minimum (0)
0x26, 0xFF, 0x00,  //   Logical Maximum (255)
0x05, 0x07,        //   Usage Page (Kbrd/Keypad)
0x19, 0x00,        //   Usage Minimum (0x00)
0x2A, 0xFF, 0x00,  //   Usage Maximum (0xFF)
0x81, 0x00,        //   Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0,              // End Collection

// 65 bytes

(a fairly reasonable keyboard, I'm not looking too closely at this if the keyboard works fine)

The mouse:

0x05, 0x01,        // Usage Page (Generic Desktop Ctrls)
0x09, 0x02,        // Usage (Mouse)
0xA1, 0x01,        // Collection (Application)
0x85, 0x01,        //   Report ID (1)
0x09, 0x01,        //   Usage (Pointer)
0xA1, 0x00,        //   Collection (Physical)
0x95, 0x05,        //     Report Count (5)
0x75, 0x01,        //     Report Size (1)
0x05, 0x09,        //     Usage Page (Button)
0x19, 0x01,        //     Usage Minimum (0x01)
0x29, 0x05,        //     Usage Maximum (0x05)
0x15, 0x00,        //     Logical Minimum (0)
0x25, 0x01,        //     Logical Maximum (1)
0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x01,        //     Report Count (1)
0x75, 0x03,        //     Report Size (3)
0x81, 0x01,        //     Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x08,        //     Report Size (8)
0x95, 0x01,        //     Report Count (1)
0x05, 0x01,        //     Usage Page (Generic Desktop Ctrls)
0x09, 0x38,        //     Usage (Wheel)
0x15, 0x81,        //     Logical Minimum (-127)
0x25, 0x7F,        //     Logical Maximum (127)
0x81, 0x06,        //     Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x0C,        //     Usage Page (Consumer)
0x0A, 0x38, 0x02,  //     Usage (AC Pan)
0x95, 0x01,        //     Report Count (1)
0x81, 0x06,        //     Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0xC0,              //   End Collection
0x85, 0x02,        //   Report ID (2)
0x09, 0x01,        //   Usage (Consumer Control)
0xA1, 0x00,        //   Collection (Physical)
0x75, 0x0C,        //     Report Size (12)
0x95, 0x02,        //     Report Count (2)
0x05, 0x01,        //     Usage Page (Generic Desktop Ctrls)
0x09, 0x30,        //     Usage (X)
0x09, 0x31,        //     Usage (Y)
0x16, 0x01, 0xF8,  //     Logical Minimum (-2047)
0x26, 0xFF, 0x07,  //     Logical Maximum (2047)
0x81, 0x06,        //     Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0xC0,              //   End Collection
0xC0,              // End Collection
0x06, 0xA0, 0xFF,  // Usage Page (Vendor Defined 0xFFA0)
0x09, 0x01,        // Usage (0x01)
0xA1, 0x01,        // Collection (Application)
0x85, 0x09,        //   Report ID (9)
0x09, 0x01,        //   Usage (0x01)
0x15, 0x00,        //   Logical Minimum (0)
0x26, 0xFF, 0x00,  //   Logical Maximum (255)
0x75, 0x08,        //   Report Size (8)
0x95, 0x07,        //   Report Count (7)
0x91, 0x82,        //   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile)
0x09, 0x02,        //   Usage (0x02)
0x75, 0x08,        //   Report Size (8)
0x95, 0x07,        //   Report Count (7)
0x81, 0x82,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0,              // End Collection

// 115 bytes

Not sure what the third one is:

0x05, 0x0C,        // Usage Page (Consumer)
0x09, 0x01,        // Usage (Consumer Control)
0xA1, 0x01,        // Collection (Application)
0x85, 0x01,        //   Report ID (1)
0x05, 0x0C,        //   Usage Page (Consumer)
0x09, 0x01,        //   Usage (Consumer Control)
0xA1, 0x01,        //   Collection (Application)
0x75, 0x10,        //     Report Size (16)
0x95, 0x01,        //     Report Count (1)
0x15, 0x01,        //     Logical Minimum (1)
0x26, 0x9C, 0x02,  //     Logical Maximum (668)
0x19, 0x01,        //     Usage Minimum (Consumer Control)
0x2A, 0x9C, 0x02,  //     Usage Maximum (AC Distribute Vertically)
0x81, 0x00,        //     Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0,              //   End Collection
0x05, 0x01,        //   Usage Page (Generic Desktop Ctrls)
0x09, 0x80,        //   Usage (Sys Control)
0xA1, 0x01,        //   Collection (Application)
0x09, 0x82,        //     Usage (Sys Sleep)
0x09, 0x82,        //     Usage (Sys Sleep)
0x09, 0x83,        //     Usage (Sys Wake Up)
0x15, 0x00,        //     Logical Minimum (0)
0x25, 0x01,        //     Logical Maximum (1)
0x75, 0x01,        //     Report Size (1)
0x95, 0x03,        //     Report Count (3)
0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x01,        //     Report Size (1)
0x95, 0x05,        //     Report Count (5)
0x81, 0x01,        //     Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0,              //   End Collection
0x06, 0xBC, 0xFF,  //   Usage Page (Vendor Defined 0xFFBC)
0x09, 0x88,        //   Usage (0x88)
0xA1, 0x01,        //   Collection (Application)
0x75, 0x08,        //     Report Size (8)
0x95, 0x02,        //     Report Count (2)
0x15, 0x01,        //     Logical Minimum (1)
0x26, 0xFF, 0x00,  //     Logical Maximum (255)
0x19, 0x01,        //     Usage Minimum (0x01)
0x2A, 0xFF, 0x00,  //     Usage Maximum (0xFF)
0x81, 0x00,        //     Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0,              //   End Collection
0xC0,              // End Collection

// 85 bytes

As far as I can see there is nothing too unexpected here.

The mouse reports 5 buttons, vertical and horizontal mousewheels and X and Y axes with a good resolution (usually the values are on 8 bits, but here they put them on 12 bits).

I have checked Linux sourcecode and they don't have any specific quirks for this device.

If you have a syslog with extra traces, that could be helpful.

Can you check in Input preferences if anything moves when you press the mouse buttons? The mouse drawn there should highlight each button as you press it. It could be that they work, but they don't have the right button IDs and so they send button events that nothing in the OS does anything with (for buttons numbers 4 and up).

comment:6 by donn, 8 months ago

Input preferences can't see any mouse button action at all from the USB mouse. Set to whatever number of mouse buttons. Movement is adjustable, no buttons. Apparently can control "resolution" to three levels, with a button on the mouse; I don't notice a difference.

Likewise no mouse button events in my syslog - usb_hid_control() is called for movement, but not buttons. I will try to get a complete syslog, but have to deal with space limitation. I don't know if usb_hid_control() proves there's no USB traffic from the mouse, or if there'd device data closer to the raw device that I could log without getting the USB disk data.

Cherry supplies software to monkey with the keyboard and mouse, as .msi files. They are of course unclear as to what goes on, but sounds like some of it is `run time' modification of the USB data, some of it is configuration codes that go back to the keyboard and/or mouse. I think that's mostly for non-standard keys, and as I mention the mouse default state seems to work on MacOS, but just to mention this.

by donn, 8 months ago

Attachment: syslog3 added

some 1st button clicks, then move to select restart

comment:7 by donn, 8 months ago

I see an extra mouse report - first one stacks up 5 buttons, then the second one comes along and from what I can make out presents no information whatever. Then the mouse handler says there's a mouse with no buttons. I'm looking into some hack that will cause the empty mouse report to be ignored, to see if that makes any difference.

comment:8 by donn, 8 months ago

Never mind that, guess that was a red herring. The no-buttons mouse is apparently "Report ID (2)", wherein is the Generic Desktop Control X and Y axes that MouseProtocolHandler::AddHandlers() wants. Unfortunately, the buttons come in a separate, preceding collection Report ID (1), and I guess to get the full mouse protocol handler I have to merge these two somehow.

comment:9 by pulkomandy, 8 months ago

Ah, yes, this mouse is a bit unusual, it sends two separate reports for mouse movement and mouse buttons. Usually these are all sent together as a single report, since it's only a few bytes.

This shouldn't be a problem, except the HID handler tries to identifies these reports separately.

We see the one for the mouse movements for example:

2607	KERN: usb_hid: mouse device with 0 buttons and no wheel

But we don't see one for the mouse buttons.

Having them as separate reports would normally not be a problem, except the mouse protocol handler is quite specific about what it considers as a mouse:

https://cgit.haiku-os.org/haiku/tree/src/add-ons/kernel/drivers/input/hid_shared/MouseProtocolHandler.cpp#n108

Reports that don't include X and Y movements are ignored.

I see two options here:

Last edited 8 months ago by pulkomandy (previous) (diff)

comment:10 by donn, 8 months ago

Yes, I took the 2nd option. That of course needed some scaffolding to keep track of what has already been reported, and also needed to move axis item member vars from references to pointers, because I need to initialize the handler object when there aren't any axis items. Somewhere in there I lost mouse movement, but the mouse is now reported with 5 buttons, wheel and horizontal pan, so one step forward and one back.

Added ProtocolHandler flag field where MouseProtocolHandler can note that its various elements are going to be needed, and then as they're found, that flag is cleared. So when the axis values came along, I found a handler that still needed mouse axes, which turned out to be one that already had 5 buttons. So that's working as intended. Just not really working.

comment:11 by donn, 8 months ago

It works. It took a fair amount of hacking. KeyboardProtocolHandler has two reports, but only one input report.

Along with holding two different input reports, and the revisions needed to set up the handler with multiple reports during discovery, I also had to fiddle around with WaitForReport. There may be a better way to do this. I picked the axes report to run WaitForReport, and at that time deal with various conditions that could occur other than OK/None. At this point, I can query the reports (added Status() to HIDReport) and determine which report really has anything.

If it's the axis report, I set the axes; if there's actually a second wheel/button report (my case), I use the saved last button data.

If the B_OK report is the second wheel/button, I increment its busy count and do the button evaluation. I can't run WaitForReport again, because while the B_OK status remains, the data is gone, so I have a new function WaitMoreReport that just ups the busy count.

I think that's the basics. Along the way. I had to eliminate all the by-reference passing. The axis report items, naturally, because at the time the handler is initialized, I don't have the axis report yet. The reports, because for whatever reason, when trying to pass them by reference, I ended up with two copies of the 2nd report; I don't understand why, but neither do I understand the point of dereferencing these pointers and then passing them by reference. So they're pointers now.

MouseProtocolHandler got worked over pretty heavily. Context diff is 485 lines. A little of that is junk and duplicated code, but it's a significant change. The other modules have only minor changes if any. I will clean it up a little and drop it here.

by donn, 8 months ago

Attachment: tworeport.diff added

comment:12 by donn, 8 months ago

Et voilà, how I got the mouse with two report structures working.

comment:13 by waddlesplash, 8 months ago

Is there any chance that, instead of waiting for two reports in one MouseReportHandler to send one message to userland, we send two messages to userland?

comment:14 by donn, 8 months ago

This would mean doubling up handlers on a single pathname? I bet it would be more complicated than this, though for all I know it may be done by another handler here.

comment:15 by waddlesplash, 8 months ago

Can't we just send two mouse_movement structs? One for the clicks, one for the movement, depending on what report we receive?

comment:16 by pulkomandy, 8 months ago

No, because each mouse_movement must include the complete button state (all currently pressed buttons). If you don't take that into account, all mouse movements would release the buttons, making it impossible to do a drag gesture for example.

comment:17 by waddlesplash, 8 months ago

MouseProtocolHandler already stores "last buttons", can't we just report those?

comment:18 by donn, 8 months ago

Indeed, that's how it works - the axis reports come in by themselves, and the handler fills in buttons from stored state. But it can do that because it's the same handler.

Because it's the same handler, it has to juggle the two different reports. I don't know, there might be a way to have two separate handlers linked together, so they can share the incoming data. If you like that kind of thing.

comment:19 by waddlesplash, 8 months ago

Right, but I'm suggesting here that the "WaitMoreReport" logic be ditched. After all, if a mouse sends the reports separately, it may (e.g.) send a click message without an X/Y message.

comment:20 by donn, 8 months ago

Yes, it does send button data without x/y. (Button state change to be precise - button on, then another for button off.)

Depending on what with/without means here - at the handler level, I believe there are no WaitForReport outcomes with two valid reports. You get one report. Presumably because whoever calls this stuff in is parceling them out one at a time.

If I understand the dispatching model here (which I may not!) ... the handlers dispatch themselves, by issuing this WaitForReport in advance, to be fulfilled when the specified event occurs. So to get either report, whichever comes first, I have to put out two WaitForReports in parallel, or I have to pass from one to the next using this super elegant WaitMoreReport technique.

comment:21 by donn, 8 months ago

If it seems awkward, it sure is. Probably in the bigger picture, ReportID shouldn't be waiting for itself, because it gets awkward when some functionality may depend on processing multiple report IDs that arrive in an unpredictable order.

I.e., if I could get something like id = WaitForReport(report1, report2, ...) ...

comment:22 by pulkomandy, 8 months ago

At the low level USB protocol, there isn't so much of a problem: you schedule a read on the endpoint, (there is a single endpoint here), you get some data,andthen you look at the report id to decide what it is.

It looks like the architecture of our usb stack splits this daêastream too early into separate reports, which are then difficult to reassemble. But thereare other cases where this may be needed, for example the joystick handler can receive several reports and expose them as several joysticks connected to the same usb port (I used this and I have confirmed that Windows behaves the same)

comment:23 by donn, 8 months ago

Does single endpoint mean, among other things, that the logic in all the handlers after WaitForReport - PROTOCOL_HANDLER_COOKIE_FLAG_CLOSED etc. - is asking the same things about the same state?

comment:24 by donn, 8 months ago

for example the joystick handler can receive several reports and expose them as several joysticks connected to the same usb port

In the current setup, this would be multiple instances of JoystickProtocolHandler, am I right? Which would work out fine, as long as the AddHandler function recognizes the desired report types, the only issue being (again) multiple report IDs that need to be available to the same handler.

That's what I had in mind for the mouse handler - I think it would support multiple mice with multiple record IDs each, as long as report packages showed up in a reasonable order.

comment:25 by pulkomandy, 8 months ago

Does single endpoint mean, among other things, that the logic in all the handlers after WaitForReport - PROTOCOL_HANDLER_COOKIE_FLAG_CLOSED etc. - is asking the same things about the same state?

The entry point to all this is HIDParser::SetReport:

https://cgit.haiku-os.org/haiku/tree/src/add-ons/kernel/drivers/input/hid_shared/HIDParser.cpp#n462

This is called from the transfer callback whenever we get some input from the device. At this point, you get a report, starting with a "report ID" byte. But after that, the data is split into different reports that are handled too independently in your case.

https://xref.landonf.org/source/xref/haiku/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.cpp#345

Yes, you can see that the Joystick handler has a loop on all reports and creates a report handler for each of them: https://cgit.haiku-os.org/haiku/tree/src/add-ons/kernel/drivers/input/hid_shared/JoystickProtocolHandler.cpp#n174

as long as report packages showed up in a reasonable order

I think there isn't really an "order" here. The idea of having different reports is that the device can send just what changed. If the mouse moves, it will send just X and Y deltas. If a button is clicked, it will send only a button report. So, typically you will get several mouse move reports, and only rarely, when a button is clicked, you will get a mouse buttons one. (but maybe your mouse does not do this, and always alternates between the two reports. In that case, I'm not sure why they would do it this way).

So you have to handle these independantly from each other, and "complete" them with the last available state:

  • When receiving a button change, complete it with 0 values for X and Y deltas
  • When receiving a mouse move, complete it with the last known state of the buttons

In the end, you must return from the Read() function whenever a new report comes, and provide a complete mouse_movenent structure: https://cgit.haiku-os.org/haiku/tree/src/add-ons/kernel/drivers/input/hid_shared/MouseProtocolHandler.cpp#n254 which may include data from the latest report, but also from the previous ones since the reports only provide part of the information. This would be similar to how the keyboard report handler keeps track of currently pressed keys in the fCurrentKeys array: https://cgit.haiku-os.org/haiku/tree/src/add-ons/kernel/drivers/input/hid_shared/KeyboardProtocolHandler.cpp#n552 (it does this for a different reason: the reports do not include the complete state of the keyboard, but rather key press and release events).

So, in the end, if we keep the current HID parser design, a way to wait for either one of the reports would be the best way to handle this. That being said, it looks like the HID Parser notifies all reports anyways: https://cgit.haiku-os.org/haiku/tree/src/add-ons/kernel/drivers/input/hid_shared/HIDParser.cpp#n479

So, you can wait on one of them only, and if it returns B_INTERRUPTED and no data, check the other one (I think you can call WaitForReport with a 0 timeout), as your B_INTERRUPTED notification on the first one is likely caused by the other report being delivered.

If we want to do a larger refactor, it may make sense to move the condition variable up one level, so that HIDReport::WaitForReport is replaced by a "wait for any report on this endpoint", which is mostly what happens anyways, due to the for loop in HIDParser::SetReport waking up all reports whenever something happens.

comment:26 by donn, 8 months ago

Yes, that's how my MouseProtocolHandler is dealing with it - if no data, check the other. WaitMoreReport is just there for the atomic block that WaitForReport didn't set up. I didn't really try to figure out why, but when I called a second WaitForReport, I got B_OK but the data was gone. The work in the MaybeScheduleTransfer substance of WaitForReport has already been done, and doing it over did not seem to help.

And yes, if WaitForReport were not specific to the HIDReport object and instead returned the set of available reports, or set of matching available reports, that would obviously be a more direct solution for handlers that need to deal with multiple report IDs.

Note: See TracTickets for help on using tickets.