#!/usr/bin/env python3 ########################################################################## # Copyright (c) 2019 ETH Zurich. # All rights reserved. # # This file is distributed under the terms in the attached LICENSE file. # If you do not find this file, copies can be found by writing to: # ETH Zurich D-INFK, CAB F.78, Universitaetstr. 6, CH-8092 Zurich, # Attn: Systems Group. # # Script to control the Toradex boards over the USB UART # ########################################################################## import os import sys import time import usb import argparse import cmd class AOSBoard: BITMODE_CBUS = 0x20 SIO_SET_BITMODE_REQUEST = 0x0b def __init__(self, dev): self._dev = dev def _ftdi_set_bitmode(self, bitmask): """ FTDIs CBUS bitmode expect the following value: CBUS Bits 3210 3210 |------ Output Control 0->LO, 1->HI |----------- Input/Output 0->Input, 1->Output This script assumes: - CBUS3 connected to RESET_EXT# - CBUS2 connected to OE# (recovery mode) """ bm_request_type = usb.util.build_request_type( usb.util.CTRL_OUT, usb.util.CTRL_TYPE_VENDOR, usb.util.CTRL_RECIPIENT_DEVICE ) wValue = bitmask | (AOSBoard.BITMODE_CBUS << 32) self._dev.ctrl_transfer(bm_request_type, AOSBoard.SIO_SET_BITMODE_REQUEST, wValue) def on(self): # Set CBUS3 tristate, module run... self._ftdi_set_bitmode(0x00) def off(self): # Set CBUS3 low, module in reset... self._ftdi_set_bitmode(0x80) def reset(self): self.off() time.sleep(0.1) self.on() def enter_recovery(self): # Set recovery bit low self._ftdi_set_bitmode(0x40) time.sleep(0.2) # Set reset bit low self._ftdi_set_bitmode(0xC0) time.sleep(0.1) # Set reset bit tristate self._ftdi_set_bitmode(0x40) time.sleep(0.01) # Set recovery bit tristate self._ftdi_set_bitmode(0x00) class BoardShell(cmd.Cmd): def __init__(self, board): super().__init__() self._board = board self.prompt = "aos-ctrl> " def do_on(self, args): "Turn the board on." self._board.on() def do_off(self, args): "Turn the board off." self._board.off() def do_reset(self, args): "Reset the board." self._board.reset() def do_recovery(self, args): "Reset the board and enter recovery mode." self._board.enter_recovery() def do_exit(self, args): "Exit the command prompt." return True def postcmd(self, stop, line): return stop def get_usb_backend(): if sys.platform == "win32": # On Windows we can use libusb1 in combination with usbdk # https://github.com/libusb/libusb/wiki/Windows#Driver_Installation import usb.backend.libusb1 libusb_path = os.environ.get("LIBUSB_PATH") backend = usb.backend.libusb1.get_backend(find_library=lambda x: libusb_path) if backend is None: print("Couldn't find libusb backend. Try setting LIBUSB_PATH environment variable.", file=sys.stderr) return None # PyUSB doesn't expose libusb option to use usbdk # Workaround: https://github.com/pyusb/pyusb/issues/200#issuecomment-392621726 from ctypes import c_void_p, c_int backend.lib.libusb_set_option.argtypes = [c_void_p, c_int] backend.lib.libusb_set_option(backend.ctx, 1) return backend return None def main(args): dev = usb.core.find( custom_match=lambda d: \ d.idVendor == 0x0403 and d.idProduct == 0x6001 and ( (args.board is None) or (d.serial_number == args.board) ), backend=get_usb_backend() ) if dev is None: if args.board is None: print("No board found.", file=sys.stderr) else: print("Board with serial '%s' not found." % (args.board), file=sys.stderr) exit(1) board = AOSBoard(dev) shell = BoardShell(board) if args.command is None: shell.cmdloop() else: shell.onecmd(args.command) if __name__ == "__main__": parser = argparse.ArgumentParser(description="Controls the Toradex Colibri boards") parser.add_argument( "--board", "-b", metavar="SERIAL", type=str, help="Serial of the board to control" ) parser.add_argument( "command", metavar="CMD", type=str, nargs='?', help="Commands to run" ) args = parser.parse_args() main(args)