Skip to content

Instantly share code, notes, and snippets.

@twitchyliquid64
Created August 20, 2017 02:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save twitchyliquid64/a093ce11245274a2adeb631ccd2ba7eb to your computer and use it in GitHub Desktop.
Save twitchyliquid64/a093ce11245274a2adeb631ccd2ba7eb to your computer and use it in GitHub Desktop.
Example code for reading settings from the MCP2221 or MCP2221A chip, and writing product/vendor strings. Trivial to set/read other settings as well.
# Make sure you install pyusb and libusb on your system yo
import usb.core
import usb.util
import usb.control
HID_INTERFACE = 0x02
INPUT_ENDPOINT = 0x83
OUTPUT_ENDPOINT = 0x3
HID_PKT_SIZE = 64
STATUS_COMMAND = '\x10' + ('\x00' * 63)
class FlashError(Exception):
pass
class ByteDecoder(object):
def __init__(self, b, multiplier):
self.b = b
self.multiplier = multiplier
def value(self, data):
return data[self.b] * self.multiplier
class HexDecoder(object):
def __init__(self, start, end):
self.start = start
self.end = end
def value(self, data):
section = data[self.start:self.end]
section.reverse()
return ''.join('{:02x}'.format(x) for x in section)
class BitDecoder(object):
def __init__(self, byte, bit):
self.byte = byte
self.bit = bit
def value(self, data):
return bool(data[self.byte] & (1 << self.bit))
class EnumDecoder(object):
def __init__(self, byte, mask, opts):
self.byte = byte
self.mask = mask
self.opts = opts
def value(self, data):
return self.opts[data[self.byte] & self.mask]
READ_FLASH_CHIP_SETTINGS = '\x00'
CHIP_SETTINGS_MAP = {
'Provide serial number on enumeration': BitDecoder(4, 7),
'USB vendorID': HexDecoder(8, 10),
'USB productID': HexDecoder(10, 12),
'Requested mA': ByteDecoder(13, 2),
'Chip security': EnumDecoder(4, 0b11, {0: 'Unsecured', 1: 'Password-protected', 2: 'Permanently-locked', 3: 'Permanently-locked'})
}
READ_SERIAL_NUMBER = '\x04'
def getManufacturer(device):
try:
return usb.util.get_string(device, device.iManufacturer)
except ValueError:
return ''
def getProduct(device):
try:
return usb.util.get_string(device, device.iProduct)
except ValueError:
return ''
def writeFlash(device, data):
device.write(OUTPUT_ENDPOINT, '\xB1' + data)
info = device.read(INPUT_ENDPOINT, HID_PKT_SIZE)
assert info[0] == 0xB1
if info[1] == 0x02:
raise FlashError('Command not supported')
if info[1] == 0x03:
raise FlashError('Command not allowed')
def writeProduct(device, name):
if len(name) > 29:
raise ValueError('Too long')
ps = ''.join([c+'\x00' for c in name])
b = '\x03' + chr((2*len(name))+2) + '\x03' + ps
return writeFlash(device, b)
def writeManufacturer(device, name):
if len(name) > 29:
raise ValueError('Too long')
ps = ''.join([c+'\x00' for c in name])
b = '\x02' + chr((2*len(name))+2) + '\x03' + ps
return writeFlash(device, b)
def readFlash(device, section):
device.write(OUTPUT_ENDPOINT, '\xB0' + section + ('\x00' * 62))
info = device.read(INPUT_ENDPOINT, HID_PKT_SIZE)
assert info[0] == 0xB0
if info[1] == 0x01:
raise FlashError('Command not supported')
return info
def readChipSettings(device):
chip_settings = readFlash(device, READ_FLASH_CHIP_SETTINGS)
output = dict()
for attr in CHIP_SETTINGS_MAP:
output[attr] = CHIP_SETTINGS_MAP[attr].value(chip_settings)
return output
def readSerialNumber(device):
sn = readFlash(device, READ_SERIAL_NUMBER)
assert sn[3] == 0x3
length = sn[2]
l = [chr(x) if x > 0 else '' for x in sn[4:4+length]]
return ''.join(l)
def getStatus(device):
device.write(OUTPUT_ENDPOINT, STATUS_COMMAND)
info = device.read(INPUT_ENDPOINT, HID_PKT_SIZE)
assert info[0] == 0x10
output = {
'HW revision': chr(info[46]) + chr(info[47]),
'Firmware revision': chr(info[48]) + chr(info[49]),
'SN': readSerialNumber(device)
}
chip_settings = readChipSettings(device)
output.update(chip_settings)
return output
def lsUSB():
dev = usb.core.find(find_all=True)
for device in dev:
print '\033[92m ~=== ' + str(getProduct(device)) + ' ===~\033[0m'
print ' \033[95m' + str(getManufacturer(device)) + '\033[0m'
print ' productID=' + hex(device.idProduct) + ' vendorID=' + hex(device.idVendor)
print device.get_active_configuration()
if __name__ == '__main__':
device = usb.core.find(idVendor=0x4d8, idProduct=0xdd)
if device is None:
raise ValueError('No MCP2221A device found')
#device.reset() - try this line if shiz isnt working
#tell the kernel to stop tracking it
try:
if device.is_kernel_driver_active(HID_INTERFACE) is True:
device.detach_kernel_driver(HID_INTERFACE)
except usb.core.USBError as e:
raise ValueError("Kernel driver won't give up control over device: %s" % str(e))
#device.set_configuration() - try this line if shiz isnt working
#start using the device with pre-determined endpoint numbers
status = getStatus(device)
print getProduct(device) + ' found'
for attr in status:
print '\t %s => %s' % (attr, status[attr])
if raw_input("Everything looks good. Write manufacturer/product strings? (y/N): ").lower() == 'y':
writeProduct(device, '<PRODUCT NAME HERE>')
writeManufacturer(device, '<MANUFACTURER NAME HERE>')
else:
print("Abort.")
print("USB device information:")
lsUSB()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment