def check_interfaces(): ok = True def test_serial_port(glob, name): try: with open_serial_port(glob): info('%s port is OK', name) return True except Exception: error('%s port is not working', name) return False info('Checking interfaces...') ok = test_serial_port(DEBUGGER_PORT_GDB_GLOB, 'GDB') and ok ok = test_serial_port(DEBUGGER_PORT_CLI_GLOB, 'CLI') and ok try: init_can_iface() info('CAN interface is OK') except Exception: logging.debug('CAN check error', exc_info=True) error('CAN interface is not working') ok = False if not ok: fatal('Required interfaces are not available. Please check your hardware configuration. ' 'If this application is running on a virtual machine, make sure that hardware ' 'sharing is configured correctly.')
def handle_serial_port_hanging(): fatal( 'DRWATSON HAS DETECTED A PROBLEM WITH CONNECTED HARDWARE AND NEEDS TO TERMINATE.\n' 'A serial port operation has timed out. This usually indicates a problem with the connected ' 'hardware or its drivers. Please disconnect all USB devices currently connected to this computer, ' "then connect them back and restart Drwatson. If you're using a virtual machine, please reboot it.", use_abort=True)
def generate_signed_image_for(comma_separated_uid_integers): import struct import os try: unique_id_integers = list( map(int, comma_separated_uid_integers.split(','))) if len(unique_id_integers) != 4: raise ValueError( 'Unique ID must be composed of exactly 4 integers') unique_id = struct.pack('<IIII', *unique_id_integers) assert len(unique_id) == 128 / 8 except Exception as ex: fatal('Could not parse unique ID: %s', ex) info('Requesting signature for unique ID %s', ' '.join(['%02x' % x for x in unique_id])) gensign_response = api.generate_signature(unique_id, PRODUCT_NAME) firmware_with_signature = firmware_base.ljust( SIGNATURE_OFFSET, b'\xFF') + gensign_response.signature image_name = '-'.join(map(str, unique_id_integers)) + '.bin' with open(image_name, 'wb') as f: f.write(firmware_with_signature) os.chmod(image_name, 0o666) info('Signed image stored into %r', image_name)
def check_interfaces(): ok = True def test_serial_port(glob, name): try: with open_serial_port(DEBUGGER_PORT_GDB_GLOB): info("%s port is OK", name) return True except Exception: error("%s port is not working", name) return False info("Checking interfaces...") ok = test_serial_port(DEBUGGER_PORT_GDB_GLOB, "GDB") and ok ok = test_serial_port(DEBUGGER_PORT_CLI_GLOB, "CLI") and ok try: init_can_iface() info("CAN interface is OK") except Exception: logging.debug("CAN check error", exc_info=True) error("CAN interface is not working") ok = False if not ok: fatal( "Required interfaces are not available. Please check your hardware configuration. " "If this application is running on a virtual machine, make sure that hardware " "sharing is configured correctly." )
def handle_serial_port_hanging(): fatal( "DRWATSON HAS DETECTED A PROBLEM WITH CONNECTED HARDWARE AND NEEDS TO TERMINATE.\n" "A serial port operation has timed out. This usually indicates a problem with the connected " "hardware or its drivers. Please disconnect all USB devices currently connected to this computer, " "then connect them back and restart Drwatson. If you're using a virtual machine, please reboot it.", use_abort=True, )
def resolve_adapter_uid(): if '/' not in args.iface: return iface_real = os.path.normpath(os.path.realpath(args.iface)) for p in glob.glob('/dev/serial/by-id/*'): p_real = os.path.normpath(os.path.realpath(p)) if p_real == iface_real: try: uid = re.findall(r'[-_]([0-9A-Fa-f]{8,32})', p)[0] except IndexError: uid = None logger.debug('SLCAN adapter port: %r --> %r, UID: %r', p, p_real, uid) return uid.lower() fatal('Could not determine the UID of the SLCAN adapter')
def generate_signed_image_for(comma_separated_uid_integers): import struct import os try: unique_id_integers = list(map(int, comma_separated_uid_integers.split(','))) if len(unique_id_integers) != 4: raise ValueError('Unique ID must be composed of exactly 4 integers') unique_id = struct.pack('<IIII', *unique_id_integers) assert len(unique_id) == 128 / 8 except Exception as ex: fatal('Could not parse unique ID: %s', ex) info('Requesting signature for unique ID %s', ' '.join(['%02x' % x for x in unique_id])) gensign_response = api.generate_signature(unique_id, PRODUCT_NAME) firmware_with_signature = firmware_base.ljust(SIGNATURE_OFFSET, b'\xFF') + gensign_response.signature image_name = '-'.join(map(str, unique_id_integers)) + '.bin' with open(image_name, 'wb') as f: f.write(firmware_with_signature) os.chmod(image_name, 0o666) info('Signed image stored into %r', image_name)
with open(image_name, 'wb') as f: f.write(firmware_with_signature) os.chmod(image_name, 0o666) info('Signed image stored into %r', image_name) with CLIWaitCursor(): firmware_base = download(args.firmware) assert 0 < len(firmware_base) <= SIGNATURE_OFFSET, 'Firmware size is incorrect' if args.generate_signed_image_for: generate_signed_image_for(args.generate_signed_image_for) exit(0) if not args.iface: fatal('Iface is required') execute_shell_command('ifconfig %s down && ip link set %s up type can bitrate %d sample-point 0.875', args.iface, args.iface, bootloader.CAN_BITRATE, ignore_failure=True) def load_and_start_firmware(bootloader_interface, firmware_image): while True: try: info('Flashing the firmware [%d bytes]...', len(firmware_image)) bootloader_interface.unlock() bootloader_interface.load_firmware(firmware_image) except Exception as ex: error('Flashing failed: %r', ex) if not input('Try harder?', yes_no=True): abort('Flashing failed') else:
f.write(firmware_with_signature) os.chmod(image_name, 0o666) info('Signed image stored into %r', image_name) with CLIWaitCursor(): firmware_base = download(args.firmware) assert 0 < len( firmware_base) <= SIGNATURE_OFFSET, 'Firmware size is incorrect' if args.generate_signed_image_for: generate_signed_image_for(args.generate_signed_image_for) exit(0) if not args.iface: fatal('Iface is required') execute_shell_command( 'ifconfig %s down && ip link set %s up type can bitrate %d sample-point 0.875', args.iface, args.iface, bootloader.CAN_BITRATE, ignore_failure=True) def load_and_start_firmware(bootloader_interface, firmware_image): while True: try: info('Flashing the firmware [%d bytes]...', len(firmware_image)) bootloader_interface.unlock() bootloader_interface.load_firmware(firmware_image) except Exception as ex:
def process_one_device(set_device_info): out = input( '1. Connect DroneCode Probe to the debug connector.\n' '2. Connect CAN to the first CAN connector on the Babel; leave the other CAN connector empty.\n' '3. Connect USB to the Micro USB port on the Babel.\n' '4. If you want to skip firmware upload, type F now.\n' '5. Press ENTER.') skip_fw_upload = 'f' in out.lower() if not skip_fw_upload: info('Loading the firmware') with CLIWaitCursor(): try: load_firmware_via_gdb( firmware_data, toolchain_prefix=TOOLCHAIN_PREFIX, load_offset=FLASH_OFFSET, gdb_port=glob_one(DEBUGGER_PORT_GDB_GLOB), gdb_monitor_scan_command='swdp_scan') except Exception as ex: logging.info('Firmware load error', exc_info=True) fatal( 'Could not load firmware; check the debug connector; error: %r', ex) else: info('Firmware upload skipped') info('Waiting for the device to boot...') wait_for_boot() with closing( drwatson.can.SLCAN(get_target_serial_port_symlink(), bitrate=CAN_BITRATE, default_timeout=1)) as drv_target: info('Reading Zubax ID...') zubax_id = read_zubax_id(drv_target) unique_id = b64decode(zubax_id['hw_unique_id']) product_id = zubax_id['product_id'] assert PRODUCT_NAME == product_id set_device_info(product_id, unique_id) info('Configuring the adapter...') drv_target.execute_cli_command( 'cfg set can.terminator_on 1') # Terminator ON logger.info('Adapter state:\n%s', drv_target.execute_cli_command('stat')) with closing(uavcan.driver.make_driver( args.iface, bitrate=CAN_BITRATE)) as drv_test: random_frames = [ make_random_can_frame() for _ in range(NUM_TEST_FRAMES) ] # If we're using another Zubax Babel as a CAN adapter, this command will disable its CAN power output. # Otherwise the command will silently fail. The CAN power output must be disabled because it interferes # with the power supply delivery testing. try: drv_test.execute_cli_command('cfg set can.power_on 0', lambda _: None) except Exception as ex: logger.debug('CAN adapter CLI command failed: %s', ex) info('Testing CAN bus exchange: target --> test') for idx, rf in enumerate(random_frames): drv_target.send(rf.id, rf.data, rf.extended) received = drv_test.receive(1) enforce(received is not None, 'Timeout when trying to receive frame %d', idx + 1) enforce( received.extended == rf.extended and received.data == rf.data and received.id == rf.id, 'Received frame %d [%r] does not match the reference [%r]', idx + 1, received, rf) info('Testing CAN bus exchange: test --> target') for idx, rf in enumerate(random_frames): drv_test.send(rf.id, rf.data, rf.extended) try: received = drv_target.receive(1) except TimeoutError: fatal('Timeout when trying to receive frame %d', idx + 1) enforce( received['ext'] == rf.extended and received['data'] == rf.data and received['id'] == rf.id, 'Received frame %d [%r] does not match the reference [%r]', idx + 1, received, rf) info('Test exchange OK (2x%d frames)', len(random_frames)) info('Testing power supply...') drv_target.execute_cli_command( 'cfg set can.power_on 0') # Bus power OFF time.sleep(2) stat = yaml.load(drv_target.execute_cli_command('stat')) enforce( BUS_VOLTAGE_RANGE_OFF[0] <= stat['bus_voltage'] <= BUS_VOLTAGE_RANGE_OFF[1], 'Invalid voltage on the bus (power is turned OFF): %r volts; ' 'there may be a short circuit on the board', stat['bus_voltage']) info('Bus voltage: %r', stat['bus_voltage']) drv_target.execute_cli_command( 'cfg set can.power_on 1') # Bus power ON time.sleep(2) stat = yaml.load(drv_target.execute_cli_command('stat')) enforce( BUS_VOLTAGE_RANGE_ON[0] <= stat['bus_voltage'] <= BUS_VOLTAGE_RANGE_ON[1], 'Invalid voltage on the bus (power is turned ON): %r volts; ' 'the power supply circuit is malfunctioning', stat['bus_voltage']) info('Bus voltage: %r', stat['bus_voltage']) info('Power supply is OK') info('Testing LED indicators...') # LED1 - CAN Power - Red # LED2 - Terminator ON - Orange # LED3 - Status - Blue # LED4 - CAN Activity - Green enforce(input('Is LED1 (CAN power, RED) turned on?', yes_no=True), 'CAN Power LED is not working') enforce( input('Is LED2 (terminator, ORANGE) turned on?', yes_no=True), 'Terminator and/or its LED are not working') enforce( input('Is LED3 (status, BLUE) blinking about once a second?', yes_no=True), 'Status LED is not working') def generate_traffic(): drv_target.send(0, b'', False) time.sleep(0.2) with BackgroundSpinner(generate_traffic): enforce( input('Is LED4 (activity, GREEN) blinking quickly?', yes_no=True), 'Activity LED is not working, or the bus has been disconnected' ) info('Resetting configuration to factory defaults...') drv_target.execute_cli_command('cfg erase') info('Installing signature...') # Getting the signature info('Requesting signature for unique ID %s', binascii.hexlify(unique_id).decode()) gen_sign_response = licensing_api.generate_signature( unique_id, PRODUCT_NAME) if gen_sign_response.new: info('New signature has been generated') else: info( 'This particular device was signed earlier, reusing the existing signature' ) base64_signature = b64encode(gen_sign_response.signature).decode() logger.info('Generated signature in Base64: %s', base64_signature) # Installing the signature; this may fail if the device has been signed earlier - the failure will be ignored out = drv_target.execute_cli_command('zubax_id %s' % base64_signature) logger.debug( 'Signature installation response (may fail, which is OK): %r', out) # Reading the signature back and verifying it installed_signature = read_zubax_id(drv_target)['hw_signature'] logger.info('Installed signature in Base64: %s', installed_signature) enforce( b64decode(installed_signature) == gen_sign_response.signature, 'Written signature does not match the generated signature') info('Signature has been installed and verified')