This week my Zalman optical computer mouse died on me, I’ve had it for almost 4 years, so it did last a good while. The scroll wheel started to slip causing a very annoying issue of documents rolling about by themselves, so i threw it in the bin and went out and purchased a new one.
I ended up getting a new mouse from the local ASDA supermarket, sold under the name of ‘BlackWeb AYA gaming mouse’. Mostly because i thought it looked rather cool with it’s LED colour changing feature, as well as the fact that it was only £21.
(If you’re just looking for the software to control the BlackWeb AYA mouse LED from Linux, scroll to the bottom of the article and you’ll find the links to my github repository.)
The device features are advertised as;
- 16.8 million colours
- 12,800 dpi optical sensor
- on the fly dpi switching
- 1,000Hz polling rate
- breathing light effect
- macro manager
- 6 programmable buttons
- software included
What interested me is that the box didn’t mention any specific OS support (not even a mention of Windows), so i took a gamble hoping it might just have full Linux support. I was obviously wrong.
Upon first connecting it to the computer, the mouse immediately lit up red as the default colour, and it worked like any other generic HID device, requiring no special drivers to function. This is what appears in `dmesg‘
[ 5.912200] usb 9-1: New USB device found, idVendor=3938, idProduct=1101 [ 5.912202] usb 9-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0 [ 5.912203] usb 9-1: Product: Blackweb Optical Gaming Mouse [ 5.912204] usb 9-1: Manufacturer: Gaming Mouse [ 5.919435] input: Gaming Mouse Blackweb Optical Gaming Mouse as /devices/pci0000:00/0000:00:13.0/usb9/9-1/9-1:1.0/0003:3938:1101.0002/input/input3 [ 5.956392] hid-generic 0003:3938:1101.0002: input,hidraw1: USB HID v1.11 Mouse [Gaming Mouse Blackweb Optical Gaming Mouse] on usb-0000:00:13.0-1/input0 [ 5.961789] input: Gaming Mouse Blackweb Optical Gaming Mouse as /devices/pci0000:00/0000:00:13.0/usb9/9-1/9-1:1.1/0003:3938:1101.0003/input/input4 [ 6.013350] hid-generic 0003:3938:1101.0003: input,hiddev0,hidraw2: USB HID v1.11 Keyboard [Gaming Mouse Blackweb Optical Gaming Mouse] on usb-0000:00:13.0-1/input1
The device listed under `lsusb‘
$ lsusb | grep 3938:1101 Bus 009 Device 002: ID 3938:1101
As you can see it is using the generic HID driver. The first thing i wondered, was how to change the LED colour, because the mouse only came with a CD containing software for a windows driver.
I searched through /sys and /dev for a while trying to find something representative of the colour red, hoping i may just be able to “echo” an RGB colour value to the mouse, but had no success.
I ended up finding a solution to manually set the mouse polling speed through the usbhid module,
options usbhid mousepoll=1
Where 0=default, 1=1000Hz, 2=750Hz, 4=500Hz, 8=250Hz
So that was that out of the way.
Next i went ahead and had a look at the Windows software by setting up a Windows 10 virtual machine. After enabling USB pass through for the mouse, i installed the driver and opened the driver software, setting the mouse LED to a custom colour, and this set the LED as expected. The interesting thing was that after closing the virtual machine (and even powering off the host system), the mouse continued to stick to the same colour. So the mouse stored the colour value on the hardware itself as a persistent setting.
I left it at that for a day, being a simple solution, until i came across a forum post mentioning Wireshark has USB sniffing capabilities. After alot of searching i realised it might be possible to reverse engineer the device to control it from Linux natively. Despite having never done anything like this before.
The first thing to do was check which USB Bus the mouse was connected on, `lsusb‘ provides that information, and in my case it was Bus 009. So heading into Wireshark, i set the sniffer interface to usbmon9.
To avoid getting alot of spam information in the packet capture from other devices, i used a second connected mouse to perform any clicking whilst the packet capture was running, and also setting a filter rule within Wireshark like so:
usb.bus_id == 9 && usb.device_address == 2
The first step was to open the driver software, set a new colour for the LED, and then stop the packet capture. To my surprise i spotted the RGB colour value of the newly set colour in a datastream heading from the host to the USB mouse.
The colour i had set (as hexadecimal RGB) was FF8041
0000 c0 63 e4 97 01 88 ff ff 53 02 00 02 09 00 00 00 .c......S....... 0010 52 38 d5 58 00 00 00 00 9b 0c 01 00 8d ff ff ff R8.X............ 0020 08 00 00 00 08 00 00 00 21 09 07 03 01 00 08 00 ........!....... 0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0040 07 0a 01 00 ff 80 41 00 ......A.
The relevant part of this datastream is:
0000 07 0a 01 00 ff 80 41 00
The next step is to understand what the other information does. The colour value is surrounded by 00’s, assuming these are separators/padding. We then have 3 bytes left at the beginning, which i could only assume is a command to set the LED to the colour that follows.
I managed to whip up a small python script using PyUSB to interface with the mouse, that allowed this command to be sent to the mouse from within Linux natively.
Here’s an example showing how the request/command can be replayed, to tell the mouse to turn the LED blue:
import usb.core import usb.util # find the device dev = usb.core.find(idVendor=0x3938, idProduct=0x1101) # detach the kernel driver dev.detach_kernel_driver(1) usb.util.claim_interface(dev,1) dev.set_interface_altsetting(interface=1,alternate_setting=0) # set a hardcoded colour colors = [0x00, 0x00, 0xFF] # concatenate the colours into the expected 8 bytes data = [0x07, 0x0a, 0x01, 0x00] + colors + [0x00] # send the data to the mouse dev.ctrl_transfer(bmRequestType=0x21, bRequest=0x09, wValue=0x0307, wIndex=0x0001, data_or_wLength=data,timeout=1000) # reclaim the device usb.util.release_interface(dev,1) dev.attach_kernel_driver(1)
The values for bmRequestType, bRequest, wValue and wIndex were all present in Wireshark for the captured frame. So simply filling them in the same here appeared to work.
To my amazement the mouse LED instantly turned blue! But there was a small issue. Upon rebooting / powering off the machine, or simply hotplugging the device, meant the mouse would reset to its default colour each time (or the last colour that was set from within the virtual machine).
I realised there must be another command which tells the mouse to store a persistent colour. So going through the packet capture log once again, trying anything that contained a datastream seemed like a logical approach.
Under Windows when setting the LED colour, the mouse flashes 5 times momentarily, and i hadn’t seen this from setting the LED colour through a PyUSB script.
I’d almost given up, after several hours of trying absolutely everything in the packet capture, but eventually found it:
0000 40 38 f6 a8 01 88 ff ff 53 02 00 02 09 00 00 00 @8......S....... 0010 52 38 d5 58 00 00 00 00 a6 ef 03 00 8d ff ff ff R8.X............ 0020 08 00 00 00 08 00 00 00 21 09 07 03 01 00 08 00 ........!....... 0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0040 07 04 01 28 00 00 00 00 ...(....
Sending these 8 bytes to the mouse, triggered the LED to flash 5 times!
0000 07 04 01 28 00 00 00 00
Hotplugging the mouse, now kept the colour persistent. Simply adjusting the ‘data’ variable to contain these 8 bytes worked flawlessly to store the colour onto the hardware as a persistent setting. I suppose it is like a “save” command.
data = [0x07, 0x04, 0x01, 0x28, 0x00, 0x00, 0x00, 0x00]
I ended up putting together a slightly better script to set and store the LED colour of the mouse. You can get the code from my github repository here:
Or by cloning the repository directly from a terminal:
$ git clone firstname.lastname@example.org:Jigoku/ayatool.git
It’s as simple as running the below command, where each argument is the RGB value to set.
# ./ayatool.py 50 100 255
I have since started working on a PyQt based interface to control more features easily, which includes a colorpicker widget, after cloning the git repository you can launch this by running:
It still needs some improvements, but should be useful for changing the settings more easily (including profile slots and polling rate!).
If this helped you with the BlackWeb AYA mouse on Linux (or helped with you reversing a different USB mouse) I’d love to hear about it! Cheers.