Skip to content

Instantly share code, notes, and snippets.

@marcnewlin
Created May 23, 2020 03:10
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save marcnewlin/fc9c19aec598da46c02b5070fcdb9130 to your computer and use it in GitHub Desktop.
Save marcnewlin/fc9c19aec598da46c02b5070fcdb9130 to your computer and use it in GitHub Desktop.
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):
return
# look for the dongle in HID mode
if self.get_usb_device(0x046d, 0xc52b):
# enter the bootloader
try:
self.send_command(0x21, 0x09, 0x0210, 0x0002, "\x10\xFF\x80\xF0\x49\x43\x50", ep=0x83)
except usb.core.USBError:
pass
# wait up to 5 seconds for the bootloader to show up
start = time.time()
while time.time() - start < 5:
try:
if self.get_usb_device(0x046d, 0xaaaa, config=1):
break
except AttributeError:
continue
# 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):
self.dongle.detach_kernel_driver(ep)
if config is None:
self.dongle.set_configuration()
else:
self.dongle.set_configuration(config)
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 = self.dongle.read(ep, 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)
f.write(resp[4:4+resp[3]])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment