Reverse Engineering Logitech Unifying USB Protocol

*For those of you looking for something to let you attach more than one logitech device in linux, you should go to the downloads page.

Just finished a reverse engineering and building a “driver” that will let you link more than one device to a Logitech unifying receiver in Linux!  Yay!  (Big thanks here goes to Kevin, who helped me through some bugs/was a console guru)

For those of you who haven’t used the “unified” series of products from logitech, the selling point is that you can have multiple wireless peripherals (Keyboards or mice) that all link up to single TINY USB dongle.  To pair to the first device, all you do is plug in the receiver, and turn on your peripheral.  After that, they are paired!  This is awesome for windows/Mac users, because they can use the provided software to connect more devices.  For linux users, this is not so easy.  There are no drivers, and this means there is no way to pair more than one device to a receiver.  This means that to use a keyboard and a mouse, you need to:

  1. Find a win/mac machine, pair, and basically pray that the devices never get unpaired
  2. Use one receiver per device (expensive in terms of usb ports)
  3. Write your own program to pair devices

Since I had already cracked a receiver open and soldered it onto the mobo of my EEEpc, I decided that 1) and 2) were out of the question.  Thankfully I had another receiver and another machine to test on.  I decided my program really only needs to do one thing, which is to pair to devices not near other receivers.  I figured I could take advantage of a case that the Logitech engineers must have planned for, loosing a receiver.  Since these things are so tiny, and can be easily misplaced/stolen/eaten, I assumed that already paired devices would be willing to pair to another device given that they are not within range of a paired receiver.  If within range of a paired receiver, it doesn’t make sense for a device to pair, because then your mouse would pair to other peoples receivers more or less at random (ever time you turned it on).

I started by booting into windows and using a program called usblyser to analyze usb traffic.  It turns out it was mostly useless, due to the clunky interface, and I didn’t want to install pyusb on my windows partition, so I moved on to a different solution.  This time around I installed virtual box from oracle (NOT the OSE edition, which doesn’t have usb extensions).  I created a VM running windows XP, downloaded the software from logitech, and let the VM grab the usb dongle.  I paired and unpaired a few devices to test it.

Now I was ready for some sniffing.  I started out by mounting the debug filesystem so I could use usbmon (usb monitoring) which is some kind of linux utility.  To do this I used:

sudo mount -t debugfs none_debugfs /sys/kernel/debug

sudo modprobe usbmon

The I used wireshark to look at the output.  This was a HUGE MISTAKE.  For some reason, wireshark thinks USB is big endian.  This is basically the direction it “reads” data that is transmitted.  for example, if it received ‘011’ it would think that the most significant bit was 0, then 1 then 1, so it would say the decimal value was 3.  a little endian interpretation would say the MSB was 1, then 1 then 0 so that the value is 6.

For the longest time I thought the wValue and wIndex of the control command I was trying to send were 0x1002 and 0x0200, because Wireshark was telling me that the setup packet in the USB request block was “21 09 10 02 02 00 07 00”.  I should have known something was fishy, because I knew the request length was 7 because 7 bytes were returned.  What didn’t dawn on me until I looked at the raw usbmon logs was that the values of the wValue, wIndex and wLength were reversed.  It should have read:

“21 09 02 10 00 02 00 07”

Without the right setup packet, I kept getting USB I/O and USB pipe errors.  The moral of the story is that usbmon doesn’t need no stinkin’ wireshark.  Just use:

cat /sys/kernel/debug/usb/usbmon/u0

To look at the raw output.  When I did that I got a lot of garbage, which I then had to sort through carefully and think about.  I went tested the logitech software by pressing buttons and watching the USB requests it generated.  Here what I noticed:

Opening the program:  This generated TONS of USB traffic that I really didn’t want to sift though.  I made a gamble here and assumed logitech didn’t want this to be hard to do, and that this traffic was the program asking the dongle things like “Are you there? do you have any attached devices? what can you tell me about them?” and such.  If worst had come to worst, I would have just replicated these packets without knowing what they did.

Hitting the “Advanced” button:  This also generated tons of USB traffic.  Boo!  I think what mostly goes on here is that the program queries the device to see if anything new has been attached/dropped out of range/has been used, because the GUI here shows all the attached devices, and if you use a device a little icon blinks.  The requests looked like a simple call and response, but I didn’t want to mess with that much traffic.

Hitting the “next” button to get to the pairing scree:  This generated very little traffic (3 requests) and it also seemed to be what put the dongle “in the mood” to pair.  I decided that this was a good point to start my “attack” on the protocol.  I also made the call here that there would probably not be a ton of complications or security here, because there is no reason to make this protocol less likely to work/install security.

Taking that as my starting point, I booted/shut down my VM and the program several times, each time hitting the next button.  The following always occurred when I did that:

e20bb300 755547433 S Co:2:008:0 s 21 09 0210 0002 0007 7 = 10ff80b2 01503c
e20bb300 755547611 C Co:2:008:0 0 7 >
e7b69400 755549571 C Ii:2:008:3 0:2 7 = 10ff4a01 000000
e20bb300 755550445 S Ii:2:008:3 -115:2 32 <
e20bb300 755551614 C Ii:2:008:3 0:2 7 = 10ff80b2 000000
efb5ef00 755552476 S Ii:2:008:3 -115:2 32 <

This is in dense usbmon-speak, but if you take a look at the documentation its not so bad.  The deciphered version of the first two lines is just that a control transfer was submitted (S) and completed (C).  The setup packet for the transfer was “21 09 0210 0002 0007” meaning it was a class request sending 7 bytes of data out, and the data was “10ff80b2 01503c”.  I guessed that this was probably the “Go pair with stuff” command, and wrote a little python to send it:

dev.ctrl_transfer(0x21,0x09,0x0210,0x0002, [0x10,0xff,0x80,0xb2,0x01,0x50,0x3c])

This gave me an error that was something like “device busy”.  This is because my computer thought that the dongle was a HID device, which it is.  But for now, we want to suspend that function.  So I closed down my VM and typed:

sudo modprobe -r usbhid

to temporarily stop usbhid from using it.  Once I got the control transfer to work, I tried pairing a device.  Nothing appeared to happen, other than the initial pairing of one device to the receiver.  I decided that I would be best to duplicate the rest of the traffic that happened when hitting the next button before I gave up.

The next request I wanted to duplicate was a read of 7 bytes from endpoint 3 (based on usbmon).  For the longest time I thought the right code was:

dev.read(3,7,0)

Because I wanted to read from endpoint 3, 7 bytes, interface 0.  WRONG.  It turns out that endpoint 3 has an address described by the usb protocol, a useful explanation of which can be found here.  What I also discovered with the verbose mode (lsusb -v) of lsusb, was this:

bEndpointAddress     0x83  EP 3 IN

hey, thats not 3.  The right line was actually:

dev.read(0x83,0x7,2)

The two came from guessing an interface by luck, but I don’t think there are normally out of the range [0,4].

Testing this again showed that this was indeed the right command to do something…?  It at least lets me pair my keyboard and my mouse to the same receiver!  I don’t completely understand what this line means, but I also saw it when USB requests were made when leaving pairing mode.  Maybe it says something about the device, like how many devices are paired to it, or their names; all I know is that it is necessary to push the receiver into pairing.  The final thing to do is to turn HID devices back on with:

sudo modprobe usbhid

The little script that plays these requests will be available on the Downloads page.  The modprobing needs to be done manually.  If you want to help improve it, or if you have some logs from your receiver that look different on the “next”/pairing step, I would love to see them.

36 thoughts on “Reverse Engineering Logitech Unifying USB Protocol

  1. Antonio says:

    Thank you for writing this!
    However, i tried to download the driver from the file hosting platform you’re using, but sadly i cannot, maybe the file isn’t available anymore?

  2. birchroader says:

    First, thanks for doing this at all…
    And then to the issues 🙂
    * The uploaded files seems to be a .tar and not .tar.gz and should perhaps be renamed
    * I neded to press ^c for it to get past the ‘cat’ of 0u
    * /sys/kernel/debug/usb/usbmon/u0 should be 0u, at least in ubuntu 11.10

    • Nathan says:

      I had the same issues you mentioned. Additionally the cat command produced no output except for when I inserted the dongle while cat was holding up the program. I suspect that this was in the script to purge this psudo-file before making use of it.

      The worse part is that the script didn’t seemed to work at all for me at first. My trackball came back after it’s done, but my keyboard never showed up. I was able to get it to pair by switching on the keyboard while the dongle was in pairing mode. This is most likely in the instructions for pairing under Windows, but those are long forgotten. It may be a good idea for the script to tell the user to turn off the not yet paired devices before talking to the dongle and then telling them to turn those devices on along with the 5 second message.

    • tequals0 says:

      I don’t know about that link, but you can pair new devices with my script, and the unifying system always only works with a single receiver at a time. You also do not ned to unpair the device- just pair it to a new receiver and it is good to go.

  3. Thorsten Schnebeck says:

    Hi,
    just tried to pair my old M505 and a new K360 on the unified receiver on Kubuntu 12.04
    I notices that the usbmon numberation of linux 3.2 is different:
    ls /sys/kernel/debug/usb/usbmon/
    0s 0u 1s 1t 1u 2s 2t 2u 3s 3t 3u 4s 4t 4u 5s 5t 5u 6s 6t 6u

    So I used “0u”
    os.system(‘cat /sys/kernel/debug/usb/usbmon/0u’)

    Than I also had to removed hid_logitech_dj and added
    os.system(‘sudo modprobe -r hid_logitech_dj’)

    And I had to press ctrl-c to interrupt ‘cat’
    Then everything worked as advertised – after replugging of the receiver 🙂

    Very nice! Thanks a lot!

    Thorsten

  4. gblogswildgc says:

    Nice. This python script worked while the .c utility on the kernal mailing list didn’t. That one just gave me error 32 and broken pipe. This one worked out of the gate. Awesome – thanks for doing this little hack! I tried the logitech utility on virtual box w/ usb and it woldn’t talk to the reciever, so this is a godsend.

  5. logitechack says:

    Hello everyone. I am trying to hack a Logitech unifying receiver to talk to an old incompatible VX Nano. I already know that I have a 2012 version Unifying USB receiver that is a newer version than the USB compatible with VX Nano. However I want to hack my way into pairing the two into a happy couple. I am using Windows 7. Any suggestions? Where should I start from?

    • tequals0 says:

      Maybe, but it would probably be tougher than just getting a bluetooth dongle. The unifying receiver may also lack an interface for sending/receiving bluetooth commands.

  6. Ankush Kohli says:

    Can the unifying receiver used as a Bluetooth device to pair my phone’s media with my laptop? (as my laptop doesn’t have an inbuilt Bluetooth) n if yes, How?

    • tequals0 says:

      I dont know- but probably not. the unifying receiver does work over blutooth frequencies, (IIRC, YMMV) but it is only accessable through the commands avalible at the usb interface, which limits the things you can do with it.

  7. Peter Wu says:

    I started RE’ing yesterday using usbmon and qemu and just stumbled upon http://julien.danjou.info/blog/2012/logitech-unifying-upower which links to a protocol spec: http://6xq.net/git/lars/lshidpp.git/plain/doc/logitech_hidpp_2.0_specification_draft_2012-06-04.pdf . Definitely worth checking out, as are the drivers/hid/hid-logitech-dj.[hc] (which learned me about report_id, device_index, report_type and report_params).

    You should always unload usbhid before using usbmon, I got an Oops due to null pointer dereference when starting qemu while usbhid also acquired the device. I created small awk script that colors the output and highlights the aforementioned fields, see https://lekensteyn.nl/files/logitech/. My notes may become available later, my ultimate goal is to create a program that allows devices to be discovered and (un)paired while understanding the protocol (instead of writing a sequence of random bytes without knowing what it does).

  8. Peter Wu says:

    That specification was incomplete, but I found something much more interesting: http://www.google.com/patents/US8386651 It contains pictures of the pairing program and describes the protocol in much more detail (29 pages including pictures). After the pictures and legal mumbo-jumbo, the interesting protocol description can be found on page 25 (it starts at the end of page 24)

  9. NilePlumb says:

    Is there a tweak/hack/patch available that allows you to use logitech keyboards/mice over a standard bluetooth receiver (or vice-versa, standard bluetooth over unifying reciever)? I just bought a Bluetooth 4.0 dongle/reciever/adapter and thought it’d be better to just burn through one USB port if possible.

  10. Red Baron says:

    I just bought a Logitech m185 mouse with a nano wireless receiver and not only it works perfectly normal (using Ubuntu 12.04LTS) but it even has the Linux-logo (along with Windows and MacOS) on the package.

    • tequals0 says:

      The nano receiver is different (iirc) it is a one device to one receiver pair. Unifying receivers work fine in one device mode in linux, but to pair multiple devices you need the logitech software or the script on this page.

  11. LukeGM says:

    Hi to anyone that’s looking for linux software to pair logitech unifying devices with the receiver. There’s now a package / program called solaar available (yes that solar spelt with an extra “a”). I’m not sure when this program became available, but I think only recently.
    The good thing is that it appears to provide all or almost all of the functionality that the logitech windows application does.
    Google “solaar” and away you go.
    Good Luck
    Lukegm
    ps. this doesn’t in anyway diminish the work done by our friend Tequals0, which was awesome.

    • Peter Wu says:

      Solaar has been around for some time, I found it after finishing ltunify. Details can be found on http://pwr.github.io/Solaar/

      Note that UPower also provides battery information (also visible in KDE’s battery monitor for example), so I have actually little use now Solaar. (pairing is provided by ltunify, battery information via `upower -d` or `upower -i …`).

    • tequals0 says:

      Yes, but it would take a long time. You would have to write custom firmware for the chip that is in the receivers and figure out a way to program them but it seems plausible. But if you want two things that have usb host to talk to each other, why not give each one a wifi connection?

  12. Ilan Bauminger says:

    I am trying to understand what protocol (I assume on top of the 2.4 G) is being used between the Keyboard and the PC.
    Can someone help?
    THANKS, ilan

  13. MJ says:

    Nice, thanks for this, do you thing it’s possible to connect one mouse to multiple receivers? Not that you can control 2 PCs at one time, but if one is off you control the other and vice versa..

  14. Slartibartfarst says:

    Interesting article. Thanks.
    I wonder if you could help to unravel a problem with this unifying process?
    Using Win10 the other day, I took 3 meeces (Logitech M185, M215, M515).
    They all worked fine, separately, using their 3 separate, individual dongles. They used the USB device “Logitech HID-compliant cordless mouse”.
    As M515 was a Unifying dongle, I downloaded the Unifying software from Logitech support.
    Then I installed it and, using the unifying software, paired the 3 meece to the M515 dongle. It worked a treat. Very impressive. The UI can display a report of the battery status (if the software can fetch the battery status) and the firmware version of each mouse paired.
    Then I unpaired them all. That seemed to work fine too, according to the UI report.
    Then I uninstalled the unifying software.
    However, when I then tried to use M185 with its own dongle, it simply would not work. Same for M215 and M515.
    So I re-installed the unifying software. Made no difference.
    I then re-pared the meece (using the Unifying software) to the M515 dongle. It worked a treat, as before.
    So, the unification seems to aggregate mice or other devices when it pairs, without discrimination, and it seems that the firmware within each mouse is changed on pairing, so THAT mouse can ONLY work through the unifying dongle, and henceforth THAT mouse cannot work through its original dongle as a “Logitech HID-compliant cordless mouse”.

    So, it seems that I may have just disabled 3 mice (or 2 at least), in testing out the Logitech pairing functionality.

    What I would like to know is, if the mouse firmware has indeed been changed (as would seem to be the case, by deduction), then how can one revert/restore that firmware to its default initial state?Leaving the batteries out of the mouse for a while doesn’t seem to do it.
    I haven’t found any info on this so far, which is why I posted this curiosity to your webpage.

    • tequals0 says:

      Hey Slartibartfarst- Each mouse can only be paired to one dongle, but each dongle can be paired to many mice. It kind of makes sense if you think about it- if you had a mouse paired to two dongles, you could end up with it talking to two computers- for most people that does not make any sense.

      On startup, the mouse will pair with the first receiver it sees with 0 paired devices- that is what happens when you take it out, plug it in, and it “just works”. If you want to do that again, you can try to find manual unpair options for the device- each one seems different, and generally involves holding some key or something while turning it on and off. Otherwise you will need the unifying software, which is designed to manage the pairing. If you have windows, it is probably best to just keep it.

Leave a reply to nestico Cancel reply