libratbag / libratbag

A DBus daemon to configure input devices, mainly high-end and gaming mice
MIT License
2.03k stars 250 forks source link

Microsoft Classic Intellimouse support #537

Open madsl opened 5 years ago

madsl commented 5 years ago

Microsoft recently did another rerelease of the classic IntelliMouse Explorer 3.0 in June this year. This mouse has an adjustable DPI setting that you can set in Mouse and Keyboard Center 10 in Windows 10. It would be very nice if it were possible to adjust this DPI the same way in Linux.

What info is needed for this?

The mouse: https://www.microsoft.com/accessories/en-us/products/mice/microsoft-classic-intellimouse

whot commented 5 years ago

Simply said, someone needs to reverse-engineer the protocol and figure out how to talk to the mouse. Or alternatively get Microsoft to provide us with the protocol specifications :)

HughPH commented 5 years ago

This guide applies to basically any USB device. You'll need a Windows VM to run the Windows software and Wireshark to monitor the messages while you perform the actions. https://blog.flozz.fr/2016/03/27/steelseries-rival-100-reverse-engineering-dun-peripherique-usb/

It's in French but Google Translate does a fairly good job with it.

If you save the WireShark packet captures, (ideally one per operation, unfortunately they can't be annotated) someone might pick it up and implement it.

madsl commented 5 years ago

I made a rough capture - my mouse was already set to 800 DPI. I then went into Mouse and Keyboard center and dragged the DPI slider to the maximum (3200) and to the minimum (400) and every point in between (200 DPI between each point for a totalt of 15 points). Some back and forth movements of the slider too... intellimouseclassic.pcapng.gz

madsl commented 5 years ago

You also have the possibility to remap the keys and record macros in Mouse and Keyboard Center, but I guess that might be unnecessary to make a packet capture for that..? (Isn't stuff like that most likely implemented on the driver side...?)

HughPH commented 5 years ago

Yeah remapping and macros are usually dealt with in software. As an architect I'd like to envisage a future where the functionality of libratrag or something similar is extended such that it forms a standard underlying layer and the major desktop environments each have an extended mouse configuration page.

vanillajonathan commented 5 years ago

@madsl The USB device identifier is needed. You can get it by running lsusb.

madsl commented 5 years ago

@vanillajonathan Bus 001 Device 008: ID 045e:0823 Microsoft Corp.

vanillajonathan commented 5 years ago

With this you could try create a .device file. There is an example file here. https://github.com/libratbag/libratbag/blob/master/data/devices/device.example

Filename: microsoft-intellimouse-classic-2018.device

It would look something like this:

[Device]
Name=Microsoft Classic Intellimouse
DeviceMatch=usb:045e:0823
whot commented 5 years ago

just fwiw, this is the hid descriptor of the device, at least the one that was sent to me (I don't have the mouse)

0x05, 0x01,                    // Usage Page (Generic Desktop)        0
0x09, 0x02,                    // Usage (Mouse)                       2
0xa1, 0x01,                    // Collection (Application)            4
0x05, 0x01,                    //  Usage Page (Generic Desktop)       6
0x09, 0x02,                    //  Usage (Mouse)                      8
0xa1, 0x02,                    //  Collection (Logical)               10
0x85, 0x1a,                    //   Report ID (26)                    12
0x09, 0x01,                    //   Usage (Pointer)                   14
0xa1, 0x00,                    //   Collection (Physical)             16
0x05, 0x09,                    //    Usage Page (Button)              18
0x19, 0x01,                    //    Usage Minimum (1)                20
0x29, 0x05,                    //    Usage Maximum (5)                22
0x95, 0x05,                    //    Report Count (5)                 24
0x75, 0x01,                    //    Report Size (1)                  26
0x15, 0x00,                    //    Logical Minimum (0)              28
0x25, 0x01,                    //    Logical Maximum (1)              30
0x81, 0x02,                    //    Input (Data,Var,Abs)             32
0x75, 0x03,                    //    Report Size (3)                  34
0x95, 0x01,                    //    Report Count (1)                 36
0x81, 0x01,                    //    Input (Cnst,Arr,Abs)             38
0x05, 0x01,                    //    Usage Page (Generic Desktop)     40
0x09, 0x30,                    //    Usage (X)                        42
0x09, 0x31,                    //    Usage (Y)                        44
0x95, 0x02,                    //    Report Count (2)                 46
0x75, 0x10,                    //    Report Size (16)                 48
0x16, 0x01, 0x80,              //    Logical Minimum (-32767)         50
0x26, 0xff, 0x7f,              //    Logical Maximum (32767)          53
0x81, 0x06,                    //    Input (Data,Var,Rel)             56
0x09, 0x38,                    //    Usage (Wheel)                    58
0x35, 0x00,                    //    Physical Minimum (0)             60
0x45, 0x00,                    //    Physical Maximum (0)             62
0x95, 0x01,                    //    Report Count (1)                 64
0x75, 0x10,                    //    Report Size (16)                 66
0x16, 0x01, 0x80,              //    Logical Minimum (-32767)         68
0x26, 0xff, 0x7f,              //    Logical Maximum (32767)          71
0x81, 0x06,                    //    Input (Data,Var,Rel)             74
0xc0,                          //   End Collection                    76
0xc0,                          //  End Collection                     77
0xc0,                          // End Collection                      78

slightly off-topic:

As an architect I'd like to envisage a future where the functionality of libratrag or something similar is extended such that it forms a standard underlying layer and the major desktop environments each have an extended mouse configuration page.

that's why ratbagd is a systemd daemon accessible via DBus. We had a chat with the GNOME designers a few years back and concluded that it's too niche to provide as full integration into the settings panels. better to have a specific app (like piper) to deal with this.

bentiss commented 5 years ago

An other note. According to the wireshark capture, the device has 2 HID interfaces. The one @whot mentioned and an other one that contains the feature reports to configure it (enumeration happens at lines 1205 and 1210):

0x05, 0x0c,                    // Usage Page (Consumer Devices)       0
0x09, 0x01,                    // Usage (Consumer Control)            2
0xa1, 0x01,                    // Collection (Application)            4
0x85, 0x1c,                    //  Report ID (28)                     6
0x06, 0x00, 0xff,              //  Usage Page (Vendor Defined Page 1) 8
0x0a, 0x22, 0xff,              //  Usage (Vendor Usage 0xff22)        11
0x15, 0x00,                    //  Logical Minimum (0)                14
0x25, 0x04,                    //  Logical Maximum (4)                16
0x75, 0x08,                    //  Report Size (8)                    18
0x95, 0x01,                    //  Report Count (1)                   20
0x81, 0x02,                    //  Input (Data,Var,Abs)               22
0x0a, 0x25, 0xff,              //  Usage (Vendor Usage 0xff25)        24
0x15, 0x00,                    //  Logical Minimum (0)                27
0x25, 0x04,                    //  Logical Maximum (4)                29
0x75, 0x08,                    //  Report Size (8)                    31
0x95, 0x01,                    //  Report Count (1)                   33
0x81, 0x02,                    //  Input (Data,Var,Abs)               35
0x0a, 0x26, 0xff,              //  Usage (Vendor Usage 0xff26)        37
0x15, 0x00,                    //  Logical Minimum (0)                40
0x27, 0xff, 0xff, 0x00, 0x00,  //  Logical Maximum (65535)            42
0x75, 0x10,                    //  Report Size (16)                   47
0x95, 0x01,                    //  Report Count (1)                   49
0x81, 0x02,                    //  Input (Data,Var,Abs)               51
0x85, 0x24,                    //  Report ID (36)                     53
0x06, 0x00, 0xff,              //  Usage Page (Vendor Defined Page 1) 55
0x0a, 0x0a, 0xfa,              //  Usage (Vendor Usage 0xfa0a)        58
0x15, 0x00,                    //  Logical Minimum (0)                61
0x26, 0xff, 0x00,              //  Logical Maximum (255)              63
0x75, 0x08,                    //  Report Size (8)                    66
0x95, 0x1f,                    //  Report Count (31)                  68
0xb1, 0x02,                    //  Feature (Data,Var,Abs)             70
0x85, 0x27,                    //  Report ID (39)                     72
0x06, 0x00, 0xff,              //  Usage Page (Vendor Defined Page 1) 74
0x0a, 0x0a, 0xfa,              //  Usage (Vendor Usage 0xfa0a)        77
0x81, 0x02,                    //  Input (Data,Var,Abs)               80
0x85, 0x2a,                    //  Report ID (42)                     82
0x06, 0x00, 0xff,              //  Usage Page (Vendor Defined Page 1) 84
0x0a, 0x0a, 0xfa,              //  Usage (Vendor Usage 0xfa0a)        87
0x95, 0x08,                    //  Report Count (8)                   90
0x81, 0x02,                    //  Input (Data,Var,Abs)               92
0xc0,                          // End Collection                      94

There seem to be also 1 more USB endpoints as we are seeing events from the 1.8.3 device.

In the capture, I can see that there are a bunch of SET_REPORT and GET_REPORT on report IDs 36 and 39.

From what I can see, the protocol is not trivial. You'll need to analyze it by interacting with the UI to get the mapping between one DPI change and which reports are sent to the device.

rpavlik commented 4 years ago

So not sure my odds, but I do have contacts at MS that I might be able to network thru to get to the hardware division for protocol info. If nothing else, I've had experience reversing USB before, and I now have one of these mice, so interested in helping. I like the buttery-smoothness, but a little higher speed would be a worthwhile tradeoff :)

FFY00 commented 4 years ago

Yes, that would be great. If you can get them to share some documentation we can add support.

rpavlik commented 4 years ago

No guarantees - I'll put it on my list for after they find the answer to the current question I've posed to them :)

FFY00 commented 4 years ago

Sure, thank you :)

paw-eloquent-safe commented 4 years ago

I've got a Pro IntelliMouse (045E:082A) and I reverse engineered (partially) the used protocol, it's quite simple but has some quirks.

If we take a look at the capture of @madsl, the Pro IntelliMouse and Classic IntelliMouse, at first glance seem very similiar.

So basically with both, you send a SET_REPORT for REPORT_ID 0x24.

Classic IntelliMouse

ReportID,PropertyID,Length,Data
[0x24],[0x96],[0x03],[0x00, 0x40, 0x06]

Pro IntelliMouse

ReportID,PropertyID,Length,Data
[0x24],[0x96],[0x02],[0x80, 0x3E]

Where the property id is the property you want to change, in this case 0x96 corresponds to DPI. After the data part you can just pad it with 0x00's until the total size matches the report count. The Pro has a different report count (72) than the Classic (32)

I've also made a gist to test my theory, if somebody with a Classic IntelliMouse would be willing to try it out and tell me if it works, that would be great.

For the Pro IntelliMouse see this source file

madsl commented 4 years ago

Omg awesome! I have both, I'll test as soon as I get home.

madsl commented 4 years ago

I'm not able to get rid of "Resource busy" when trying intellimouse.py with a Pro IntelliMouse (tried on the terminal with no X running too):

$ sudo python -c 'import intellimouse; intellimouse.IntelliMouse()' Password: Traceback (most recent call last): File "", line 1, in File "/home/mads/kode/intellimouse.py", line 34, in init self.device.set_configuration() File "/usr/lib/python3.7/site-packages/usb/core.py", line 869, in set_configuration self._ctx.managed_set_configuration(self, configuration) File "/usr/lib/python3.7/site-packages/usb/core.py", line 102, in wrapper return f(self, *args, **kwargs) File "/usr/lib/python3.7/site-packages/usb/core.py", line 148, in managed_set_configuration self.backend.set_configuration(self.handle, cfg.bConfigurationValue) File "/usr/lib/python3.7/site-packages/usb/backend/libusb1.py", line 794, in set_configuration _check(self.lib.libusb_set_configuration(dev_handle.handle, config_value)) File "/usr/lib/python3.7/site-packages/usb/backend/libusb1.py", line 595, in _check raise USBError(_strerror(ret), ret, _libusb_errno[ret]) usb.core.USBError: [Errno 16] Resource busy

FFY00 commented 4 years ago

If you are using hidapi with the libusb backend in intellimouse.py, I think libusb by default tries to claim the device which probably can't happen since we are using it. Use hidapi with the hidraw backend instead.

paw-eloquent-safe commented 4 years ago

I'm not able to get rid of "Resource busy" when trying intellimouse.py with a Pro IntelliMouse (tried on the terminal with no X running too):

$ sudo python -c 'import intellimouse; intellimouse.IntelliMouse()' Password: Traceback (most recent call last): File "", line 1, in File "/home/mads/kode/intellimouse.py", line 34, in init self.device.set_configuration() File "/usr/lib/python3.7/site-packages/usb/core.py", line 869, in set_configuration self._ctx.managed_set_configuration(self, configuration) File "/usr/lib/python3.7/site-packages/usb/core.py", line 102, in wrapper return f(self, *args, **kwargs) File "/usr/lib/python3.7/site-packages/usb/core.py", line 148, in managed_set_configuration self.backend.set_configuration(self.handle, cfg.bConfigurationValue) File "/usr/lib/python3.7/site-packages/usb/backend/libusb1.py", line 794, in set_configuration _check(self.lib.libusb_set_configuration(dev_handle.handle, config_value)) File "/usr/lib/python3.7/site-packages/usb/backend/libusb1.py", line 595, in _check raise USBError(_strerror(ret), ret, _libusb_errno[ret]) usb.core.USBError: [Errno 16] Resource busy

@madsl please try again, with sudo python3, it should be "fixed" now.. Note you won't be able to use your mouse while configuring (so have a spare one handy). @FFY00 yeah, using hidapi would probably be better and easier, but this is fine for my use case 🤷

madsl commented 4 years ago

@K-Visscher nice, it worked!

$ sudo ipython Password: Python 3.7.7 (default, May 8 2020, 21:56:26) Type 'copyright', 'credits' or 'license' for more information IPython 7.5.0 -- An enhanced Interactive Python. Type '?' for help. In [1]: import intellimouse
In [2]: m = intellimouse.IntelliMouse()
In [3]: m.device
Out[3]: <DEVICE ID 045e:082a on Bus 001 Address 004> In [4]: m.get_color()
Out[4]: 16712705 In [5]: m.get_polling_rate
Out[5]: <bound method IntelliMouse.get_polling_rate of <intellimouse.IntelliMouse object at 0x7f732b7d9e90>> In [6]: m.get_polling_rate()
Out[6]: 1000 In [7]:
In [7]: m.get_dpi()
Out[7]: 1600 In [8]: m.set_dpi(2000)
In [9]: m.get_dpi()
Out[9]: 2000 In [10]:
Do you really want to exit ([y]/n)? libusb: error [seek_to_next_config] descriptor is not a config desc (type 0x00) Segmentation fault

paw-eloquent-safe commented 4 years ago

@K-Visscher nice, it worked!

$ sudo ipython Password: Python 3.7.7 (default, May 8 2020, 21:56:26) Type 'copyright', 'credits' or 'license' for more information IPython 7.5.0 -- An enhanced Interactive Python. Type '?' for help. In [1]: import intellimouse In [2]: m = intellimouse.IntelliMouse() In [3]: m.device Out[3]: <DEVICE ID 045e:082a on Bus 001 Address 004> In [4]: m.get_color() Out[4]: 16712705 In [5]: m.get_polling_rate Out[5]: <bound method IntelliMouse.get_polling_rate of <intellimouse.IntelliMouse object at 0x7f732b7d9e90>> In [6]: m.get_polling_rate() Out[6]: 1000 In [7]: In [7]: m.get_dpi() Out[7]: 1600 In [8]: m.set_dpi(2000) In [9]: m.get_dpi() Out[9]: 2000 In [10]: Do you really want to exit ([y]/n)? libusb: error [seek_to_next_config] descriptor is not a config desc (type 0x00) Segmentation fault

To get rid of that nasty looking segfault exit using exit(), or let the mouse IntelliMoise object go out of scope like so m = None

madsl commented 4 years ago

@K-Visscher It worked setting DPI on the Classic after adding your reattach patch to your gist :)

$ sudo ipython Python 3.7.7 (default, May 8 2020, 21:56:26) Type 'copyright', 'credits' or 'license' for more information IPython 7.5.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: from intellimouse_classic import IntelliMouse

In [2]: m = IntelliMouse()

In [3]: m.set_dpi(400)

In [4]: exit
libusb: error [seek_to_next_config] descriptor is not a config desc (type 0x8f) Segmentation fault

After doing this, the DPI was set to 400 :)

Jon-guy30 commented 1 year ago

Hello all, I hope you don't mind me bringing up this old thread. I just ordered the Microsoft Classic Intellimouse Pro version and looked up information on Linux support for this hardware since on Windows one can customize it with Microsoft keyboard and mouse center software. I didn't seem to find much information about Linux support for this mouse except this thread when I remembered there's a gui tool for configuring gaming mouse on Linux which depend on this library. So I searched around in issues regarding this mouse and found this thread. It seems to be the only thread online discussing this particular mouse.

I'm wondering if I use piper the gui tool is it going to work with this mouse? The rival mice has the "rival cfg" CLI tool and it also works in piper supposedly. Apparently you guys above got it to work under Linux? When I look here though https://github.com/libratbag/libratbag/tree/master/data/devices it's not listed, so is it implemented yet? It looks like such a basic mouse with pretty much only a top sensor (for gaming) and little customization, but I don't know how difficult it is to implement.

I plan on being on Windows a little while longer, or if this mouse is supported I might switch sooner, we'll see.

vanillajonathan commented 1 year ago

@madsl & @k-visscher You who have experience with these devices, perhaps you could get them working with libratbag?

okkiv commented 1 year ago

I'm looking through the sources at the moment and will try to add support for the classic and pro

MLSci commented 2 months ago

I also have a Microsoft 5050 Mouse that has left and right side buttons, which can be programmed as keyboard shortcut in Microsoft Keyboard and Mouse Center. It'll be nice to be able to use it in Linux too. I'm not familiar with how it can be implemented, but let me know if there's anything I can do from my end.

Keep it up :)