Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
unifying dongle firmware readout - tested on CU0007 with FW rev RQR12.11
#!/usr/bin/env python
# sorry this is Python 2 -- the bootloader trigger breaks with Python 3 and I was too lazy to debug
import usb, time, struct
# Logitech Unifying dongle
class unifying_dongle:
# constructor
def __init__(self):
# look for the dongle in Bootloader mode
if self.get_usb_device(0x046d, 0xaaaa):
# look for the dongle in HID mode
if self.get_usb_device(0x046d, 0xc52b):
# enter the bootloader
self.send_command(0x21, 0x09, 0x0210, 0x0002, "\x10\xFF\x80\xF0\x49\x43\x50", ep=0x83)
except usb.core.USBError:
# wait up to 5 seconds for the bootloader to show up
start = time.time()
while time.time() - start < 5:
if self.get_usb_device(0x046d, 0xaaaa, config=1):
except AttributeError:
# verify that we entered the bootloader
if not self.dongle:
raise Exception("failed to enter bootloader")
# open/claim a USB device
def get_usb_device(self, vid, pid, config=None):
self.dongle = usb.core.find(idVendor=vid, idProduct=pid)
if self.dongle:
for ep in range(3):
if self.dongle.is_kernel_driver_active(ep):
if config is None:
return True
return False
# send a command to the dongle
def send_command(self, request_type, request, value, index, data, ep=0x81, timeout=1000):
ret = self.dongle.ctrl_transfer(request_type, request, value, index, data, timeout)
response =, 32, timeout)
return response
# instantiate the dongle
dongle = unifying_dongle()
# dump the flash to dump.bin
with open("dump.bin", "wb") as f:
for x in range(0, 0x8000, 28):
resp = dongle.send_command(0x21, 0x09, 0x0200, 0x0000, "\x10" + struct.pack("!H", x) + struct.pack("H", 28) + "\x00"*27)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.