These are dangerous as you will import not only the functions defined in the other module but also all of it's imports! This makes automated cleanups brittle.
132 lines
3.5 KiB
Python
132 lines
3.5 KiB
Python
import logging
|
|
import errno
|
|
import usb.core as ucore
|
|
from binascii import hexlify, unhexlify
|
|
from .util import assert_status
|
|
from struct import unpack
|
|
from usb.core import USBError
|
|
from .blobs import init_hardcoded, init_hardcoded_clean_slate
|
|
|
|
supported_devices = [
|
|
(0x138a, 0x0090),
|
|
(0x138a, 0x0097),
|
|
(0x06cb, 0x009a),
|
|
]
|
|
|
|
|
|
class CancelledException(Exception):
|
|
pass
|
|
|
|
|
|
class Usb():
|
|
def __init__(self):
|
|
self.interrupt_cb = None
|
|
self.trace_enabled = False
|
|
self.dev = None
|
|
self.cancel = False
|
|
|
|
def open(self, vendor=None, product=None):
|
|
if vendor is not None and product is not None:
|
|
dev = ucore.find(idVendor=vendor, idProduct=product)
|
|
else:
|
|
|
|
def match(d):
|
|
return (d.idVendor, d.idProduct) in supported_devices
|
|
|
|
dev = ucore.find(custom_match=match)
|
|
|
|
self.open_dev(dev)
|
|
|
|
def open_devpath(self, busnum, address):
|
|
def match(d):
|
|
return d.bus == busnum and d.address == address
|
|
|
|
dev = ucore.find(custom_match=match)
|
|
|
|
self.open_dev(dev)
|
|
|
|
def open_dev(self, dev):
|
|
if dev is None:
|
|
raise Exception('No matching devices found')
|
|
|
|
self.dev = dev
|
|
self.dev.default_timeout = 15000
|
|
|
|
def close(self):
|
|
if self.dev is not None:
|
|
try:
|
|
self.dev.reset()
|
|
self.dev = None
|
|
except:
|
|
pass
|
|
|
|
def usb_dev(self):
|
|
return self.dev
|
|
|
|
def send_init(self):
|
|
#self.dev.set_configuration()
|
|
|
|
# TODO analyse responses, detect hardware type
|
|
assert_status(self.cmd(unhexlify('01'))) # RomInfo.get()
|
|
assert_status(self.cmd(unhexlify('19')))
|
|
|
|
# 43 -- get partition header(?) (02 -- fwext partition)
|
|
# c28c745a in response is a FwextBuildtime = 0x5A748CC2
|
|
rsp = self.cmd(unhexlify('4302')) # get_fw_info()
|
|
|
|
assert_status(self.cmd(init_hardcoded))
|
|
|
|
(err, ), rsp = unpack('<H', rsp[:2]), rsp[2:]
|
|
if err != 0:
|
|
# fwext is not loaded
|
|
logging.info('Clean slate')
|
|
self.cmd(init_hardcoded_clean_slate)
|
|
|
|
def cmd(self, out):
|
|
if callable(out):
|
|
out = out()
|
|
if not out:
|
|
return 0
|
|
self.trace('>cmd> %s' % hexlify(out).decode())
|
|
self.dev.write(1, out)
|
|
resp = self.dev.read(129, 100 * 1024)
|
|
resp = bytes(resp)
|
|
self.trace('<cmd< %s' % hexlify(resp).decode())
|
|
return resp
|
|
|
|
def read_82(self):
|
|
try:
|
|
resp = self.dev.read(130, 1024 * 1024, timeout=10000)
|
|
resp = bytes(resp)
|
|
self.trace('<130< %s' % hexlify(resp).decode())
|
|
return resp
|
|
except Exception as e:
|
|
self.trace('<130< Error: %s' % repr(e))
|
|
return None
|
|
|
|
# FIXME There is a chance of a race condition here
|
|
def cancel(self):
|
|
self.cancel = True
|
|
|
|
def wait_int(self):
|
|
self.cancel = False
|
|
|
|
while True:
|
|
try:
|
|
resp = self.dev.read(131, 1024, timeout=100)
|
|
resp = bytes(resp)
|
|
self.trace('<int< %s' % hexlify(resp).decode())
|
|
return resp
|
|
except USBError as e:
|
|
if e.errno == errno.ETIMEDOUT:
|
|
if self.cancel:
|
|
raise CancelledException()
|
|
else:
|
|
raise e
|
|
|
|
def trace(self, s):
|
|
if self.trace_enabled:
|
|
logging.debug(s)
|
|
|
|
|
|
usb = Usb()
|