194 lines
8 KiB
Python
Executable file
194 lines
8 KiB
Python
Executable file
#!/home/pi/git/abschlussarbeit/bin/python3
|
|
|
|
"""Simple FTDI EEPROM configurator.
|
|
"""
|
|
|
|
# Copyright (c) 2019-2024, Emmanuel Blot <emmanuel.blot@free.fr>
|
|
# All rights reserved.
|
|
#
|
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
from argparse import ArgumentParser, FileType
|
|
from io import StringIO
|
|
from logging import Formatter, StreamHandler, DEBUG, ERROR
|
|
from sys import exit as sys_exit, modules, stderr, stdout
|
|
from textwrap import fill
|
|
from traceback import format_exc
|
|
from pyftdi import FtdiLogger
|
|
from pyftdi.eeprom import FtdiEeprom
|
|
from pyftdi.ftdi import Ftdi
|
|
from pyftdi.misc import add_custom_devices, hexdump
|
|
|
|
# pylint: disable=too-many-locals
|
|
# pylint: disable=too-many-branches
|
|
# pylint: disable=too-many-statements
|
|
|
|
|
|
def main():
|
|
"""Main routine"""
|
|
debug = False
|
|
try:
|
|
argparser = ArgumentParser(description=modules[__name__].__doc__)
|
|
argparser.add_argument('device', nargs='?', default='ftdi:///?',
|
|
help='serial port device name')
|
|
|
|
files = argparser.add_argument_group(title='Files')
|
|
files.add_argument('-i', '--input', type=FileType('rt'),
|
|
help='input ini file to load EEPROM content')
|
|
files.add_argument('-l', '--load', default='all',
|
|
choices=('all', 'raw', 'values'),
|
|
help='section(s) to load from input file')
|
|
files.add_argument('-o', '--output',
|
|
help='output ini file to save EEPROM content')
|
|
files.add_argument('-V', '--virtual', type=FileType('r'),
|
|
help='use a virtual device, specified as YaML')
|
|
|
|
device = argparser.add_argument_group(title='Device')
|
|
device.add_argument('-P', '--vidpid', action='append',
|
|
help='specify a custom VID:PID device ID '
|
|
'(search for FTDI devices)')
|
|
device.add_argument('-M', '--eeprom',
|
|
help='force an EEPROM model')
|
|
device.add_argument('-S', '--size', type=int,
|
|
choices=FtdiEeprom.eeprom_sizes,
|
|
help='force an EEPROM size')
|
|
|
|
fmt = argparser.add_argument_group(title='Format')
|
|
fmt.add_argument('-x', '--hexdump', action='store_true',
|
|
help='dump EEPROM content as ASCII')
|
|
fmt.add_argument('-X', '--hexblock', type=int,
|
|
help='dump EEPROM as indented hexa blocks')
|
|
|
|
config = argparser.add_argument_group(title='Configuration')
|
|
config.add_argument('-s', '--serial-number',
|
|
help='set serial number')
|
|
config.add_argument('-m', '--manufacturer',
|
|
help='set manufacturer name')
|
|
config.add_argument('-p', '--product',
|
|
help='set product name')
|
|
config.add_argument('-c', '--config', action='append',
|
|
help='change/configure a property as key=value '
|
|
'pair')
|
|
config.add_argument('--vid', type=lambda x: int(x, 16),
|
|
help='shortcut to configure the USB vendor ID')
|
|
config.add_argument('--pid', type=lambda x: int(x, 16),
|
|
help='shortcut to configure the USB product ID')
|
|
|
|
action = argparser.add_argument_group(title='Action')
|
|
action.add_argument('-e', '--erase', action='store_true',
|
|
help='erase the whole EEPROM content')
|
|
action.add_argument('-E', '--full-erase', action='store_true',
|
|
default=False,
|
|
help='erase the whole EEPROM content, including '
|
|
'the CRC')
|
|
action.add_argument('-u', '--update', action='store_true',
|
|
help='perform actual update, use w/ care')
|
|
|
|
extra = argparser.add_argument_group(title='Extras')
|
|
extra.add_argument('-v', '--verbose', action='count', default=0,
|
|
help='increase verbosity')
|
|
extra.add_argument('-d', '--debug', action='store_true',
|
|
help='enable debug mode')
|
|
args = argparser.parse_args()
|
|
debug = args.debug
|
|
|
|
if not args.device:
|
|
argparser.error('Serial device not specified')
|
|
|
|
loglevel = max(DEBUG, ERROR - (10 * args.verbose))
|
|
loglevel = min(ERROR, loglevel)
|
|
if debug:
|
|
formatter = Formatter('%(asctime)s.%(msecs)03d %(name)-20s '
|
|
'%(message)s', '%H:%M:%S')
|
|
else:
|
|
formatter = Formatter('%(message)s')
|
|
FtdiLogger.set_formatter(formatter)
|
|
FtdiLogger.set_level(loglevel)
|
|
FtdiLogger.log.addHandler(StreamHandler(stderr))
|
|
|
|
if args.virtual:
|
|
# pylint: disable=import-outside-toplevel
|
|
from pyftdi.usbtools import UsbTools
|
|
# Force PyUSB to use PyFtdi test framework for USB backends
|
|
UsbTools.BACKENDS = ('pyftdi.tests.backend.usbvirt', )
|
|
# Ensure the virtual backend can be found and is loaded
|
|
backend = UsbTools.find_backend()
|
|
loader = backend.create_loader()()
|
|
loader.load(args.virtual)
|
|
|
|
try:
|
|
add_custom_devices(Ftdi, args.vidpid, force_hex=True)
|
|
except ValueError as exc:
|
|
argparser.error(str(exc))
|
|
|
|
eeprom = FtdiEeprom()
|
|
eeprom.open(args.device, size=args.size, model=args.eeprom)
|
|
if args.erase or args.full_erase:
|
|
eeprom.erase()
|
|
if args.input:
|
|
eeprom.load_config(args.input, args.load)
|
|
if args.serial_number:
|
|
eeprom.set_serial_number(args.serial_number)
|
|
if args.manufacturer:
|
|
eeprom.set_manufacturer_name(args.manufacturer)
|
|
if args.product:
|
|
eeprom.set_product_name(args.product)
|
|
for conf in args.config or []:
|
|
if conf in ('?', 'help'):
|
|
helpstr = ', '.join(sorted(eeprom.properties))
|
|
print(fill(helpstr, initial_indent=' ',
|
|
subsequent_indent=' '))
|
|
sys_exit(1)
|
|
for sep in ':=':
|
|
if sep in conf:
|
|
name, value = conf.split(sep, 1)
|
|
if not value:
|
|
argparser.error(f'Configuration {conf} without value')
|
|
if value == 'help':
|
|
value = '?'
|
|
helpio = StringIO()
|
|
eeprom.set_property(name, value, helpio)
|
|
helpstr = helpio.getvalue()
|
|
if helpstr:
|
|
print(fill(helpstr, initial_indent=' ',
|
|
subsequent_indent=' '))
|
|
sys_exit(1)
|
|
break
|
|
else:
|
|
argparser.error(f'Missing name:value separator in {conf}')
|
|
if args.vid:
|
|
eeprom.set_property('vendor_id', args.vid)
|
|
if args.pid:
|
|
eeprom.set_property('product_id', args.pid)
|
|
if args.hexdump:
|
|
print(hexdump(eeprom.data))
|
|
if args.hexblock is not None:
|
|
indent = ' ' * args.hexblock
|
|
for pos in range(0, len(eeprom.data), 16):
|
|
hexa = ' '.join([f'{x:02x}' for x in eeprom.data[pos:pos+16]])
|
|
print(indent, hexa, sep='')
|
|
if args.update:
|
|
if eeprom.commit(False, no_crc=args.full_erase):
|
|
eeprom.reset_device()
|
|
if args.verbose > 0:
|
|
eeprom.dump_config()
|
|
if args.output:
|
|
if args.output == '-':
|
|
eeprom.save_config(stdout)
|
|
else:
|
|
with open(args.output, 'wt') as ofp:
|
|
eeprom.save_config(ofp)
|
|
|
|
except (ImportError, IOError, NotImplementedError, ValueError) as exc:
|
|
print(f'\nError: {exc}', file=stderr)
|
|
if debug:
|
|
print(format_exc(chain=False), file=stderr)
|
|
sys_exit(1)
|
|
except KeyboardInterrupt:
|
|
sys_exit(2)
|
|
finally:
|
|
eeprom.close()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|