summaryrefslogtreecommitdiff
path: root/extra
diff options
context:
space:
mode:
Diffstat (limited to 'extra')
-rw-r--r--extra/README5
-rwxr-xr-xextra/cr50_rma_open/cr50_rma_open.py665
-rw-r--r--extra/ftdi_hostcmd/.gitignore1
-rw-r--r--extra/ftdi_hostcmd/Makefile37
-rw-r--r--extra/ftdi_hostcmd/README6
-rw-r--r--extra/ftdi_hostcmd/test_cmds.c620
-rw-r--r--extra/i2c_pseudo/.gitignore10
-rw-r--r--extra/i2c_pseudo/50-i2c-pseudo.rules1
-rw-r--r--extra/i2c_pseudo/Documentation.txt291
-rw-r--r--extra/i2c_pseudo/Makefile21
-rw-r--r--extra/i2c_pseudo/README20
-rwxr-xr-xextra/i2c_pseudo/check_stream_open.sh23
-rw-r--r--extra/i2c_pseudo/i2c-pseudo.c3212
-rwxr-xr-xextra/i2c_pseudo/install38
-rw-r--r--extra/lightbar/.gitignore1
-rw-r--r--extra/lightbar/Makefile28
-rw-r--r--extra/lightbar/README39
-rw-r--r--extra/lightbar/input.c80
-rw-r--r--extra/lightbar/main.c329
-rw-r--r--extra/lightbar/programs/bad-decode-32.bin1
-rw-r--r--extra/lightbar/programs/bad-decode-8.binbin3 -> 0 bytes
-rw-r--r--extra/lightbar/programs/bad-jump.bin1
-rw-r--r--extra/lightbar/programs/bad-opcode.bin1
-rw-r--r--extra/lightbar/programs/green-pulse.binbin19 -> 0 bytes
-rw-r--r--extra/lightbar/programs/green-pulse.lbs8
-rw-r--r--extra/lightbar/programs/infinite-jump.binbin2 -> 0 bytes
-rw-r--r--extra/lightbar/programs/infinite-jump.lbs1
-rw-r--r--extra/lightbar/programs/konami.binbin144 -> 0 bytes
-rw-r--r--extra/lightbar/programs/konami.lbs89
-rw-r--r--extra/lightbar/programs/rainbow-shift.binbin31 -> 0 bytes
-rw-r--r--extra/lightbar/programs/rainbow-shift.lbs8
-rw-r--r--extra/lightbar/programs/red-green-blink.binbin29 -> 0 bytes
-rw-r--r--extra/lightbar/programs/red-green-blink.lbs14
-rw-r--r--extra/lightbar/programs/s0.binbin58 -> 0 bytes
-rw-r--r--extra/lightbar/programs/s0.lbs22
-rw-r--r--extra/lightbar/programs/s0s3.binbin43 -> 0 bytes
-rw-r--r--extra/lightbar/programs/s0s3.lbs16
-rw-r--r--extra/lightbar/programs/s3.binbin37 -> 0 bytes
-rw-r--r--extra/lightbar/programs/s3.lbs17
-rw-r--r--extra/lightbar/programs/s3s0.binbin34 -> 0 bytes
-rw-r--r--extra/lightbar/programs/s3s0.lbs11
-rw-r--r--extra/lightbar/simulation.h117
-rw-r--r--extra/lightbar/windows.c293
-rw-r--r--extra/rma_reset/.gitignore5
-rw-r--r--extra/rma_reset/Makefile55
-rw-r--r--extra/rma_reset/board.h11
-rw-r--r--extra/rma_reset/rma_reset.c707
-rw-r--r--extra/sps_errs/.gitignore1
-rw-r--r--extra/sps_errs/Makefile37
-rw-r--r--extra/sps_errs/README28
-rw-r--r--extra/sps_errs/prog.c447
-rw-r--r--extra/stack_analyzer/README.md105
-rw-r--r--extra/stack_analyzer/example_annotation.yaml44
-rwxr-xr-xextra/stack_analyzer/run_tests.sh9
-rwxr-xr-xextra/stack_analyzer/stack_analyzer.py1872
-rwxr-xr-xextra/stack_analyzer/stack_analyzer_unittest.py830
-rw-r--r--extra/tigertool/README.md28
-rw-r--r--extra/tigertool/ecusb/__init__.py9
-rw-r--r--extra/tigertool/ecusb/pty_driver.py304
-rw-r--r--extra/tigertool/ecusb/stm32uart.py248
-rw-r--r--extra/tigertool/ecusb/stm32usb.py119
-rw-r--r--extra/tigertool/ecusb/tiny_servo_common.py174
-rw-r--r--extra/tigertool/ecusb/tiny_servod.py54
-rwxr-xr-xextra/tigertool/flash_dfu.sh57
-rwxr-xr-xextra/tigertool/make_pkg.sh28
-rwxr-xr-xextra/tigertool/tigertool.py266
-rw-r--r--extra/touchpad_updater/Makefile36
-rw-r--r--extra/touchpad_updater/touchpad_updater.c669
-rw-r--r--extra/usb_console/.gitignore1
-rw-r--r--extra/usb_console/Makefile34
-rw-r--r--extra/usb_console/usb_console.c457
-rw-r--r--extra/usb_gpio/.gitignore1
-rw-r--r--extra/usb_gpio/Makefile34
-rw-r--r--extra/usb_gpio/usb_gpio.c159
-rw-r--r--extra/usb_power/__init__.py0
-rw-r--r--extra/usb_power/board/kevin/kevin.board18
-rw-r--r--extra/usb_power/board/kevin/kevin_all.scenario18
-rw-r--r--extra/usb_power/board/marlin/marlin.board74
-rw-r--r--extra/usb_power/board/marlin/marlin_all_A.scenario42
-rw-r--r--extra/usb_power/board/marlin/marlin_all_B.scenario28
-rw-r--r--extra/usb_power/board/marlin/marlin_common.scenario1
-rw-r--r--extra/usb_power/board/marlin/marlin_pvc.scenario1
-rw-r--r--extra/usb_power/board/marlin/marlin_short.scenario1
-rw-r--r--extra/usb_power/board/marlin/marlin_vbat.scenario1
-rw-r--r--extra/usb_power/convert_power_log_board.py92
-rwxr-xr-xextra/usb_power/convert_servo_ina.py80
-rw-r--r--extra/usb_power/marlin_v.scenario1
-rw-r--r--extra/usb_power/powerlog.README.md210
-rwxr-xr-xextra/usb_power/powerlog.py908
-rw-r--r--extra/usb_power/powerlog_unittest.py54
-rw-r--r--extra/usb_power/stats_manager.py401
-rw-r--r--extra/usb_power/stats_manager_unittest.py315
-rw-r--r--extra/usb_serial/.gitignore12
-rw-r--r--extra/usb_serial/51-google-serial-fallback.rules6
-rw-r--r--extra/usb_serial/51-google-serial.rules33
-rw-r--r--extra/usb_serial/Makefile10
-rw-r--r--extra/usb_serial/README.md5
-rwxr-xr-xextra/usb_serial/add_usb_serial_id32
-rwxr-xr-xextra/usb_serial/console.py298
-rwxr-xr-xextra/usb_serial/install88
-rw-r--r--extra/usb_serial/raiden.c46
-rw-r--r--extra/usb_updater/.gitignore6
-rw-r--r--extra/usb_updater/Makefile55
-rw-r--r--extra/usb_updater/c2d2.json15
-rw-r--r--extra/usb_updater/desc_parser.c377
-rw-r--r--extra/usb_updater/desc_parser.h58
l---------extra/usb_updater/ecusb1
-rwxr-xr-xextra/usb_updater/fw_update.py426
-rw-r--r--extra/usb_updater/sample_descriptor87
-rw-r--r--extra/usb_updater/servo_micro.json15
-rwxr-xr-xextra/usb_updater/servo_updater.py456
-rw-r--r--extra/usb_updater/servo_v4.json15
-rw-r--r--extra/usb_updater/servo_v4p1.json15
-rw-r--r--extra/usb_updater/sweetberry.json14
-rw-r--r--extra/usb_updater/usb_updater2.c1244
115 files changed, 0 insertions, 18414 deletions
diff --git a/extra/README b/extra/README
deleted file mode 100644
index 874a7b1b32..0000000000
--- a/extra/README
+++ /dev/null
@@ -1,5 +0,0 @@
-
-This directory is for experiments only. It is not built automatically,
-required, supported, guaranteed to work, or necessarily well-documented.
-
-The contents may change without warning at any time.
diff --git a/extra/cr50_rma_open/cr50_rma_open.py b/extra/cr50_rma_open/cr50_rma_open.py
deleted file mode 100755
index 42ddbbac2d..0000000000
--- a/extra/cr50_rma_open/cr50_rma_open.py
+++ /dev/null
@@ -1,665 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-# Copyright 2018 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# Used to access the cr50 console and handle RMA Open
-"""Open cr50 using RMA authentication.
-
-Run RMA Open to enable CCD on Cr50. The utility can be used to get a
-url that will generate an authcode to open cr50. It can also be used to
-try opening cr50 with the generated authcode.
-
-The last challenge is the only valid one, so don't generate a challenge
-10 times and then use the first URL. You can only use the last one.
-
-For RMA Open:
-Connect suzyq to the dut and your workstation.
-
-Check the basic setup with
- sudo python cr50_rma_open.py -c
-
-If the setup is broken. Follow the debug print statements to try to fix
-the error. Rerun until the script says Cr50 setup ok.
-
-After the setup is verified, run the following command to generate the
-challenge url
- sudo python cr50_rma_open.py -g -i $HWID
-
-Go to the URL from by that command to generate an authcode. Once you have
-the authcode, you can use it to open cr50.
- sudo python cr50_rma_open.py -a $AUTHCODE
-
-If for some reason hardware write protect doesn't get disabled during rma
-open or gets enabled at some point the script can be used to disable
-write protect.
- sudo python cr50_rma_open.py -w
-
-When prepping devices for the testlab, you need to enable testlab mode.
-Prod cr50 images can't enable testlab mode. If the device is running a
-prod image, you can skip this step.
- sudo python cr50_rma_open.py -t
-"""
-
-import argparse
-import glob
-import logging
-import re
-import subprocess
-import sys
-import time
-
-import serial
-
-SCRIPT_VERSION = 5
-CCD_IS_UNRESTRICTED = 1 << 0
-WP_IS_DISABLED = 1 << 1
-TESTLAB_IS_ENABLED = 1 << 2
-RMA_OPENED = CCD_IS_UNRESTRICTED | WP_IS_DISABLED
-URL = ('https://www.google.com/chromeos/partner/console/cr50reset?'
- 'challenge=%s&hwid=%s')
-RMA_SUPPORT_PROD = '0.3.3'
-RMA_SUPPORT_PREPVT = '0.4.5'
-DEV_MODE_OPEN_PROD = '0.3.9'
-DEV_MODE_OPEN_PREPVT = '0.4.7'
-TESTLAB_PROD = '0.3.10'
-CR50_USB = '18d1:5014'
-CR50_LSUSB_CMD = ['lsusb', '-vd', CR50_USB]
-ERASED_BID = 'ffffffff'
-
-DEBUG_MISSING_USB = """
-Unable to find Cr50 Device 18d1:5014
-
-DEBUG MISSING USB:
- - Make sure suzyq is plugged into the correct DUT port
- - Try flipping the cable
- - unplug the cable for 5s then plug it back in
-"""
-
-DEBUG_DEVICE = """
-DEBUG DEVICE COMMUNICATION:
-Issues communicating with %s
-
-A 18d1:5014 device exists, so make sure you have selected the correct
-/dev/ttyUSB
-"""
-
-DEBUG_SERIALNAME = """
-DEBUG SERIALNAME:
-Found the USB device, but can't match the usb serialname. Check the
-serialname you passed into cr50_rma_open or try running without a
-serialname.
-"""
-
-DEBUG_CONNECTION = """
-DEBUG CONNECTION:
-Found the USB device but cant communicate with any of the consoles.
-
-Try Running cr50_rma_open again. If it still fails unplug the ccd cable
-for 5 seconds and plug it back in.
-"""
-
-DEBUG_TOO_MANY_USB_DEVICES = """
-DEBUG SELECT USB:
-More than one cr50 usb device was found. Disconnect all but one device
-or use the -s option with the correct usb serialname.
-"""
-
-DEBUG_ERASED_BOARD_ID = """
-DEBUG ERASED BOARD ID:
-If you are using a prePVT device run
-/usr/share/cros/cr50-set-board-id.sh proto
-
-If you are running a MP device, please talk to someone.
-"""
-
-DEBUG_AUTHCODE_MISMATCH = """
-DEBUG AUTHCODE MISMATCH:
- - Check the URL matches the one generated by the last cr50_rma_open
- run.
- - Check you used the correct authcode.
- - Make sure the cr50 version is greater than 3.3.
- - try generating another URL by rerunning the generate command and
- rerunning the process.
-"""
-
-DEBUG_DUT_CONTROL_OSERROR = """
-Run from chroot if you are trying to use a /dev/pts ccd servo console
-"""
-
-class RMAOpen(object):
- """Used to find the cr50 console and run RMA open"""
-
- ENABLE_TESTLAB_CMD = 'ccd testlab enabled\n'
-
- def __init__(self, device=None, usb_serial=None, servo_port=None, ip=None):
- self.servo_port = servo_port if servo_port else '9999'
- self.ip = ip
- if device:
- self.set_cr50_device(device)
- elif servo_port:
- self.find_cr50_servo_uart()
- else:
- self.find_cr50_device(usb_serial)
- logging.info('DEVICE: %s', self.device)
- self.check_version()
- self.print_platform_info()
- logging.info('Cr50 setup ok')
- self.update_ccd_state()
- self.using_ccd = self.device_is_running_with_servo_ccd()
-
- def _dut_control(self, control):
- """Run dut-control and return the response"""
- try:
- cmd = ['dut-control', '-p', self.servo_port, control]
- return subprocess.check_output(cmd, encoding='utf-8').strip()
- except OSError:
- logging.warning(DEBUG_DUT_CONTROL_OSERROR)
- raise
-
- def find_cr50_servo_uart(self):
- """Save the device used for the console.
-
- Find the console and configure it, so it can be used with this script.
- """
- self._dut_control('cr50_uart_timestamp:off')
- self.device = self._dut_control('cr50_uart_pty').split(':')[-1]
-
- def set_cr50_device(self, device):
- """Save the device used for the console"""
- self.device = device
-
- def send_cmd_get_output(self, cmd, nbytes=0):
- """Send a cr50 command and get the output
-
- Args:
- cmd: The cr50 command string
- nbytes: The number of bytes to read from the console. If 0 read all
- of the console output.
- Returns:
- The command output
- """
- try:
- ser = serial.Serial(self.device, timeout=1)
- except OSError:
- logging.warning('Permission denied %s', self.device)
- logging.warning('Try running cr50_rma_open with sudo')
- raise
- write_cmd = cmd + '\n\n'
- ser.write(write_cmd.encode('utf-8'))
- if nbytes:
- output = ser.read(nbytes)
- else:
- output = ser.readall()
- ser.close()
-
- output = output.decode('utf-8').strip() if output else ''
- # Return only the command output
- split_cmd = cmd + '\r'
- if cmd and split_cmd in output:
- return ''.join(output.rpartition(split_cmd)[1::]).split('>')[0]
- return output
-
- def device_is_running_with_servo_ccd(self):
- """Return True if the device is a servod ccd console"""
- # servod uses /dev/pts consoles. Non-servod uses /dev/ttyUSBX
- if '/dev/pts' not in self.device:
- return False
- # If cr50 doesn't show rdd is connected, cr50 the device must not be
- # a ccd device
- if 'Rdd: connected' not in self.send_cmd_get_output('ccdstate'):
- return False
- # Check if the servod is running with ccd. This requires the script
- # is run in the chroot, so run it last.
- if 'ccd_cr50' not in self._dut_control('servo_type'):
- return False
- logging.info('running through servod ccd')
- return True
-
- def get_rma_challenge(self):
- """Get the rma_auth challenge
-
- There are two challenge formats
-
- "
- ABEQ8 UGA4F AVEQP SHCKV
- DGGPR N8JHG V8PNC LCHR2
- T27VF PRGBS N3ZXF RCCT2
- UBMKP ACM7E WUZUA A4GTN
- "
- and
- "
- generated challenge:
-
- CBYRYBEMH2Y75TC...rest of challenge
- "
- support extracting the challenge from both.
-
- Returns:
- The RMA challenge with all whitespace removed.
- """
- output = self.send_cmd_get_output('rma_auth').strip()
- logging.info('rma_auth output:\n%s', output)
- # Extract the challenge from the console output
- if 'generated challenge:' in output:
- return output.split('generated challenge:')[-1].strip()
- challenge = ''.join(re.findall(r' \S{5}' * 4, output))
- # Remove all whitespace
- return re.sub(r'\s', '', challenge)
-
- def generate_challenge_url(self, hwid):
- """Get the rma_auth challenge
-
- Returns:
- The RMA challenge with all whitespace removed.
- """
-
- challenge = self.get_rma_challenge()
- self.print_platform_info()
- logging.info('CHALLENGE: %s', challenge)
- logging.info('HWID: %s', hwid)
- url = URL % (challenge, hwid)
- logging.info('GOTO:\n %s', url)
- logging.info('If the server fails to debug the challenge make sure the '
- 'RLZ is allowlisted')
-
- def try_authcode(self, authcode):
- """Try opening cr50 with the authcode
-
- Raises:
- ValueError if there was no authcode match and ccd isn't open
- """
- # rma_auth may cause the system to reboot. Don't wait to read all that
- # output. Read the first 300 bytes and call it a day.
- output = self.send_cmd_get_output('rma_auth ' + authcode, nbytes=300)
- logging.info('CR50 RESPONSE: %s', output)
- logging.info('waiting for cr50 reboot')
- # Cr50 may be rebooting. Wait a bit
- time.sleep(5)
- if self.using_ccd:
- # After reboot, reset the ccd endpoints
- self._dut_control('power_state:ccd_reset')
- # Update the ccd state after the authcode attempt
- self.update_ccd_state()
-
- authcode_match = 'process_response: success!' in output
- if not self.check(CCD_IS_UNRESTRICTED):
- if not authcode_match:
- logging.warning(DEBUG_AUTHCODE_MISMATCH)
- message = 'Authcode mismatch. Check args and url'
- else:
- message = 'Could not set all capability privileges to Always'
- raise ValueError(message)
-
- def wp_is_force_disabled(self):
- """Returns True if write protect is forced disabled"""
- output = self.send_cmd_get_output('wp')
- wp_state = output.split('Flash WP:', 1)[-1].split('\n', 1)[0].strip()
- logging.info('wp: %s', wp_state)
- return wp_state == 'forced disabled'
-
- def testlab_is_enabled(self):
- """Returns True if testlab mode is enabled"""
- output = self.send_cmd_get_output('ccd testlab')
- testlab_state = output.split('mode')[-1].strip().lower()
- logging.info('testlab: %s', testlab_state)
- return testlab_state == 'enabled'
-
- def ccd_is_restricted(self):
- """Returns True if any of the capabilities are still restricted"""
- output = self.send_cmd_get_output('ccd')
- if 'Capabilities' not in output:
- raise ValueError('Could not get ccd output')
- logging.debug('CURRENT CCD SETTINGS:\n%s', output)
- restricted = 'IfOpened' in output or 'IfUnlocked' in output
- logging.info('ccd: %srestricted', '' if restricted else 'Un')
- return restricted
-
- def update_ccd_state(self):
- """Get the wp and ccd state from cr50. Save it in _ccd_state"""
- self._ccd_state = 0
- if not self.ccd_is_restricted():
- self._ccd_state |= CCD_IS_UNRESTRICTED
- if self.wp_is_force_disabled():
- self._ccd_state |= WP_IS_DISABLED
- if self.testlab_is_enabled():
- self._ccd_state |= TESTLAB_IS_ENABLED
-
- def check(self, setting):
- """Returns true if the all of the 1s in setting are 1 in _ccd_state"""
- return self._ccd_state & setting == setting
-
- def _has_testlab_support(self):
- """Return True if you can enable testlab mode"""
- # all prepvt images can enable testlab
- if self.is_prepvt:
- return True
- return not self._running_version_is_older(DEV_MODE_OPEN_PROD)
-
- def _capabilities_allow_open_from_console(self):
- """Return True if ccd open is Always allowed from usb"""
- output = self.send_cmd_get_output('ccd')
- return (re.search('OpenNoDevMode.*Always', output) and
- re.search('OpenFromUSB.*Always', output))
-
- def _requires_dev_mode_open(self):
- """Return True if the image requires dev mode to open"""
- if self._capabilities_allow_open_from_console():
- return False
- # All prod images that support 'open' require dev mode
- if not self.is_prepvt:
- return True
- return not self._running_version_is_older(DEV_MODE_OPEN_PREPVT)
-
- def _run_on_dut(self, command):
- """Run the command on the DUT."""
- return subprocess.check_output(['ssh', self.ip, command],
- encoding='utf-8')
-
- def _open_in_dev_mode(self):
- """Open Cr50 when it's in dev mode"""
- output = self.send_cmd_get_output('ccd')
- # If the device is already open, nothing needs to be done.
- if 'State: Open' not in output:
- # Verify the device is in devmode before trying to run open.
- if 'dev_mode' not in output:
- logging.warning('Enter dev mode to open ccd or update to %s',
- TESTLAB_PROD)
- raise ValueError('DUT not in dev mode')
- if not self.ip:
- logging.warning("If your DUT doesn't have ssh support, run "
- "'gsctool -a -o' from the AP")
- raise ValueError('Cannot run ccd open without dut ip')
- self._run_on_dut('gsctool -a -o')
- # Wait >1 second for cr50 to update ccd state
- time.sleep(3)
- output = self.send_cmd_get_output('ccd')
- if 'State: Open' not in output:
- raise ValueError('Could not open cr50')
- logging.info('ccd is open')
-
- def enable_testlab(self):
- """Disable write protect"""
- if not self._has_testlab_support():
- logging.warning('Testlab mode is not supported in prod iamges')
- return
- # Some cr50 images need to be in dev mode before they can be opened.
- if self._requires_dev_mode_open():
- self._open_in_dev_mode()
- else:
- self.send_cmd_get_output('ccd open')
- logging.info('Enabling testlab mode reqires pressing the power button.')
- logging.info('Once the process starts keep tapping the power button '
- 'for 10 seconds.')
- input("Press Enter when you're ready to start...")
- end_time = time.time() + 15
-
- ser = serial.Serial(self.device, timeout=1)
- printed_lines = ''
- output = ''
- # start ccd testlab enable
- ser.write(self.ENABLE_TESTLAB_CMD.encode('utf-8'))
- logging.info('start pressing the power button\n\n')
- # Print all of the cr50 output as we get it, so the user will have more
- # information about pressing the power button. Tapping the power button
- # a couple of times should do it, but this will give us more confidence
- # the process is still running/worked.
- try:
- while time.time() < end_time:
- output += ser.read(100).decode('utf-8')
- full_lines = output.rsplit('\n', 1)[0]
- new_lines = full_lines
- if printed_lines:
- new_lines = full_lines.split(printed_lines, 1)[-1].strip()
- logging.info('\n%s', new_lines)
- printed_lines = full_lines
-
- # Make sure the process hasn't ended. If it has, print the last
- # of the output and exit.
- new_lines = output.split(printed_lines, 1)[-1]
- if 'CCD test lab mode enabled' in output:
- # print the last of the ou
- logging.info(new_lines)
- break
- elif 'Physical presence check timeout' in output:
- logging.info(new_lines)
- logging.warning('Did not detect power button press in time')
- raise ValueError('Could not enable testlab mode try again')
- finally:
- ser.close()
- # Wait for the ccd hook to update things
- time.sleep(3)
- # Update the state after attempting to disable write protect
- self.update_ccd_state()
- if not self.check(TESTLAB_IS_ENABLED):
- raise ValueError('Could not enable testlab mode try again')
-
- def wp_disable(self):
- """Disable write protect"""
- logging.info('Disabling write protect')
- self.send_cmd_get_output('wp disable')
- # Update the state after attempting to disable write protect
- self.update_ccd_state()
- if not self.check(WP_IS_DISABLED):
- raise ValueError('Could not disable write protect')
-
- def check_version(self):
- """Make sure cr50 is running a version that supports RMA Open"""
- output = self.send_cmd_get_output('version')
- if not output.strip():
- logging.warning(DEBUG_DEVICE, self.device)
- raise ValueError('Could not communicate with %s' % self.device)
-
- version = re.search(r'RW.*\* ([\d\.]+)/', output).group(1)
- logging.info('Running Cr50 Version: %s', version)
- self.running_ver_fields = [int(field) for field in version.split('.')]
-
- # prePVT images have even major versions. Prod have odd
- self.is_prepvt = self.running_ver_fields[1] % 2 == 0
- rma_support = RMA_SUPPORT_PREPVT if self.is_prepvt else RMA_SUPPORT_PROD
-
- logging.info('%s RMA support added in: %s',
- 'prePVT' if self.is_prepvt else 'prod', rma_support)
- if not self.is_prepvt and self._running_version_is_older(TESTLAB_PROD):
- raise ValueError('Update cr50. No testlab support in old prod '
- 'images.')
- if self._running_version_is_older(rma_support):
- raise ValueError('%s does not have RMA support. Update to at '
- 'least %s' % (version, rma_support))
-
- def _running_version_is_older(self, target_ver):
- """Returns True if running version is older than target_ver."""
- target_ver_fields = [int(field) for field in target_ver.split('.')]
- for i, field in enumerate(self.running_ver_fields):
- if field > int(target_ver_fields[i]):
- return False
- return True
-
- def device_matches_devid(self, devid, device):
- """Return True if the device matches devid.
-
- Use the sysinfo output from device to determine if it matches devid
-
- Returns:
- True if sysinfo from device shows the given devid. False if there
- is no output or sysinfo doesn't contain the devid.
- """
- self.set_cr50_device(device)
- sysinfo = self.send_cmd_get_output('sysinfo')
- # Make sure there is some output, and it shows it's from Cr50
- if not sysinfo or 'cr50' not in sysinfo:
- return False
- logging.debug('Sysinfo output: %s', sysinfo)
- # The cr50 device id should be in the sysinfo output, if we found
- # the right console. Make sure it is
- return devid in sysinfo
-
- def find_cr50_device(self, usb_serial):
- """Find the cr50 console device
-
- The Cr50 usb serialname matches the cr50 devid. Convert the serialname
- to devid. Use that to check all of the consoles and find cr50's.
-
- Args:
- usb_serial: an optional string. The serialname of the cr50 usb
- device
- Raises:
- ValueError if the console can't be found with the given serialname
- """
- usb_serial = self.find_cr50_usb(usb_serial)
- logging.info('SERIALNAME: %s', usb_serial)
- devid = '0x' + ' 0x'.join(usb_serial.lower().split('-'))
- logging.info('DEVID: %s', devid)
-
- # Get all the usb devices
- devices = glob.glob('/dev/ttyUSB*')
- # Typically Cr50 has the lowest number. Sort the devices, so we're more
- # likely to try the cr50 console first.
- devices.sort()
-
- # Find the one that is the cr50 console
- for device in devices:
- logging.info('testing %s', device)
- if self.device_matches_devid(devid, device):
- logging.info('found device: %s', device)
- return
- logging.warning(DEBUG_CONNECTION)
- raise ValueError('Found USB device, but could not communicate with '
- 'cr50 console')
-
- def print_platform_info(self):
- """Print the cr50 BID RLZ code"""
- bid_output = self.send_cmd_get_output('bid')
- bid = re.search(r'Board ID: (\S+?)[:,]', bid_output).group(1)
- if bid == ERASED_BID:
- logging.warning(DEBUG_ERASED_BOARD_ID)
- raise ValueError('Cannot run RMA Open when board id is erased')
- bid = int(bid, 16)
- chrs = [chr((bid >> (8 * i)) & 0xff) for i in range(4)]
- logging.info('RLZ: %s', ''.join(chrs[::-1]))
-
- @staticmethod
- def find_cr50_usb(usb_serial):
- """Make sure the Cr50 USB device exists"""
- try:
- output = subprocess.check_output(CR50_LSUSB_CMD, encoding='utf-8')
- except:
- logging.warning(DEBUG_MISSING_USB)
- raise ValueError('Could not find Cr50 USB device')
- serialnames = re.findall(r'iSerial +\d+ (\S+)\s', output)
- if usb_serial:
- if usb_serial not in serialnames:
- logging.warning(DEBUG_SERIALNAME)
- raise ValueError('Could not find usb device "%s"' % usb_serial)
- return usb_serial
- if len(serialnames) > 1:
- logging.info('Found Cr50 device serialnames %s',
- ', '.join(serialnames))
- logging.warning(DEBUG_TOO_MANY_USB_DEVICES)
- raise ValueError('Too many cr50 usb devices')
- return serialnames[0]
-
- def print_dut_state(self):
- """Print CCD RMA and testlab mode state."""
- if not self.check(CCD_IS_UNRESTRICTED):
- logging.info('CCD is still restricted.')
- logging.info('Run cr50_rma_open.py -g -i $HWID to generate a url')
- logging.info('Run cr50_rma_open.py -a $AUTHCODE to open cr50 with '
- 'an authcode')
- elif not self.check(WP_IS_DISABLED):
- logging.info('WP is still enabled.')
- logging.info('Run cr50_rma_open.py -w to disable write protect')
- if self.check(RMA_OPENED):
- logging.info('RMA Open complete')
-
- if not self.check(TESTLAB_IS_ENABLED) and self.is_prepvt:
- logging.info('testlab mode is disabled.')
- logging.info('If you are prepping a device for the testlab, you '
- 'should enable testlab mode.')
- logging.info('Run cr50_rma_open.py -t to enable testlab mode')
-
-
-def parse_args(argv):
- """Get cr50_rma_open args."""
- parser = argparse.ArgumentParser(
- description=__doc__, formatter_class=argparse.RawTextHelpFormatter)
- parser.add_argument('-g', '--generate_challenge', action='store_true',
- help='Generate Cr50 challenge. Must be used with -i')
- parser.add_argument('-t', '--enable_testlab', action='store_true',
- help='enable testlab mode')
- parser.add_argument('-w', '--wp_disable', action='store_true',
- help='Disable write protect')
- parser.add_argument('-c', '--check_connection', action='store_true',
- help='Check cr50 console connection works')
- parser.add_argument('-s', '--serialname', type=str, default='',
- help='The cr50 usb serialname')
- parser.add_argument('-D', '--debug', action='store_true',
- help='print debug messages')
- parser.add_argument('-d', '--device', type=str, default='',
- help='cr50 console device ex /dev/ttyUSB0')
- parser.add_argument('-i', '--hwid', type=str, default='',
- help='The board hwid. Needed to generate a challenge')
- parser.add_argument('-a', '--authcode', type=str, default='',
- help='The authcode string from the challenge url')
- parser.add_argument('-P', '--servo_port', type=str, default='',
- help='the servo port')
- parser.add_argument('-I', '--ip', type=str, default='',
- help='The DUT IP. Necessary to do ccd open')
- return parser.parse_args(argv)
-
-
-def main(argv):
- """Run cr50 rma open."""
- opts = parse_args(argv)
-
- loglevel = logging.INFO
- log_format = '%(levelname)7s'
- if opts.debug:
- loglevel = logging.DEBUG
- log_format += ' - %(lineno)3d:%(funcName)-15s'
- log_format += ' - %(message)s'
- logging.basicConfig(level=loglevel, format=log_format)
-
- tried_authcode = False
- logging.info('Running cr50_rma_open version %s', SCRIPT_VERSION)
-
- cr50_rma_open = RMAOpen(opts.device, opts.serialname, opts.servo_port,
- opts.ip)
- if opts.check_connection:
- sys.exit(0)
-
- if not cr50_rma_open.check(CCD_IS_UNRESTRICTED):
- if opts.generate_challenge:
- if not opts.hwid:
- logging.warning('--hwid necessary to generate challenge url')
- sys.exit(0)
- cr50_rma_open.generate_challenge_url(opts.hwid)
- sys.exit(0)
- elif opts.authcode:
- logging.info('Using authcode: %s', opts.authcode)
- cr50_rma_open.try_authcode(opts.authcode)
- tried_authcode = True
-
- if not cr50_rma_open.check(WP_IS_DISABLED) and (tried_authcode or
- opts.wp_disable):
- if not cr50_rma_open.check(CCD_IS_UNRESTRICTED):
- raise ValueError("Can't disable write protect unless ccd is "
- "open. Run through the rma open process first")
- if tried_authcode:
- logging.warning('RMA Open did not disable write protect. File a '
- 'bug')
- logging.warning('Trying to disable it manually')
- cr50_rma_open.wp_disable()
-
- if not cr50_rma_open.check(TESTLAB_IS_ENABLED) and opts.enable_testlab:
- if not cr50_rma_open.check(CCD_IS_UNRESTRICTED):
- raise ValueError("Can't enable testlab mode unless ccd is open."
- "Run through the rma open process first")
- cr50_rma_open.enable_testlab()
-
- cr50_rma_open.print_dut_state()
-
-
-if __name__ == '__main__':
- sys.exit(main(sys.argv[1:]))
diff --git a/extra/ftdi_hostcmd/.gitignore b/extra/ftdi_hostcmd/.gitignore
deleted file mode 100644
index 9831af29be..0000000000
--- a/extra/ftdi_hostcmd/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-test_cmds
diff --git a/extra/ftdi_hostcmd/Makefile b/extra/ftdi_hostcmd/Makefile
deleted file mode 100644
index d46b4b1c72..0000000000
--- a/extra/ftdi_hostcmd/Makefile
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright 2015 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# Use your own libmpsse if you want, but we're going to use the files
-# that are part of the Chromium OS trunks_client program.
-PLATFORM2 = ../../../../platform2
-MPSSE_DIR = $(PLATFORM2)/trunks/ftdi
-
-PROG = test_cmds
-SRCS = test_cmds.c $(MPSSE_DIR)/mpsse.c $(MPSSE_DIR)/support.c
-
-CFLAGS = \
- -std=gnu99 \
- -g3 \
- -O3 \
- -Wall \
- -Werror \
- -Wpointer-arith \
- -Wcast-align \
- -Wcast-qual \
- -Wundef \
- -Wsign-compare \
- -Wredundant-decls \
- -Wmissing-declarations
-
-CFLAGS += -I../../include -I${MPSSE_DIR} -I${PLATFORM2}
-
-CFLAGS += $(shell pkg-config --cflags libusb-1.0 libftdi1)
-LIBS += $(shell pkg-config --libs libusb-1.0 libftdi1)
-
-$(PROG): $(SRCS) Makefile
- gcc $(CFLAGS) $(SRCS) $(LDFLAGS) $(LIBS) -o $@
-
-.PHONY: clean
-clean:
- rm -rf $(PROG)
diff --git a/extra/ftdi_hostcmd/README b/extra/ftdi_hostcmd/README
deleted file mode 100644
index 4de2f45fb3..0000000000
--- a/extra/ftdi_hostcmd/README
+++ /dev/null
@@ -1,6 +0,0 @@
-
-Ubuntu Trusty uses an ancient version of libftdi.
-
-You'll probably want to grab the latest libftdi1-1.2.tar.bz2 from
-http://www.intra2net.com/en/developer/libftdi/ and install it into /usr
-instead.
diff --git a/extra/ftdi_hostcmd/test_cmds.c b/extra/ftdi_hostcmd/test_cmds.c
deleted file mode 100644
index 4552476d0f..0000000000
--- a/extra/ftdi_hostcmd/test_cmds.c
+++ /dev/null
@@ -1,620 +0,0 @@
-/* Copyright 2015 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include <signal.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "mpsse.h"
-
-#include "ec_commands.h"
-
-static int opt_verbose;
-
-/* Communication handle */
-static struct mpsse_context *mpsse;
-
-/* enum ec_status meaning */
-static const char *ec_strerr(enum ec_status r)
-{
- static const char * const strs[] = {
- "SUCCESS",
- "INVALID_COMMAND",
- "ERROR",
- "INVALID_PARAM",
- "ACCESS_DENIED",
- "INVALID_RESPONSE",
- "INVALID_VERSION",
- "INVALID_CHECKSUM",
- "IN_PROGRESS",
- "UNAVAILABLE",
- "TIMEOUT",
- "OVERFLOW",
- "INVALID_HEADER",
- "REQUEST_TRUNCATED",
- "RESPONSE_TOO_BIG",
- "BUS_ERROR",
- "BUSY",
- };
- if (r >= EC_RES_SUCCESS && r <= EC_RES_BUSY)
- return strs[r];
-
- return "<undefined result>";
-};
-
-
-/****************************************************************************
- * Debugging output
- */
-
-#define LINELEN 16
-
-static void showline(uint8_t *buf, int len)
-{
- int i;
- printf(" ");
- for (i = 0; i < len; i++)
- printf(" %02x", buf[i]);
- for (i = len; i < LINELEN; i++)
- printf(" ");
- printf(" ");
- for (i = 0; i < len; i++)
- printf("%c",
- (buf[i] >= ' ' && buf[i] <= '~') ? buf[i] : '.');
- printf("\n");
-}
-
-static void show(const char *fmt, uint8_t *buf, int len)
-{
- int i, m, n;
-
- if (!opt_verbose)
- return;
-
- printf(fmt, len);
-
- m = len / LINELEN;
- n = len % LINELEN;
-
- for (i = 0; i < m; i++)
- showline(buf + i * LINELEN, LINELEN);
- if (n)
- showline(buf + m * LINELEN, n);
-}
-
-/****************************************************************************
- * Send command & receive result
- */
-
-/*
- * With proto v3, the kernel driver asks the EC for the max param size
- * (EC_CMD_GET_PROTOCOL_INFO) at probe time, because it can vary depending on
- * the bus and/or the supported commands.
- *
- * FIXME: For now we'll just hard-code a size.
- */
-static uint8_t txbuf[128];
-
-/*
- * Load the output buffer with a proto v3 request (header, then data, with
- * checksum correct in header).
- */
-static size_t prepare_request(int cmd, int version,
- const uint8_t *data, size_t data_len)
-{
- struct ec_host_request *request;
- size_t i, total_len;
- uint8_t csum = 0;
-
- total_len = sizeof(*request) + data_len;
- if (total_len > sizeof(txbuf)) {
- printf("Request too large (%zd > %zd)\n",
- total_len, sizeof(txbuf));
- return -1;
- }
-
- /* Header first */
- request = (struct ec_host_request *)txbuf;
- request->struct_version = EC_HOST_REQUEST_VERSION;
- request->checksum = 0;
- request->command = cmd;
- request->command_version = version;
- request->reserved = 0;
- request->data_len = data_len;
-
- /* Then data */
- memcpy(txbuf + sizeof(*request), data, data_len);
-
- /* Update checksum */
- for (i = 0; i < total_len; i++)
- csum += txbuf[i];
- request->checksum = -csum;
-
- return total_len;
-}
-
-/*
- * Sends prepared proto v3 command using the SPI protocol
- *
- * Returns zero if command was sent, nonzero otherwise.
- */
-static int send_request(uint8_t *txbuf, size_t len)
-{
- uint8_t *tptr;
- size_t i;
- int ret = 0;
-
- show("Transfer(%d) =>\n", txbuf, len);
- tptr = Transfer(mpsse, txbuf, len);
-
- if (!tptr) {
- fprintf(stderr, "Transfer failed: %s\n",
- ErrorString(mpsse));
- return -1;
- }
-
- show("Transfer(%d) <=\n", tptr, len);
-
- /* Make sure the EC was listening */
- for (i = 0; i < len; i++) {
- switch (tptr[i]) {
- case EC_SPI_PAST_END:
- case EC_SPI_RX_BAD_DATA:
- case EC_SPI_NOT_READY:
- ret = tptr[i];
- /* FALLTHROUGH */
- default:
- break;
- }
- if (ret)
- break;
- }
- free(tptr);
- return ret;
-}
-
-
-/* Timeout flag, so we don't wait forever */
-static int timedout;
-static void alarm_handler(int sig)
-{
- timedout = 1;
-}
-
-/*
- * Read proto v3 response from SPI bus
- *
- * The response header and data are copied into the provided locations.
- *
- * Return value:
- * 0 = response received (check hdr for EC result and body size)
- * -1 = problems
- */
-static int get_response(struct ec_host_response *hdr,
- uint8_t *bodydest, size_t bodylen)
-{
- uint8_t *hptr = 0, *bptr = 0;
- uint8_t sum = 0;
- int ret = -1;
- size_t i;
-
- /* Give up eventually */
- timedout = 0;
- if (SIG_ERR == signal(SIGALRM, alarm_handler)) {
- perror("Problem with signal handler");
- goto out;
- }
- alarm(3);
-
- /* Read a byte at a time until we see the start of the frame.
- * This is slow, but still faster than the EC. */
- while (1) {
- uint8_t *ptr = Read(mpsse, 1);
- if (!ptr) {
- fprintf(stderr, "Read failed: %s\n",
- ErrorString(mpsse));
- alarm(0);
- goto out;
- }
- if (*ptr == EC_SPI_FRAME_START) {
- free(ptr);
- break;
- }
- free(ptr);
-
- if (timedout) {
- fprintf(stderr, "timed out\n");
- goto out;
- }
- }
- alarm(0);
-
- /* Now read the response header */
- hptr = Read(mpsse, sizeof(*hdr));
- if (!hptr) {
- fprintf(stderr, "Read failed: %s\n",
- ErrorString(mpsse));
- goto out;
- }
- show("Header(%d):\n", hptr, sizeof(*hdr));
- memcpy(hdr, hptr, sizeof(*hdr));
-
- /* Check the header */
- if (hdr->struct_version != EC_HOST_RESPONSE_VERSION) {
- printf("response version %d (should be %d)\n",
- hdr->struct_version,
- EC_HOST_RESPONSE_VERSION);
- goto out;
- }
-
- if (hdr->data_len > bodylen) {
- printf("response data_len %d is > %zd\n",
- hdr->data_len,
- bodylen);
- goto out;
- }
-
- /* Read the data */
- if (hdr->data_len) {
- bptr = Read(mpsse, hdr->data_len);
- if (!bptr) {
- fprintf(stderr, "Read failed: %s\n",
- ErrorString(mpsse));
- goto out;
- }
- show("Body(%d):\n", bptr, hdr->data_len);
- memcpy(bodydest, bptr, hdr->data_len);
- }
-
- /* Verify the checksum */
- for (i = 0; i < sizeof(hdr); i++)
- sum += hptr[i];
- for (i = 0; i < hdr->data_len; i++)
- sum += bptr[i];
- if (sum)
-
- printf("Checksum invalid\n");
- else
- ret = 0;
-
-out:
- if (hptr)
- free(hptr);
- if (bptr)
- free(bptr);
- return ret;
-}
-
-
-/*
- * Send command, wait for result. Return zero if communication succeeded; check
- * response to see if the EC liked the command.
- */
-static int send_cmd(int cmd, int version,
- void *outbuf,
- size_t outsize,
- struct ec_host_response *resp,
- void *inbuf,
- size_t insize)
-{
-
- size_t len;
- int ret = -1;
-
- /* Load up the txbuf with the stuff to send */
- len = prepare_request(cmd, version, outbuf, outsize);
- if (len < 0)
- return -1;
-
- if (MPSSE_OK != Start(mpsse)) {
- fprintf(stderr, "Start failed: %s\n",
- ErrorString(mpsse));
- return -1;
- }
-
- if (0 == send_request(txbuf, len) &&
- 0 == get_response(resp, inbuf, insize))
- ret = 0;
-
- if (MPSSE_OK != Stop(mpsse)) {
- fprintf(stderr, "Stop failed: %s\n",
- ErrorString(mpsse));
- return -1;
- }
-
- return ret;
-}
-
-
-/****************************************************************************
- * Probe for basic protocol info
- */
-
-/**
- * Try to talk to the attached(?) device.
- *
- * @return zero on success
- */
-static int probe_v3(void)
-{
- struct ec_host_response resp;
- struct ec_response_get_protocol_info info;
- int i, ret;
-
- memset(&resp, 0, sizeof(resp));
- memset(&info, 0, sizeof(info));
-
- if (opt_verbose)
- printf("Trying EC_CMD_GET_PROTOCOL_INFO...\n");
-
- ret = send_cmd(EC_CMD_GET_PROTOCOL_INFO, 0,
- 0, 0,
- &resp,
- &info, sizeof(info));
-
- if (ret) {
- printf("EC_CMD_GET_PROTOCOL_INFO failed\n");
- return -1;
- }
-
- if (EC_RES_SUCCESS != resp.result) {
- printf("EC result is %d: %s\n",
- resp.result, ec_strerr(resp.result));
- return -1;
- }
-
- printf("EC_CMD_GET_PROTOCOL_INFO Success!\n");
- printf(" protocol_versions: ");
- for (i = 0; i < 32; i++)
- if (info.protocol_versions & (1 << i))
- printf(" %d", i);
- printf("\n");
- printf(" max_request_packet_size: %d\n",
- info.max_request_packet_size);
- printf(" max_response_packet_size: %d\n",
- info.max_response_packet_size);
- printf(" flags: 0x%x\n",
- info.flags);
-
- return 0;
-}
-
-/****************************************************************************
- * Pretty-print the host commands that the device admits to having
- */
-
-struct lookup {
- uint16_t cmd;
- const char * const desc;
-};
-
-static struct lookup cmd_table[] = {
- {0x00, "EC_CMD_PROTO_VERSION"},
- {0x01, "EC_CMD_HELLO"},
- {0x02, "EC_CMD_GET_VERSION"},
- {0x03, "EC_CMD_READ_TEST"},
- {0x04, "EC_CMD_GET_BUILD_INFO"},
- {0x05, "EC_CMD_GET_CHIP_INFO"},
- {0x06, "EC_CMD_GET_BOARD_VERSION"},
- {0x07, "EC_CMD_READ_MEMMAP"},
- {0x08, "EC_CMD_GET_CMD_VERSIONS"},
- {0x09, "EC_CMD_GET_COMMS_STATUS"},
- {0x0a, "EC_CMD_TEST_PROTOCOL"},
- {0x0b, "EC_CMD_GET_PROTOCOL_INFO"},
- {0x0c, "EC_CMD_GSV_PAUSE_IN_S5"},
- {0x0d, "EC_CMD_GET_FEATURES"},
- {0x10, "EC_CMD_FLASH_INFO"},
- {0x11, "EC_CMD_FLASH_READ"},
- {0x12, "EC_CMD_FLASH_WRITE"},
- {0x13, "EC_CMD_FLASH_ERASE"},
- {0x15, "EC_CMD_FLASH_PROTECT"},
- {0x16, "EC_CMD_FLASH_REGION_INFO"},
- {0x17, "EC_CMD_VBNV_CONTEXT"},
- {0x20, "EC_CMD_PWM_GET_FAN_TARGET_RPM"},
- {0x21, "EC_CMD_PWM_SET_FAN_TARGET_RPM"},
- {0x22, "EC_CMD_PWM_GET_KEYBOARD_BACKLIGHT"},
- {0x23, "EC_CMD_PWM_SET_KEYBOARD_BACKLIGHT"},
- {0x24, "EC_CMD_PWM_SET_FAN_DUTY"},
- {0x28, "EC_CMD_LIGHTBAR_CMD"},
- {0x29, "EC_CMD_LED_CONTROL"},
- {0x2a, "EC_CMD_VBOOT_HASH"},
- {0x2b, "EC_CMD_MOTION_SENSE_CMD"},
- {0x2c, "EC_CMD_FORCE_LID_OPEN"},
- {0x30, "EC_CMD_USB_CHARGE_SET_MODE"},
- {0x40, "EC_CMD_PSTORE_INFO"},
- {0x41, "EC_CMD_PSTORE_READ"},
- {0x42, "EC_CMD_PSTORE_WRITE"},
- {0x44, "EC_CMD_RTC_GET_VALUE"},
- {0x45, "EC_CMD_RTC_GET_ALARM"},
- {0x46, "EC_CMD_RTC_SET_VALUE"},
- {0x47, "EC_CMD_RTC_SET_ALARM"},
- {0x48, "EC_CMD_PORT80_LAST_BOOT"},
- {0x48, "EC_CMD_PORT80_READ"},
- {0x50, "EC_CMD_THERMAL_SET_THRESHOLD"},
- {0x51, "EC_CMD_THERMAL_GET_THRESHOLD"},
- {0x52, "EC_CMD_THERMAL_AUTO_FAN_CTRL"},
- {0x53, "EC_CMD_TMP006_GET_CALIBRATION"},
- {0x54, "EC_CMD_TMP006_SET_CALIBRATION"},
- {0x55, "EC_CMD_TMP006_GET_RAW"},
- {0x60, "EC_CMD_MKBP_STATE"},
- {0x61, "EC_CMD_MKBP_INFO"},
- {0x62, "EC_CMD_MKBP_SIMULATE_KEY"},
- {0x64, "EC_CMD_MKBP_SET_CONFIG"},
- {0x65, "EC_CMD_MKBP_GET_CONFIG"},
- {0x66, "EC_CMD_KEYSCAN_SEQ_CTRL"},
- {0x67, "EC_CMD_GET_NEXT_EVENT"},
- {0x70, "EC_CMD_TEMP_SENSOR_GET_INFO"},
- {0x87, "EC_CMD_HOST_EVENT_GET_B"},
- {0x88, "EC_CMD_HOST_EVENT_GET_SMI_MASK"},
- {0x89, "EC_CMD_HOST_EVENT_GET_SCI_MASK"},
- {0x8d, "EC_CMD_HOST_EVENT_GET_WAKE_MASK"},
- {0x8a, "EC_CMD_HOST_EVENT_SET_SMI_MASK"},
- {0x8b, "EC_CMD_HOST_EVENT_SET_SCI_MASK"},
- {0x8c, "EC_CMD_HOST_EVENT_CLEAR"},
- {0x8e, "EC_CMD_HOST_EVENT_SET_WAKE_MASK"},
- {0x8f, "EC_CMD_HOST_EVENT_CLEAR_B"},
- {0x90, "EC_CMD_SWITCH_ENABLE_BKLIGHT"},
- {0x91, "EC_CMD_SWITCH_ENABLE_WIRELESS"},
- {0x92, "EC_CMD_GPIO_SET"},
- {0x93, "EC_CMD_GPIO_GET"},
- {0x94, "EC_CMD_I2C_READ"},
- {0x95, "EC_CMD_I2C_WRITE"},
- {0x96, "EC_CMD_CHARGE_CONTROL"},
- {0x97, "EC_CMD_CONSOLE_SNAPSHOT"},
- {0x98, "EC_CMD_CONSOLE_READ"},
- {0x99, "EC_CMD_BATTERY_CUT_OFF"},
- {0x9a, "EC_CMD_USB_MUX"},
- {0x9b, "EC_CMD_LDO_SET"},
- {0x9c, "EC_CMD_LDO_GET"},
- {0x9d, "EC_CMD_POWER_INFO"},
- {0x9e, "EC_CMD_I2C_PASSTHRU"},
- {0x9f, "EC_CMD_HANG_DETECT"},
- {0xa0, "EC_CMD_CHARGE_STATE"},
- {0xa1, "EC_CMD_CHARGE_CURRENT_LIMIT"},
- {0xa2, "EC_CMD_EXT_POWER_CURRENT_LIMIT"},
- {0xb0, "EC_CMD_SB_READ_WORD"},
- {0xb1, "EC_CMD_SB_WRITE_WORD"},
- {0xb2, "EC_CMD_SB_READ_BLOCK"},
- {0xb3, "EC_CMD_SB_WRITE_BLOCK"},
- {0xb4, "EC_CMD_BATTERY_VENDOR_PARAM"},
- {0xb5, "EC_CMD_SB_FW_UPDATE"},
- {0xd2, "EC_CMD_REBOOT_EC"},
- {0xd3, "EC_CMD_GET_PANIC_INFO"},
- {0xd1, "EC_CMD_REBOOT"},
- {0xdb, "EC_CMD_RESEND_RESPONSE"},
- {0xdc, "EC_CMD_VERSION0"},
- {0x100, "EC_CMD_PD_EXCHANGE_STATUS"},
- {0x104, "EC_CMD_PD_HOST_EVENT_STATUS"},
- {0x101, "EC_CMD_USB_PD_CONTROL"},
- {0x102, "EC_CMD_USB_PD_PORTS"},
- {0x103, "EC_CMD_USB_PD_POWER_INFO"},
- {0x110, "EC_CMD_USB_PD_FW_UPDATE"},
- {0x111, "EC_CMD_USB_PD_RW_HASH_ENTRY"},
- {0x112, "EC_CMD_USB_PD_DEV_INFO"},
- {0x113, "EC_CMD_USB_PD_DISCOVERY"},
- {0x114, "EC_CMD_PD_CHARGE_PORT_OVERRIDE"},
- {0x115, "EC_CMD_PD_GET_LOG_ENTRY"},
- {0x116, "EC_CMD_USB_PD_GET_AMODE"},
- {0x117, "EC_CMD_USB_PD_SET_AMODE"},
- {0x118, "EC_CMD_PD_WRITE_LOG_ENTRY"},
- {0x200, "EC_CMD_BLOB"},
-};
-
-#define ARRAY_SIZE(A) (sizeof(A) / sizeof(A[0]))
-
-static void show_command(uint16_t c)
-{
- unsigned int i;
- const char *desc = "<unknown>";
-
- for (i = 0; i < ARRAY_SIZE(cmd_table); i++)
- if (cmd_table[i].cmd == c) {
- desc = cmd_table[i].desc;
- break;
- }
-
- printf(" %02x %s\n", c, desc);
-}
-
-static void scan_commands(uint16_t start, uint16_t stop)
-{
- struct ec_params_get_cmd_versions_v1 q_vers;
- struct ec_response_get_cmd_versions r_vers;
- struct ec_host_response ec_resp;
- uint16_t i;
-
- memset(&ec_resp, 0, sizeof(ec_resp));
-
- printf("Supported host commands:\n");
- for (i = start; i <= stop; i++) {
-
- if (opt_verbose)
- printf("Querying CMD %02x\n", i);
-
- q_vers.cmd = i;
- if (0 != send_cmd(EC_CMD_GET_CMD_VERSIONS, 1,
- &q_vers, sizeof(q_vers),
- &ec_resp,
- &r_vers, sizeof(r_vers))) {
- printf("query failed on cmd %02x - aborting\n", i);
- return;
- }
-
- switch (ec_resp.result) {
- case EC_RES_SUCCESS:
- if (opt_verbose)
- printf("Yes: ");
- show_command(i);
- break;
- case EC_RES_INVALID_PARAM:
- if (opt_verbose)
- printf("No\n");
- break;
- default:
- printf("lookup of cmd %02x returned %d %s\n", i,
- ec_resp.result,
- ec_strerr(ec_resp.result));
- }
- }
-}
-
-/****************************************************************************/
-
-static void usage(char *progname)
-{
- printf("Usage: %s [-v] [start [stop]]\n", progname);
-}
-
-int main(int argc, char *argv[])
-{
- int retval = 1;
- int errorcnt = 0;
- int i;
- uint16_t start = cmd_table[0].cmd;
- uint16_t stop = cmd_table[ARRAY_SIZE(cmd_table) - 1].cmd;
-
- while ((i = getopt(argc, argv, ":v")) != -1) {
- switch (i) {
- case 'v':
- opt_verbose++;
- break;
- case '?':
- printf("unrecognized option: -%c\n", optopt);
- errorcnt++;
- break;
- }
- }
- if (errorcnt) {
- usage(argv[0]);
- return 1;
- }
-
- /* Range (no error checking) */
- if (optind < argc)
- start = (uint16_t)strtoull(argv[optind++], 0, 0);
- if (optind < argc)
- stop = (uint16_t)strtoull(argv[optind++], 0, 0);
-
- /* Find something to talk to */
- mpsse = MPSSE(SPI0, 1000000, 0);
- if (!mpsse) {
- printf("Can't find a device to open\n");
- return 1;
- }
-
- if (0 != probe_v3())
- goto out;
-
- scan_commands(start, stop);
-
- retval = 0;
-out:
- Close(mpsse);
- mpsse = 0;
- return retval;
-}
diff --git a/extra/i2c_pseudo/.gitignore b/extra/i2c_pseudo/.gitignore
deleted file mode 100644
index 98ec2e970f..0000000000
--- a/extra/i2c_pseudo/.gitignore
+++ /dev/null
@@ -1,10 +0,0 @@
-.i2c-pseudo.ko.cmd
-.i2c-pseudo.mod.o.cmd
-.i2c-pseudo.o.cmd
-.tmp_versions/
-Module.symvers
-i2c-pseudo.ko
-i2c-pseudo.mod.c
-i2c-pseudo.mod.o
-i2c-pseudo.o
-modules.order
diff --git a/extra/i2c_pseudo/50-i2c-pseudo.rules b/extra/i2c_pseudo/50-i2c-pseudo.rules
deleted file mode 100644
index 22a4c8daf0..0000000000
--- a/extra/i2c_pseudo/50-i2c-pseudo.rules
+++ /dev/null
@@ -1 +0,0 @@
-DEVPATH=="/devices/virtual/i2c-pseudo/*", GROUP="plugdev", MODE="0660"
diff --git a/extra/i2c_pseudo/Documentation.txt b/extra/i2c_pseudo/Documentation.txt
deleted file mode 100644
index 77de99574b..0000000000
--- a/extra/i2c_pseudo/Documentation.txt
+++ /dev/null
@@ -1,291 +0,0 @@
-----Introduction----
-
-Usually I2C adapters are implemented in a kernel driver. It is also possible to
-implement an adapter in userspace, through the /dev/i2c-pseudo-controller
-interface. Load module i2c-pseudo for this.
-
-Use cases for this module include:
-
-[A] Using local I2C device drivers, particularly i2c-dev, with I2C busses on
-remote systems. For example, interacting with a Device Under Test (DUT)
-connected to a Linux host through a debug interface, or interacting with a
-remote host over a network.
-
-[B] Support I2C device driver tests that are too complex for the i2c-stub
-module. For example, when simulating an I2C device where its driver might
-issue a sequence of reads and writes without interruption, and the value a
-certain address must change during the sequence.
-
-Any possible use case could of course be implemented as a kernel driver.
-However, it can be much faster and easier to implement such things in userspace,
-thanks to the far greater code reuse possibilities (libraries), the plethora of
-programming language options for rapid iteration, and not needing to understand
-how to implement Linux kernel drivers.
-
-This is not intended to replace kernel drivers for actual I2C busses on the
-local host machine.
-
-
-
-----Details----
-
-Each time /dev/i2c-pseudo-controller is opened, and the correct initialization
-command is written to it (ADAPTER_START), a new I2C adapter is created. The
-adapter will live until its file descriptor is closed. Multiple pseudo adapters
-can co-exist simultaneously, controlled by the same or different userspace
-processes. When an I2C device driver sends an I2C message to a pseudo adapter,
-the message becomes readable from its file descriptor. If a reply is written
-before the adapter timeout expires, that reply will be sent back to the I2C
-device driver.
-
-Reads and writes are buffered inside i2c-pseudo such that userspace controllers
-may split them up into arbitrarily small chunks. Multiple commands, or portions
-of multiple commands, may be read or written together.
-
-Blocking I/O is the default. Non-blocking I/O is supported as well, enabled by
-O_NONBLOCK. Polling is supported, with or without non-blocking I/O. A special
-command (ADAPTER_SHUTDOWN) is available to unblock any pollers or blocked
-reads or writes, as a convenience for a multi-threaded or multi-process program
-that wants to exit.
-
-It is safe to access a single controller fd from multiple threads or processes
-concurrently, though it is up to the controller to ensure proper ordering, and
-to ensure that writes for different commands do not get interleaved. However,
-it is recommended (not required) that controller implementations have only one
-reader thread and one writer thread, which may or may not be the same thread.
-Avoiding multiple readers and multiple writers greatly simplifies controller
-implementation, and there is likely no performance benefit to be gained from
-concurrent reads or concurrent writes due to how i2c-pseudo serializes them
-internally. After all, on a real I2C bus only one I2C message can be active at
-a time.
-
-Commands are newline-terminated, both those read from the controller device, and
-those written to it.
-
-
-
-----Read Commands----
-
-The commands that may be read from a pseudo controller device are:
-
-
-Read Command: I2C_ADAPTER_NUM <num>
-Example: "I2C_ADAPTER_NUM 5\n"
-
-Details: This is read in response to the GET_ADAPTER_NUM command being written.
-The number is the I2C adapter number in decimal. This can only occur after
-ADAPTER_START, because before that the number is not known and cannot be
-predicted reliably.
-
-
-Read Command: I2C_PSEUDO_ID <num>
-Example: "I2C_PSEUDO_ID 98\n"
-
-Details: This is read in response to the GET_PSEUDO_ID command being written.
-The number is the pseudo ID in decimal.
-
-
-Read Command: I2C_BEGIN_XFER
-Example: "I2C_BEGIN_XFER\n"
-
-Details: This indicates the start of an I2C transaction request, in other words
-the start of the I2C messages from a single invocation of the I2C adapter's
-master_xfer() callback. This can only occur after ADAPTER_START.
-
-
-Read Command: I2C_XFER_REQ <xfer_id> <msg_id> <addr> <flags> <data_len> [<write_byte>[:...]]
-Example: "I2C_XFER_REQ 3 0 0x0070 0x0000 2 AB:9F\n"
-Example: "I2C_XFER_REQ 3 1 0x0070 0x0001 4\n"
-
-Details: This is a single I2C message that a device driver requested be sent on
-the bus, in other words a single struct i2c_msg from master_xfer() msgs arg.
-
-The xfer_id is a number representing the whole I2C transaction, thus all
-I2C_XFER_REQ between a I2C_BEGIN_XFER + I2C_COMMIT_XFER pair share an xfer_id.
-The purpose is to ensure replies from the userspace controller are always
-properly matched to the intended master_xfer() request. The first transaction
-has xfer_id 0, and it increases by 1 with each transaction, however it will
-eventually wrap back to 0 if enough transactions happen during the lifetime of a
-pseudo adapter. It is guaranteed to have a large enough maximum value such that
-there can never be multiple outstanding transactions with the same ID, due to an
-internal limit in i2c-pseudo that will block master_xfer() calls when the
-controller is falling behind in its replies.
-
-The msg_id is a decimal number representing the index of the I2C message within
-its transaction, in other words the index in master_xfer() *msgs array arg.
-This starts at 0 after each I2C_BEGIN_XFER. This is guaranteed to not wrap.
-
-The addr is the hexadecimal I2C address for this I2C message. The address is
-right-aligned without any read/write bit.
-
-The flags are the same bitmask flags used in struct i2c_msg, in hexadecimal
-form. Of particular importance to any pseudo controller is the read bit, which
-is guaranteed to be 0x1 per Linux I2C documentation.
-
-The data_len is the decimal number of either how many bytes to write that will
-follow, or how many bytes to read and reply with if this is a read request.
-
-If this is a read, data_len will be the final field in this command. If this is
-a write, data_len will be followed by the given number of colon-separated
-hexadecimal byte values, in the format shown in the example above.
-
-
-Read Command: I2C_COMMIT_XFER
-Example: "I2C_COMMIT_XFER\n"
-
-Details: This indicates the end of an I2C transaction request, in other words
-the end of the I2C messages from a single invocation of the I2C adapter's
-master_xfer() callback. This should be read exactly once after each
-I2C_BEGIN_XFER, with a varying number of I2C_XFER_REQ between them.
-
-
-
-----Write Commands----
-
-The commands that may be written to a pseudo controller device are:
-
-
-Write Command: SET_ADAPTER_NAME_SUFFIX <suffix>
-Example: "SET_ADAPTER_NAME_SUFFIX My Adapter\n"
-
-Details: Sets a suffix to append to the auto-generated I2C adapter name. Only
-valid before ADAPTER_START. A space or other separator character will be placed
-between the auto-generated name and the suffix, so there is no need to include a
-leading separator in the suffix. If the resulting name is too long for the I2C
-adapter name field, it will be quietly truncated.
-
-
-Write Command: SET_ADAPTER_TIMEOUT_MS <ms>
-Example: "SET_ADAPTER_TIMEOUT_MS 2000\n"
-
-Details: Sets the timeout in milliseconds for each I2C transaction, in other
-words for each master_xfer() reply. Only valid before ADAPTER_START. The I2C
-subsystem will automatically time out transactions based on this setting. Set
-to 0 to use the I2C subsystem default timeout. The default timeout for new
-pseudo adapters where this command has not been used is configurable at
-i2c-pseudo module load time, and itself has a default independent from the I2C
-subsystem default. (Though if the i2c-pseudo module level default is set to 0,
-that has the same meaning as here.)
-
-
-Write Command: ADAPTER_START
-Example: "ADAPTER_START\n"
-
-Details: Tells i2c-pseudo to actually create the I2C adapter. Only valid once
-per open controller fd.
-
-
-Write Command: GET_ADAPTER_NUM
-Example: "GET_ADAPTER_NUM\n"
-
-Details: Asks i2c-pseudo for the number assigned to this I2C adapter by the I2C
-subsystem. Only valid after ADAPTER_START, because before that the number
-is not known and cannot be predicted reliably.
-
-
-Write Command: GET_PSEUDO_ID
-Example: "GET_PSEUDO_ID\n"
-
-Details: Asks i2c-pseudo for the pseudo ID of this I2C adapter. The pseudo ID
-will not be reused for the lifetime of the i2c-pseudo module, unless an internal
-counter wraps. I2C clients can use this to track specific instances of pseudo
-adapters, even when adapter numbers have been reused.
-
-
-Write Command: I2C_XFER_REPLY <xfer_id> <msg_id> <addr> <flags> <errno> [<read_byte>[:...]]
-Example: "I2C_XFER_REPLY 3 0 0x0070 0x0000 0\n"
-Example: "I2C_XFER_REPLY 3 1 0x0070 0x0001 0 0B:29:02:D9\n"
-
-Details: This is how a pseudo controller can reply to I2C_XFER_REQ. Only valid
-after I2C_XFER_REQ. A pseudo controller should write one of these for each
-I2C_XFER_REQ it reads, including for failures, so that I2C device drivers need
-not wait for the adapter timeout upon failure (if failure is known sooner).
-
-The fields in common with I2C_XFER_REQ have their same meanings, and their
-values are expected to exactly match what was read in the I2C_XFER_REQ command
-that this is in reply to.
-
-The errno field is how the pseudo controller indicates success or failure for
-this I2C message. A 0 value indicates success. A non-zero value indicates a
-failure. Pseudo controllers are encouraged to use errno values to encode some
-meaning in a failure response, but that is not a requirement, and the I2C
-adapter interface does not provide a way to pass per-message errno values to a
-device driver anyways.
-
-Pseudo controllers are encouraged to reply in the same order as messages were
-received, however i2c-pseudo will properly match up out-of-order replies with
-their original requests.
-
-
-Write Command: ADAPTER_SHUTDOWN
-Example: "ADAPTER_SHUTDOWN\n"
-
-Details: This tells i2c-pseudo that the pseudo controller wants to shutdown and
-intends to close the controller device fd soon. Use of this is OPTIONAL, it is
-perfectly valid to close the controller device fd without ever using this
-command.
-
-This commands unblocks any blocked controller I/O (reads, writes, or polls), and
-that is its main purpose.
-
-Any I2C transactions attempted by a device driver after this command will fail,
-and will not be passed on to the userspace controller.
-
-This DOES NOT delete the I2C adapter. Only closing the fd will do that. That
-MAY CHANGE in the future, such that this does delete the I2C adapter. (However
-this will never be required, it will always be okay to simply close the fd.)
-
-
-
-----Example userspace controller code----
-
-In C, a simple exchange between i2c-pseudo and userspace might look like the
-example below. Note that for brevity this lacks any error checking and
-handling, which a real pseudo controller implementation should have.
-
-int fd;
-char buf[1<<12];
-
-fd = open("/dev/i2c-pseudo-controller", O_RDWR);
-/* Create the I2C adapter. */
-dprintf(fd, "ADAPTER_START\n");
-
-/*
- * Pretend this I2C adapter number is 5, and the first I2C xfer sent to it was
- * from this command (using its i2c-dev interface):
- * $ i2cset -y 5 0x70 0xC2
- *
- * Then this read would place the following into *buf:
- * "I2C_BEGIN_XFER\n"
- * "I2C_XFER_REQ 0 0 0x0070 0x0000 1 C2\n"
- * "I2C_COMMIT_XFER\n"
- */
-read(fd, buf, sizeof(buf));
-
-/* This reply would allow the i2cset command above to exit successfully. */
-dprintf(fd, "I2C_XFER_REPLY 0 0 0x0070 0x0000 0\n");
-
-/*
- * Now pretend the next I2C xfer sent to this adapter was from:
- * $ i2cget -y 5 0x70 0xAB
- *
- * Then this read would place the following into *buf:
- * "I2C_BEGIN_XFER\n"
- * "I2C_XFER_REQ 1 0 0x0070 0x0000 1 AB\n"
- * "I2C_XFER_REQ 1 1 0x0070 0x0001 1\n'"
- * "I2C_COMMIT_XFER\n"
- */
-read(fd, buf, sizeof(buf));
-
-/*
- * These replies would allow the i2cget command above to print the following to
- * stdout and exit successfully:
- * 0x0b
- *
- * Note that it is also valid to write these together in one write().
- */
-dprintf(fd, "I2C_XFER_REPLY 1 0 0x0070 0x0000 0\n");
-dprintf(fd, "I2C_XFER_REPLY 1 1 0x0070 0x0001 0 0B\n");
-
-/* Destroy the I2C adapter. */
-close(fd);
diff --git a/extra/i2c_pseudo/Makefile b/extra/i2c_pseudo/Makefile
deleted file mode 100644
index f7fda6e2de..0000000000
--- a/extra/i2c_pseudo/Makefile
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-# Makefile for i2c-pseudo module. Typical usage:
-# $ make
-# $ sudo make modules_install
-# $ make clean
-
-obj-m := i2c-pseudo.o
-
-.PHONY: all
-
-all: modules
-
-CFLAGS_i2c-pseudo.o += "-DHAVE_STREAM_OPEN=$(shell "$M"/check_stream_open.sh)"
-
-.DEFAULT:
- $(MAKE) -C /lib/modules/$(shell uname -r)/build \
- M=$(shell pwd) \
- $(MAKECMDGOALS)
diff --git a/extra/i2c_pseudo/README b/extra/i2c_pseudo/README
deleted file mode 100644
index 96efa062b1..0000000000
--- a/extra/i2c_pseudo/README
+++ /dev/null
@@ -1,20 +0,0 @@
-This directory contains the i2c-pseudo Linux kernel module.
-
-The i2c-pseudo module was written with the intention of being submitted upstream
-in the Linux kernel. This copy exists because of as 2019-03 this module is not
-yet in the upstream kernel, and even if/when this is included, it may take years
-before making its way to the prepackaged Linux distribution kernels typically
-used by CrOS developers.
-
-See Documentation.txt for more information about the module itself. That file
-is Documentation/i2c/pseudo-controller-interface in the upstream patch.
-
-When servod starts, if the i2c-pseudo module is loaded servod will automatically
-create an I2C pseudo adapter for the Servo I2C bus. That I2C adapter may then
-be used in userspace through i2c-dev (/dev/i2c-<N>). The i2c-tools package
-provides command line utilities for interfacing with i2c-dev devices, and some
-CrOS software can work directly with i2c-dev devices as well, such as iteflash
-which is used by flash_ec when reflashing an ITE EC through a Servo.
-
-Automated installation:
-$ ./install
diff --git a/extra/i2c_pseudo/check_stream_open.sh b/extra/i2c_pseudo/check_stream_open.sh
deleted file mode 100755
index da802cb282..0000000000
--- a/extra/i2c_pseudo/check_stream_open.sh
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/sh
-#
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-# This checks whether stream_open symbol is available from the target kernel.
-#
-# Output meanings:
-# -1 : stream_open is not available
-# 0 : unknown whether or not stream_open is available
-# 1 : stream_open is available
-
-symbols="$(< "/lib/modules/$(uname -r)/build/Module.symvers" \
- awk '{print $2}' | grep -E '^(nonseekable_open|stream_open)$')"
-
-if echo "${symbols}" | grep -q '^stream_open$'; then
- echo 1
-elif echo "${symbols}" | grep -q '^nonseekable_open$'; then
- echo -1
-else
- echo 0
-fi
diff --git a/extra/i2c_pseudo/i2c-pseudo.c b/extra/i2c_pseudo/i2c-pseudo.c
deleted file mode 100644
index e4f1852cd8..0000000000
--- a/extra/i2c_pseudo/i2c-pseudo.c
+++ /dev/null
@@ -1,3212 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * This Linux kernel module implements pseudo I2C adapters that can be backed
- * by userspace programs. This allows for implementing an I2C bus from
- * userspace, which can tunnel the I2C commands through another communication
- * channel to a remote I2C bus.
- */
-
-#include <linux/build_bug.h>
-#include <linux/cdev.h>
-#include <linux/completion.h>
-#include <linux/device.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/i2c.h>
-#include <linux/init.h>
-#include <linux/jiffies.h>
-#include <linux/kernel.h>
-#include <linux/kobject.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/poll.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/time64.h>
-#include <linux/types.h>
-#include <linux/uaccess.h>
-#include <linux/wait.h>
-#include <stdarg.h>
-
-/* Minimum i2cp_limit module parameter value. */
-#define I2CP_ADAPTERS_MIN 0
-/* Maximum i2cp_limit module parameter value. */
-#define I2CP_ADAPTERS_MAX 256
-/* Default i2cp_limit module parameter value. */
-#define I2CP_DEFAULT_LIMIT 8
-/* Value for alloc_chrdev_region() baseminor arg. */
-#define I2CP_CDEV_BASEMINOR 0
-#define I2CP_TIMEOUT_MS_MIN 0
-#define I2CP_TIMEOUT_MS_MAX (60 * MSEC_PER_SEC)
-#define I2CP_DEFAULT_TIMEOUT_MS (3 * MSEC_PER_SEC)
-
-/* Used in struct device.kobj.name field. */
-#define I2CP_DEVICE_NAME "i2c-pseudo-controller"
-/* Value for alloc_chrdev_region() name arg. */
-#define I2CP_CHRDEV_NAME "i2c_pseudo"
-/* Value for class_create() name arg. */
-#define I2CP_CLASS_NAME "i2c-pseudo"
-/* Value for alloc_chrdev_region() count arg. Should always be 1. */
-#define I2CP_CDEV_COUNT 1
-
-#define I2CP_ADAP_START_CMD "ADAPTER_START"
-#define I2CP_ADAP_SHUTDOWN_CMD "ADAPTER_SHUTDOWN"
-#define I2CP_GET_NUMBER_CMD "GET_ADAPTER_NUM"
-#define I2CP_NUMBER_REPLY_CMD "I2C_ADAPTER_NUM"
-#define I2CP_GET_PSEUDO_ID_CMD "GET_PSEUDO_ID"
-#define I2CP_PSEUDO_ID_REPLY_CMD "I2C_PSEUDO_ID"
-#define I2CP_SET_NAME_SUFFIX_CMD "SET_ADAPTER_NAME_SUFFIX"
-#define I2CP_SET_TIMEOUT_CMD "SET_ADAPTER_TIMEOUT_MS"
-#define I2CP_BEGIN_MXFER_REQ_CMD "I2C_BEGIN_XFER"
-#define I2CP_COMMIT_MXFER_REQ_CMD "I2C_COMMIT_XFER"
-#define I2CP_MXFER_REQ_CMD "I2C_XFER_REQ"
-#define I2CP_MXFER_REPLY_CMD "I2C_XFER_REPLY"
-
-/* Maximum size of a controller command. */
-#define I2CP_CTRLR_CMD_LIMIT 255
-/* Maximum number of controller read responses to allow enqueued at once. */
-#define I2CP_CTRLR_RSP_QUEUE_LIMIT 256
-/* The maximum size of a single controller read response. */
-#define I2CP_MAX_MSG_BUF_SIZE 16384
-/* Maximum size of a controller read or write. */
-#define I2CP_RW_SIZE_LIMIT 1048576
-
-/*
- * Marks the end of a controller command or read response.
- *
- * Fundamentally, controller commands and read responses could use different end
- * marker characters, but for validity they should be the same.
- *
- * This must be a variable, not a macro, because it is passed to copy_to_user()
- * by address. Taking the address of a character literal causes a compiler
- * error. Making these C strings instead of characters would allow for that
- * (with other implications), but then copy_to_user() itself refuses to compile,
- * because of an assertion that the copy size (1) must match the size of the
- * string literal (2 with its trailing null).
- */
-static const char i2cp_ctrlr_end_char = '\n';
-/* Separator between I2C message header fields in the controller bytestream. */
-static const char i2cp_ctrlr_header_sep_char = ' ';
-/* Separator between I2C message data bytes in the controller bytestream. */
-static const char i2cp_ctrlr_data_sep_char = ':';
-
-/*
- * This used instead of strcmp(in_str, other_str) because in_str may have null
- * characters within its in_size boundaries, which could cause an unintended
- * match.
- */
-#define STRING_NEQ(in_str, in_size, other_str) \
- (in_size != strlen(other_str) || memcmp(other_str, in_str, in_size))
-
-#define STR_HELPER(num) #num
-#define STR(num) STR_HELPER(num)
-
-#define CONST_STRLEN(str) (sizeof(str) - 1)
-
-/*
- * The number of pseudo I2C adapters permitted. This default value can be
- * overridden at module load time. Must be in the range
- * [I2CP_ADAPTERS_MIN, I2CP_ADAPTERS_MAX].
- *
- * As currently used, this MUST NOT be changed during or after module
- * initialization. If the ability to change this at runtime is desired, an
- * audit of the uses of this variable will be necessary.
- */
-static unsigned int i2cp_limit = I2CP_DEFAULT_LIMIT;
-module_param(i2cp_limit, uint, 0444);
-
-/*
- * The default I2C pseudo adapter timeout, in milliseconds.
- * 0 means use Linux I2C adapter default.
- * Can be changed per adapter by the controller.
- */
-static unsigned int i2cp_default_timeout_ms = I2CP_DEFAULT_TIMEOUT_MS;
-module_param(i2cp_default_timeout_ms, uint, 0444);
-
-struct i2cp_controller;
-
-/* This tracks all I2C pseudo adapters. */
-struct i2cp_counters {
- /* This must be held while accessing any fields. */
- struct mutex lock;
- unsigned int count;
- /*
- * This is used to make a strong attempt at avoiding ID reuse,
- * especially during the lifetime of a userspace i2c-dev client. This
- * can wrap by design, and thus makes no perfect guarantees.
- */
- /* Same type as struct i2cp_controller.id field. */
- unsigned int next_ctrlr_id;
- struct i2cp_controller **all_controllers;
-};
-
-static struct class *i2cp_class;
-static dev_t i2cp_dev_num;
-
-struct i2cp_device {
- struct i2cp_counters counters;
- struct cdev cdev;
- struct device device;
-};
-
-static struct i2cp_device *i2cp_device;
-
-/*
- * An instance of this struct in i2cp_cmds[] array defines a command that a
- * controller process may write to the I2C pseudo character device, hereafter a
- * "write command."
- *
- * A write command consists of one or more header fields, followed optionally by
- * data. Each header field is fully buffered before being sent to
- * header_receiver(). Data is not fully buffered, it is chunked in fixed
- * increments set by the return value of the final header_receiver() call.
- *
- * Every write command begins with its name. The name is used both to map the
- * command to an instance of this struct, and as the first header field.
- *
- * A header field ends at either i2cp_ctrlr_end_char or
- * i2cp_ctrlr_header_sep_char, neither of which is ever included in header field
- * values passed to a callback.
- *
- * A command always ends at i2cp_ctrlr_end_char. Anything written after that by
- * the controller is treated as a new command.
- *
- * After i2cp_ctrlr_header_sep_char the return value of header_receiver() from
- * the previous header field is used to determine whether subsequent input is
- * another header field, or data.
- *
- * Once header_receiver() has indicated that data is expected, all input until
- * i2cp_ctrlr_end_char will be handled as data, and header_receiver() will not
- * be called again for the command.
- *
- * For a given I2C pseudo controller instance there will never be more than one
- * write command in flight at once, and there will never be more than one of
- * these callbacks executing at once. These callbacks need not do any
- * cross-thread synchronization among themselves.
- *
- * Note: Data may contain i2cp_ctrlr_header_sep_char.
- *
- * Note: There are no restrictions on the use of the null char ('\0') in either
- * header fields or data. (If either i2cp_ctrlr_header_sep_char or
- * i2cp_ctrlr_end_char is null then the respective restrictions around those
- * characters apply as usual, of course.) Write command implementations need
- * not use or expect null, but they must at least handle it gracefully and fail
- * without bad side effects, same as with any unexpected input.
- */
-struct i2cp_cmd {
- /*
- * Set these to the command name.
- *
- * The command name must not contain i2cp_ctrlr_header_sep_char or
- * i2cp_ctrlr_end_char. The behavior otherwise is undefined; such a
- * command would be uncallable, and could become either a build-time or
- * runtime error.
- *
- * The command name must be unique in the i2cp_cmds[] array. The
- * behavior with duplicate command names is undefined, subject to
- * change, and subject to become either a build-time or runtime error.
- */
- char *cmd_string; /* Must be non-NULL. */
- size_t cmd_size; /* Must be non-zero. */
-
- /*
- * This is called once for each I2C pseudo controller to initialize
- * *data, prior to that pointer being passed to any other callbacks.
- *
- * This will only be called before the I2C adapter device is added.
- *
- * *data will be set to NULL before this is called.
- *
- * This callback may be NULL, in which case *data will remain NULL upon
- * initialization.
- *
- * This should return -errno upon failure, 0 upon success. All
- * non-negative return values are currently treated as success but
- * positive values are reserved for potential future use.
- *
- * Initialization failure will cause the whole I2C pseudo controller to
- * fail to initialize or function, thus *data will not be passed to any
- * other callbacks.
- */
- int (*data_creator)(void **data);
- /*
- * This is called once when shutdown of an I2C pseudo controller is
- * imminent, and no further I2C replies can be processed.
- *
- * This callback may be NULL.
- */
- void (*data_shutdown)(void *data);
- /*
- * This is called once upon termination of each I2C pseudo controller to
- * free any resources held by @data.
- *
- * This will never be called while the I2C adapter device is active.
- * Normally that means this is called after the I2C adapter device has
- * been deleted, but it is also possible for this to be called during
- * I2C pseudo controller initialization if a subsequent initialization
- * step failed, as part of failure handling cleanup.
- *
- * This will only be called after a successful return value from
- * data_creator().
- *
- * This will be passed the same *data pointer that data_creator() placed
- * in its **data output arg.
- *
- * The *data pointer will not be used again by the write command system
- * after the start of this function call.
- *
- * This callback may be NULL.
- */
- void (*data_destroyer)(void *data);
- /*
- * This is called to process write command header fields, including the
- * command name itself as the first header field in every command.
- *
- * This is called once for each header field, in order, including the
- * initial command name.
- *
- * @data is the value of *data from data_creator(). (Thus NULL if
- * data_creator field is NULL.)
- *
- * @in and @in_size are the header value. It will never contain
- * i2cp_ctrlr_header_sep_char or i2cp_ctrlr_end_char.
- *
- * in[in_size] is guaranteed to be null. There may be null characters
- * inside the buffer boundary indicated by @in_size as well though!
- *
- * @non_blocking indicates whether O_NONBLOCK is set on the controller
- * file descriptor. This is not expected to be relevant to most write
- * command callback implementations, however it should be respected if
- * relevant. In other words, if this is true do not block indefinitely,
- * instead return EAGAIN or EWOULDBLOCK. If this is false never return
- * EAGAIN or EWOULDBLOCK.
- *
- * Return -errno to indicate a failure. After a failure the next and
- * final callback invocation for the command will be cmd_completer().
- *
- * Return 0 to indicate success _and_ that another header field is
- * expected next. The next header field will be fully buffered before
- * being sent to this callback, just as the current one was.
- *
- * Return a positive value to indicate success _and_ that data is
- * expected next. The exact positive value sets the chunk size used to
- * buffer the data and pass it to data_receiver. All invocations of
- * data_receiver are guaranteed to receive data in a _multiple_ of the
- * chunk size, except the final invocation, because
- * i2cp_ctrlr_end_char could be received on a non-chunk-size boundary.
- * The return value should be less than I2CP_CTRLR_CMD_LIMIT, as that
- * minus one is the maximum that will ever be buffered at once, and thus
- * the maximum that will ever be sent to a single invocation of
- * data_receiver.
- *
- * If the command is expected to end after a header field without any
- * data, it is encouraged to return 1 here and have data_receiver
- * indicate a failure if it is called. That avoids having the
- * unexpected input buffered unnecessarily.
- *
- * This callback MUST NOT be NULL.
- */
- int (*header_receiver)(void *data, char *in, size_t in_size,
- bool non_blocking);
- /*
- * This is called to process write command data, when requested by the
- * header_receiver() return value.
- *
- * This may be invoked multiple times for each data field, with the data
- * broken up into sequential non-overlapping chunks.
- *
- * @in and @in_size are data. The data will never contain
- * i2cp_ctrlr_end_char.
- *
- * in[in_size] is guaranteed to be null. There may be null characters
- * inside the buffer boundary indicated by @in_size as well though!
- *
- * @in_size is guaranteed to be a multiple of the chunk size as
- * specified by the last return value from header_receiver(), unless
- * either the chunk size is >= I2CP_CTRLR_CMD_LIMIT, or
- * i2cp_ctrlr_end_char was reached on a non-chunk-sized boundary.
- *
- * @in_size is guaranteed to be greater than zero, and less than
- * I2CP_CTRLR_CMD_LIMIT.
- *
- * @non_blocking indicates whether O_NONBLOCK is set on the controller
- * file descriptor. This is not expected to be relevant to most write
- * command callback implementations, however it should be respected if
- * relevant. In other words, if this is true do not block indefinitely,
- * instead return EAGAIN or EWOULDBLOCK. If this is false never return
- * EAGAIN or EWOULDBLOCK.
- *
- * This should return -errno upon failure, 0 upon success. All
- * non-negative return values are currently treated as success but
- * positive values are reserved for potential future use. After a
- * failure the next and final callback invocation for the command will
- * be cmd_completer().
- *
- * If header_receiver() never returns a positive number, this callback
- * should be NULL. Otherwise, this callback MUST NOT be NULL.
- */
- int (*data_receiver)(void *data, char *in, size_t in_size,
- bool non_blocking);
- /*
- * This is called to complete processing of a command, after it has been
- * received in its entirety.
- *
- * If @receive_status is positive, it is an error code from the invoking
- * routines themselves, e.g. if the controller process wrote a header
- * field >= I2CP_CTRLR_CMD_LIMIT.
- *
- * If @receive_status is zero, it means all invocations of
- * header_receiver and data_receiver returned successful values and the
- * entire write command was received successfully.
- *
- * If @receive_status is negative, it is the value returned by the last
- * header_receiver or data_receiver invocation.
- *
- * @non_blocking indicates whether O_NONBLOCK is set on the controller
- * file descriptor. This is not expected to be relevant to most write
- * command callback implementations, however it should be respected if
- * relevant. In other words, if this is true do not block indefinitely,
- * instead return EAGAIN or EWOULDBLOCK. If this is false never return
- * EAGAIN or EWOULDBLOCK.
- *
- * This is called exactly once for each write command. This is true
- * regardless of the value of @non_blocking and regardless of the return
- * value of this function, so it is imperative that this function
- * perform any necessary cleanup tasks related to @data, even if
- * non_blocking=true and blocking is required!
- *
- * Thus, even with non_blocking=true, it would only ever make sense to
- * return -EAGAIN from this function if the struct i2cp_cmd
- * implementation is able to perform the would-be blocked cmd_completer
- * operation later, e.g. upon invocation of a callback for the next
- * write command, or by way of a background thread.
- *
- * This should return -errno upon failure, 0 upon success. All
- * non-negative return values are currently treated as success but
- * positive values are reserved for potential future use.
- *
- * An error should be returned only to indicate a new error that
- * happened during the execution of this callback. Any error from
- * @receive_status should *not* be copied to the return value of this
- * callback.
- *
- * This callback may be NULL.
- */
- int (*cmd_completer)(void *data, struct i2cp_controller *pdata,
- int receive_status, bool non_blocking);
-};
-
-/*
- * These are indexes of i2cp_cmds[]. Every element in that array should have a
- * corresponding value in this enum, and the enum value should be used in the
- * i2cp_cmds[] initializer.
- *
- * Command names are matched in this order, so sort by expected frequency.
- */
-enum {
- I2CP_CMD_MXFER_REPLY_IDX = 0,
- I2CP_CMD_ADAP_START_IDX,
- I2CP_CMD_ADAP_SHUTDOWN_IDX,
- I2CP_CMD_GET_NUMBER_IDX,
- I2CP_CMD_GET_PSEUDO_ID_IDX,
- I2CP_CMD_SET_NAME_SUFFIX_IDX,
- I2CP_CMD_SET_TIMEOUT_IDX,
- /* Keep this at the end! This must equal ARRAY_SIZE(i2cp_cmds). */
- I2CP_NUM_WRITE_CMDS,
-};
-
-/*
- * All values must be >= 0. This should not contain any error values.
- *
- * The state for a new controller must have a zero value, so that
- * zero-initialized memory results in the correct default value.
- */
-enum i2cp_ctrlr_state {
- /*
- * i2c_add_adapter() has not been called yet, or has only returned
- * failure.
- */
- I2CP_CTRLR_STATE_NEW = 0,
- /*
- * i2c_add_adapter() has return success, and the controller has not
- * requested shutdown yet.
- */
- I2CP_CTRLR_STATE_RUNNING,
- /*
- * i2c_add_adapter() has returned success, and the controller has
- * requested shutdown.
- *
- * Note that it is perfectly acceptable for a pseudo controller fd to be
- * closed and released without shutdown having been requested
- * beforehand. Thus, this state is purely optional in the lifetime of a
- * controller.
- */
- I2CP_CTRLR_STATE_SHUTDN_REQ,
-};
-
-/*
- * Avoid allocating this struct on the stack, it contains a large buffer as a
- * direct member.
- *
- * To avoid deadlocks, never attempt to hold more than one of the locks in this
- * structure at once, with the following exceptions:
- * - It is permissible to acquire read_rsp_queue_lock while holding cmd_lock.
- * - It is permissible to acquire read_rsp_queue_lock while holding rsp_lock.
- */
-struct i2cp_controller {
- unsigned int index;
- /*
- * Never modify the ID after initialization.
- *
- * This should be an unsigned integer type large enough to hold
- * I2CP_ADAPTERS_MAX.
- */
- unsigned int id;
- /*
- * Only i2cp_cdev_open() and i2cp_cdev_release() may access this field.
- * Other functions called by them, or called by the I2C subsystem, may
- * of course take a reference to this same struct i2c_adapter. However
- * no other functions besides the aforementioned two may access the
- * i2c_adapter field of struct i2cp_controller.
- */
- struct i2c_adapter i2c_adapter;
-
- struct mutex startstop_lock;
- enum i2cp_ctrlr_state startstop_state;
-
- wait_queue_head_t poll_wait_queue;
-
- /* This must be held while read or writing cmd_* fields. */
- struct mutex cmd_lock;
- /*
- * This becomes the @receive_status arg to struct i2cp_cmd.cmd_completer
- * callback.
- *
- * A negative value is an error number from
- * struct i2cp_cmd.header_receiver or struct i2cp_cmd.data_receiver.
- *
- * A zero value means no error has occurred so far in processing the
- * current write reply command.
- *
- * A positive value is an error number from a non-command-specific part
- * of write command processing, e.g. from the
- * struct file_operations.write callback itself, or function further up
- * its call stack that is not specific to any particular write command.
- */
- int cmd_receive_status;
- /*
- * Index of i2cp_cmds[] and .cmd_data[] plus one, i.e. value of 1 means
- * 0 index. Value of 0 (zero) means the controller is waiting for a new
- * command.
- */
- int cmd_idx_plus_one;
- int cmd_data_increment;
- size_t cmd_size;
- /* Add one for trailing null character. */
- char cmd_buf[I2CP_CTRLR_CMD_LIMIT + 1];
- void *cmd_data[I2CP_NUM_WRITE_CMDS];
-
- struct completion read_rsp_queued;
- /* This must be held while read or writing read_rsp_queue_* fields. */
- struct mutex read_rsp_queue_lock;
- /*
- * This is a FIFO queue of struct i2cp_rsp.queue .
- *
- * This MUST be strictly used as FIFO. Only consume or pop the first
- * item. Only append to the end. Users of this queue assume this FIFO
- * behavior is strictly followed, and their uses of read_rsp_queue_lock
- * would not be safe otherwise.
- */
- struct list_head read_rsp_queue_head;
- unsigned int read_rsp_queue_length;
-
- /* This must be held while reading or writing rsp_* fields. */
- struct mutex rsp_lock;
- bool rsp_invalidated;
- /*
- * Holds formatted string from most recently popped item of
- * read_rsp_queue_head if it was not wholly consumed by the last
- * controller read.
- */
- char *rsp_buf_start;
- char *rsp_buf_pos;
- ssize_t rsp_buf_remaining;
-};
-
-struct i2cp_cmd_mxfer_reply {
- /*
- * This lock MUST be held while reading or modifying any part of this
- * struct i2cp_cmd_mxfer_reply, unless you can guarantee that nothing
- * else can access this struct concurrently, such as during
- * initialization.
- *
- * The struct i2cp_cmd_mxfer_reply_data.reply_queue_lock of the
- * struct i2cp_cmd_mxfer_reply_data.reply_queue_head list which contains
- * this struct i2cp_cmd_mxfer_reply.reply_queue_item MUST be held when
- * attempting to acquire this lock.
- *
- * It is NOT required to keep
- * struct i2cp_cmd_mxfer_reply_data.reply_queue_lock held after
- * acquisition of this lock (unless also manipulating
- * struct i2cp_cmd_mxfer_reply_data.reply_queue_* of course).
- */
- struct mutex lock;
-
- /*
- * Never modify the ID after initialization.
- *
- * This should be an unsigned integer type large enough to hold
- * I2CP_CTRLR_RSP_QUEUE_LIMIT. If changing this type, audit for printf
- * format strings that need updating!
- */
- unsigned int id;
- /* Number of I2C messages successfully processed, or negative error. */
- int ret;
- /* Same type as struct i2c_algorithm.master_xfer @num arg. */
- int num_msgs;
- /* Same type as struct i2c_algorithm.master_xfer @msgs arg. */
- struct i2c_msg *msgs;
- /* Same length (not size) as *msgs array. */
- bool *completed;
- /* Number of completed[] array entries with true value. */
- int num_completed_true;
-
- /*
- * This is for use in struct i2cp_cmd_mxfer_reply_data.reply_queue_head
- * FIFO queue.
- *
- * Any time this is deleted from its containing
- * struct i2cp_cmd_mxfer_reply_data.reply_queue_head list, either
- * list_del_init() MUST be used (not list_del()), OR this whole
- * struct i2cp_cmd_mxfer_reply MUST be freed.
- *
- * That way, if this struct is not immediately freed, the code which
- * eventually frees it can test whether it still needs to be deleted
- * from struct i2cp_cmd_mxfer_reply_data.reply_queue_head by using
- * list_empty() on reply_queue_item. (Calling list_del() on an
- * already-deleted list item is unsafe.)
- */
- struct list_head reply_queue_item;
- struct completion data_filled;
-};
-
-/*
- * The state for receiving the first field must have a zero value, so that
- * zero-initialized memory results in the correct default value.
- */
-enum i2cp_cmd_mxfer_reply_state {
- I2CP_CMD_MXFER_REPLY_STATE_CMD_NEXT = 0,
- I2CP_CMD_MXFER_REPLY_STATE_ID_NEXT,
- I2CP_CMD_MXFER_REPLY_STATE_INDEX_NEXT,
- I2CP_CMD_MXFER_REPLY_STATE_ADDR_NEXT,
- I2CP_CMD_MXFER_REPLY_STATE_FLAGS_NEXT,
- I2CP_CMD_MXFER_REPLY_STATE_ERRNO_NEXT,
- I2CP_CMD_MXFER_REPLY_STATE_DATA_NEXT,
- /*
- * This is used to tell subsequent callback invocations that the write
- * command currently being received is invalid, when the receiver wants
- * to quietly discard the write command instead of loudly returning an
- * error.
- */
- I2CP_CMD_MXFER_REPLY_STATE_INVALID,
-};
-
-struct i2cp_cmd_mxfer_reply_data {
- /* This must be held while read or writing reply_queue_* fields. */
- struct mutex reply_queue_lock;
- /*
- * This is used to make a strong attempt at avoiding ID reuse,
- * especially for overlapping master_xfer() calls.
- *
- * This can wrap by design, and thus makes no perfect guarantees over
- * the lifetime of an I2C pseudo adapter.
- *
- * No code should assume uniqueness, not even for master_xfer() calls of
- * overlapping lifetimes. When the controller writes a master_xfer()
- * reply command, assume that it is for the oldest outstanding instance
- * of the ID number specified.
- */
- /* Same type as struct i2cp_cmd_mxfer_reply.id field. */
- unsigned int next_mxfer_id;
- /*
- * This is a FIFO queue of struct i2cp_cmd_mxfer_reply.reply_queue_item.
- *
- * This MUST be strictly used as FIFO. Only consume or pop the first
- * item. Only append to the end. Users of this queue assume this FIFO
- * behavior is strictly followed, and their uses of reply_queue_lock may
- * not be safe otherwise.
- */
- struct list_head reply_queue_head;
- unsigned int reply_queue_length;
- struct i2cp_cmd_mxfer_reply *reply_queue_current_item;
-
- enum i2cp_cmd_mxfer_reply_state state;
-
- /* Same type as struct i2cp_cmd_mxfer_reply.id field. */
- unsigned int current_id;
- /* Same type as struct i2c_msg.addr field. */
- u16 current_addr;
- /* Same type as struct i2c_msg.flags field. */
- u16 current_flags;
- /* Same type as struct i2c_algorithm.master_xfer @num arg. */
- int current_msg_idx;
- /* Same type as struct i2c_msg.len field. */
- u16 current_buf_idx;
-};
-
-struct i2cp_cmd_set_name_suffix_data {
- char name_suffix[sizeof_field(struct i2c_adapter, name)];
- size_t name_suffix_len;
-};
-
-struct i2cp_cmd_set_timeout_data {
- int field_pos;
- unsigned int timeout_ms;
-};
-
-struct i2cp_rsp {
- /*
- * This callback is invoked to format its associated data for passing to
- * the userspace controller process when it read()s the I2C pseudo
- * controller character device.
- *
- * @data will be the data pointer from this struct instance.
- *
- * @out is an output argument. Upon positive return value, *out must be
- * set to a buffer which the caller will take ownership of, and which
- * can be freed with kfree().
- *
- * Upon positive return value, @data must NOT be freed.
- *
- * The formatter will be called repeatedly for the same data until it
- * returns non-positive.
- *
- * Upon non-positive return value, *out should not be modified.
- *
- * Upon non-positive return value, the formatter should have freed data
- * with kfree(). Implicitly this means any allocations owned by *data
- * should have been freed by the formatter as well.
- *
- * A negative return value indicates an error occurred and the data
- * cannot be formatted successfully. The error code may or may not
- * eventually be propagated back to the I2C pseudo adapter controller.
- *
- * A positive return value is the number of characters/bytes to use from
- * the *out buffer, always starting from index 0. It should NOT include
- * a trailing NULL character unless that character should be propagated
- * to the I2C pseudo adapter controller! It therefore does NOT need to
- * be the full size of the allocated *out buffer, instead it can be
- * less. (The size is not needed by kfree().)
- *
- * The formatter owns the memory pointed to by data. The invoking code
- * will never mutate or free data. Thus, upon non-positive return value
- * from the formatter, the formatter must have already performed any
- * reference counting decrement or memory freeing necessary to ensure
- * data does not live beyond its final use.
- *
- * There will never be more than one formatter callback in flight at
- * once for a given I2C pseudo controller. This is true even in the
- * face of concurrent reads by the controller.
- *
- * The formatter must NOT use i2cp_ctrlr_end_char in anywhere in *out
- * (within the size range indicated by the return value; past that does
- * not matter). The i2cp_ctrlr_end_char will be added automatically by
- * the caller after a zero return value (successful completion) from the
- * formatter.
- *
- * The formatter must never create or return a buffer larger than
- * I2CP_MAX_MSG_BUF_SIZE. The formatter is encouraged to avoid that by
- * generating and returning the output in chunks, taking advantage of
- * the guarantee that it will be called repeatedly until exhaustion
- * (zero return value) or failure (negative return value). If the
- * formatter expects its formatted output or natural subsets of it to
- * always fit within I2CP_MAX_MSG_BUF_SIZE, and it is called with input
- * data not meeting that expectation, the formatter should return
- * -ERANGE to indicate this condition.
- */
- ssize_t (*formatter)(void *data, char **out);
- void *data;
-
- struct list_head queue;
-};
-
-struct i2cp_rsp_buffer {
- char *buf;
- ssize_t size;
-};
-
-struct i2cp_rsp_master_xfer {
- /* Never modify the ID after initialization. */
- /* Same type as struct i2cp_cmd_mxfer_reply.id field. */
- unsigned int id;
-
- /* These types match those of struct i2c_algorithm.master_xfer args. */
- struct i2c_msg *msgs;
- int num;
-
- /*
- * Always initialize fields below here to zero. They are for internal
- * use by i2cp_rsp_master_xfer_formatter().
- */
- int num_msgs_done; /* type of @num field */
- size_t buf_start_plus_one;
-};
-
-/* vanprintf - See anprintf() documentation. */
-static ssize_t vanprintf(char **out, ssize_t max_size, gfp_t gfp,
- const char *fmt, va_list ap)
-{
- int ret;
- ssize_t buf_size;
- char *buf = NULL;
- va_list args1;
-
- va_copy(args1, ap);
- ret = vsnprintf(NULL, 0, fmt, ap);
- if (ret < 0)
- goto fail_before_args1;
- if (max_size >= 0 && ret > max_size) {
- ret = -ERANGE;
- goto fail_before_args1;
- }
-
- buf_size = ret + 1;
- buf = kmalloc(buf_size, gfp);
- if (buf == NULL) {
- ret = -ENOMEM;
- goto fail_before_args1;
- }
-
- ret = vsnprintf(buf, buf_size, fmt, args1);
- va_end(args1);
- if (ret < 0)
- goto fail_after_args1;
- if (ret + 1 != buf_size) {
- ret = -ENOTRECOVERABLE;
- goto fail_after_args1;
- }
-
- *out = buf;
- return ret;
-
- fail_before_args1:
- va_end(args1);
- fail_after_args1:
- kfree(buf);
- if (ret >= 0)
- ret = -ENOTRECOVERABLE;
- return ret;
-}
-
-/*
- * anprintf - Format a string and place it into a newly allocated buffer.
- * @out: Address of the pointer to place the buffer address into. Will only be
- * written to with a successful positive return value.
- * @max_size: If non-negative, the maximum buffer size that this function will
- * attempt to allocate. If the formatted string including trailing null
- * character would not fit, no buffer will be allocated, and an error will
- * be returned. (Thus max_size of 0 will always result in an error.)
- * @gfp: GFP flags for kmalloc().
- * @fmt: The format string to use.
- * @...: Arguments for the format string.
- *
- * Return value meanings:
- *
- * >=0: A buffer of this size was allocated and its address written to *out.
- * The caller now owns the buffer and is responsible for freeing it with
- * kfree(). The final character in the buffer, not counted in this
- * return value, is the trailing null. This is the same return value
- * meaning as snprintf(3).
- *
- * <0: An error occurred. Negate the return value for the error number.
- * @out will not have been written to. Errors that might come from
- * snprintf(3) may come from this function as well. Additionally, the
- * following errors may occur from this function:
- *
- * ERANGE: A buffer larger than @max_size would be needed to fit the
- * formatted string including its trailing null character.
- *
- * ENOMEM: Allocation of the output buffer failed.
- *
- * ENOTRECOVERABLE: An unexpected condition occurred. This may indicate
- * a bug.
- */
-static ssize_t anprintf(char **out, ssize_t max_size, gfp_t gfp,
- const char *fmt, ...)
-{
- ssize_t ret;
- va_list args;
-
- va_start(args, fmt);
- ret = vanprintf(out, max_size, gfp, fmt, args);
- va_end(args);
- return ret;
-}
-
-static ssize_t i2cp_rsp_buffer_formatter(void *data, char **out)
-{
- struct i2cp_rsp_buffer *rsp_buf;
-
- rsp_buf = data;
- if (rsp_buf->buf) {
- if (rsp_buf->size > 0) {
- *out = rsp_buf->buf;
- rsp_buf->buf = NULL;
- return rsp_buf->size;
- }
- kfree(rsp_buf->buf);
- }
- kfree(rsp_buf);
- return 0;
-}
-
-static ssize_t i2cp_rsp_master_xfer_formatter(void *data, char **out)
-{
- ssize_t ret;
- size_t i, buf_size, byte_start, byte_limit;
- char *buf_start, *buf_pos;
- struct i2cp_rsp_master_xfer *mxfer_rsp;
- struct i2c_msg *i2c_msg;
-
- mxfer_rsp = data;
-
- /*
- * This condition is set by a previous call to this function with the
- * same data, when it returned an error but was not consuming the final
- * i2c_msg.
- */
- if (!mxfer_rsp->msgs) {
- ++mxfer_rsp->num_msgs_done;
- ret = 0;
- goto maybe_free;
- }
-
- i2c_msg = &mxfer_rsp->msgs[mxfer_rsp->num_msgs_done];
-
- /*
- * If this is a read, or if this is a write and we've finished writing
- * the data buffer, we are done with this i2c_msg.
- */
- if (mxfer_rsp->buf_start_plus_one >= 1 &&
- (i2c_msg->flags & I2C_M_RD ||
- mxfer_rsp->buf_start_plus_one >= (size_t)i2c_msg->len + 1)) {
- ++mxfer_rsp->num_msgs_done;
- mxfer_rsp->buf_start_plus_one = 0;
- ret = 0;
- goto maybe_free;
- }
-
- if (mxfer_rsp->buf_start_plus_one <= 0) {
- /*
- * The length is not strictly necessary with the explicit
- * end-of-message marker (i2cp_ctrlr_end_char), however it
- * serves as a useful validity check for controllers to verify
- * that no bytes were lost in kernel->userspace transmission.
- */
- ret = anprintf(&buf_start, I2CP_MAX_MSG_BUF_SIZE, GFP_KERNEL,
- "%*s%c%u%c%d%c0x%04X%c0x%04X%c%u",
- (int)strlen(I2CP_MXFER_REQ_CMD), I2CP_MXFER_REQ_CMD,
- i2cp_ctrlr_header_sep_char, mxfer_rsp->id,
- i2cp_ctrlr_header_sep_char, mxfer_rsp->num_msgs_done,
- i2cp_ctrlr_header_sep_char, i2c_msg->addr,
- i2cp_ctrlr_header_sep_char, i2c_msg->flags,
- i2cp_ctrlr_header_sep_char, i2c_msg->len);
- if (ret > 0) {
- *out = buf_start;
- mxfer_rsp->buf_start_plus_one = 1;
- /*
- * If we have a zero return value, it means the output buffer
- * was allocated as size one, containing only a terminating null
- * character. This would be a bug given the requested format
- * string above. Also, formatter functions must not mutate *out
- * when returning zero. So if this matches, free the useless
- * buffer and return an error.
- */
- } else if (ret == 0) {
- ret = -EINVAL;
- kfree(buf_start);
- }
- goto maybe_free;
- }
-
- byte_start = mxfer_rsp->buf_start_plus_one - 1;
- byte_limit = min_t(size_t, i2c_msg->len - byte_start,
- I2CP_MAX_MSG_BUF_SIZE / 3);
- /* 3 chars per byte == 2 chars for hex + 1 char for separator */
- buf_size = byte_limit * 3;
-
- buf_start = kzalloc(buf_size, GFP_KERNEL);
- if (!buf_start) {
- ret = -ENOMEM;
- goto maybe_free;
- }
-
- for (buf_pos = buf_start, i = 0; i < byte_limit; ++i) {
- *buf_pos++ = (i || byte_start) ?
- i2cp_ctrlr_data_sep_char : i2cp_ctrlr_header_sep_char;
- buf_pos = hex_byte_pack_upper(
- buf_pos, i2c_msg->buf[byte_start + i]);
- }
- *out = buf_start;
- ret = buf_size;
- mxfer_rsp->buf_start_plus_one += i;
-
- maybe_free:
- if (ret <= 0) {
- if (mxfer_rsp->num_msgs_done >= mxfer_rsp->num) {
- kfree(mxfer_rsp->msgs);
- kfree(mxfer_rsp);
- /*
- * If we are returning an error but have not consumed all of
- * mxfer_rsp yet, we must not attempt to output any more I2C
- * messages from the same mxfer_rsp. Setting mxfer_rsp->msgs to
- * NULL tells the remaining invocations with this mxfer_rsp to
- * output nothing.
- *
- * There can be more invocations with the same mxfer_rsp even
- * after returning an error here because
- * i2cp_adapter_master_xfer() reuses a single
- * struct i2cp_rsp_master_xfer (mxfer_rsp) across multiple
- * struct i2cp_rsp (rsp_wrappers), one for each struct i2c_msg
- * within the mxfer_rsp.
- */
- } else if (ret < 0) {
- kfree(mxfer_rsp->msgs);
- mxfer_rsp->msgs = NULL;
- }
- }
- return ret;
-}
-
-static ssize_t i2cp_id_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- int ret;
- struct i2c_adapter *adap;
- struct i2cp_controller *pdata;
-
- adap = container_of(dev, struct i2c_adapter, dev);
- pdata = container_of(adap, struct i2cp_controller, i2c_adapter);
- ret = snprintf(buf, PAGE_SIZE, "%u\n", pdata->id);
- if (ret >= PAGE_SIZE)
- return -ERANGE;
- return ret;
-}
-
-static const struct device_attribute i2cp_id_dev_attr = {
- .attr = {
- .name = "i2c-pseudo-id",
- .mode = 0444,
- },
- .show = i2cp_id_show,
-};
-
-static enum i2cp_ctrlr_state i2cp_adap_get_state(struct i2cp_controller *pdata)
-{
- enum i2cp_ctrlr_state ret;
-
- mutex_lock(&pdata->startstop_lock);
- ret = pdata->startstop_state;
- mutex_unlock(&pdata->startstop_lock);
- return ret;
-}
-
-static int i2cp_cmd_mxfer_reply_data_creator(void **data)
-{
- struct i2cp_cmd_mxfer_reply_data *cmd_data;
-
- cmd_data = kzalloc(sizeof(*cmd_data), GFP_KERNEL);
- if (!cmd_data)
- return -ENOMEM;
- mutex_init(&cmd_data->reply_queue_lock);
- INIT_LIST_HEAD(&cmd_data->reply_queue_head);
- *data = cmd_data;
- return 0;
-}
-
-/*
- * Notify pending I2C requests of the shutdown. There is no possibility of
- * further I2C replies at this point. This stops the I2C requests from waiting
- * for the adapter timeout, which could have been set arbitrarily long by the
- * userspace controller.
- */
-static void i2cp_cmd_mxfer_reply_data_shutdown(void *data)
-{
- struct list_head *list_ptr;
- struct i2cp_cmd_mxfer_reply_data *cmd_data;
- struct i2cp_cmd_mxfer_reply *mxfer_reply;
-
- cmd_data = data;
- mutex_lock(&cmd_data->reply_queue_lock);
- list_for_each(list_ptr, &cmd_data->reply_queue_head) {
- mxfer_reply = list_entry(list_ptr, struct i2cp_cmd_mxfer_reply,
- reply_queue_item);
- mutex_lock(&mxfer_reply->lock);
- complete_all(&mxfer_reply->data_filled);
- mutex_unlock(&mxfer_reply->lock);
- }
- mutex_unlock(&cmd_data->reply_queue_lock);
-}
-
-static void i2cp_cmd_mxfer_reply_data_destroyer(void *data)
-{
- /*
- * We do not have to worry about racing with in-flight I2C messages
- * because data_destroyer callbacks are guaranteed to never be called
- * while the I2C adapter device is active.
- */
- kfree(data);
-}
-
-static inline bool i2cp_mxfer_reply_is_current(
- struct i2cp_cmd_mxfer_reply_data *cmd_data,
- struct i2cp_cmd_mxfer_reply *mxfer_reply)
-{
- int i;
-
- i = cmd_data->current_msg_idx;
- return cmd_data->current_id == mxfer_reply->id &&
- i >= 0 && i < mxfer_reply->num_msgs &&
- cmd_data->current_addr == mxfer_reply->msgs[i].addr &&
- cmd_data->current_flags == mxfer_reply->msgs[i].flags;
-}
-
-/* cmd_data->reply_queue_lock must be held. */
-static inline struct i2cp_cmd_mxfer_reply *i2cp_mxfer_reply_find_current(
- struct i2cp_cmd_mxfer_reply_data *cmd_data)
-{
- struct list_head *list_ptr;
- struct i2cp_cmd_mxfer_reply *mxfer_reply;
-
- list_for_each(list_ptr, &cmd_data->reply_queue_head) {
- mxfer_reply = list_entry(list_ptr, struct i2cp_cmd_mxfer_reply,
- reply_queue_item);
- if (i2cp_mxfer_reply_is_current(cmd_data, mxfer_reply))
- return mxfer_reply;
- }
- return NULL;
-}
-
-/* cmd_data->reply_queue_lock must NOT already be held. */
-static inline void i2cp_mxfer_reply_update_current(
- struct i2cp_cmd_mxfer_reply_data *cmd_data)
-{
- mutex_lock(&cmd_data->reply_queue_lock);
- cmd_data->reply_queue_current_item = i2cp_mxfer_reply_find_current(
- cmd_data);
- mutex_unlock(&cmd_data->reply_queue_lock);
-}
-
-static int i2cp_cmd_mxfer_reply_header_receiver(void *data, char *in,
- size_t in_size, bool non_blocking)
-{
- int ret, reply_errno = 0;
- struct i2cp_cmd_mxfer_reply_data *cmd_data;
-
- cmd_data = data;
-
- switch (cmd_data->state) {
- case I2CP_CMD_MXFER_REPLY_STATE_CMD_NEXT:
- /* Expect the msg/reply ID header field next. */
- cmd_data->state = I2CP_CMD_MXFER_REPLY_STATE_ID_NEXT;
- return 0;
- case I2CP_CMD_MXFER_REPLY_STATE_ID_NEXT:
- case I2CP_CMD_MXFER_REPLY_STATE_INDEX_NEXT:
- case I2CP_CMD_MXFER_REPLY_STATE_ADDR_NEXT:
- case I2CP_CMD_MXFER_REPLY_STATE_FLAGS_NEXT:
- case I2CP_CMD_MXFER_REPLY_STATE_ERRNO_NEXT:
- break;
- default:
- /* Reaching here is a bug. */
- /*
- * Testing this before checking for null characters ensures the
- * correct error is indicated.
- */
- return -EINVAL;
- }
-
- /*
- * The command name is logically outside the control of this function,
- * and may contain null characters, even if that would be nonsensical.
- * Thus it is handled above, followed by this check, and below here
- * the rest of the header fields are handled. Some of them use
- * functions that could mishandle input which contains nulls. An actual
- * error would be okay, however if the input were consumed incorrectly
- * without an error, that could lead to subtle bugs.
- */
- if (memchr(in, '\0', in_size))
- return -EPROTO;
-
- switch (cmd_data->state) {
- case I2CP_CMD_MXFER_REPLY_STATE_ID_NEXT:
- ret = kstrtouint(in, 0, &cmd_data->current_id);
- if (ret < 0)
- return ret;
- cmd_data->state = I2CP_CMD_MXFER_REPLY_STATE_INDEX_NEXT;
- return 0;
- case I2CP_CMD_MXFER_REPLY_STATE_INDEX_NEXT:
- ret = kstrtoint(in, 0, &cmd_data->current_msg_idx);
- if (ret < 0)
- return ret;
- cmd_data->state = I2CP_CMD_MXFER_REPLY_STATE_ADDR_NEXT;
- return 0;
- case I2CP_CMD_MXFER_REPLY_STATE_ADDR_NEXT:
- ret = kstrtou16(in, 0, &cmd_data->current_addr);
- if (ret < 0)
- return ret;
- cmd_data->state = I2CP_CMD_MXFER_REPLY_STATE_FLAGS_NEXT;
- return 0;
- case I2CP_CMD_MXFER_REPLY_STATE_FLAGS_NEXT:
- ret = kstrtou16(in, 0, &cmd_data->current_flags);
- if (ret < 0)
- return ret;
- cmd_data->state = I2CP_CMD_MXFER_REPLY_STATE_ERRNO_NEXT;
- return 0;
- case I2CP_CMD_MXFER_REPLY_STATE_ERRNO_NEXT:
- ret = kstrtoint(in, 0, &reply_errno);
- if (ret < 0)
- return ret;
- break;
- default:
- /* Reaching here is a bug. */
- return -EINVAL;
- }
-
- /*
- * Only I2CP_CMD_MXFER_REPLY_STATE_ERRNO_NEXT can reach this point.
- * Now that we've received all of the headers, find the matching
- * mxfer_reply.
- */
- i2cp_mxfer_reply_update_current(cmd_data);
-
- if (reply_errno || !cmd_data->reply_queue_current_item) {
- /*
- * reply_errno:
- * Drop the specific errno for now. The Linux I2C API
- * does not provide a way to return an errno for a
- * specific message within a master_xfer() call. The
- * cmd_completer callback will indicate this
- * controller-reported failure by not incrementing
- * mxfer_reply->ret for this I2C msg reply.
- *
- * cmd_data->reply_queue_current_item == NULL:
- * No matching mxfer_reply was found. Discard any
- * further input in this command. The cmd_completer
- * callback will indicate this failure to the
- * controller.
- */
- cmd_data->state = I2CP_CMD_MXFER_REPLY_STATE_INVALID;
- /*
- * Ask for data bytes in multiples of 1, i.e. no
- * boundary requirements, because the we're just going
- * to discard it. The next field could even be a header
- * instead of data, but it doesn't matter, we're going
- * to continue discarding the write input until the end
- * of this write command.
- */
- return 1;
- }
-
- cmd_data->state = I2CP_CMD_MXFER_REPLY_STATE_DATA_NEXT;
- /*
- * Ask for data bytes in multiples of 3. Expected format is
- * hexadecimal NN:NN:... e.g. "3C:05:F1:01" is a possible 4 byte
- * data value.
- */
- return 3;
-}
-
-static int i2cp_cmd_mxfer_reply_data_receiver(void *data, char *in,
- size_t in_size, bool non_blocking)
-{
- int ret;
- char u8_hex[3] = {0};
- struct i2cp_cmd_mxfer_reply_data *cmd_data;
- struct i2cp_cmd_mxfer_reply *mxfer_reply;
- struct i2c_msg *i2c_msg;
-
- cmd_data = data;
-
- if (cmd_data->state == I2CP_CMD_MXFER_REPLY_STATE_INVALID)
- return 0;
- if (cmd_data->state != I2CP_CMD_MXFER_REPLY_STATE_DATA_NEXT)
- /* Reaching here is a bug. */
- return -EINVAL;
-
- mutex_lock(&cmd_data->reply_queue_lock);
- mxfer_reply = cmd_data->reply_queue_current_item;
- if (!mxfer_reply) {
- /* Reaching here is a bug. */
- mutex_unlock(&cmd_data->reply_queue_lock);
- return -EINVAL;
- }
- mutex_lock(&mxfer_reply->lock);
- mutex_unlock(&cmd_data->reply_queue_lock);
-
- if (cmd_data->current_msg_idx < 0 ||
- cmd_data->current_msg_idx >= mxfer_reply->num_msgs) {
- /* Reaching here is a bug. */
- ret = -EINVAL;
- goto unlock;
- }
-
- i2c_msg = &mxfer_reply->msgs[cmd_data->current_msg_idx];
-
- if (!(i2c_msg->flags & I2C_M_RD)) {
- /* The controller responded to a write with data. */
- ret = -EIO;
- goto unlock;
- }
-
- if (i2c_msg->flags & I2C_M_RECV_LEN) {
- /*
- * When I2C_M_RECV_LEN is set, struct i2c_algorithm.master_xfer
- * is expected to increment struct i2c_msg.len by the actual
- * amount of bytes read.
- *
- * Given the above, an initial struct i2c_msg.len value of 0
- * would be reasonable, since it will be incremented for each
- * byte read.
- *
- * An initial value of 1 representing the expected size byte
- * also makes sense, and appears to be common practice.
- *
- * We consider a larger initial value to indicate a bug in the
- * I2C/SMBus client, because it's difficult to reconcile such a
- * value with the documented requirement that struct i2c_msg.len
- * be "incremented by the number of block data bytes received."
- * Besides returning an error, our only options would be to
- * ignore and blow away a value that was potentially meaningful
- * to the client (e.g. if it indicates the maximum buffer size),
- * assume the value is the buffer size or expected read size
- * (which would conflict with the documentation), or just
- * blindly increment it, leaving it at a value greater than the
- * actual number of bytes we wrote to the buffer, and likely
- * indicating a size larger than the actual buffer allocation.
- */
- if (cmd_data->current_buf_idx == 0) {
- if (i2c_msg->len > 1) {
- ret = -EPROTO;
- goto unlock;
- }
- /*
- * Subtract the read size byte because the in_size
- * increment in the loop below will re-add it.
- */
- i2c_msg->len = 0;
- }
- }
-
- while (in_size > 0 && cmd_data->current_buf_idx < i2c_msg->len) {
- if (in_size < 2 ||
- (in_size > 2 && in[2] != i2cp_ctrlr_data_sep_char) ||
- memchr(in, '\0', 2)) {
- /*
- * Reaching here is a bug in the userspace I2C pseudo
- * adapter controller. (Or possibly a bug in this
- * module itself, of course.)
- */
- ret = -EIO;
- goto unlock;
- }
- /*
- * When using I2C_M_RECV_LEN, the buffer is required to be able
- * to hold:
- *
- * I2C_SMBUS_BLOCK_MAX
- * +1 byte for the read size (first byte)
- * +1 byte for the optional PEC byte (last byte if present).
- *
- * If reading the next byte would exceed that, return EPROTO
- * error per Documentation/i2c/fault-codes .
- */
- if (i2c_msg->flags & I2C_M_RECV_LEN &&
- i2c_msg->len >= I2C_SMBUS_BLOCK_MAX + 2) {
- ret = -EPROTO;
- goto unlock;
- }
- /* Use u8_hex to get a terminating null byte for kstrtou8(). */
- memcpy(u8_hex, in, 2);
- /*
- * TODO: Do we need to do anything different based on the
- * I2C_M_DMA_SAFE bit? Do we ever need to use copy_to_user()?
- */
- ret = kstrtou8(u8_hex, 16,
- &i2c_msg->buf[cmd_data->current_buf_idx]);
- if (ret < 0)
- goto unlock;
- if (i2c_msg->flags & I2C_M_RECV_LEN)
- ++i2c_msg->len;
- ++cmd_data->current_buf_idx;
- in += min_t(size_t, 3, in_size);
- in_size -= min_t(size_t, 3, in_size);
- }
-
- /* Quietly ignore any bytes beyond the buffer size. */
- ret = 0;
-
- unlock:
- mutex_unlock(&mxfer_reply->lock);
- return ret;
-}
-
-static int i2cp_cmd_mxfer_reply_cmd_completer(void *data,
- struct i2cp_controller *pdata, int receive_status, bool non_blocking)
-{
- int ret;
- struct i2cp_cmd_mxfer_reply_data *cmd_data;
- struct i2cp_cmd_mxfer_reply *mxfer_reply;
- struct i2c_msg *i2c_msg;
-
- cmd_data = data;
- mutex_lock(&cmd_data->reply_queue_lock);
-
- mxfer_reply = cmd_data->reply_queue_current_item;
- if (!mxfer_reply) {
- mutex_unlock(&cmd_data->reply_queue_lock);
- ret = -EIO;
- goto reset_cmd_data;
- }
-
- mutex_lock(&mxfer_reply->lock);
-
- if (mxfer_reply->completed[cmd_data->current_msg_idx]) {
- /* We already received a reply for this msg. */
- mutex_unlock(&cmd_data->reply_queue_lock);
- mutex_unlock(&mxfer_reply->lock);
- ret = -EIO;
- goto reset_cmd_data;
- }
-
- mxfer_reply->completed[cmd_data->current_msg_idx] = true;
- if (++mxfer_reply->num_completed_true >= mxfer_reply->num_msgs) {
- list_del_init(&mxfer_reply->reply_queue_item);
- --cmd_data->reply_queue_length;
- cmd_data->reply_queue_current_item = NULL;
- complete_all(&mxfer_reply->data_filled);
- }
-
- mutex_unlock(&cmd_data->reply_queue_lock);
- i2c_msg = &mxfer_reply->msgs[cmd_data->current_msg_idx];
-
- if (!receive_status &&
- cmd_data->state == I2CP_CMD_MXFER_REPLY_STATE_DATA_NEXT &&
- (!(i2c_msg->flags & I2C_M_RD) ||
- cmd_data->current_buf_idx >= i2c_msg->len))
- ++mxfer_reply->ret;
-
- mutex_unlock(&mxfer_reply->lock);
- ret = 0;
-
- reset_cmd_data:
- cmd_data->state = I2CP_CMD_MXFER_REPLY_STATE_CMD_NEXT;
- cmd_data->current_id = 0;
- cmd_data->current_addr = 0;
- cmd_data->current_flags = 0;
- cmd_data->current_msg_idx = 0;
- cmd_data->current_buf_idx = 0;
- return ret;
-}
-
-static int i2cp_cmd_adap_start_header_receiver(void *data, char *in,
- size_t in_size, bool non_blocking)
-{
- /*
- * No more header fields or data are expected. This directs any further
- * input in this command to the data_receiver, which for this write
- * command will unconditionally indicate a controller error.
- */
- return 1;
-}
-
-static int i2cp_cmd_adap_start_data_receiver(void *data, char *in,
- size_t in_size, bool non_blocking)
-{
- /*
- * Reaching here means the controller wrote extra data in the command
- * line after the initial command name. That is unexpected and
- * indicates a controller bug.
- */
- return -EPROTO;
-}
-
-static int i2cp_cmd_adap_start_cmd_completer(void *data,
- struct i2cp_controller *pdata, int receive_status, bool non_blocking)
-{
- int ret;
-
- /* Refuse to start if there were errors processing this command. */
- if (receive_status)
- return 0;
-
- /*
- * Acquire pdata->startstop_lock manually instead of using
- * i2cp_adap_get_state() in order to keep the lock while calling
- * i2c_add_adapter().
- */
- mutex_lock(&pdata->startstop_lock);
-
- if (pdata->startstop_state != I2CP_CTRLR_STATE_NEW) {
- ret = -EISCONN;
- goto unlock;
- }
-
- /* Add the I2C adapter. */
- ret = i2c_add_adapter(&pdata->i2c_adapter);
- if (ret < 0)
- goto unlock;
-
- pdata->startstop_state = I2CP_CTRLR_STATE_RUNNING;
-
- /* Add the I2C pseudo controller ID sysfs file. */
- ret = device_create_file(&pdata->i2c_adapter.dev, &i2cp_id_dev_attr);
- if (ret < 0)
- goto unlock;
-
- ret = 0;
-
- unlock:
- mutex_unlock(&pdata->startstop_lock);
- return ret;
-}
-
-static int i2cp_cmd_adap_shutdown_header_receiver(void *data, char *in,
- size_t in_size, bool non_blocking)
-{
- /*
- * No more header fields or data are expected. This directs any further
- * input in this command to the data_receiver, which for this write
- * command will unconditionally indicate a controller error.
- */
- return 1;
-}
-
-static int i2cp_cmd_adap_shutdown_data_receiver(void *data, char *in,
- size_t in_size, bool non_blocking)
-{
- /*
- * Reaching here means the controller wrote extra data in the command
- * line after the initial command name. That is unexpected and
- * indicates a controller bug.
- */
- return -EPROTO;
-}
-
-static int i2cp_cmd_adap_shutdown_cmd_completer(void *data,
- struct i2cp_controller *pdata, int receive_status, bool non_blocking)
-{
- /* Refuse to shutdown if there were errors processing this command. */
- if (receive_status)
- return 0;
-
- mutex_lock(&pdata->startstop_lock);
- pdata->startstop_state = I2CP_CTRLR_STATE_SHUTDN_REQ;
- mutex_unlock(&pdata->startstop_lock);
-
- /* Wake up blocked controller readers. */
- complete_all(&pdata->read_rsp_queued);
- /* Wake up blocked controller pollers. */
- wake_up_interruptible_all(&pdata->poll_wait_queue);
- return 0;
-}
-
-static int i2cp_cmd_get_number_header_receiver(void *data, char *in,
- size_t in_size, bool non_blocking)
-{
- /*
- * No more header fields or data are expected. This directs any further
- * input in this command to the data_receiver, which for this write
- * command will unconditionally indicate a controller error.
- */
- return 1;
-}
-
-static int i2cp_cmd_get_number_data_receiver(void *data, char *in,
- size_t in_size, bool non_blocking)
-{
- /*
- * Reaching here means the controller wrote extra data in the command
- * line after the initial command name. That is unexpected and
- * indicates a controller bug.
- */
- return -EPROTO;
-}
-
-static int i2cp_cmd_get_number_cmd_completer(void *data,
- struct i2cp_controller *pdata, int receive_status, bool non_blocking)
-{
- ssize_t ret;
- int i2c_adap_nr;
- struct i2cp_rsp_buffer *rsp_buf;
- struct i2cp_rsp *rsp_wrapper;
-
- /* Abort if there were errors processing this command. */
- if (receive_status)
- return 0;
-
- /*
- * Check the pseudo controller startstop_state. If it's running, get
- * the I2C adapter number.
- *
- * Acquire pdata->startstop_lock manually instead of using
- * i2cp_adap_get_state() in order to keep the lock while retrieving the
- * I2C adapter number.
- */
- mutex_lock(&pdata->startstop_lock);
- if (pdata->startstop_state != I2CP_CTRLR_STATE_RUNNING) {
- mutex_unlock(&pdata->startstop_lock);
- return -ENOTCONN;
- }
- i2c_adap_nr = pdata->i2c_adapter.nr;
- mutex_unlock(&pdata->startstop_lock);
-
- rsp_wrapper = kzalloc(sizeof(*rsp_wrapper), GFP_KERNEL);
- if (!rsp_wrapper)
- return -ENOMEM;
-
- rsp_buf = kzalloc(sizeof(*rsp_buf), GFP_KERNEL);
- if (!rsp_buf) {
- ret = -ENOMEM;
- goto fail_after_rsp_wrapper_alloc;
- }
-
- ret = anprintf(&rsp_buf->buf, I2CP_MAX_MSG_BUF_SIZE, GFP_KERNEL,
- "%*s%c%d",
- (int)strlen(I2CP_NUMBER_REPLY_CMD), I2CP_NUMBER_REPLY_CMD,
- i2cp_ctrlr_header_sep_char, i2c_adap_nr);
- if (ret < 0) {
- goto fail_after_rsp_buf_alloc;
- } else if (ret == 0) {
- ret = -EINVAL;
- goto fail_after_buf_alloc;
- }
- rsp_buf->size = ret;
-
- rsp_wrapper->data = rsp_buf;
- rsp_wrapper->formatter = i2cp_rsp_buffer_formatter;
-
- mutex_lock(&pdata->read_rsp_queue_lock);
- if (pdata->read_rsp_queue_length >= I2CP_CTRLR_RSP_QUEUE_LIMIT) {
- ret = -ENOBUFS;
- mutex_unlock(&pdata->read_rsp_queue_lock);
- goto fail_after_buf_alloc;
- }
-
- list_add_tail(&rsp_wrapper->queue, &pdata->read_rsp_queue_head);
- ++pdata->read_rsp_queue_length;
- complete(&pdata->read_rsp_queued);
-
- mutex_unlock(&pdata->read_rsp_queue_lock);
- return 0;
-
- fail_after_buf_alloc:
- kfree(rsp_buf->buf);
- fail_after_rsp_buf_alloc:
- kfree(rsp_buf);
- fail_after_rsp_wrapper_alloc:
- kfree(rsp_wrapper);
- return ret;
-}
-
-static int i2cp_cmd_get_pseudo_id_header_receiver(void *data, char *in,
- size_t in_size, bool non_blocking)
-{
- /*
- * No more header fields or data are expected. This directs any further
- * input in this command to the data_receiver, which for this write
- * command will unconditionally indicate a controller error.
- */
- return 1;
-}
-
-static int i2cp_cmd_get_pseudo_id_data_receiver(void *data, char *in,
- size_t in_size, bool non_blocking)
-{
- /*
- * Reaching here means the controller wrote extra data in the command
- * line after the initial command name. That is unexpected and
- * indicates a controller bug.
- */
- return -EPROTO;
-}
-
-static int i2cp_cmd_get_pseudo_id_cmd_completer(void *data,
- struct i2cp_controller *pdata, int receive_status, bool non_blocking)
-{
- ssize_t ret;
- struct i2cp_rsp_buffer *rsp_buf;
- struct i2cp_rsp *rsp_wrapper;
-
- /* Abort if there were errors processing this command. */
- if (receive_status)
- return 0;
-
- rsp_wrapper = kzalloc(sizeof(*rsp_wrapper), GFP_KERNEL);
- if (!rsp_wrapper)
- return -ENOMEM;
-
- rsp_buf = kzalloc(sizeof(*rsp_buf), GFP_KERNEL);
- if (!rsp_buf) {
- ret = -ENOMEM;
- goto fail_after_rsp_wrapper_alloc;
- }
-
- ret = anprintf(&rsp_buf->buf, I2CP_MAX_MSG_BUF_SIZE, GFP_KERNEL,
- "%*s%c%u",
- (int)strlen(I2CP_PSEUDO_ID_REPLY_CMD), I2CP_PSEUDO_ID_REPLY_CMD,
- i2cp_ctrlr_header_sep_char, pdata->id);
- if (ret < 0) {
- goto fail_after_rsp_buf_alloc;
- } else if (ret == 0) {
- ret = -EINVAL;
- goto fail_after_buf_alloc;
- }
- rsp_buf->size = ret;
-
- rsp_wrapper->data = rsp_buf;
- rsp_wrapper->formatter = i2cp_rsp_buffer_formatter;
-
- mutex_lock(&pdata->read_rsp_queue_lock);
- if (pdata->read_rsp_queue_length >= I2CP_CTRLR_RSP_QUEUE_LIMIT) {
- ret = -ENOBUFS;
- mutex_unlock(&pdata->read_rsp_queue_lock);
- goto fail_after_buf_alloc;
- }
-
- list_add_tail(&rsp_wrapper->queue, &pdata->read_rsp_queue_head);
- ++pdata->read_rsp_queue_length;
- complete(&pdata->read_rsp_queued);
-
- mutex_unlock(&pdata->read_rsp_queue_lock);
- return 0;
-
- fail_after_buf_alloc:
- kfree(rsp_buf->buf);
- fail_after_rsp_buf_alloc:
- kfree(rsp_buf);
- fail_after_rsp_wrapper_alloc:
- kfree(rsp_wrapper);
- return ret;
-}
-
-static int i2cp_cmd_set_name_suffix_data_creator(void **data)
-{
- struct i2cp_cmd_set_name_suffix_data *cmd_data;
-
- cmd_data = kzalloc(sizeof(*cmd_data), GFP_KERNEL);
- if (!cmd_data)
- return -ENOMEM;
- *data = cmd_data;
- return 0;
-}
-
-static void i2cp_cmd_set_name_suffix_data_destroyer(void *data)
-{
- kfree(data);
-}
-
-static int i2cp_cmd_set_name_suffix_header_receiver(void *data, char *in,
- size_t in_size, bool non_blocking)
-{
- return 1;
-}
-
-static int i2cp_cmd_set_name_suffix_data_receiver(void *data, char *in,
- size_t in_size, bool non_blocking)
-{
- size_t remaining;
- struct i2cp_cmd_set_name_suffix_data *cmd_data;
-
- cmd_data = data;
- remaining = sizeof(cmd_data->name_suffix) - cmd_data->name_suffix_len;
- /* Quietly truncate the suffix if necessary. */
- /* The suffix may need to be further truncated later. */
- if (in_size > remaining)
- in_size = remaining;
- memcpy(&cmd_data->name_suffix[cmd_data->name_suffix_len], in, in_size);
- cmd_data->name_suffix_len += in_size;
- return 0;
-}
-
-static int i2cp_cmd_set_name_suffix_cmd_completer(void *data,
- struct i2cp_controller *pdata, int receive_status, bool non_blocking)
-{
- int ret;
- struct i2cp_cmd_set_name_suffix_data *cmd_data;
-
- /* Abort if there were errors processing this command. */
- if (receive_status)
- return 0;
-
- /*
- * Acquire pdata->startstop_lock manually instead of using
- * i2cp_adap_get_state() in order to keep the lock while
- * setting the I2C adapter name.
- */
- mutex_lock(&pdata->startstop_lock);
-
- if (pdata->startstop_state != I2CP_CTRLR_STATE_NEW) {
- ret = -EISCONN;
- goto unlock;
- }
-
- cmd_data = data;
- ret = snprintf(pdata->i2c_adapter.name, sizeof(pdata->i2c_adapter.name),
- "I2C pseudo ID %u %*s", pdata->id,
- (int)cmd_data->name_suffix_len, cmd_data->name_suffix);
- if (ret < 0)
- goto unlock;
-
- ret = 0;
-
- unlock:
- mutex_unlock(&pdata->startstop_lock);
- return ret;
-}
-
-static int i2cp_cmd_set_timeout_data_creator(void **data)
-{
- struct i2cp_cmd_set_timeout_data *cmd_data;
-
- cmd_data = kzalloc(sizeof(*cmd_data), GFP_KERNEL);
- if (!cmd_data)
- return -ENOMEM;
- *data = cmd_data;
- return 0;
-}
-
-static void i2cp_cmd_set_timeout_data_destroyer(void *data)
-{
- kfree(data);
-}
-
-static int i2cp_cmd_set_timeout_header_receiver(void *data, char *in,
- size_t in_size, bool non_blocking)
-{
- int ret;
- struct i2cp_cmd_set_timeout_data *cmd_data;
-
- cmd_data = data;
- switch (cmd_data->field_pos++) {
- case 0:
- return 0;
- case 1:
- ret = kstrtouint(in, 0, &cmd_data->timeout_ms);
- if (ret < 0)
- return ret;
- return 1;
- }
- /* Reaching here is a bug. */
- return -EINVAL;
-}
-
-static int i2cp_cmd_set_timeout_data_receiver(void *data, char *in,
- size_t in_size, bool non_blocking)
-{
- /*
- * Reaching here means the controller wrote extra data in the command
- * line. That is unexpected and indicates a controller bug.
- */
- return -EPROTO;
-}
-
-static int i2cp_cmd_set_timeout_cmd_completer(void *data,
- struct i2cp_controller *pdata, int receive_status, bool non_blocking)
-{
- int ret;
- struct i2cp_cmd_set_timeout_data *cmd_data;
-
- /* Abort if there were errors processing this command. */
- if (receive_status)
- return 0;
-
- /*
- * Acquire pdata->startstop_lock manually instead of using
- * i2cp_adap_get_state() in order to keep the lock while setting the
- * I2C adapter name.
- */
- mutex_lock(&pdata->startstop_lock);
-
- if (pdata->startstop_state != I2CP_CTRLR_STATE_NEW) {
- ret = -EISCONN;
- goto unlock;
- }
-
- cmd_data = data;
- if (cmd_data->timeout_ms < I2CP_TIMEOUT_MS_MIN ||
- cmd_data->timeout_ms > I2CP_TIMEOUT_MS_MAX) {
- ret = -ERANGE;
- goto unlock;
- }
-
- pdata->i2c_adapter.timeout = msecs_to_jiffies(cmd_data->timeout_ms);
- ret = 0;
-
- unlock:
- mutex_unlock(&pdata->startstop_lock);
- return ret;
-}
-
-/* Command names are matched in this order, so sort by expected frequency. */
-/* All elements should be initialized in their I2CP_CMD_*_IDX position. */
-static const struct i2cp_cmd i2cp_cmds[] = {
- [I2CP_CMD_MXFER_REPLY_IDX] = {
- .cmd_string = I2CP_MXFER_REPLY_CMD,
- .cmd_size = CONST_STRLEN(I2CP_MXFER_REPLY_CMD),
- .data_creator = i2cp_cmd_mxfer_reply_data_creator,
- .data_shutdown = i2cp_cmd_mxfer_reply_data_shutdown,
- .data_destroyer = i2cp_cmd_mxfer_reply_data_destroyer,
- .header_receiver = i2cp_cmd_mxfer_reply_header_receiver,
- .data_receiver = i2cp_cmd_mxfer_reply_data_receiver,
- .cmd_completer = i2cp_cmd_mxfer_reply_cmd_completer,
- },
- [I2CP_CMD_ADAP_START_IDX] = {
- .cmd_string = I2CP_ADAP_START_CMD,
- .cmd_size = CONST_STRLEN(I2CP_ADAP_START_CMD),
- .header_receiver = i2cp_cmd_adap_start_header_receiver,
- .data_receiver = i2cp_cmd_adap_start_data_receiver,
- .cmd_completer = i2cp_cmd_adap_start_cmd_completer,
- },
- [I2CP_CMD_ADAP_SHUTDOWN_IDX] = {
- .cmd_string = I2CP_ADAP_SHUTDOWN_CMD,
- .cmd_size = CONST_STRLEN(I2CP_ADAP_SHUTDOWN_CMD),
- .header_receiver = i2cp_cmd_adap_shutdown_header_receiver,
- .data_receiver = i2cp_cmd_adap_shutdown_data_receiver,
- .cmd_completer = i2cp_cmd_adap_shutdown_cmd_completer,
- },
- [I2CP_CMD_GET_NUMBER_IDX] = {
- .cmd_string = I2CP_GET_NUMBER_CMD,
- .cmd_size = CONST_STRLEN(I2CP_GET_NUMBER_CMD),
- .header_receiver = i2cp_cmd_get_number_header_receiver,
- .data_receiver = i2cp_cmd_get_number_data_receiver,
- .cmd_completer = i2cp_cmd_get_number_cmd_completer,
- },
- [I2CP_CMD_GET_PSEUDO_ID_IDX] = {
- .cmd_string = I2CP_GET_PSEUDO_ID_CMD,
- .cmd_size = CONST_STRLEN(I2CP_GET_PSEUDO_ID_CMD),
- .header_receiver = i2cp_cmd_get_pseudo_id_header_receiver,
- .data_receiver = i2cp_cmd_get_pseudo_id_data_receiver,
- .cmd_completer = i2cp_cmd_get_pseudo_id_cmd_completer,
- },
- [I2CP_CMD_SET_NAME_SUFFIX_IDX] = {
- .cmd_string = I2CP_SET_NAME_SUFFIX_CMD,
- .cmd_size = CONST_STRLEN(I2CP_SET_NAME_SUFFIX_CMD),
- .data_creator = i2cp_cmd_set_name_suffix_data_creator,
- .data_destroyer = i2cp_cmd_set_name_suffix_data_destroyer,
- .header_receiver = i2cp_cmd_set_name_suffix_header_receiver,
- .data_receiver = i2cp_cmd_set_name_suffix_data_receiver,
- .cmd_completer = i2cp_cmd_set_name_suffix_cmd_completer,
- },
- [I2CP_CMD_SET_TIMEOUT_IDX] = {
- .cmd_string = I2CP_SET_TIMEOUT_CMD,
- .cmd_size = CONST_STRLEN(I2CP_SET_TIMEOUT_CMD),
- .data_creator = i2cp_cmd_set_timeout_data_creator,
- .data_destroyer = i2cp_cmd_set_timeout_data_destroyer,
- .header_receiver = i2cp_cmd_set_timeout_header_receiver,
- .data_receiver = i2cp_cmd_set_timeout_data_receiver,
- .cmd_completer = i2cp_cmd_set_timeout_cmd_completer,
- },
-};
-
-/* Returns whether or not there is response queue data to read. */
-/* Must be called with pdata->rsp_lock held. */
-static inline bool i2cp_poll_in(struct i2cp_controller *pdata)
-{
- return pdata->rsp_invalidated || pdata->rsp_buf_remaining != 0 ||
- !list_empty(&pdata->read_rsp_queue_head);
-}
-
-static inline int i2cp_fill_rsp_buf(struct i2cp_rsp *rsp_wrapper,
- struct i2cp_rsp_buffer *rsp_buf, char *contents, size_t size)
-{
- rsp_buf->buf = kmemdup(contents, size, GFP_KERNEL);
- if (!rsp_buf->buf)
- return -ENOMEM;
- rsp_buf->size = size;
- rsp_wrapper->data = rsp_buf;
- rsp_wrapper->formatter = i2cp_rsp_buffer_formatter;
- return 0;
-}
-
-#define I2CP_FILL_RSP_BUF_WITH_LITERAL(rsp_wrapper, rsp_buf, str_literal)\
- i2cp_fill_rsp_buf(\
- rsp_wrapper, rsp_buf, str_literal, strlen(str_literal))
-
-static int i2cp_adapter_master_xfer(struct i2c_adapter *adap,
- struct i2c_msg *msgs, int num)
-{
- int i, ret = 0;
- long wait_ret;
- size_t wrappers_length, wrapper_idx = 0, rsp_bufs_idx = 0;
- struct i2cp_controller *pdata;
- struct i2cp_rsp **rsp_wrappers;
- struct i2cp_rsp_buffer *rsp_bufs[2] = {0};
- struct i2cp_rsp_master_xfer *mxfer_rsp;
- struct i2cp_cmd_mxfer_reply_data *cmd_data;
- struct i2cp_cmd_mxfer_reply *mxfer_reply;
-
- if (num <= 0) {
- if (num < 0)
- return -EINVAL;
- return ret;
- }
-
- pdata = adap->algo_data;
- cmd_data = pdata->cmd_data[I2CP_CMD_MXFER_REPLY_IDX];
-
- switch (i2cp_adap_get_state(pdata)) {
- case I2CP_CTRLR_STATE_RUNNING:
- break;
- case I2CP_CTRLR_STATE_SHUTDN_REQ:
- return ret;
- default:
- /* Reaching here is a bug, even with a valid enum value. */
- return -EINVAL;
- }
-
- wrappers_length = (size_t)num + ARRAY_SIZE(rsp_bufs);
- rsp_wrappers = kcalloc(wrappers_length, sizeof(*rsp_wrappers),
- GFP_KERNEL);
- if (!rsp_wrappers)
- return -ENOMEM;
-
- mxfer_reply = kzalloc(sizeof(*mxfer_reply), GFP_KERNEL);
- if (!mxfer_reply) {
- ret = -ENOMEM;
- goto return_after_rsp_wrappers_ptrs_alloc;
- }
-
- mxfer_reply->num_msgs = num;
- init_completion(&mxfer_reply->data_filled);
- mutex_init(&mxfer_reply->lock);
-
- mxfer_reply->msgs = kcalloc(num, sizeof(*mxfer_reply->msgs),
- GFP_KERNEL);
- if (!mxfer_reply->msgs) {
- ret = -ENOMEM;
- goto return_after_mxfer_reply_alloc;
- }
-
- mxfer_reply->completed = kcalloc(num, sizeof(*mxfer_reply->completed),
- GFP_KERNEL);
- if (!mxfer_reply->completed) {
- ret = -ENOMEM;
- goto return_after_reply_msgs_alloc;
- }
-
- for (i = 0; i < num; ++i) {
- mxfer_reply->msgs[i].addr = msgs[i].addr;
- mxfer_reply->msgs[i].flags = msgs[i].flags;
- mxfer_reply->msgs[i].len = msgs[i].len;
- if (msgs[i].flags & I2C_M_RD)
- /* Copy the address, not the data. */
- mxfer_reply->msgs[i].buf = msgs[i].buf;
- }
-
- for (i = 0; i < ARRAY_SIZE(rsp_bufs); ++i) {
- rsp_bufs[i] = kzalloc(sizeof(*rsp_bufs[i]), GFP_KERNEL);
- if (!rsp_bufs[i]) {
- ret = -ENOMEM;
- goto return_after_reply_completed_alloc;
- }
- }
-
- mxfer_rsp = kzalloc(sizeof(*mxfer_rsp), GFP_KERNEL);
- if (!mxfer_rsp) {
- ret = -ENOMEM;
- goto fail_after_individual_rsp_bufs_alloc;
- }
-
- mxfer_rsp->id = cmd_data->next_mxfer_id++;
- mxfer_rsp->num = num;
-
- mxfer_rsp->msgs = kcalloc(num, sizeof(*mxfer_rsp->msgs), GFP_KERNEL);
- if (!mxfer_rsp->msgs) {
- ret = -ENOMEM;
- goto fail_after_mxfer_rsp_alloc;
- }
-
- for (i = 0; i < num; ++i) {
- mxfer_rsp->msgs[i].addr = msgs[i].addr;
- mxfer_rsp->msgs[i].flags = msgs[i].flags;
- mxfer_rsp->msgs[i].len = msgs[i].len;
- if (msgs[i].flags & I2C_M_RD)
- continue;
- /* Copy the data, not the address. */
- mxfer_rsp->msgs[i].buf = kmemdup(msgs[i].buf, msgs[i].len,
- GFP_KERNEL);
- if (!mxfer_rsp->msgs[i].buf) {
- ret = -ENOMEM;
- goto fail_after_rsp_msgs_alloc;
- }
- }
-
- for (i = 0; i < wrappers_length; ++i) {
- rsp_wrappers[i] = kzalloc(sizeof(*rsp_wrappers[i]), GFP_KERNEL);
- if (!rsp_wrappers[i]) {
- ret = -ENOMEM;
- goto fail_after_individual_rsp_wrappers_alloc;
- }
- }
-
- ret = I2CP_FILL_RSP_BUF_WITH_LITERAL(rsp_wrappers[wrapper_idx++],
- rsp_bufs[rsp_bufs_idx++], I2CP_BEGIN_MXFER_REQ_CMD);
- if (ret < 0)
- goto fail_after_individual_rsp_wrappers_alloc;
-
- for (i = 0; i < num; ++i) {
- rsp_wrappers[wrapper_idx]->data = mxfer_rsp;
- rsp_wrappers[wrapper_idx++]->formatter =
- i2cp_rsp_master_xfer_formatter;
- }
-
- ret = I2CP_FILL_RSP_BUF_WITH_LITERAL(rsp_wrappers[wrapper_idx++],
- rsp_bufs[rsp_bufs_idx++], I2CP_COMMIT_MXFER_REQ_CMD);
- if (ret < 0)
- goto fail_after_individual_rsp_wrappers_alloc;
-
- BUILD_BUG_ON(rsp_bufs_idx != ARRAY_SIZE(rsp_bufs));
-
- mutex_lock(&pdata->read_rsp_queue_lock);
- if (pdata->read_rsp_queue_length >= I2CP_CTRLR_RSP_QUEUE_LIMIT) {
- ret = -ENOBUFS;
- goto fail_with_read_rsp_queue_lock;
- }
-
- mutex_lock(&cmd_data->reply_queue_lock);
- if (cmd_data->reply_queue_length >= I2CP_CTRLR_RSP_QUEUE_LIMIT) {
- ret = -ENOBUFS;
- goto fail_with_reply_queue_lock;
- }
-
- mxfer_reply->id = mxfer_rsp->id;
- list_add_tail(&mxfer_reply->reply_queue_item,
- &cmd_data->reply_queue_head);
- ++cmd_data->reply_queue_length;
-
- for (i = 0; i < wrappers_length; ++i) {
- list_add_tail(&rsp_wrappers[i]->queue,
- &pdata->read_rsp_queue_head);
- complete(&pdata->read_rsp_queued);
- }
- pdata->read_rsp_queue_length += wrappers_length;
-
- mutex_unlock(&cmd_data->reply_queue_lock);
- mutex_unlock(&pdata->read_rsp_queue_lock);
-
- /* Wake up the userspace controller if it was polling. */
- wake_up_interruptible(&pdata->poll_wait_queue);
- /* Wait for a response from the userspace controller. */
- wait_ret = wait_for_completion_killable_timeout(
- &mxfer_reply->data_filled, adap->timeout);
-
- mutex_lock(&cmd_data->reply_queue_lock);
- /*
- * Ensure mxfer_reply is not in use before dequeuing and freeing it.
- * This depends on the requirement that mxfer_reply->lock only be
- * acquired while holding cmd_data->reply_queue_lock.
- */
- mutex_lock(&mxfer_reply->lock);
-
- if (wait_ret == -ERESTARTSYS)
- ret = -EINTR;
- else if (wait_ret < 0)
- ret = wait_ret;
- else
- ret = mxfer_reply->ret;
-
- /*
- * This depends on other functions that might delete
- * mxfer_reply->reply_queue_item from cmd_data->reply_queue_head using
- * list_del_init(), never list_del().
- */
- if (!list_empty(&mxfer_reply->reply_queue_item)) {
- list_del(&mxfer_reply->reply_queue_item);
- --cmd_data->reply_queue_length;
- if (mxfer_reply == cmd_data->reply_queue_current_item)
- cmd_data->reply_queue_current_item = NULL;
- }
-
- mutex_unlock(&mxfer_reply->lock);
- mutex_unlock(&cmd_data->reply_queue_lock);
- goto return_after_reply_msgs_alloc;
-
- fail_with_reply_queue_lock:
- mutex_unlock(&cmd_data->reply_queue_lock);
- fail_with_read_rsp_queue_lock:
- mutex_unlock(&pdata->read_rsp_queue_lock);
- fail_after_individual_rsp_wrappers_alloc:
- for (i = 0; i < wrappers_length; ++i)
- kfree(rsp_wrappers[i]);
- fail_after_rsp_msgs_alloc:
- for (i = 0; i < num; ++i)
- kfree(mxfer_rsp->msgs[i].buf);
- kfree(mxfer_rsp->msgs);
- fail_after_mxfer_rsp_alloc:
- kfree(mxfer_rsp);
- fail_after_individual_rsp_bufs_alloc:
- for (i = 0; i < ARRAY_SIZE(rsp_bufs); ++i) {
- kfree(rsp_bufs[i]->buf);
- kfree(rsp_bufs[i]);
- }
- return_after_reply_completed_alloc:
- kfree(mxfer_reply->completed);
- return_after_reply_msgs_alloc:
- kfree(mxfer_reply->msgs);
- return_after_mxfer_reply_alloc:
- kfree(mxfer_reply);
- return_after_rsp_wrappers_ptrs_alloc:
- kfree(rsp_wrappers);
- return ret;
-}
-
-/*
- * If more functionality than this needs to be supported, add a write command
- * for the controller to specify its additional functionality prior to
- * ADAPTER_START. Basic I2C functionality should remain implied and required.
- *
- * These functionalities in particular could be worth supporting:
- * I2C_FUNC_10BIT_ADDR
- * I2C_FUNC_NOSTART
- * I2C_FUNC_PROTOCOL_MANGLING
- */
-static u32 i2cp_adapter_functionality(struct i2c_adapter *adap)
-{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
-}
-
-static const struct i2c_algorithm i2cp_algorithm = {
- .master_xfer = i2cp_adapter_master_xfer,
- .functionality = i2cp_adapter_functionality,
-};
-
-/* this_pseudo->counters.lock must _not_ be held when calling this. */
-static void i2cp_remove_from_counters(struct i2cp_controller *pdata,
- struct i2cp_device *this_pseudo)
-{
-
- mutex_lock(&this_pseudo->counters.lock);
- this_pseudo->counters.all_controllers[pdata->index] = NULL;
- --this_pseudo->counters.count;
- mutex_unlock(&this_pseudo->counters.lock);
-}
-
-static int i2cp_cdev_open(struct inode *inodep, struct file *filep)
-{
- int ret = 0;
- unsigned int i, num_cmd_data_created = 0;
- unsigned int ctrlr_id;
- struct i2cp_controller *pdata;
- struct i2cp_device *this_pseudo;
-
- /* Is there any way to find this through @inodep? */
- this_pseudo = i2cp_device;
-
- /*
- * HAVE_STREAM_OPEN value meanings:
- * -1 : stream_open() is not available
- * 0 : unknown if stream_open() is or is not available
- * 1 : stream_open() is available
- */
-#if HAVE_STREAM_OPEN >= 0
- /* I2C pseudo adapter controllers are non-seekable pure I/O streams. */
- stream_open(inodep, filep);
-#else
- /* I2C pseudo adapter controllers are not seekable. */
- nonseekable_open(inodep, filep);
-#endif
- /* Refuse fsnotify events. Modeled after /dev/ptmx implementation. */
- filep->f_mode |= FMODE_NONOTIFY;
-
- /* Allocate the I2C adapter. */
- pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
-
- INIT_LIST_HEAD(&pdata->read_rsp_queue_head);
- init_waitqueue_head(&pdata->poll_wait_queue);
- init_completion(&pdata->read_rsp_queued);
- mutex_init(&pdata->startstop_lock);
- mutex_init(&pdata->cmd_lock);
- mutex_init(&pdata->rsp_lock);
- mutex_init(&pdata->read_rsp_queue_lock);
-
- for (i = 0; i < ARRAY_SIZE(i2cp_cmds); ++i) {
- if (!i2cp_cmds[i].data_creator)
- continue;
- ret = i2cp_cmds[i].data_creator(&pdata->cmd_data[i]);
- if (ret < 0)
- break;
- }
- num_cmd_data_created = i;
- if (ret < 0)
- goto fail_after_cmd_data_created;
-
- mutex_lock(&this_pseudo->counters.lock);
-
- for (i = 0; i < i2cp_limit; ++i)
- if (!this_pseudo->counters.all_controllers[i])
- break;
- if (i >= i2cp_limit) {
- mutex_unlock(&this_pseudo->counters.lock);
- ret = -ENOSPC;
- goto fail_after_cmd_data_created;
- }
- pdata->index = i;
-
- for (ctrlr_id = this_pseudo->counters.next_ctrlr_id;;) {
- /* Determine whether ctrlr_id is already in use. */
- for (i = 0; i < i2cp_limit; ++i) {
- if (this_pseudo->counters.all_controllers[i] &&
- (this_pseudo->counters.all_controllers[i]->id ==
- ctrlr_id))
- break;
- }
- /* If ctrlr_id is available, use it. */
- if (i >= i2cp_limit) {
- pdata->id = ctrlr_id;
- this_pseudo->counters.next_ctrlr_id = ctrlr_id + 1;
- ++this_pseudo->counters.count;
- this_pseudo->counters.all_controllers[pdata->index] =
- pdata;
- break;
- }
- /* Increment ctrlr_id, and check for wrapping. */
- if (++ctrlr_id == this_pseudo->counters.next_ctrlr_id) {
- mutex_unlock(&this_pseudo->counters.lock);
- ret = -ENOSPC;
- goto fail_after_cmd_data_created;
- }
- }
-
- mutex_unlock(&this_pseudo->counters.lock);
-
- /* Initialize the I2C adapter. */
- pdata->i2c_adapter.owner = THIS_MODULE;
- pdata->i2c_adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
- pdata->i2c_adapter.algo = &i2cp_algorithm;
- pdata->i2c_adapter.algo_data = pdata;
- pdata->i2c_adapter.timeout = msecs_to_jiffies(i2cp_default_timeout_ms);
- pdata->i2c_adapter.dev.parent = &this_pseudo->device;
- ret = snprintf(pdata->i2c_adapter.name, sizeof(pdata->i2c_adapter.name),
- "I2C pseudo ID %u", pdata->id);
- if (ret < 0)
- goto fail_after_counters_update;
-
- /* Return success. */
- filep->private_data = pdata;
- return 0;
-
- fail_after_counters_update:
- i2cp_remove_from_counters(pdata, this_pseudo);
- fail_after_cmd_data_created:
- for (i = 0; i < num_cmd_data_created; ++i)
- if (i2cp_cmds[i].data_destroyer)
- i2cp_cmds[i].data_destroyer(pdata->cmd_data[i]);
- kfree(pdata);
- return ret;
-}
-
-static int i2cp_cdev_release(struct inode *inodep, struct file *filep)
-{
- int i;
- bool adapter_was_added = false;
- struct i2cp_controller *pdata;
- struct i2cp_device *this_pseudo;
-
- pdata = filep->private_data;
- this_pseudo = container_of(pdata->i2c_adapter.dev.parent,
- struct i2cp_device, device);
-
- /*
- * The select(2) man page makes it clear that the behavior of pending
- * select()/poll()/epoll_wait() on a fd that gets closed while waiting
- * is undefined and should never be relied on. However since we are
- * about to free pdata and therefore free pdata->poll_wait_queue, safest
- * to wake up anyone waiting on it in an attempt to not leave them in a
- * completely undefined state.
- */
- wake_up_interruptible_all(&pdata->poll_wait_queue);
- /*
- * Linux guarantees there are no outstanding reads or writes when a
- * struct file is released, so no further synchronization with the other
- * struct file_operations callbacks should be needed.
- */
- filep->private_data = NULL;
-
- mutex_lock(&pdata->startstop_lock);
- if (pdata->startstop_state != I2CP_CTRLR_STATE_NEW) {
- /*
- * Defer deleting the adapter until after releasing
- * pdata->startstop_state. This avoids deadlocking with any
- * overlapping i2cp_adapter_master_xfer() calls, which also
- * acquire the lock in order to check the state.
- */
- adapter_was_added = true;
- /*
- * Instruct any overlapping i2cp_adapter_master_xfer() calls to
- * return immediately.
- */
- pdata->startstop_state = I2CP_CTRLR_STATE_SHUTDN_REQ;
- }
- mutex_unlock(&pdata->startstop_lock);
-
- /*
- * Wake up blocked I2C requests. This is an optimization so that they
- * don't need to wait for the I2C adapter timeout, since there is no
- * possibility of any further I2C replies.
- */
- for (i = 0; i < ARRAY_SIZE(i2cp_cmds); ++i)
- if (i2cp_cmds[i].data_shutdown)
- i2cp_cmds[i].data_shutdown(pdata->cmd_data[i]);
-
- if (adapter_was_added)
- i2c_del_adapter(&pdata->i2c_adapter);
-
- for (i = 0; i < ARRAY_SIZE(i2cp_cmds); ++i) {
- if (i2cp_cmds[i].data_destroyer)
- i2cp_cmds[i].data_destroyer(pdata->cmd_data[i]);
- pdata->cmd_data[i] = NULL;
- }
-
- i2cp_remove_from_counters(pdata, this_pseudo);
- kfree(pdata);
- return 0;
-}
-
-/* The caller must hold pdata->rsp_lock. */
-/* Return value is whether or not to continue in calling loop. */
-static bool i2cp_cdev_read_iteration(char __user **buf, size_t *count,
- ssize_t *ret, bool non_blocking, struct i2cp_controller *pdata)
-{
- long wait_ret;
- ssize_t copy_size;
- unsigned long copy_ret;
- struct i2cp_rsp *rsp_wrapper = NULL;
-
- /*
- * If a previous read response buffer has been exhausted, free
- * it.
- *
- * This is done at the beginning of the while(count>0) loop
- * because...?
- */
- if (pdata->rsp_buf_start && !pdata->rsp_buf_remaining) {
- kfree(pdata->rsp_buf_start);
- pdata->rsp_buf_start = NULL;
- pdata->rsp_buf_pos = NULL;
- }
-
- /*
- * If we have no formatter callback output queued (neither
- * successful output nor error), go through the FIFO queue of
- * read responses until a formatter returns non-zero (successful
- * output or failure).
- */
- while (pdata->rsp_buf_remaining == 0) {
- /*
- * If pdata->rsp_invalidated is true, it means the
- * previous read() returned an error. Now that the
- * error has already been propagated to userspace, we
- * can write the end character for the invalidated read
- * response.
- */
- if (pdata->rsp_invalidated) {
- pdata->rsp_invalidated = false;
- goto write_end_char;
- }
-
- /* If we have already read some bytes successfully, even
- * if less than requested, we should return as much as
- * we can without blocking further. Same if we have an
- * error to return.
- */
- if (non_blocking || *ret != 0) {
- if (!try_wait_for_completion(&pdata->read_rsp_queued)) {
- if (*ret == 0)
- *ret = -EAGAIN;
- /*
- * If we are out of read responses,
- * return whatever we have written to
- * the userspace buffer so far, even if
- * it's nothing.
- */
- return false;
- }
- } else {
- wait_ret = wait_for_completion_killable(
- &pdata->read_rsp_queued);
- if (wait_ret == -ERESTARTSYS) {
- if (*ret == 0)
- *ret = -EINTR;
- return false;
- } else if (wait_ret < 0) {
- if (*ret == 0)
- *ret = wait_ret;
- return false;
- }
- }
-
- mutex_lock(&pdata->read_rsp_queue_lock);
- if (!list_empty(&pdata->read_rsp_queue_head))
- rsp_wrapper = list_first_entry(
- &pdata->read_rsp_queue_head,
- struct i2cp_rsp, queue);
- /*
- * Avoid holding pdata->read_rsp_queue_lock while
- * executing a formatter, allocating memory, or doing
- * anything else that might block or take non-trivial
- * time. This avoids blocking the enqueuing of new read
- * responses for any significant time, even during large
- * controller reads.
- */
- mutex_unlock(&pdata->read_rsp_queue_lock);
-
- if (!rsp_wrapper) {
- /* This should only happen if shutdown was requested. */
- if (i2cp_adap_get_state(pdata) !=
- I2CP_CTRLR_STATE_SHUTDN_REQ)
- *ret = -EINVAL;
- return false;
- }
-
- pdata->rsp_buf_remaining = rsp_wrapper->formatter(
- rsp_wrapper->data, &pdata->rsp_buf_start);
-
- if (pdata->rsp_buf_remaining > 0) {
- pdata->rsp_buf_pos = pdata->rsp_buf_start;
- /*
- * We consumed a completion for this rsp_wrapper
- * but we are leaving it in
- * pdata->read_rsp_queue_head. Re-add a
- * completion for it.
- *
- * Since overlapping reads are effectively
- * serialized via use of pdata->rsp_lock, we
- * could take shortcuts in how
- * pdata->read_rsp_queued is used to avoid the
- * need for re-incrementing it here. However by
- * maintaining the invariant of consuming a
- * completion each time an item from
- * pdata->read_rsp_queue_head is consumed
- * (whether or not it ends up being removed from
- * the queue in that iteration), the completion
- * logic is simpler to follow, and more easily
- * lends itself to a future refactor of this
- * read operation to not hold pdata->rsp_lock
- * continuously.
- */
- complete(&pdata->read_rsp_queued);
- break;
- }
-
- /*
- * The formatter should not mutate pdata->rsp_buf_start
- * if it returned non-positive. Just in case, we handle
- * such a bug gracefully here.
- */
- kfree(pdata->rsp_buf_start);
- pdata->rsp_buf_start = NULL;
-
- mutex_lock(&pdata->read_rsp_queue_lock);
- list_del(&rsp_wrapper->queue);
- --pdata->read_rsp_queue_length;
- mutex_unlock(&pdata->read_rsp_queue_lock);
-
- kfree(rsp_wrapper);
- rsp_wrapper = NULL;
-
- /* Check if the formatter callback returned an error.
- *
- * If we have _not_ written any bytes to the userspace
- * buffer yet, return now with the error code from the
- * formatter.
- *
- * If we _have_ written bytes already, return now with
- * the number of bytes written, and leave the error code
- * from the formatter in pdata->rsp_buf_remaining so it
- * can be returned on the next read, before any bytes
- * are written.
- *
- * In either case, we deliberately return the error
- * before writing the end character for the invalidated
- * read response, so that the userspace controller knows
- * to discard the response.
- */
- if (pdata->rsp_buf_remaining < 0) {
- if (*ret == 0) {
- *ret = pdata->rsp_buf_remaining;
- pdata->rsp_buf_remaining = 0;
- }
- pdata->rsp_invalidated = true;
- return false;
- }
-
- write_end_char:
- copy_size = sizeof(i2cp_ctrlr_end_char);
- /*
- * This assertion is just in case someone changes
- * i2cp_ctrlr_end_char to a string. Such a change would require
- * handling it like a read response buffer, including ensuring
- * that we not write more than *count. So long as it's a single
- * character, we can avoid an extra check of *count in this code
- * block, we already know it's greater than zero.
- */
- BUILD_BUG_ON(copy_size != 1);
- copy_ret = copy_to_user(*buf, &i2cp_ctrlr_end_char,
- copy_size);
- copy_size -= copy_ret;
- /*
- * After writing to the userspace buffer, we need to
- * update various counters including the return value,
- * then continue from the start of the outer while loop
- * because it's possible *count has reached zero.
- *
- * Those exact same steps must be done after copying
- * from a read response buffer to the userspace buffer,
- * so jump to that code instead of duplicating it.
- */
- goto after_copy_to_user;
- }
-
- copy_size = max_t(ssize_t, 0,
- min_t(ssize_t, *count, pdata->rsp_buf_remaining));
- copy_ret = copy_to_user(*buf, pdata->rsp_buf_pos, copy_size);
- copy_size -= copy_ret;
- pdata->rsp_buf_remaining -= copy_size;
-
- if (pdata->rsp_buf_remaining > 0) {
- pdata->rsp_buf_pos += copy_size;
- } else {
- kfree(pdata->rsp_buf_start);
- pdata->rsp_buf_start = NULL;
- pdata->rsp_buf_pos = NULL;
- }
-
- /*
- * When jumping here, the following variables should be set:
- * copy_ret: Return value from copy_to_user() (bytes not copied).
- * copy_size: The number of bytes successfully copied by copy_to_user(). In
- * other words, this should be the size arg to copy_to_user() minus its
- * return value (bytes not copied).
- */
- after_copy_to_user:
- *ret += copy_size;
- *count -= copy_size;
- *buf += copy_size;
-
- return !copy_ret;
-}
-
-static ssize_t i2cp_cdev_read(struct file *filep, char __user *buf,
- size_t count, loff_t *f_ps)
-{
- ssize_t ret = 0;
- bool non_blocking;
- struct i2cp_controller *pdata;
-
- /*
- * Just in case this could change out from under us, best to keep a
- * consistent view for the duration of this syscall.
- */
- non_blocking = !!(filep->f_flags & O_NONBLOCK);
- pdata = filep->private_data;
-
- if (count > (size_t)I2CP_RW_SIZE_LIMIT)
- count = I2CP_RW_SIZE_LIMIT;
-
- /*
- * Since read() calls are effectively serialized by way of
- * pdata->rsp_lock, we MUST NOT block on obtaining that lock if in
- * non-blocking mode, because it might be held by a blocking read().
- */
- if (!non_blocking)
- mutex_lock(&pdata->rsp_lock);
- else if (!mutex_trylock(&pdata->rsp_lock))
- return -EAGAIN;
-
- /*
- * Check if a formatter callback returned an error that hasn't yet been
- * returned to the controller. Do this before the while(count>0) loop
- * because read(2) with zero count is allowed to report errors.
- */
- if (pdata->rsp_buf_remaining < 0) {
- BUILD_BUG_ON(ret != 0);
- ret = pdata->rsp_buf_remaining;
- pdata->rsp_buf_remaining = 0;
- goto unlock;
- }
-
- while (count > 0 && i2cp_cdev_read_iteration(
- &buf, &count, &ret, non_blocking, pdata))
- ;
-
- unlock:
- mutex_unlock(&pdata->rsp_lock);
- return ret;
-}
-
-/* Must be called with pdata->cmd_lock held. */
-/* Must never consume past first i2cp_ctrlr_end_char in @start. */
-static ssize_t i2cp_receive_ctrlr_cmd_header(
- struct i2cp_controller *pdata, char *start, size_t remaining,
- bool non_blocking)
-{
- int found_deliminator_char = 0;
- int i, cmd_idx;
- ssize_t copy_size, ret = 0, stop, buf_remaining;
-
- buf_remaining = I2CP_CTRLR_CMD_LIMIT - pdata->cmd_size;
- stop = min_t(ssize_t, remaining, buf_remaining + 1);
-
- for (i = 0; i < stop; ++i)
- if (start[i] == i2cp_ctrlr_end_char ||
- start[i] == i2cp_ctrlr_header_sep_char) {
- found_deliminator_char = 1;
- break;
- }
-
- if (i <= buf_remaining) {
- copy_size = i;
- } else {
- copy_size = buf_remaining;
- if (!pdata->cmd_receive_status)
- /*
- * Exceeded max size of I2C pseudo controller command
- * buffer. The command currently being written will be
- * ignored.
- *
- * Positive error number is deliberate here.
- */
- pdata->cmd_receive_status = ENOBUFS;
- }
-
- memcpy(&pdata->cmd_buf[pdata->cmd_size], start, copy_size);
- pdata->cmd_size += copy_size;
-
- if (!found_deliminator_char || pdata->cmd_size <= 0)
- return copy_size + found_deliminator_char;
-
- /* This may be negative. */
- cmd_idx = pdata->cmd_idx_plus_one - 1;
-
- if (cmd_idx < 0) {
- for (i = 0; i < ARRAY_SIZE(i2cp_cmds); ++i)
- if (i2cp_cmds[i].cmd_size == pdata->cmd_size &&
- !memcmp(i2cp_cmds[i].cmd_string, pdata->cmd_buf,
- pdata->cmd_size))
- break;
- if (i >= ARRAY_SIZE(i2cp_cmds)) {
- /* unrecognized command */
- ret = -EIO;
- goto clear_buffer;
- }
- cmd_idx = i;
- pdata->cmd_idx_plus_one = cmd_idx + 1;
- }
-
- /*
- * If we have write bytes queued and we encountered i2cp_ctrlr_end_char
- * or i2cp_ctrlr_header_sep_char, invoke the header_receiver callback.
- */
- if (!pdata->cmd_receive_status) {
- ret = i2cp_cmds[cmd_idx].header_receiver(
- pdata->cmd_data[cmd_idx], pdata->cmd_buf,
- pdata->cmd_size, non_blocking);
- if (ret > 0) {
- if (ret > I2CP_CTRLR_CMD_LIMIT) {
- ret = -EINVAL;
- goto clear_buffer;
- }
- pdata->cmd_data_increment = ret;
- } else if (ret < 0) {
- pdata->cmd_receive_status = ret;
- }
- }
-
- clear_buffer:
- pdata->cmd_size = 0;
- /*
- * Ensure a trailing null character for the next header_receiver() or
- * data_receiver() invocation.
- */
- memset(pdata->cmd_buf, 0, sizeof(pdata->cmd_buf));
-
- if (ret < 0) {
- if (pdata->cmd_idx_plus_one >= 1 && !pdata->cmd_receive_status)
- /* Negate to get a positive error number. */
- pdata->cmd_receive_status = -ret;
- return ret;
- }
- return copy_size + found_deliminator_char;
-}
-
-/* Must be called with pdata->cmd_lock held. */
-/* Must never consume past first i2cp_ctrlr_end_char in @start. */
-static ssize_t i2cp_receive_ctrlr_cmd_data(struct i2cp_controller *pdata,
- char *start, size_t remaining, bool non_blocking)
-{
- ssize_t i, ret, size_holder;
- int cmd_idx;
-
- /* If cmd_idx ends up negative here, it is a bug. */
- cmd_idx = pdata->cmd_idx_plus_one - 1;
- if (cmd_idx < 0)
- return -EINVAL;
-
- size_holder = min_t(size_t,
- (I2CP_CTRLR_CMD_LIMIT -
- (I2CP_CTRLR_CMD_LIMIT % pdata->cmd_data_increment)) -
- pdata->cmd_size,
- (((pdata->cmd_size + remaining) /
- pdata->cmd_data_increment) *
- pdata->cmd_data_increment) - pdata->cmd_size);
-
- /* Size of current buffer plus all remaining write bytes. */
- size_holder = pdata->cmd_size + remaining;
- /*
- * Avoid rounding down to zero. If there are insufficient write
- * bytes remaining to grow the buffer to 1x of the requested
- * data byte increment, we'll copy what is available to the
- * buffer, and just leave it queued without any further command
- * handler invocations in this write() (unless i2cp_ctrlr_end_char is
- * found, in which case we will always invoke the data_receiver for any
- * remaining data bytes, and will always invoke the cmd_completer).
- */
- if (size_holder > pdata->cmd_data_increment)
- /*
- * Round down to the nearest multiple of the requested
- * data byte increment.
- */
- size_holder -= size_holder % pdata->cmd_data_increment;
- /*
- * Take the smaller of:
- *
- * [A] 2nd min_t() arg: The number of bytes that we would want the
- * buffer to end up with if it had unlimited space (computed
- * above).
- *
- * [B] 3rd min_t() arg: The number of bytes that we would want the
- * buffer to end up with if there were unlimited write bytes
- * remaining (computed in-line below).
- */
- size_holder = min_t(ssize_t, size_holder, (I2CP_CTRLR_CMD_LIMIT - (
- I2CP_CTRLR_CMD_LIMIT % pdata->cmd_data_increment)));
- /*
- * Subtract the existing buffer size to get the number of bytes we
- * actually want to copy from the remaining write bytes in this loop
- * iteration, assuming no i2cp_ctrlr_end_char.
- */
- size_holder -= pdata->cmd_size;
-
- /*
- * Look for i2cp_ctrlr_end_char. If we find it, we will copy up to but
- * *not* including its position.
- */
- for (i = 0; i < size_holder; ++i)
- if (start[i] == i2cp_ctrlr_end_char)
- break;
-
- /* Copy from the remaining write bytes to the command buffer. */
- memcpy(&pdata->cmd_buf[pdata->cmd_size], start, i);
- pdata->cmd_size += i;
-
- /*
- * If we have write bytes queued and *either* we encountered
- * i2cp_ctrlr_end_char *or* we have a multiple of
- * pdata->cmd_data_increment, invoke the data_receiver callback.
- */
- if (pdata->cmd_size > 0 &&
- (i < size_holder ||
- pdata->cmd_size % pdata->cmd_data_increment == 0)) {
- if (!pdata->cmd_receive_status) {
- ret = i2cp_cmds[cmd_idx].data_receiver(
- pdata->cmd_data[cmd_idx], pdata->cmd_buf,
- pdata->cmd_size, non_blocking);
- if (ret < 0)
- pdata->cmd_receive_status = ret;
- }
- pdata->cmd_size = 0;
- /*
- * Ensure a trailing null character for the next
- * header_receiver() or data_receiver() invocation.
- */
- memset(pdata->cmd_buf, 0, sizeof(pdata->cmd_buf));
- }
-
- /* If i2cp_ctrlr_end_char was found, skip past it. */
- if (i < size_holder)
- ++i;
- return i;
-}
-
-/* Must be called with pdata->cmd_lock held. */
-static int i2cp_receive_ctrlr_cmd_complete(struct i2cp_controller *pdata,
- bool non_blocking)
-{
- int ret = 0, cmd_idx;
-
- /* This may be negative. */
- cmd_idx = pdata->cmd_idx_plus_one - 1;
-
- if (cmd_idx >= 0 && i2cp_cmds[cmd_idx].cmd_completer) {
- ret = i2cp_cmds[cmd_idx].cmd_completer(pdata->cmd_data[cmd_idx],
- pdata, pdata->cmd_receive_status, non_blocking);
- if (ret > 0)
- ret = 0;
- }
-
- pdata->cmd_idx_plus_one = 0;
- pdata->cmd_receive_status = 0;
- pdata->cmd_data_increment = 0;
-
- pdata->cmd_size = 0;
- /*
- * Ensure a trailing null character for the next header_receiver() or
- * data_receiver() invocation.
- */
- memset(pdata->cmd_buf, 0, sizeof(pdata->cmd_buf));
-
- return ret;
-}
-
-static ssize_t i2cp_cdev_write(struct file *filep, const char __user *buf,
- size_t count, loff_t *f_ps)
-{
- ssize_t ret = 0;
- bool non_blocking;
- size_t remaining;
- char *kbuf, *start;
- struct i2cp_controller *pdata;
-
- /*
- * Just in case this could change out from under us, best to keep a
- * consistent view for the duration of this syscall.
- *
- * Write command implementations, i.e. struct i2cp_cmd implementations,
- * do NOT have to support blocking writes. For example, if a write of
- * an I2C message reply is received for a message that the pseudo
- * adapter never requested or expected, it makes more sense to indicate
- * an error than to block until possibly receiving a master_xfer request
- * for that I2C message, even if blocking is permitted.
- *
- * Furthermore, controller writes MUST NEVER block indefinitely, even
- * when non_blocking is false. E.g. while non_blocking may be used to
- * select between mutex_trylock and mutex_lock*, even in the
- * latter case the lock should never be blocked on I/O, on userspace, or
- * on anything else outside the control of this driver. It IS
- * permissable for the lock to be blocked on processing of previous or
- * concurrent write input, so long as that processing does not violate
- * these rules.
- */
- non_blocking = !!(filep->f_flags & O_NONBLOCK);
- pdata = filep->private_data;
-
- if (count > (size_t)I2CP_RW_SIZE_LIMIT)
- count = I2CP_RW_SIZE_LIMIT;
-
- kbuf = kzalloc(count, GFP_KERNEL);
- if (!kbuf) {
- ret = -ENOMEM;
- goto free_kbuf;
- }
- if (copy_from_user(kbuf, buf, count)) {
- ret = -EFAULT;
- goto free_kbuf;
- }
-
- start = kbuf;
- remaining = count;
-
- /*
- * Since write() calls are effectively serialized by way of
- * pdata->cmd_lock, we MUST NOT block on obtaining that lock if in
- * non-blocking mode, because it might be held by a blocking write().
- */
- if (!non_blocking) {
- mutex_lock(&pdata->cmd_lock);
- } else if (!mutex_trylock(&pdata->cmd_lock)) {
- ret = -EAGAIN;
- goto free_kbuf;
- }
-
- while (remaining) {
- if (pdata->cmd_data_increment <= 0)
- ret = i2cp_receive_ctrlr_cmd_header(
- pdata, start, remaining, non_blocking);
- else
- ret = i2cp_receive_ctrlr_cmd_data(
- pdata, start, remaining, non_blocking);
- if (ret < 0)
- break;
- if (ret == 0 || ret > remaining) {
- ret = -EINVAL;
- break;
- }
-
- remaining -= ret;
- start += ret;
-
- if (ret > 0 && start[-1] == i2cp_ctrlr_end_char) {
- ret = i2cp_receive_ctrlr_cmd_complete(
- pdata, non_blocking);
- if (ret < 0)
- break;
- }
- }
-
- mutex_unlock(&pdata->cmd_lock);
- wake_up_interruptible_sync(&pdata->poll_wait_queue);
-
- if (ret >= 0)
- /* If successful the whole write is always consumed. */
- ret = count;
-
- free_kbuf:
- kfree(kbuf);
- return ret;
-}
-
-/*
- * The select/poll/epoll implementation in this module is designed around these
- * controller behavior assumptions:
- *
- * - If any reader of a given controller makes use of polling, all will.
- *
- * - Upon notification of available data to read, a reader will fully consume it
- * in a read() loop until receiving EAGAIN, EWOULDBLOCK, or EOF.
- *
- * - Only one reader need be woken upon newly available data, however it is okay
- * if more than one are sometimes woken.
- *
- * - If more than one reader is woken, or otherwise acts in parallel, it is the
- * responsibility of the readers to either ensure that only one at a time
- * consumes all input until EAGAIN/EWOULDBLOCK, or that they properly
- * recombine any data that was split among them.
- *
- * - All of the above applies to writers as well.
- *
- * Notes:
- *
- * - If a reader does not read all available data until EAGAIN/EWOULDBLOCK after
- * being woken from poll, there may be no wake event for the remaining
- * available data, causing it to remain unread until further data becomes
- * available and triggers another wake event. The same applies to writers -
- * they are only guaranteed to be woken /once/ per blocked->unblocked
- * transition, so after being woken they should continue writing until either
- * the controller is out of data or EAGAIN/EWOULDBLOCK is encountered.
- *
- * - It is strongly suggested that controller implementations have only one
- * reader (thread) and one writer (thread), which may or may not be the same
- * thread. After all only one message can be active on an I2C bus at a time,
- * and this driver implementation reflects that. Avoiding multiple readers
- * and multiple writers greatly simplifies controller implementation, and
- * there is likely nothing to be gained from performing any of their work in
- * parallel.
- *
- * - Implementation detail: Reads are effectively serialized by a per controller
- * read lock. From the perspective of other readers, the controller device
- * will appear blocked, with appropriate behavior based on the O_NONBLOCK bit.
- * THIS IS SUBJECT TO CHANGE!
- *
- * - Implementation detail: Writes are effectively serialized by a per
- * controller write lock. From the perspective of other writers, the
- * controller device will appear blocked, with appropriate behavior based on
- * the O_NONBLOCK bit. THIS IS SUBJECT TO CHANGE!
- *
- * - Implementation detail: In the initial implementation, the only scenario
- * where a controller will appear blocked for writes is if another write is in
- * progress. Thus, a single writer should never see the device blocked. THIS
- * IS SUBJECT TO CHANGE! When using O_NONBLOCK, a controller should correctly
- * handle EAGAIN/EWOULDBLOCK even if it has only one writer.
- */
-static __poll_t i2cp_cdev_poll(struct file *filep, poll_table *ptp)
-{
- __poll_t poll_ret = 0;
- struct i2cp_controller *pdata;
-
- pdata = filep->private_data;
-
- poll_wait(filep, &pdata->poll_wait_queue, ptp);
-
- if (mutex_trylock(&pdata->rsp_lock)) {
- if (i2cp_poll_in(pdata))
- poll_ret |= POLLIN | POLLRDNORM;
- mutex_unlock(&pdata->rsp_lock);
- }
-
- if (!mutex_is_locked(&pdata->cmd_lock))
- poll_ret |= POLLOUT | POLLWRNORM;
-
- if (i2cp_adap_get_state(pdata) == I2CP_CTRLR_STATE_SHUTDN_REQ)
- poll_ret |= POLLHUP;
-
- return poll_ret;
-}
-
-static const struct file_operations i2cp_fileops = {
- .owner = THIS_MODULE,
- .open = i2cp_cdev_open,
- .release = i2cp_cdev_release,
- .read = i2cp_cdev_read,
- .write = i2cp_cdev_write,
- .poll = i2cp_cdev_poll,
- .llseek = no_llseek,
-};
-
-static ssize_t i2cp_limit_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- int ret;
-
- ret = snprintf(buf, PAGE_SIZE, "%u\n", i2cp_limit);
- if (ret >= PAGE_SIZE)
- return -ERANGE;
- return ret;
-}
-
-static struct device_attribute i2cp_limit_dev_attr = {
- .attr = {
- .name = "limit",
- .mode = 0444,
- },
- .show = i2cp_limit_show,
-};
-
-static ssize_t i2cp_count_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- int count, ret;
- struct i2cp_device *this_pseudo;
-
- this_pseudo = container_of(dev, struct i2cp_device, device);
-
- mutex_lock(&this_pseudo->counters.lock);
- count = this_pseudo->counters.count;
- mutex_unlock(&this_pseudo->counters.lock);
-
- ret = snprintf(buf, PAGE_SIZE, "%u\n", count);
- if (ret >= PAGE_SIZE)
- return -ERANGE;
- return ret;
-}
-
-static struct device_attribute i2cp_count_dev_attr = {
- .attr = {
- .name = "count",
- .mode = 0444,
- },
- .show = i2cp_count_show,
-};
-
-static struct attribute *i2cp_device_sysfs_attrs[] = {
- &i2cp_limit_dev_attr.attr,
- &i2cp_count_dev_attr.attr,
- NULL,
-};
-
-static const struct attribute_group i2cp_device_sysfs_group = {
- .attrs = i2cp_device_sysfs_attrs,
-};
-
-static const struct attribute_group *i2cp_device_sysfs_groups[] = {
- &i2cp_device_sysfs_group,
- NULL,
-};
-
-static void i2c_p_device_release(struct device *dev)
-{
- struct i2cp_device *this_pseudo;
-
- this_pseudo = container_of(dev, struct i2cp_device, device);
- kfree(this_pseudo->counters.all_controllers);
- kfree(this_pseudo);
-}
-
-static inline void i2c_p_class_destroy(void)
-{
- struct class *class;
-
- class = i2cp_class;
- i2cp_class = NULL;
- class_destroy(class);
-}
-
-static int __init i2cp_init(void)
-{
- int ret = -1;
-
- if (i2cp_limit < I2CP_ADAPTERS_MIN || i2cp_limit > I2CP_ADAPTERS_MAX) {
- pr_err("%s: i2cp_limit=%u, must be in range ["
- STR(I2CP_ADAPTERS_MIN) ", " STR(I2CP_ADAPTERS_MAX)
- "]\n", __func__, i2cp_limit);
- return -EINVAL;
- }
-
- i2cp_class = class_create(THIS_MODULE, I2CP_CLASS_NAME);
- if (IS_ERR(i2cp_class))
- return PTR_ERR(i2cp_class);
-
- i2cp_class->dev_groups = i2cp_device_sysfs_groups;
-
- ret = alloc_chrdev_region(&i2cp_dev_num, I2CP_CDEV_BASEMINOR,
- I2CP_CDEV_COUNT, I2CP_CHRDEV_NAME);
- if (ret < 0)
- goto fail_after_class_create;
-
- i2cp_device = kzalloc(sizeof(*i2cp_device), GFP_KERNEL);
- if (!i2cp_device) {
- ret = -ENOMEM;
- goto fail_after_chrdev_register;
- }
-
- i2cp_device->device.devt = i2cp_dev_num;
- i2cp_device->device.class = i2cp_class;
- i2cp_device->device.release = i2c_p_device_release;
- device_initialize(&i2cp_device->device);
-
- ret = dev_set_name(&i2cp_device->device, "%s", I2CP_DEVICE_NAME);
- if (ret < 0)
- goto fail_after_device_init;
-
- mutex_init(&i2cp_device->counters.lock);
- i2cp_device->counters.all_controllers = kcalloc(i2cp_limit,
- sizeof(*i2cp_device->counters.all_controllers), GFP_KERNEL);
- if (!i2cp_device->counters.all_controllers) {
- ret = -ENOMEM;
- goto fail_after_device_init;
- }
-
- cdev_init(&i2cp_device->cdev, &i2cp_fileops);
- i2cp_device->cdev.owner = THIS_MODULE;
-
- ret = cdev_device_add(&i2cp_device->cdev, &i2cp_device->device);
- if (ret < 0)
- goto fail_after_device_init;
-
- return 0;
-
- fail_after_device_init:
- put_device(&i2cp_device->device);
- fail_after_chrdev_register:
- unregister_chrdev_region(i2cp_dev_num, I2CP_CDEV_COUNT);
- fail_after_class_create:
- i2c_p_class_destroy();
- return ret;
-}
-
-static void __exit i2cp_exit(void)
-{
- cdev_device_del(&i2cp_device->cdev, &i2cp_device->device);
- put_device(&i2cp_device->device);
- unregister_chrdev_region(i2cp_dev_num, I2CP_CDEV_COUNT);
- i2c_p_class_destroy();
-}
-
-MODULE_AUTHOR("Matthew Blecker <matthewb@ihavethememo.net");
-MODULE_DESCRIPTION("Driver for userspace I2C adapter implementations.");
-MODULE_LICENSE("GPL");
-
-module_init(i2cp_init);
-module_exit(i2cp_exit);
diff --git a/extra/i2c_pseudo/install b/extra/i2c_pseudo/install
deleted file mode 100755
index e66dcbd719..0000000000
--- a/extra/i2c_pseudo/install
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/bin/sh
-#
-# This attempts to build and install the i2c-pseudo Linux kernel module.
-# Installs a udev rule making i2c-pseudo devices read-write by users in plugdev.
-
-set -e
-cd "$(dirname "$0")"
-
-make clean
-make
-ret=0
-sudo make modules_install || ret="$?"
-
-# Only install udev rule if plugdev group exists.
-if getent group plugdev > /dev/null; then
- sudo cp -iv 50-i2c-pseudo.rules /etc/udev/rules.d/ \
- || echo 1>&2 "NOTICE: Failed to copy udev rules file."
-fi
-
-if [ "$ret" -eq 0 ]; then
- make clean
- sudo depmod -a
- sudo modprobe i2c-pseudo
- echo "SUCCESS: installed and loaded i2c-pseudo module"
-else
- echo 1>&2 "WARNING: make modules_install failed with exit status $ret."
- echo 1>&2 "The module has not been installed for future reuse."
- echo 1>&2 "Will still attempt to load the module, using insmod instead of "\
-"modprobe."
- ret=0
- sudo insmod i2c-pseudo.ko || ret="$?"
- if [ "$ret" -ne 0 ]; then
- make clean
- exit "$ret"
- fi
- echo "PARTIAL SUCCESS: loaded i2c-pseudo module (not installed)"
- make clean
-fi
diff --git a/extra/lightbar/.gitignore b/extra/lightbar/.gitignore
deleted file mode 100644
index 964154302a..0000000000
--- a/extra/lightbar/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-lightbar
diff --git a/extra/lightbar/Makefile b/extra/lightbar/Makefile
deleted file mode 100644
index ce84428869..0000000000
--- a/extra/lightbar/Makefile
+++ /dev/null
@@ -1,28 +0,0 @@
-# Copyright 2014 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-PROG= lightbar
-HEADERS= simulation.h
-SRCS= main.c windows.c input.c ../../common/lightbar.c
-
-# comment this out if you don't have libreadline installed
-HAS_GNU_READLINE=1
-
-INCLUDE= -I. -I../../include
-CFLAGS= -g -Wall -Werror -pthread ${INCLUDE} -DLIGHTBAR_SIMULATION
-LDFLAGS = -lX11 -lxcb -lrt
-
-ifneq ($(HAS_GNU_READLINE),)
-CFLAGS += -DHAS_GNU_READLINE
-LDFLAGS += -lreadline
-endif
-
-all: ${PROG}
-
-${PROG} : ${SRCS} ${HEADERS} Makefile
- gcc ${CFLAGS} ${SRCS} ${LDFLAGS} -o ${PROG}
-
-.PHONY: clean
-clean:
- rm -f ${PROG}
diff --git a/extra/lightbar/README b/extra/lightbar/README
deleted file mode 100644
index 1862f922e4..0000000000
--- a/extra/lightbar/README
+++ /dev/null
@@ -1,39 +0,0 @@
-Lightbar simulator
-------------------------------------------------------------------------------
-
-Build with "make lightbar". The executable is "./lightbar".
-
-You may need to install libxcb1-dev or similar.
-
-This provides a simulation environment for the lightbar task, compiling
-common/lightbar.c from the EC source, but faking the rest of the EC.
-
-The EC console is on stdin/stdout, delivering all input to the lightbar's
-console command handler (so it prefixes any input with "lightbar"). The
-lightbar itself is displayed in an X window. You can click in that window to
-emulate changes to the battery level, AC connection, and brightness, all of
-which are normally outside the lightbar task's direct control.
-
-The initial sequence is "S5". Try issuing the command "seq s3s0" to see
-something more familiar.
-
-
-Note: the Pixel lightbar circuitry has three modes of operation:
-
-Unpowered
-
- When the host CPU is off (S5/G3), all power to the lightbar and its
- controller circuitry is lost.
-
-On
-
- When the host CPU is on (S0) or suspended (S3), the lightbar is powered
- again. After every power loss, it will need to be reinitialized by calling
- lb_init() before it can be used.
-
-Standby
-
- The lightbar controller ICs can turn off all the LED outputs to conserve
- power. This is the initial state when power is applied. You can turn the
- LEDs off manually by calling lb_off(). When suspended, the controller will
- respond to commands, but the LEDs aren't lit. Turn them on with lb_on().
diff --git a/extra/lightbar/input.c b/extra/lightbar/input.c
deleted file mode 100644
index e6c5485e39..0000000000
--- a/extra/lightbar/input.c
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2014 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-
-#include "simulation.h"
-
-#ifdef HAS_GNU_READLINE
-#include <readline/readline.h>
-#include <readline/history.h>
-
-char *get_input(const char *prompt)
-{
- static char *line;
-
- if (line) {
- free(line);
- line = 0;
- }
-
- line = readline(prompt);
-
- if (line && *line)
- add_history(line);
-
- return line;
-}
-
-#else /* no readline */
-
-char *get_input(const char *prompt)
-{
- static char mybuf[80];
- char *got;
- printf("%s", prompt);
- got = fgets(mybuf, sizeof(mybuf), stdin);
- return got;
-}
-
-#endif /* HAS_GNU_READLINE */
-
-void *entry_input(void *ptr)
-{
- char *got, buf[80];
- char *str, *word, *saveptr;
- int argc;
- char *argv[40];
- int ret;
-
- do {
- got = get_input("lightbar% ");
- if (got) {
- strcpy(buf, got);
- argc = 0;
- argv[argc++] = "lightbar";
- word = str = buf;
- while (word && argc < ARRAY_SIZE(argv)) {
- word = strtok_r(str, " \t\r\n", &saveptr);
- if (word)
- argv[argc++] = word;
- str = 0;
- }
- argv[argc] = 0;
- ret = fake_consolecmd_lightbar(argc, argv);
- if (ret)
- printf("ERROR %d\n", ret);
- }
-
- } while (got);
-
- exit(0);
-
- return 0;
-}
diff --git a/extra/lightbar/main.c b/extra/lightbar/main.c
deleted file mode 100644
index 5acf3d427a..0000000000
--- a/extra/lightbar/main.c
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- * Copyright 2014 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include <assert.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <pthread.h>
-#include <stdint.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <time.h>
-
-#include "simulation.h"
-
-static void *(*thread_fns[])(void *) = {
- entry_windows,
- entry_lightbar,
- entry_input,
-};
-
-int main(int argc, char *argv[])
-{
- int i;
- pthread_t thread[ARRAY_SIZE(thread_fns)];
-
- printf("\nLook at the README file.\n");
- printf("Click in the window.\n");
- printf("Type \"help\" for commands.\n\n");
- fflush(stdout);
-
- init_windows();
-
- for (i = 0; i < ARRAY_SIZE(thread_fns); i++)
- assert(0 == pthread_create(&thread[i], NULL, thread_fns[i], 0));
-
- for (i = 0; i < ARRAY_SIZE(thread_fns); i++)
- pthread_join(thread[i], NULL);
-
- return 0;
-}
-
-void *entry_lightbar(void *ptr)
-{
- lightbar_task();
- return 0;
-}
-
-/****************************************************************************/
-/* Fake functions. We only have to implement enough for lightbar.c */
-
-/* timespec uses nanoseconds */
-#define TS_USEC 1000L
-#define TS_MSEC 1000000L
-#define TS_SEC 1000000000L
-
-static void timespec_incr(struct timespec *v, time_t secs, long nsecs)
-{
- v->tv_sec += secs;
- /* The nanosecond sum won't overflow, but might have a carry. */
- v->tv_nsec += nsecs;
- v->tv_sec += v->tv_nsec / TS_SEC;
- v->tv_nsec %= TS_SEC;
-}
-
-
-static pthread_mutex_t task_mutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_cond_t task_cond = PTHREAD_COND_INITIALIZER;
-static uint32_t task_event;
-
-uint32_t task_wait_event(int timeout_us)
-{
- struct timespec t;
- uint32_t event;
-
- pthread_mutex_lock(&task_mutex);
-
- if (timeout_us > 0) {
- clock_gettime(CLOCK_REALTIME, &t);
- timespec_incr(&t, timeout_us / SECOND, timeout_us * TS_USEC);
-
- if (ETIMEDOUT == pthread_cond_timedwait(&task_cond,
- &task_mutex, &t))
- task_event |= TASK_EVENT_TIMER;
- } else {
- pthread_cond_wait(&task_cond, &task_mutex);
- }
-
- pthread_mutex_unlock(&task_mutex);
- event = task_event;
- task_event = 0;
- return event;
-}
-
-uint32_t task_set_event(task_id_t tskid, /* always LIGHTBAR */
- uint32_t event)
-{
- pthread_mutex_lock(&task_mutex);
- task_event = event;
- pthread_cond_signal(&task_cond);
- pthread_mutex_unlock(&task_mutex);
- return 0;
-}
-
-
-
-/* Stubbed functions */
-
-void cprintf(int zero, const char *fmt, ...)
-{
- va_list ap;
- char *s;
- char *newfmt = strdup(fmt);
-
- for (s = newfmt; *s; s++)
- if (*s == '%' && s[1] == 'T')
- *s = 'T';
-
- va_start(ap, fmt);
- vprintf(newfmt, ap);
- va_end(ap);
-
- free(newfmt);
-}
-
-void cprints(int zero, const char *fmt, ...)
-{
- va_list ap;
-
- printf("[TT ");
- va_start(ap, fmt);
- vprintf(fmt, ap);
- va_end(ap);
- printf("]\n");
-}
-
-timestamp_t get_time(void)
-{
- static struct timespec t_start;
- struct timespec t;
- timestamp_t ret;
-
- if (!t_start.tv_sec)
- clock_gettime(CLOCK_REALTIME, &t_start);
- clock_gettime(CLOCK_REALTIME, &t);
- ret.val = (t.tv_sec - t_start.tv_sec) * SECOND +
- (t.tv_nsec - t_start.tv_nsec) / TS_USEC;
- return ret;
-}
-
-/* We could implement these if we wanted to test their usage. */
-int system_add_jump_tag(uint16_t tag, int version, int size, const void *data)
-{
- return 0;
-}
-
-uint8_t *system_get_jump_tag(uint16_t tag, int *version, int *size)
-{
- return 0;
-}
-
-/* Copied from util/ectool.c */
-int lb_read_params_from_file(const char *filename,
- struct lightbar_params_v1 *p)
-{
- FILE *fp;
- char buf[80];
- int val[4];
- int r = 1;
- int line = 0;
- int want, got;
- int i;
-
- fp = fopen(filename, "rb");
- if (!fp) {
- fprintf(stderr, "Can't open %s: %s\n",
- filename, strerror(errno));
- return 1;
- }
-
- /* We must read the correct number of params from each line */
-#define READ(N) do { \
- line++; \
- want = (N); \
- got = -1; \
- if (!fgets(buf, sizeof(buf), fp)) \
- goto done; \
- got = sscanf(buf, "%i %i %i %i", \
- &val[0], &val[1], &val[2], &val[3]); \
- if (want != got) \
- goto done; \
- } while (0)
-
-
- /* Do it */
- READ(1); p->google_ramp_up = val[0];
- READ(1); p->google_ramp_down = val[0];
- READ(1); p->s3s0_ramp_up = val[0];
- READ(1); p->s0_tick_delay[0] = val[0];
- READ(1); p->s0_tick_delay[1] = val[0];
- READ(1); p->s0a_tick_delay[0] = val[0];
- READ(1); p->s0a_tick_delay[1] = val[0];
- READ(1); p->s0s3_ramp_down = val[0];
- READ(1); p->s3_sleep_for = val[0];
- READ(1); p->s3_ramp_up = val[0];
- READ(1); p->s3_ramp_down = val[0];
- READ(1); p->tap_tick_delay = val[0];
- READ(1); p->tap_gate_delay = val[0];
- READ(1); p->tap_display_time = val[0];
-
- READ(1); p->tap_pct_red = val[0];
- READ(1); p->tap_pct_green = val[0];
- READ(1); p->tap_seg_min_on = val[0];
- READ(1); p->tap_seg_max_on = val[0];
- READ(1); p->tap_seg_osc = val[0];
- READ(3);
- p->tap_idx[0] = val[0];
- p->tap_idx[1] = val[1];
- p->tap_idx[2] = val[2];
-
- READ(2);
- p->osc_min[0] = val[0];
- p->osc_min[1] = val[1];
- READ(2);
- p->osc_max[0] = val[0];
- p->osc_max[1] = val[1];
- READ(2);
- p->w_ofs[0] = val[0];
- p->w_ofs[1] = val[1];
-
- READ(2);
- p->bright_bl_off_fixed[0] = val[0];
- p->bright_bl_off_fixed[1] = val[1];
-
- READ(2);
- p->bright_bl_on_min[0] = val[0];
- p->bright_bl_on_min[1] = val[1];
-
- READ(2);
- p->bright_bl_on_max[0] = val[0];
- p->bright_bl_on_max[1] = val[1];
-
- READ(3);
- p->battery_threshold[0] = val[0];
- p->battery_threshold[1] = val[1];
- p->battery_threshold[2] = val[2];
-
- READ(4);
- p->s0_idx[0][0] = val[0];
- p->s0_idx[0][1] = val[1];
- p->s0_idx[0][2] = val[2];
- p->s0_idx[0][3] = val[3];
-
- READ(4);
- p->s0_idx[1][0] = val[0];
- p->s0_idx[1][1] = val[1];
- p->s0_idx[1][2] = val[2];
- p->s0_idx[1][3] = val[3];
-
- READ(4);
- p->s3_idx[0][0] = val[0];
- p->s3_idx[0][1] = val[1];
- p->s3_idx[0][2] = val[2];
- p->s3_idx[0][3] = val[3];
-
- READ(4);
- p->s3_idx[1][0] = val[0];
- p->s3_idx[1][1] = val[1];
- p->s3_idx[1][2] = val[2];
- p->s3_idx[1][3] = val[3];
-
- for (i = 0; i < ARRAY_SIZE(p->color); i++) {
- READ(3);
- p->color[i].r = val[0];
- p->color[i].g = val[1];
- p->color[i].b = val[2];
- }
-
-#undef READ
-
- /* Yay */
- r = 0;
-done:
- if (r)
- fprintf(stderr, "problem with line %d: wanted %d, got %d\n",
- line, want, got);
- fclose(fp);
- return r;
-}
-
-int lb_load_program(const char *filename, struct lightbar_program *prog)
-{
- FILE *fp;
- size_t got;
- int rc;
-
- fp = fopen(filename, "rb");
- if (!fp) {
- fprintf(stderr, "Can't open %s: %s\n",
- filename, strerror(errno));
- return 1;
- }
-
- rc = fseek(fp, 0, SEEK_END);
- if (rc) {
- fprintf(stderr, "Couldn't find end of file %s",
- filename);
- fclose(fp);
- return 1;
- }
- rc = (int) ftell(fp);
- if (rc > EC_LB_PROG_LEN) {
- fprintf(stderr, "File %s is too long, aborting\n", filename);
- fclose(fp);
- return 1;
- }
- rewind(fp);
-
- memset(prog->data, 0, EC_LB_PROG_LEN);
- got = fread(prog->data, 1, EC_LB_PROG_LEN, fp);
- if (rc != got)
- fprintf(stderr, "Warning: did not read entire file\n");
- prog->size = got;
- fclose(fp);
- return 0;
-}
diff --git a/extra/lightbar/programs/bad-decode-32.bin b/extra/lightbar/programs/bad-decode-32.bin
deleted file mode 100644
index 1d5d0c6c75..0000000000
--- a/extra/lightbar/programs/bad-decode-32.bin
+++ /dev/null
@@ -1 +0,0 @@
-UUU \ No newline at end of file
diff --git a/extra/lightbar/programs/bad-decode-8.bin b/extra/lightbar/programs/bad-decode-8.bin
deleted file mode 100644
index 8352675d67..0000000000
--- a/extra/lightbar/programs/bad-decode-8.bin
+++ /dev/null
Binary files differ
diff --git a/extra/lightbar/programs/bad-jump.bin b/extra/lightbar/programs/bad-jump.bin
deleted file mode 100644
index b2c29a0bbf..0000000000
--- a/extra/lightbar/programs/bad-jump.bin
+++ /dev/null
@@ -1 +0,0 @@
-? \ No newline at end of file
diff --git a/extra/lightbar/programs/bad-opcode.bin b/extra/lightbar/programs/bad-opcode.bin
deleted file mode 100644
index 6b10f95843..0000000000
--- a/extra/lightbar/programs/bad-opcode.bin
+++ /dev/null
@@ -1 +0,0 @@
-Ã \ No newline at end of file
diff --git a/extra/lightbar/programs/green-pulse.bin b/extra/lightbar/programs/green-pulse.bin
deleted file mode 100644
index 0fdab712e9..0000000000
--- a/extra/lightbar/programs/green-pulse.bin
+++ /dev/null
Binary files differ
diff --git a/extra/lightbar/programs/green-pulse.lbs b/extra/lightbar/programs/green-pulse.lbs
deleted file mode 100644
index bccf3e5c9a..0000000000
--- a/extra/lightbar/programs/green-pulse.lbs
+++ /dev/null
@@ -1,8 +0,0 @@
- set.1 {0,1,2,3}.end.g 0xff
- delay.r 7813
- delay.w 2000000
-L0001: on
- cycle.1
- off
- wait
- jump L0001
diff --git a/extra/lightbar/programs/infinite-jump.bin b/extra/lightbar/programs/infinite-jump.bin
deleted file mode 100644
index 5407bf3ddf..0000000000
--- a/extra/lightbar/programs/infinite-jump.bin
+++ /dev/null
Binary files differ
diff --git a/extra/lightbar/programs/infinite-jump.lbs b/extra/lightbar/programs/infinite-jump.lbs
deleted file mode 100644
index 6174d7ffd4..0000000000
--- a/extra/lightbar/programs/infinite-jump.lbs
+++ /dev/null
@@ -1 +0,0 @@
-L0001: jump L0001
diff --git a/extra/lightbar/programs/konami.bin b/extra/lightbar/programs/konami.bin
deleted file mode 100644
index f7abfdc4ee..0000000000
--- a/extra/lightbar/programs/konami.bin
+++ /dev/null
Binary files differ
diff --git a/extra/lightbar/programs/konami.lbs b/extra/lightbar/programs/konami.lbs
deleted file mode 100644
index c9fa8d697a..0000000000
--- a/extra/lightbar/programs/konami.lbs
+++ /dev/null
@@ -1,89 +0,0 @@
-# Konami code easter egg
- delay.w 100000
- set.rgb {1,2}.end 0xff 0xff 0x00
- ramp.1
- wait
- cycle.1
- wait
- ramp.1
- wait
- cycle.1
- wait
- set.rgb {1,2}.end 0x00 0x00 0x00
- set.1 {0,3}.end.b 0xff
- ramp.1
- wait
- cycle.1
- wait
- ramp.1
- wait
- cycle.1
- wait
- set.1 {0,3}.end.b 0x00
- set.1 {0,1}.end.r 0xff
- ramp.1
- wait
- cycle.1
- wait
- set.1 {0,1}.end.r 0x00
- set.1 {2,3}.end.g 0xff
- ramp.1
- wait
- cycle.1
- wait
- set.1 {2,3}.end.g 0x00
- set.1 {0,1}.end.r 0xff
- ramp.1
- wait
- cycle.1
- wait
- set.1 {0,1}.end.r 0x00
- set.1 {2,3}.end.g 0xff
- ramp.1
- wait
- cycle.1
- wait
- set.1 {2,3}.end.g 0x00
- set.rgb {0,2}.end 0x00 0xff 0xff
- ramp.1
- wait
- cycle.1
- wait
- delay.w 50000
- wait
- set.rgb {0,2}.end 0x00 0x00 0x00
- set.rgb {1,3}.end 0xff 0x00 0xff
- ramp.1
- wait
- wait
- cycle.1
- wait
- delay.w 100000
- wait
- wait
- set.rgb {0,1,2,3}.end 0xff 0xff 0xff
- ramp.1
- wait
- cycle.1
- wait
- ramp.1
- wait
- cycle.1
- wait
- ramp.1
- wait
- cycle.1
- wait
- ramp.1
- wait
- cycle.1
- wait
- ramp.1
- wait
- cycle.1
- wait
- ramp.1
- wait
- cycle.1
- wait
- halt
diff --git a/extra/lightbar/programs/rainbow-shift.bin b/extra/lightbar/programs/rainbow-shift.bin
deleted file mode 100644
index a72c5b16d6..0000000000
--- a/extra/lightbar/programs/rainbow-shift.bin
+++ /dev/null
Binary files differ
diff --git a/extra/lightbar/programs/rainbow-shift.lbs b/extra/lightbar/programs/rainbow-shift.lbs
deleted file mode 100644
index e1cbcddc83..0000000000
--- a/extra/lightbar/programs/rainbow-shift.lbs
+++ /dev/null
@@ -1,8 +0,0 @@
-# The rainbow cycle program.
- set.rgb {0,1,2,3}.end 0xff 0xff 0xff
- set.rgb {0}.phase 0x00 0x55 0xaa
- set.rgb {1}.phase 0x40 0x95 0xea
- set.rgb {2}.phase 0x80 0xd5 0x2a
- set.rgb {3}.phase 0xc0 0x15 0x6a
- delay.r 7813
- cycle
diff --git a/extra/lightbar/programs/red-green-blink.bin b/extra/lightbar/programs/red-green-blink.bin
deleted file mode 100644
index 6bece444dd..0000000000
--- a/extra/lightbar/programs/red-green-blink.bin
+++ /dev/null
Binary files differ
diff --git a/extra/lightbar/programs/red-green-blink.lbs b/extra/lightbar/programs/red-green-blink.lbs
deleted file mode 100644
index d520b739bb..0000000000
--- a/extra/lightbar/programs/red-green-blink.lbs
+++ /dev/null
@@ -1,14 +0,0 @@
-# Blinks red and green with 1 second pauses.
- set.rgb {0,1,2,3}.beg 0xff 0x00 0x00
- set.rgb {0,1,2,3}.end 0x00 0xff 0x00
- delay.w 250000
- delay.r 0
- cycle.1
- wait
- ramp.1
- wait
- cycle.1
- wait
- ramp.1
- wait
- halt
diff --git a/extra/lightbar/programs/s0.bin b/extra/lightbar/programs/s0.bin
deleted file mode 100644
index b20cecd8ee..0000000000
--- a/extra/lightbar/programs/s0.bin
+++ /dev/null
Binary files differ
diff --git a/extra/lightbar/programs/s0.lbs b/extra/lightbar/programs/s0.lbs
deleted file mode 100644
index 364c3d595c..0000000000
--- a/extra/lightbar/programs/s0.lbs
+++ /dev/null
@@ -1,22 +0,0 @@
-# S0 sequence: Google colors, unless battery is low.
- set.rgb {0}.end 0x33 0x69 0xe8
- set.rgb {1}.end 0xd5 0x0f 0x25
- set.rgb {2}.end 0xee 0xb2 0x11
- set.rgb {3}.end 0x00 0x99 0x25
- delay.r 1250
- ramp.1
- set.1 {0,1,2,3}.beg.r 0xff
- delay.r 2500
- delay.w 1000000
- wait
- jump L0003
-L0001: swap
- ramp.1
-L0002: wait
-L0003: jbat L0004 L0002
- jump L0002
-L0004: swap
- ramp.1
-L0005: wait
- jbat L0005 L0001
- jump L0001
diff --git a/extra/lightbar/programs/s0s3.bin b/extra/lightbar/programs/s0s3.bin
deleted file mode 100644
index d1cb8a4af1..0000000000
--- a/extra/lightbar/programs/s0s3.bin
+++ /dev/null
Binary files differ
diff --git a/extra/lightbar/programs/s0s3.lbs b/extra/lightbar/programs/s0s3.lbs
deleted file mode 100644
index ba141f338d..0000000000
--- a/extra/lightbar/programs/s0s3.lbs
+++ /dev/null
@@ -1,16 +0,0 @@
-# S0S3 sequence: Fade out, Google color ramp up/down.
- get
- delay.r 2000
- ramp.1
- swap
- set.rgb {0}.end 0x33 0x69 0xe8
- set.rgb {1}.end 0xd5 0x0f 0x25
- set.rgb {2}.end 0xee 0xb2 0x11
- set.rgb {3}.end 0x00 0x99 0x25
- delay.r 1250
- ramp.1
- swap
- delay.r 10000
- ramp.1
- off
- halt
diff --git a/extra/lightbar/programs/s3.bin b/extra/lightbar/programs/s3.bin
deleted file mode 100644
index 7e487bb8c9..0000000000
--- a/extra/lightbar/programs/s3.bin
+++ /dev/null
Binary files differ
diff --git a/extra/lightbar/programs/s3.lbs b/extra/lightbar/programs/s3.lbs
deleted file mode 100644
index e8803a06bb..0000000000
--- a/extra/lightbar/programs/s3.lbs
+++ /dev/null
@@ -1,17 +0,0 @@
-# S3 sequence: Pulse red on low battery.
- set.rgb {0,1,2,3}.end 0xff 0x00 0x00
- cycle.1
- delay.w 5000000
-L0001: off
- wait
- jcharge L0001
- jbat L0002 L0001
- jump L0001
-L0002: on
- delay.r 1250
- ramp.1
- swap
- delay.r 10000
- ramp.1
- swap
- jump L0001
diff --git a/extra/lightbar/programs/s3s0.bin b/extra/lightbar/programs/s3s0.bin
deleted file mode 100644
index b277752d25..0000000000
--- a/extra/lightbar/programs/s3s0.bin
+++ /dev/null
Binary files differ
diff --git a/extra/lightbar/programs/s3s0.lbs b/extra/lightbar/programs/s3s0.lbs
deleted file mode 100644
index 0cac96f208..0000000000
--- a/extra/lightbar/programs/s3s0.lbs
+++ /dev/null
@@ -1,11 +0,0 @@
-# S3S0 sequence: Google color ramp up/down.
- set.rgb {0}.end 0x33 0x69 0xe8
- set.rgb {1}.end 0xd5 0x0f 0x25
- set.rgb {2}.end 0xee 0xb2 0x11
- set.rgb {3}.end 0x00 0x99 0x25
- delay.r 1250
- ramp.1
- swap
- delay.r 10000
- ramp.1
- halt
diff --git a/extra/lightbar/simulation.h b/extra/lightbar/simulation.h
deleted file mode 100644
index 4df7b69411..0000000000
--- a/extra/lightbar/simulation.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright 2014 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#ifndef __EXTRA_SIMULATION_H
-#define __EXTRA_SIMULATION_H
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#include "lb_common.h"
-#include "lightbar.h"
-
-/* Functions specific to our simulation environment */
-void *entry_windows(void *);
-void *entry_input(void *);
-void *entry_lightbar(void *);
-void init_windows(void);
-int lb_read_params_from_file(const char *filename,
- struct lightbar_params_v1 *p);
-int lb_load_program(const char *filename, struct lightbar_program *prog);
-/* Interfaces to the EC code that we're encapsulating */
-void lightbar_task(void);
-int fake_consolecmd_lightbar(int argc, char *argv[]);
-
-/* EC-specific configuration */
-#undef DEMO_MODE_DEFAULT
-#define DEMO_MODE_DEFAULT 1
-#ifndef CONFIG_CONSOLE_CMDHELP
-#define CONFIG_CONSOLE_CMDHELP
-#endif
-#ifndef CONFIG_LIGHTBAR_POWER_RAILS
-#define CONFIG_LIGHTBAR_POWER_RAILS
-#endif
-
-
-/* Stuff that's too interleaved with the rest of the EC to just include */
-
-/* Test an important condition at compile time, not run time */
-#define _BA1_(cond, line) \
- extern int __build_assertion_ ## line[1 - 2*!(cond)] \
- __attribute__ ((unused))
-#define _BA0_(c, x) _BA1_(c, x)
-#define BUILD_ASSERT(cond) _BA0_(cond, __LINE__)
-
-#define BUILD_CHECK_INLINE(value, cond_true) ((value) / (!!(cond_true)))
-
-/* Number of elements in an array */
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
-
-/* Non-standard standard library functions */
-void cprintf(int zero, const char *fmt, ...);
-void cprints(int zero, const char *fmt, ...);
-#define ccprintf(fmt...) cprintf(0, fmt)
-#define strtoi strtol
-
-/* Task events */
-#define TASK_EVENT_CUSTOM_BIT(x) BUILD_CHECK_INLINE(BIT(x), BIT(x) & 0x0fffffff)
-#define TASK_EVENT_I2C_IDLE 0x10000000
-#define TASK_EVENT_WAKE 0x20000000
-#define TASK_EVENT_MUTEX 0x40000000
-#define TASK_EVENT_TIMER 0x80000000
-
-/* Time units in usecs */
-#define MSEC 1000
-#define SECOND 1000000
-
-#define TASK_ID_LIGHTBAR 0
-#define CC_LIGHTBAR 0
-
-/* Other definitions and structs */
-#define EC_SUCCESS 0
-#define EC_ERROR_INVAL 5
-#define EC_ERROR_PARAM1 11
-#define EC_ERROR_PARAM2 12
-
-typedef int task_id_t;
-
-typedef union {
- uint64_t val;
- struct {
- uint32_t lo;
- uint32_t hi;
- } le /* little endian words */;
-} timestamp_t;
-
-struct host_cmd_handler_args {
- const void *params;
- void *response;
- int response_size;
-};
-
-/* EC functions that we have to provide */
-uint32_t task_wait_event(int timeout_us);
-uint32_t task_set_event(task_id_t tskid, uint32_t event);
-timestamp_t get_time(void);
-int system_add_jump_tag(uint16_t tag, int version, int size, const void *data);
-uint8_t *system_get_jump_tag(uint16_t tag, int *version, int *size);
-
-/* Export unused static functions to avoid compiler warnings. */
-#define DECLARE_HOOK(X, fn, Y) \
- void fake_hook_##fn(void) { fn(); }
-
-#define DECLARE_HOST_COMMAND(X, fn, Y) \
- enum ec_status fake_hostcmd_##fn(struct host_cmd_handler_args *args) \
- { return fn(args); }
-
-#define DECLARE_CONSOLE_COMMAND(X, fn, Y...) \
- int fake_consolecmd_##X(int argc, char *argv[]) \
- { return fn(argc, argv); }
-
-#endif /* __EXTRA_SIMULATION_H */
diff --git a/extra/lightbar/windows.c b/extra/lightbar/windows.c
deleted file mode 100644
index 115074363c..0000000000
--- a/extra/lightbar/windows.c
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright 2014 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include <assert.h>
-#include <pthread.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <xcb/xcb.h>
-
-#include "simulation.h"
-
-/*****************************************************************************/
-/* Window drawing stuff */
-
-/* Dimensions - may change */
-static int win_w = 1024;
-static int win_h = 32;
-
-static xcb_connection_t *c;
-static xcb_screen_t *screen;
-static xcb_drawable_t win;
-static xcb_gcontext_t foreground;
-static xcb_colormap_t colormap_id;
-
-static int fake_power;
-
-void init_windows(void)
-{
- uint32_t mask = 0;
- uint32_t values[2];
-
- /* Open the connection to the X server */
- c = xcb_connect(NULL, NULL);
-
- /* Get the first screen */
- screen = xcb_setup_roots_iterator(xcb_get_setup(c)).data;
-
- /* Get a colormap */
- colormap_id = xcb_generate_id(c);
- xcb_create_colormap(c, XCB_COLORMAP_ALLOC_NONE,
- colormap_id, screen->root, screen->root_visual);
-
- /* Create foreground GC */
- foreground = xcb_generate_id(c);
- mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES;
- values[0] = screen->white_pixel;
- values[1] = 0;
- xcb_create_gc(c, foreground, screen->root, mask, values);
-
- /* Create the window */
- win = xcb_generate_id(c);
- mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
- values[0] = screen->black_pixel;
- values[1] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS;
- xcb_create_window(c, /* Connection */
- XCB_COPY_FROM_PARENT, /* depth */
- win, /* window Id */
- screen->root, /* parent window */
- 0, 0, /* x, y */
- win_w, win_h, /* width, height */
- 10, /* border_width */
- XCB_WINDOW_CLASS_INPUT_OUTPUT, /* class */
- screen->root_visual, /* visual */
- mask, values); /* masks */
-
- /* Map the window on the screen */
- xcb_map_window(c, win);
-
- /* We flush the request */
- xcb_flush(c);
-}
-
-void cleanup(void)
-{
- xcb_destroy_window(c, win);
- xcb_free_gc(c, foreground);
- xcb_free_colormap(c, colormap_id);
- xcb_disconnect(c);
-}
-
-/*****************************************************************************/
-/* Draw the lightbar elements */
-
-/* xcb likes 16-bit colors */
-uint16_t leds[NUM_LEDS][3] = {
- {0xffff, 0x0000, 0x0000},
- {0x0000, 0xffff, 0x0000},
- {0x0000, 0x0000, 0xffff},
- {0xffff, 0xffff, 0x0000},
-};
-pthread_mutex_t leds_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-void change_gc_color(uint16_t red, uint16_t green, uint16_t blue)
-{
- uint32_t mask = 0;
- uint32_t values[2];
- xcb_alloc_color_reply_t *reply;
-
- reply = xcb_alloc_color_reply(c,
- xcb_alloc_color(c, colormap_id,
- red, green, blue),
- NULL);
- assert(reply);
-
- mask = XCB_GC_FOREGROUND;
- values[0] = reply->pixel;
- xcb_change_gc(c, foreground, mask, values);
- free(reply);
-}
-
-void update_window(void)
-{
- xcb_segment_t segments[] = {
- {0, 0, win_w, win_h},
- {0, win_h, win_w, 0},
- };
- xcb_rectangle_t rect;
- int w = win_w / NUM_LEDS;
- int i;
- uint16_t copyleds[NUM_LEDS][3];
-
- if (fake_power) {
- pthread_mutex_lock(&leds_mutex);
- memcpy(copyleds, leds, sizeof(leds));
- pthread_mutex_unlock(&leds_mutex);
-
- for (i = 0; i < NUM_LEDS; i++) {
- rect.x = i * w;
- rect.y = 0;
- rect.width = w;
- rect.height = win_h;
-
- change_gc_color(copyleds[i][0],
- copyleds[i][1],
- copyleds[i][2]);
-
- xcb_poly_fill_rectangle(c, win, foreground, 1, &rect);
- }
- } else {
- rect.x = 0;
- rect.y = 0;
- rect.width = win_w;
- rect.height = win_h;
-
- change_gc_color(0, 0, 0);
- xcb_poly_fill_rectangle(c, win, foreground, 1, &rect);
-
- change_gc_color(0x8080, 0, 0);
-
- for (i = 0; i < NUM_LEDS; i++) {
- segments[0].x1 = i * w;
- segments[0].y1 = 0;
- segments[0].x2 = segments[0].x1 + w;
- segments[0].y2 = win_h;
- segments[1].x1 = segments[0].x1;
- segments[1].y1 = win_h;
- segments[1].x2 = segments[0].x2;
- segments[1].y2 = 0;
- xcb_poly_segment(c, win, foreground, 2, segments);
- }
- }
-
- xcb_flush(c);
-}
-
-void setrgb(int led, int red, int green, int blue)
-{
- led %= NUM_LEDS;
-
- pthread_mutex_lock(&leds_mutex);
- leds[led][0] = red << 8 | red;
- leds[led][1] = green << 8 | green;
- leds[led][2] = blue << 8 | blue;
- pthread_mutex_unlock(&leds_mutex);
-
- update_window();
-}
-
-/*****************************************************************************/
-/* lb_common stubs */
-
-
-
-/* Brightness serves no purpose here. It's automatic on the Chromebook. */
-static int brightness = 0xc0;
-void lb_set_brightness(unsigned int newval)
-{
- brightness = newval;
-}
-uint8_t lb_get_brightness(void)
-{
- return brightness;
-}
-
-void lb_set_rgb(unsigned int led, int red, int green, int blue)
-{
- int i;
- if (led >= NUM_LEDS)
- for (i = 0; i < NUM_LEDS; i++)
- setrgb(i, red, green, blue);
- else
- setrgb(led, red, green, blue);
-}
-
-int lb_get_rgb(unsigned int led, uint8_t *red, uint8_t *green, uint8_t *blue)
-{
- led %= NUM_LEDS;
- pthread_mutex_lock(&leds_mutex);
- *red = leds[led][0];
- *green = leds[led][1];
- *blue = leds[led][2];
- pthread_mutex_unlock(&leds_mutex);
- return 0;
-}
-
-void lb_init(void)
-{
- if (fake_power)
- lb_set_rgb(NUM_LEDS, 0, 0, 0);
-};
-void lb_off(void)
-{
- fake_power = 0;
- update_window();
-};
-void lb_on(void)
-{
- fake_power = 1;
- update_window();
-};
-void lb_hc_cmd_dump(struct ec_response_lightbar *out)
-{
- printf("lightbar is %s\n", fake_power ? "on" : "off");
- memset(out, fake_power, sizeof(*out));
-};
-void lb_hc_cmd_reg(const struct ec_params_lightbar *in) { };
-
-int lb_power(int enabled)
-{
- return fake_power;
-}
-
-
-/*****************************************************************************/
-/* Event handling stuff */
-
-void *entry_windows(void *ptr)
-{
- xcb_generic_event_t *e;
- xcb_expose_event_t *ev;
- xcb_button_press_event_t *bv;
- int chg = 1;
-
- while ((e = xcb_wait_for_event(c))) {
-
- switch (e->response_type & ~0x80) {
- case XCB_EXPOSE:
- ev = (xcb_expose_event_t *)e;
- if (win_w != ev->width || win_h != ev->height) {
- win_w = ev->width;
- win_h = ev->height;
- }
- update_window();
- break;
- case XCB_BUTTON_PRESS:
- bv = (xcb_button_press_event_t *)e;
- switch (bv->detail) {
- case 1:
- demo_battery_level(-1);
- break;
- case 3:
- demo_battery_level(+1);
- break;
- case 2:
- chg = !chg;
- demo_is_charging(chg);
- break;
- }
- break;
- }
-
- free(e);
- }
-
- cleanup();
- exit(0);
- return 0;
-}
diff --git a/extra/rma_reset/.gitignore b/extra/rma_reset/.gitignore
deleted file mode 100644
index ae297b7dc0..0000000000
--- a/extra/rma_reset/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-base32.o
-curve25519-generic.o
-curve25519.o
-rma_reset
-sha256.o
diff --git a/extra/rma_reset/Makefile b/extra/rma_reset/Makefile
deleted file mode 100644
index 4a640c5b4c..0000000000
--- a/extra/rma_reset/Makefile
+++ /dev/null
@@ -1,55 +0,0 @@
-# Copyright 2017 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-CC ?= gcc
-PROGRAM := rma_reset
-SOURCE := $(PROGRAM).c
-OBJS := curve25519.o curve25519-generic.o sha256.o base32.o
-LIBS :=
-LFLAGS :=
-CFLAGS := -std=gnu99 \
- -Wall \
- -Werror \
- -Wpointer-arith \
- -Wcast-align \
- -Wcast-qual \
- -Wundef \
- -Wsign-compare \
- -Wredundant-decls \
- -Wmissing-declarations
-
-ifeq ($(DEBUG),1)
-CFLAGS += -g -O0
-else
-CFLAGS += -O3
-endif
-#
-# Add libusb-1.0 required flags
-#
-INCLUDE=-I. -I../../ -I../../fuzz -I../../test -I../../include -I../../chip/host
-LIBS += -lcrypto -lssl
-CFLAGS += ${INCLUDE}
-STANDALONE_FLAGS=${INCLUDE} -ffreestanding -fno-builtin \
- -Ibuiltin/ -D"__keep= "
-
-$(PROGRAM): $(SOURCE) $(OBJS) Makefile
- $(CC) $(CFLAGS) $(SOURCE) $(LFLAGS) $(LIBS) $(OBJS) -o $@
-
-curve25519-generic.o: ../../common/curve25519-generic.c
- $(CC) $(STANDALONE_FLAGS) -c -o curve25519-generic.o \
- ../../common/curve25519-generic.c
-
-curve25519.o: ../../common/curve25519.c
- $(CC) $(STANDALONE_FLAGS) -c -o curve25519.o ../../common/curve25519.c
-
-sha256.o: ../../common/sha256.c
- $(CC) $(STANDALONE_FLAGS) -c -o sha256.o ../../common/sha256.c
-
-base32.o: ../../common/base32.c
- $(CC) $(STANDALONE_FLAGS) -c -o base32.o ../../common/base32.c
-
-.PHONY: clean
-
-clean:
- rm -rf *.o $(PROGRAM) *~
diff --git a/extra/rma_reset/board.h b/extra/rma_reset/board.h
deleted file mode 100644
index f969ad0c56..0000000000
--- a/extra/rma_reset/board.h
+++ /dev/null
@@ -1,11 +0,0 @@
-/* Copyright 2017 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef __CROS_EC_BOARD_H
-#define __CROS_EC_BOARD_H
-
-#define CONFIG_RNG
-
-#endif /* __CROS_EC_BOARD_H */
diff --git a/extra/rma_reset/rma_reset.c b/extra/rma_reset/rma_reset.c
deleted file mode 100644
index fe1eb5e909..0000000000
--- a/extra/rma_reset/rma_reset.c
+++ /dev/null
@@ -1,707 +0,0 @@
-/* Copyright 2017 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include <ctype.h>
-#include <endian.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <openssl/bn.h>
-#include <openssl/ec.h>
-#include <openssl/obj_mac.h>
-#include <openssl/rand.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#include "rma_auth.h"
-#include "curve25519.h"
-#include "sha256.h"
-#include "base32.h"
-
-#define EC_COORDINATE_SZ 32
-#define EC_PRIV_KEY_SZ 32
-#define EC_P256_UNCOMPRESSED_PUB_KEY_SZ (EC_COORDINATE_SZ * 2 + 1)
-#define EC_P256_COMPRESSED_PUB_KEY_SZ (EC_COORDINATE_SZ + 1)
-
-#define SERVER_ADDRESS \
- "https://www.google.com/chromeos/partner/console/cr50reset/request"
-
-/* Test server keys for x25519 and p256 curves. */
-static const uint8_t rma_test_server_x25519_public_key[] = {
- 0x03, 0xae, 0x2d, 0x2c, 0x06, 0x23, 0xe0, 0x73,
- 0x0d, 0xd3, 0xb7, 0x92, 0xac, 0x54, 0xc5, 0xfd,
- 0x7e, 0x9c, 0xf0, 0xa8, 0xeb, 0x7e, 0x2a, 0xb5,
- 0xdb, 0xf4, 0x79, 0x5f, 0x8a, 0x0f, 0x28, 0x3f
-};
-
-static const uint8_t rma_test_server_x25519_private_key[] = {
- 0x47, 0x3b, 0xa5, 0xdb, 0xc4, 0xbb, 0xd6, 0x77,
- 0x20, 0xbd, 0xd8, 0xbd, 0xc8, 0x7a, 0xbb, 0x07,
- 0x03, 0x79, 0xba, 0x7b, 0x52, 0x8c, 0xec, 0xb3,
- 0x4d, 0xaa, 0x69, 0xf5, 0x65, 0xb4, 0x31, 0xad
-};
-
-#define RMA_TEST_SERVER_X25519_KEY_ID 0x10
-#define RMA_PROD_SERVER_X25519_KEY_ID 0
-
-/*
- * P256 curve keys, generated using openssl as follows:
- *
- * openssl ecparam -name prime256v1 -genkey -out key.pem
- * openssl ec -in key.pem -text -noout
- */
-static const uint8_t rma_test_server_p256_private_key[] = {
- 0x54, 0xb0, 0x82, 0x92, 0x54, 0x92, 0xfc, 0x4a,
- 0xa7, 0x6b, 0xea, 0x8f, 0x30, 0xcc, 0xf7, 0x3d,
- 0xa2, 0xf6, 0xa7, 0xad, 0xf0, 0xec, 0x7d, 0xe9,
- 0x26, 0x75, 0xd1, 0xec, 0xde, 0x20, 0x8f, 0x81
-};
-
-/*
- * P256 public key in full form, x and y coordinates with a single byte
- * prefix, 65 bytes total.
- */
-static const uint8_t rma_test_server_p256_public_key[] = {
- 0x04, 0xe7, 0xbe, 0x37, 0xaa, 0x68, 0xca, 0xcc,
- 0x68, 0xf4, 0x8c, 0x56, 0x65, 0x5a, 0xcb, 0xf8,
- 0xf4, 0x65, 0x3c, 0xd3, 0xc6, 0x1b, 0xae, 0xd6,
- 0x51, 0x7a, 0xcc, 0x00, 0x8d, 0x59, 0x6d, 0x1b,
- 0x0a, 0x66, 0xe8, 0x68, 0x5e, 0x6a, 0x82, 0x19,
- 0x81, 0x76, 0x84, 0x92, 0x7f, 0x8d, 0xb2, 0xbe,
- 0xf5, 0x39, 0x50, 0xd5, 0xfe, 0xee, 0x00, 0x67,
- 0xcf, 0x40, 0x5f, 0x68, 0x12, 0x83, 0x4f, 0xa4,
- 0x35
-};
-
-#define RMA_TEST_SERVER_P256_KEY_ID 0x20
-#define RMA_PROD_SERVER_P256_KEY_ID 0x01
-
-/* Default values which can change based on command line arguments. */
-static uint8_t server_key_id = RMA_TEST_SERVER_X25519_KEY_ID;
-static uint8_t board_id[4] = {'Z', 'Z', 'C', 'R'};
-static uint8_t device_id[8] = {'T', 'H', 'X', 1, 1, 3, 8, 0xfe};
-static uint8_t hw_id[20] = "TESTSAMUS1234";
-
-static char challenge[RMA_CHALLENGE_BUF_SIZE];
-static char authcode[RMA_AUTHCODE_BUF_SIZE];
-
-static char *progname;
-static char *short_opts = "a:b:c:d:hpk:tw:";
-static const struct option long_opts[] = {
- /* name hasarg *flag val */
- {"auth_code", 1, NULL, 'a'},
- {"board_id", 1, NULL, 'b'},
- {"challenge", 1, NULL, 'c'},
- {"device_id", 1, NULL, 'd'},
- {"help", 0, NULL, 'h'},
- {"hw_id", 1, NULL, 'w'},
- {"key_id", 1, NULL, 'k'},
- {"p256", 0, NULL, 'p'},
- {"test", 0, NULL, 't'},
- {},
-};
-
-void panic_assert_fail(const char *fname, int linenum);
-void rand_bytes(void *buffer, size_t len);
-int safe_memcmp(const void *s1, const void *s2, size_t size);
-
-void panic_assert_fail(const char *fname, int linenum)
-{
- printf("\nASSERTION FAILURE at %s:%d\n", fname, linenum);
-}
-
-int safe_memcmp(const void *s1, const void *s2, size_t size)
-{
- const uint8_t *us1 = s1;
- const uint8_t *us2 = s2;
- int result = 0;
-
- if (size == 0)
- return 0;
-
- while (size--)
- result |= *us1++ ^ *us2++;
-
- return result != 0;
-}
-
-void rand_bytes(void *buffer, size_t len)
-{
- RAND_bytes(buffer, len);
-}
-
-/*
- * Generate a p256 key pair and calculate the shared secret based on our
- * private key and the server public key.
- *
- * Return the X coordinate of the generated public key and the shared secret.
- *
- * @pub_key - the compressed public key without the prefix; by convention
- * between RMA client and server the generated pubic key would
- * always have prefix of 0x03, (the Y coordinate value is odd), so
- * it is omitted from the key blob, which allows to keep the blob
- * size at 32 bytes.
- * @secret_seed - the product of multiplying of the server point by our
- * private key, only the 32 bytes of X coordinate are returned.
- */
-static void p256_key_and_secret_seed(uint8_t pub_key[32],
- uint8_t secret_seed[32])
-{
- const EC_GROUP *group;
- EC_KEY *key;
- EC_POINT *pub;
- EC_POINT *secret_point;
- uint8_t buf[EC_P256_UNCOMPRESSED_PUB_KEY_SZ];
-
- /* Prepare structures to operate on. */
- key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
- group = EC_KEY_get0_group(key);
- pub = EC_POINT_new(group);
-
- /*
- * We might have to try multiple times, until the Y coordinate is an
- * odd value as required by convention.
- */
- do {
- EC_KEY_generate_key(key);
-
- /* Extract public key into an octal array. */
- EC_POINT_point2oct(group, EC_KEY_get0_public_key(key),
- POINT_CONVERSION_UNCOMPRESSED,
- buf, sizeof(buf), NULL);
-
- /* If Y coordinate is an odd value, we are done. */
- } while (!(buf[sizeof(buf) - 1] & 1));
-
- /* Copy X coordinate out. */
- memcpy(pub_key, buf + 1, 32);
-
- /*
- * We have our private key and the server's point coordinates (aka
- * server public key). Let's multiply the coordinates by our private
- * key to get the shared secret.
- */
-
- /* Load raw public key into the point structure. */
- EC_POINT_oct2point(group, pub, rma_test_server_p256_public_key,
- sizeof(rma_test_server_p256_public_key), NULL);
-
- secret_point = EC_POINT_new(group);
-
- /* Multiply server public key by our private key. */
- EC_POINT_mul(group, secret_point, 0, pub,
- EC_KEY_get0_private_key(key), 0);
-
- /* Pull the result back into the octal buffer. */
- EC_POINT_point2oct(group, secret_point, POINT_CONVERSION_UNCOMPRESSED,
- buf, sizeof(buf), NULL);
-
- /*
- * Copy X coordinate into the output to use as the shared secret
- * seed.
- */
- memcpy(secret_seed, buf + 1, 32);
-
- /* release resources */
- EC_KEY_free(key);
- EC_POINT_free(pub);
- EC_POINT_free(secret_point);
-}
-
-/*
- * When imitating server side, calculate the secret value given the client's
- * compressed public key (X coordinate only with 0x03 prefix implied) and
- * knowing our (server) private key.
- *
- * @secret - array to return the X coordinate of the calculated point.
- * @raw_pub_key - X coordinate of the point calculated by the client, 0x03
- * prefix implied.
- */
-static void p256_calculate_secret(uint8_t secret[32],
- const uint8_t raw_pub_key[32])
-{
- uint8_t raw_pub_key_x[EC_P256_COMPRESSED_PUB_KEY_SZ];
- EC_KEY *key;
- const uint8_t *kp = raw_pub_key_x;
- EC_POINT *secret_point;
- const EC_GROUP *group;
- BIGNUM *priv;
- uint8_t buf[EC_P256_UNCOMPRESSED_PUB_KEY_SZ];
-
- /* Express server private key as a BN. */
- priv = BN_new();
- BN_bin2bn(rma_test_server_p256_private_key, EC_PRIV_KEY_SZ, priv);
-
- /*
- * Populate a p256 key structure based on the compressed
- * representation of the client's public key.
- */
- raw_pub_key_x[0] = 3; /* Implied by convention. */
- memcpy(raw_pub_key_x + 1, raw_pub_key, sizeof(raw_pub_key_x) - 1);
- key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
- group = EC_KEY_get0_group(key);
- key = o2i_ECPublicKey(&key, &kp, sizeof(raw_pub_key_x));
-
- /* This is where the multiplication result will go. */
- secret_point = EC_POINT_new(group);
-
- /* Multiply client's point by our private key. */
- EC_POINT_mul(group, secret_point, 0,
- EC_KEY_get0_public_key(key),
- priv, 0);
-
- /* Pull the result back into the octal buffer. */
- EC_POINT_point2oct(group, secret_point, POINT_CONVERSION_UNCOMPRESSED,
- buf, sizeof(buf), NULL);
-
- /* Copy X coordinate into the output to use as the shared secret. */
- memcpy(secret, buf + 1, 32);
-}
-
-static int rma_server_side(const char *generated_challenge)
-{
- int key_id, version;
- uint8_t secret[32];
- uint8_t hmac[32];
- struct rma_challenge c;
- uint8_t *cptr = (uint8_t *)&c;
-
- /* Convert the challenge back into binary */
- if (base32_decode(cptr, 8 * sizeof(c), generated_challenge, 9) !=
- 8 * sizeof(c)) {
- printf("Error decoding challenge\n");
- return -1;
- }
-
- version = RMA_CHALLENGE_GET_VERSION(c.version_key_id);
- key_id = RMA_CHALLENGE_GET_KEY_ID(c.version_key_id);
- printf("Challenge: %s\n", generated_challenge);
- printf("Version: %d\n", version);
- printf("Server KeyID: %d\n", key_id);
-
- if (version != RMA_CHALLENGE_VERSION)
- printf("Unsupported challenge version %d\n", version);
-
- /* Calculate the shared secret, use curve based on the key ID. */
- switch (key_id) {
- case RMA_PROD_SERVER_X25519_KEY_ID:
- printf("Unsupported Prod KeyID %d\n", key_id);
- case RMA_TEST_SERVER_X25519_KEY_ID:
- X25519(secret, rma_test_server_x25519_private_key,
- c.device_pub_key);
- break;
- case RMA_PROD_SERVER_P256_KEY_ID:
- printf("Unsupported Prod KeyID %d\n", key_id);
- case RMA_TEST_SERVER_P256_KEY_ID:
- p256_calculate_secret(secret, c.device_pub_key);
- break;
- default:
- printf("Unknown KeyID %d\n", key_id);
- return 1;
- }
-
- /*
- * Auth code is a truncated HMAC of the ephemeral public key, BoardID,
- * and DeviceID.
- */
- hmac_SHA256(hmac, secret, sizeof(secret), cptr + 1, sizeof(c) - 1);
- if (base32_encode(authcode, RMA_AUTHCODE_BUF_SIZE,
- hmac, RMA_AUTHCODE_CHARS * 5, 0)) {
- printf("Error encoding auth code\n");
- return -1;
- }
- printf("Authcode: %s\n", authcode);
-
- return 0;
-};
-
-static int rma_create_test_challenge(int p256_mode)
-{
- uint8_t temp[32]; /* Private key or HMAC */
- uint8_t secret_seed[32];
- struct rma_challenge c;
- uint8_t *cptr = (uint8_t *)&c;
- uint32_t bid;
-
- /* Clear the current challenge and authcode, if any */
- memset(challenge, 0, sizeof(challenge));
- memset(authcode, 0, sizeof(authcode));
-
- memset(&c, 0, sizeof(c));
- c.version_key_id = RMA_CHALLENGE_VKID_BYTE(
- RMA_CHALLENGE_VERSION, server_key_id);
-
- memcpy(&bid, board_id, sizeof(bid));
- bid = be32toh(bid);
- memcpy(c.board_id, &bid, sizeof(c.board_id));
-
- memcpy(c.device_id, device_id, sizeof(c.device_id));
-
- if (p256_mode) {
- p256_key_and_secret_seed(c.device_pub_key, secret_seed);
- } else {
- /* Calculate a new ephemeral key pair */
- X25519_keypair(c.device_pub_key, temp);
- /* Calculate the shared secret seed. */
- X25519(secret_seed, temp, rma_test_server_x25519_public_key);
- }
-
- /* Encode the challenge */
- if (base32_encode(challenge, sizeof(challenge), cptr, 8 * sizeof(c), 9))
- return 1;
-
- /*
- * Auth code is a truncated HMAC of the ephemeral public key, BoardID,
- * and DeviceID. Those are all in the right order in the challenge
- * struct, after the version/key id byte.
- */
- hmac_SHA256(temp, secret_seed, sizeof(secret_seed),
- cptr + 1, sizeof(c) - 1);
- if (base32_encode(authcode, sizeof(authcode), temp,
- RMA_AUTHCODE_CHARS * 5, 0))
- return 1;
-
- return 0;
-}
-
-int rma_try_authcode(const char *code)
-{
- return safe_memcmp(authcode, code, RMA_AUTHCODE_CHARS);
-}
-
-static void dump_key(const char *title, const uint8_t *key, size_t key_size)
-{
- size_t i;
- const int bytes_per_line = 8;
-
- printf("\n\n\%s\n", title);
- for (i = 0; i < key_size; i++)
- printf("%02x%c", key[i], ((i + 1) % bytes_per_line) ? ' ':'\n');
-
- if (i % bytes_per_line)
- printf("\n");
-}
-
-static void print_params(int p_flag)
-{
- int i;
- const uint8_t *priv_key;
- const uint8_t *pub_key;
- int key_id;
- size_t pub_key_size;
-
- printf("\nBoard Id:\n");
- for (i = 0; i < 4; i++)
- printf("%c ", board_id[i]);
-
- printf("\n\nDevice Id:\n");
- for (i = 0; i < 3; i++)
- printf("%c ", device_id[i]);
- for (i = 3; i < 8; i++)
- printf("%02x ", device_id[i]);
-
- if (p_flag) {
- priv_key = rma_test_server_p256_private_key;
- pub_key = rma_test_server_p256_public_key;
- pub_key_size = sizeof(rma_test_server_p256_public_key);
- key_id = RMA_TEST_SERVER_P256_KEY_ID;
- } else {
- priv_key = rma_test_server_x25519_private_key;
- pub_key = rma_test_server_x25519_public_key;
- pub_key_size = sizeof(rma_test_server_x25519_public_key);
- key_id = RMA_TEST_SERVER_X25519_KEY_ID;
- }
-
- printf("\n\nServer Key Id:\n");
- printf("%02x", key_id);
-
- /* Both private keys are of the same size */
- dump_key("Server Private Key:", priv_key, EC_PRIV_KEY_SZ);
- dump_key("Server Public Key:", pub_key, pub_key_size);
-
- printf("\nChallenge:\n");
- for (i = 0; i < RMA_CHALLENGE_CHARS; i++) {
- printf("%c", challenge[i]);
- if (((i + 1) % 5) == 0)
- printf(" ");
- if (((i + 1) % 40) == 0)
- printf("\n");
- }
-
- printf("\nAuthorization Code:\n");
- for (i = 0; i < RMA_AUTHCODE_BUF_SIZE; i++)
- printf("%c", authcode[i]);
-
- printf("\n\nChallenge String:\n");
- printf("%s?challenge=", SERVER_ADDRESS);
- for (i = 0; i < RMA_CHALLENGE_CHARS; i++)
- printf("%c", challenge[i]);
- printf("&hwid=%s\n", hw_id);
-
- printf("\n");
-}
-
-static void usage(void)
-{
- printf("\nUsage: %s [--p256] --key_id <arg> --board_id <arg> "
- "--device_id <arg> --hw_id <arg> |\n"
- " --auth_code <arg> |\n"
- " --challenge <arg>\n"
- "\n"
- "This is used to generate the cr50 or server responses for rma "
- "open.\n"
- "The cr50 side can be used to generate a challenge response "
- "and sends authoriztion code to reset device.\n"
- "The server side can generate an authcode from cr50's "
- "rma challenge.\n"
- "\n"
- " -c,--challenge The challenge generated by cr50\n"
- " -k,--key_id Index of the server private key\n"
- " -b,--board_id BoardID type field\n"
- " -d,--device_id Device-unique identifier\n"
- " -a,--auth_code Reset authorization code\n"
- " -w,--hw_id Hardware id\n"
- " -h,--help Show this message\n"
- " -p,--p256 Use prime256v1 curve instead of x25519\n"
- " -t,--test "
- "Generate challenge using default test inputs\n"
- "\n", progname);
-}
-
-static int atoh(char *v)
-{
- char hn;
- char ln;
-
- hn = toupper(*v);
- ln = toupper(*(v + 1));
-
- hn -= (isdigit(hn) ? '0' : '7');
- ln -= (isdigit(ln) ? '0' : '7');
-
- if ((hn < 0 || hn > 0xf) || (ln < 0 || ln > 0xf))
- return 0;
-
- return (hn << 4) | ln;
-}
-
-static int set_server_key_id(char *id)
-{
- /* verify length */
- if (strlen(id) != 2)
- return 1;
-
- /* verify digits */
- if (!isxdigit(*id) || !isxdigit(*(id+1)))
- return 1;
-
- server_key_id = atoh(id);
-
- return 0;
-}
-
-static int set_board_id(char *id)
-{
- int i;
-
- /* verify length */
- if (strlen(id) != 8)
- return 1;
-
- /* verify digits */
- for (i = 0; i < 8; i++)
- if (!isxdigit(*(id + i)))
- return 1;
-
- for (i = 0; i < 4; i++)
- board_id[i] = atoh((id + (i*2)));
-
- return 0;
-}
-
-static int set_device_id(char *id)
-{
- int i;
-
- /* verify length */
- if (strlen(id) != 16)
- return 1;
-
- for (i = 0; i < 16; i++)
- if (!isxdigit(*(id + i)))
- return 1;
-
- for (i = 0; i < 8; i++)
- device_id[i] = atoh((id + (i*2)));
-
- return 0;
-}
-
-static int set_hw_id(char *id)
-{
- int i;
- int len;
-
- len = strlen(id);
- if (len > 20)
- len = 20;
-
- for (i = 0; i < 20; i++)
- hw_id[i] = *(id + i);
-
- return 0;
-}
-
-static int set_auth_code(char *code)
-{
- int i;
-
- if (strlen(code) != 8)
- return 1;
-
- for (i = 0; i < 8; i++)
- authcode[i] = *(code + i);
- authcode[i] = 0;
-
- return 0;
-}
-
-int main(int argc, char **argv)
-{
- int a_flag = 0;
- int b_flag = 0;
- int d_flag = 0;
- int k_flag = 0;
- int p_flag = 0;
- int t_flag = 0;
- int w_flag = 0;
- int i;
-
- progname = strrchr(argv[0], '/');
- if (progname)
- progname++;
- else
- progname = argv[0];
-
- opterr = 0;
- while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) {
- switch (i) {
- case 't':
- t_flag = 1;
- break;
- case 'c':
- return rma_server_side(optarg);
- case 'k':
- if (set_server_key_id(optarg)) {
- printf("Malformed key id\n");
- return 1;
- }
- k_flag = 1;
- break;
- case 'b':
- if (set_board_id(optarg)) {
- printf("Malformed board id\n");
- return 1;
- }
- b_flag = 1;
- break;
- case 'd':
- if (set_device_id(optarg)) {
- printf("Malformed device id\n");
- return 1;
- }
- d_flag = 1;
- break;
- case 'a':
- if (set_auth_code(optarg)) {
- printf("Malformed authorization code\n");
- return 1;
- }
- a_flag = 1;
- break;
- case 'w':
- if (set_hw_id(optarg)) {
- printf("Malformed hardware id\n");
- return 1;
- }
- w_flag = 1;
- break;
- case 'h':
- usage();
- return 0;
- case 0: /* auto-handled option */
- break;
- case '?':
- if (optopt)
- printf("Unrecognized option: -%c\n", optopt);
- else
- printf("Unrecognized option: %s\n",
- argv[optind - 1]);
- break;
- case ':':
- printf("Missing argument to %s\n", argv[optind - 1]);
- break;
- case 'p':
- p_flag = 1;
- server_key_id = RMA_TEST_SERVER_P256_KEY_ID;
- break;
- default:
- printf("Internal error at %s:%d\n", __FILE__, __LINE__);
- return 1;
- }
- }
-
- if (a_flag) {
- FILE *acode;
- char verify_authcode[RMA_AUTHCODE_BUF_SIZE];
- int rv;
-
- acode = fopen("/tmp/authcode", "r");
- if (acode == NULL) {
- printf("Please generate challenge\n");
- return 1;
- }
-
- rv = fread(verify_authcode, 1, RMA_AUTHCODE_BUF_SIZE, acode);
- if (rv != RMA_AUTHCODE_BUF_SIZE) {
- printf("Error reading saved authcode\n");
- return 1;
- }
- if (strcmp(verify_authcode, authcode) == 0)
- printf("Code Accepted\n");
- else
- printf("Invalid Code\n");
-
- } else {
- if (!t_flag) { /* Use default values */
- if (!k_flag || !b_flag || !d_flag || !w_flag) {
- printf("server-side: Flag -c is mandatory\n");
- printf("cr50-side: Flags -k, -b, -d, and -w "
- "are mandatory\n");
- return 1;
- }
- }
-
- rma_create_test_challenge(p_flag);
-
- {
- FILE *acode;
-
- acode = fopen("/tmp/authcode", "w");
- if (acode < 0)
- return 1;
- fwrite(authcode, 1, RMA_AUTHCODE_BUF_SIZE, acode);
- fclose(acode);
- }
-
- print_params(p_flag);
- }
-
- return 0;
-}
diff --git a/extra/sps_errs/.gitignore b/extra/sps_errs/.gitignore
deleted file mode 100644
index ea17491321..0000000000
--- a/extra/sps_errs/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-prog
diff --git a/extra/sps_errs/Makefile b/extra/sps_errs/Makefile
deleted file mode 100644
index 12224ad803..0000000000
--- a/extra/sps_errs/Makefile
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright 2015 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# Use your own libmpsse if you want, but we're going to use the files
-# that are part of the Chromium OS trunks_client program.
-PLATFORM2 = ../../../../platform2
-MPSSE_DIR = $(PLATFORM2)/trunks/ftdi
-
-PROG = prog
-SRCS = prog.c $(MPSSE_DIR)/mpsse.c $(MPSSE_DIR)/support.c
-
-CFLAGS = \
- -std=gnu99 \
- -g3 \
- -O3 \
- -Wall \
- -Werror \
- -Wpointer-arith \
- -Wcast-align \
- -Wcast-qual \
- -Wundef \
- -Wsign-compare \
- -Wredundant-decls \
- -Wmissing-declarations
-
-CFLAGS += -I../../include -I${MPSSE_DIR} -I${PLATFORM2}
-
-CFLAGS += $(shell pkg-config --cflags libusb-1.0 libftdi1)
-LIBS += $(shell pkg-config --libs libusb-1.0 libftdi1)
-
-$(PROG): $(SRCS) Makefile
- gcc $(CFLAGS) $(SRCS) $(LDFLAGS) $(LIBS) -o $@
-
-.PHONY: clean
-clean:
- rm -rf $(PROG)
diff --git a/extra/sps_errs/README b/extra/sps_errs/README
deleted file mode 100644
index d1fbb6b43f..0000000000
--- a/extra/sps_errs/README
+++ /dev/null
@@ -1,28 +0,0 @@
-SETUP:
-
- Attach an EC to the build host using an FTDI USB-to-SPI adapter.
-
-BUILD:
-
- make
- ./prog
-
-
-USAGE:
-
- Usage: ./prog [-v] [-c BYTES]
-
- This sends a EC_CMD_HELLO host command. The -c option can
- be used to truncate the exchange early, to see how the EC
- deals with the interruption.
-
-NOTE:
-
- Ubuntu Trusty uses an ancient version of libftdi.
-
- If building outside of the Chromium chroot, you'll probably want to grab the
- latest libftdi1-1.2.tar.bz2 from
-
- http://www.intra2net.com/en/developer/libftdi/
-
- and install it into /usr instead.
diff --git a/extra/sps_errs/prog.c b/extra/sps_errs/prog.c
deleted file mode 100644
index b649199068..0000000000
--- a/extra/sps_errs/prog.c
+++ /dev/null
@@ -1,447 +0,0 @@
-/* Copyright 2015 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include <signal.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "mpsse.h"
-
-#include "ec_commands.h"
-
-static int opt_verbose;
-static size_t stop_after = -1;
-
-/* Communication handle */
-static struct mpsse_context *mpsse;
-
-/* enum ec_status meaning */
-static const char *ec_strerr(enum ec_status r)
-{
- static const char * const strs[] = {
- "SUCCESS",
- "INVALID_COMMAND",
- "ERROR",
- "INVALID_PARAM",
- "ACCESS_DENIED",
- "INVALID_RESPONSE",
- "INVALID_VERSION",
- "INVALID_CHECKSUM",
- "IN_PROGRESS",
- "UNAVAILABLE",
- "TIMEOUT",
- "OVERFLOW",
- "INVALID_HEADER",
- "REQUEST_TRUNCATED",
- "RESPONSE_TOO_BIG",
- "BUS_ERROR",
- "BUSY",
- };
- if (r >= EC_RES_SUCCESS && r <= EC_RES_BUSY)
- return strs[r];
-
- return "<undefined result>";
-};
-
-
-/****************************************************************************
- * Debugging output
- */
-
-#define LINELEN 16
-
-static void showline(uint8_t *buf, int len)
-{
- int i;
- printf(" ");
- for (i = 0; i < len; i++)
- printf(" %02x", buf[i]);
- for (i = len; i < LINELEN; i++)
- printf(" ");
- printf(" ");
- for (i = 0; i < len; i++)
- printf("%c",
- (buf[i] >= ' ' && buf[i] <= '~') ? buf[i] : '.');
- printf("\n");
-}
-
-static void show(const char *fmt, uint8_t *buf, int len)
-{
- int i, m, n;
-
- if (!opt_verbose)
- return;
-
- printf(fmt, len);
-
- m = len / LINELEN;
- n = len % LINELEN;
-
- for (i = 0; i < m; i++)
- showline(buf + i * LINELEN, LINELEN);
- if (n)
- showline(buf + m * LINELEN, n);
-}
-
-/****************************************************************************
- * Send command & receive result
- */
-
-/*
- * With proto v3, the kernel driver asks the EC for the max param size
- * (EC_CMD_GET_PROTOCOL_INFO) at probe time, because it can vary depending on
- * the bus and/or the supported commands.
- *
- * FIXME: For now we'll just hard-code a size.
- */
-static uint8_t txbuf[128];
-
-/*
- * Load the output buffer with a proto v3 request (header, then data, with
- * checksum correct in header).
- */
-static size_t prepare_request(int cmd, int version,
- const uint8_t *data, size_t data_len)
-{
- struct ec_host_request *request;
- size_t i, total_len;
- uint8_t csum = 0;
-
- total_len = sizeof(*request) + data_len;
- if (total_len > sizeof(txbuf)) {
- printf("Request too large (%zd > %zd)\n",
- total_len, sizeof(txbuf));
- return -1;
- }
-
- /* Header first */
- request = (struct ec_host_request *)txbuf;
- request->struct_version = EC_HOST_REQUEST_VERSION;
- request->checksum = 0;
- request->command = cmd;
- request->command_version = version;
- request->reserved = 0;
- request->data_len = data_len;
-
- /* Then data */
- memcpy(txbuf + sizeof(*request), data, data_len);
-
- /* Update checksum */
- for (i = 0; i < total_len; i++)
- csum += txbuf[i];
- request->checksum = -csum;
-
- return total_len;
-}
-
-
-/* Timeout flag, so we don't wait forever */
-static int timedout;
-static void alarm_handler(int sig)
-{
- timedout = 1;
-}
-
-/*
- * Send command, wait for result. Return zero if communication succeeded; check
- * response to see if the EC liked the command.
- */
-static int send_cmd(int cmd, int version,
- void *outbuf,
- size_t outsize,
- struct ec_host_response *hdr,
- void *bodydest,
- size_t bodylen)
-{
- uint8_t *tptr, *hptr = 0, *bptr = 0;
- size_t len, i;
- uint8_t sum = 0;
- int lastone = 0x1111;
- int ret = 0;
- size_t bytes_left = stop_after;
- size_t bytes_sent = 0;
-
-
- /* Load up the txbuf with the stuff to send */
- len = prepare_request(cmd, version, outbuf, outsize);
- if (len < 0)
- return -1;
-
- if (MPSSE_OK != Start(mpsse)) {
- fprintf(stderr, "Start failed: %s\n",
- ErrorString(mpsse));
- return -1;
- }
-
- /* Send the command request */
- if (len > bytes_left) {
- printf("len %zd => %zd\n", len, bytes_left);
- len = bytes_left;
- }
-
- show("Transfer(%d) =>\n", txbuf, len);
- tptr = Transfer(mpsse, txbuf, len);
- bytes_left -= len;
- bytes_sent += len;
- if (!tptr) {
- fprintf(stderr, "Transfer failed: %s\n",
- ErrorString(mpsse));
- goto out;
- }
-
- show("Transfer(%d) <=\n", tptr, len);
-
- /* Make sure the EC was listening */
- for (i = 0; i < len; i++) {
- switch (tptr[i]) {
- case EC_SPI_PAST_END:
- case EC_SPI_RX_BAD_DATA:
- case EC_SPI_NOT_READY:
- ret = tptr[i];
- /* FALLTHROUGH */
- default:
- break;
- }
- if (ret)
- break;
- }
- free(tptr);
- if (ret) {
- printf("HEY: EC no good (0x%02x)\n", ret);
- goto out;
- }
-
- if (!bytes_left)
- goto out;
-
- /* Read until we see the response come along */
-
- /* Give up eventually */
- timedout = 0;
- if (SIG_ERR == signal(SIGALRM, alarm_handler)) {
- perror("Problem with signal handler");
- goto out;
- }
- alarm(1);
-
- if (opt_verbose)
- printf("Wait:");
-
- /* Read a byte at a time until we see the start of the frame.
- * This is slow, but still faster than the EC. */
- while (bytes_left) {
- uint8_t *ptr = Read(mpsse, 1);
- bytes_left--;
- bytes_sent++;
- if (!ptr) {
- fprintf(stderr, "Read failed: %s\n",
- ErrorString(mpsse));
- alarm(0);
- goto out;
- }
- if (opt_verbose && lastone != *ptr) {
- printf(" %02x", *ptr);
- lastone = *ptr;
- }
- if (*ptr == EC_SPI_FRAME_START) {
- free(ptr);
- break;
- }
- free(ptr);
-
- if (timedout) {
- fprintf(stderr, "timed out\n");
- goto out;
- }
- }
- alarm(0);
-
- if (opt_verbose)
- printf("\n");
-
- if (!bytes_left)
- goto out;
-
- /* Now read the response header */
- len = sizeof(*hdr);
- if (len > bytes_left) {
- printf("len %zd => %zd\n", len, bytes_left);
- len = bytes_left;
- }
-
- hptr = Read(mpsse, len);
- bytes_left -= len;
- bytes_sent += len;
- if (!hptr) {
- fprintf(stderr, "Read failed: %s\n",
- ErrorString(mpsse));
- goto out;
- }
- show("Header(%d):\n", hptr, sizeof(*hdr));
- memcpy(hdr, hptr, sizeof(*hdr));
-
- /* Check the header */
- if (hdr->struct_version != EC_HOST_RESPONSE_VERSION) {
- printf("HEY: response version %d (should be %d)\n",
- hdr->struct_version,
- EC_HOST_RESPONSE_VERSION);
- goto out;
- }
-
- if (hdr->data_len > bodylen) {
- printf("HEY: response data_len %d is > %zd\n",
- hdr->data_len,
- bodylen);
- goto out;
- }
-
- if (!bytes_left)
- goto out;
-
- len = hdr->data_len;
- if (len > bytes_left) {
- printf("len %zd => %zd\n", len, bytes_left);
- len = bytes_left;
- }
-
- /* Read the data */
- if (len) {
- bptr = Read(mpsse, len);
- bytes_left -= len;
- bytes_sent += len;
- if (!bptr) {
- fprintf(stderr, "Read failed: %s\n",
- ErrorString(mpsse));
- goto out;
- }
- show("Body(%d):\n", bptr, hdr->data_len);
- memcpy(bodydest, bptr, hdr->data_len);
- }
-
- /* Verify the checksum */
- for (i = 0; i < sizeof(hdr); i++)
- sum += hptr[i];
- for (i = 0; i < hdr->data_len; i++)
- sum += bptr[i];
- if (sum)
- printf("HEY: Checksum invalid\n");
-
-out:
- printf("sent %zd bytes\n", bytes_sent);
- if (!bytes_left)
- printf("hit byte limit\n");
- if (hptr)
- free(hptr);
- if (bptr)
- free(bptr);
-
- if (MPSSE_OK != Stop(mpsse)) {
- fprintf(stderr, "Stop failed: %s\n",
- ErrorString(mpsse));
- return -1;
- }
-
- return 0;
-}
-
-
-/****************************************************************************/
-
-/**
- * Try it.
- *
- * @return zero on success
- */
-static int hello(void)
-{
- struct ec_params_hello p;
- struct ec_host_response resp;
- struct ec_response_hello r;
- uint32_t expected;
- int retval;
-
- memset(&p, 0, sizeof(p));
- memset(&resp, 0, sizeof(resp));
- memset(&r, 0, sizeof(r));
-
- p.in_data = 0xa5a5a5a5;
- expected = p.in_data + 0x01020304;
-
- retval = send_cmd(EC_CMD_HELLO, 0,
- &p, sizeof(p),
- &resp,
- &r, sizeof(r));
-
- if (retval) {
- printf("Transmission error\n");
- return -1;
- }
-
- if (EC_RES_SUCCESS != resp.result) {
- printf("EC result is %d: %s\n",
- resp.result, ec_strerr(resp.result));
- return -1;
- }
-
- printf("sent %08x, expected %08x, got %08x => %s\n",
- p.in_data, expected, r.out_data,
- expected == r.out_data ? "yay" : "boo");
-
- return !(expected == r.out_data);
-}
-
-static void usage(char *progname)
-{
- printf("\nUsage: %s [-v] [-c BYTES]\n\n", progname);
- printf("This sends a EC_CMD_HELLO host command. The -c option can\n");
- printf("be used to truncate the exchange early, to see how the EC\n");
- printf("deals with the interruption.\n\n");
-}
-
-int main(int argc, char *argv[])
-{
- int retval = 1;
- int errorcnt = 0;
- int i;
-
- while ((i = getopt(argc, argv, ":vc:")) != -1) {
- switch (i) {
- case 'c':
- stop_after = atoi(optarg);
- printf("stopping after %zd bytes\n", stop_after);
- break;
- case 'v':
- opt_verbose++;
- break;
- case '?':
- printf("unrecognized option: -%c\n", optopt);
- errorcnt++;
- break;
- }
- }
- if (errorcnt) {
- usage(argv[0]);
- return 1;
- }
-
- /* Find something to talk to */
- mpsse = MPSSE(SPI0, 2000000, 0);
- if (!mpsse) {
- printf("Can't find a device to open\n");
- return 1;
- }
-
- if (0 != hello())
- goto out;
-
- retval = 0;
-out:
- Close(mpsse);
- mpsse = 0;
- return retval;
-}
diff --git a/extra/stack_analyzer/README.md b/extra/stack_analyzer/README.md
deleted file mode 100644
index d1c77b57d2..0000000000
--- a/extra/stack_analyzer/README.md
+++ /dev/null
@@ -1,105 +0,0 @@
-# Stack Size Analysis Tool for EC Firmware
-
-This tool does static analysis on EC firmwares to get the maximum stack usage of
-each function and task. The maximum stack usage of a function includes the stack
-used by itself and the functions it calls.
-
-## Usage
-
-Make sure the firmware of your target board has been built.
-
-In `src/platform/ec`, run `make BOARD=${BOARD} SECTION=${SECTION}
-ANNOTATION=${ANNOTATION} analyzestack` The `${SECTION}` can be `RO` or `RW`. The
-`${ANNOTATION}` is a optional annotation file, see the example_annotation.yaml,
-by default, board/$BOARD/analyzestack.yaml is used.
-
-## Output
-
-For each task, it will output the result like below,
-
-```
-Task: PD_C1, Max size: 1156 (932 + 224), Allocated size: 640
-Call Trace:
- pd_task (160) [common/usb_pd_protocol.c:1644] 1008a6e8
- -> pd_task[common/usb_pd_protocol.c:1808] 1008ac8a
- - handle_request[common/usb_pd_protocol.c:1191]
- - handle_data_request[common/usb_pd_protocol.c:798]
- -> pd_task[common/usb_pd_protocol.c:2672] 1008c222
- -> [annotation]
- pd_send_request_msg.lto_priv.263 (56) [common/usb_pd_protocol.c:653] 1009a0b4
- -> pd_send_request_msg.lto_priv.263[common/usb_pd_protocol.c:712] 1009a22e0
-```
-
-The `pd_task` uses 160 bytes on the stack and calls
-`pd_send_request_msg.lto_priv.263`.
-
-The callsites to the next function will be shown like below,
-
-```
--> pd_task[common/usb_pd_protocol.c:1808] 1008ac8a
- - handle_request[common/usb_pd_protocol.c:1191]
- - handle_data_request[common/usb_pd_protocol.c:798]
--> pd_task[common/usb_pd_protocol.c:2672] 1008c222
--> [annotation]
-```
-
-This means one callsite to the next function is at `usb_pd_protocol.c:798`, but
-it is inlined to the current function and you can follow the trace:
-`usb_pd_protocol.c:1808 -> usb_pd_protocol.c:1191 -> usb_pd_protocol.c:798` to
-find the callsite. The second callsite is at `usb_pd_protocol.c:2672`. And the
-third one is added by annotation.
-
-The unresolved indirect callsites have the similar format to the above.
-
-## Annotating Indirect Call
-
-To annotate an indirect call like this,
-
-```
-Unresolved indirect callsites:
- pd_transmit
- -> pd_transmit[common/usb_pd_protocol.c:407] 802c9c8
- - tcpm_transmit[driver/tcpm/tcpm.h:142]
-```
-
-It is an indirect call in the `tcpm_transmit`, which is inlined to the
-`pd_transmit`.
-
-You can add a annotation like the below to eliminate it.
-
-```
-add:
- tcpm_transmit[driver/tcpm/tcpm.h:142]:
- - anx74xx_tcpm_transmit
-```
-
-The source `tcpm_transmit[driver/tcpm/tcpm.h:142]` must be a full signature
-(function_name[path:line number]). So the resolver can know which indirect call
-you want to annotate and eliminate (even if it is inlined).
-
-## Annotating arrays (hooks, console commands, host commands)
-
-When a callsite calls a number of functions based on values from an constant
-array (in `.rodata` section), one can use the following syntax:
-
-```
- hook_task[common/hooks.c:197]:
- - { name: __deferred_funcs, stride: 4, offset: 0 }
- - { name: __hooks_second, stride: 8, offset: 0 }
- - { name: __hooks_tick, stride: 8, offset: 0 }
-```
-
-Where `name` is the symbol name for the start of the array (the end of the array
-is `<name>_end`), stride is the array element size, and offset is the offset of
-the function pointer in the structure. For example, above, `__deferred_funcs` is
-a simple array of function pointers, while `__hooks_tick` is an array of `struct
-hook_data` (size 8, pointer at offset 0):
-
-```
-struct hook_data {
- /* Hook processing routine. */
- void (*routine)(void);
- /* Priority; low numbers = higher priority. */
- int priority;
-};
-```
diff --git a/extra/stack_analyzer/example_annotation.yaml b/extra/stack_analyzer/example_annotation.yaml
deleted file mode 100644
index 084fabc2d1..0000000000
--- a/extra/stack_analyzer/example_annotation.yaml
+++ /dev/null
@@ -1,44 +0,0 @@
-# Size of extra stack frame needed by exception context switch.
-exception_frame_size: 64
-
-# Add some missing calls.
-add:
- # console_task also calls command_display_accel_info and command_accel_init.
- console_task:
- - command_display_accel_info
- - command_accel_init
-
- # Function name can be followed by [source code path] to indicate where is it
- # declared (there may be several functions with the same name).
- motion_lid_calc[common/motion_lid.c]:
- - get_range[driver/accel_kionix.c]
-
- # The full signature (function name[path:line number]) can be used to
- # eliminate the indirect call (see README.md).
- tcpm_transmit[driver/tcpm/tcpm.h:142]:
- - anx74xx_tcpm_transmit
-
-# Remove some call paths.
-remove:
-# Remove all callsites pointing to panic_assert_fail.
-- panic_assert_fail
-- panic
-- [software_panic]
-# Remove some invalid paths.
-- [pd_send_request_msg, set_state, pd_power_supply_reset]
-- [__tx_char, __tx_char]
-- [set_state, set_state, set_state]
-
-# Remove two invalid paths with the common prefix.
-- [pd_execute_hard_reset, set_state, [charge_manager_update_dualrole, pd_dfp_exit_mode]]
-# It is equivalent to the following two lines,
-# - [pd_execute_hard_reset, set_state, charge_manager_update_dualrole]
-# - [pd_execute_hard_reset, set_state, pd_dfp_exit_mode]
-
-# Remove four invalid paths with the common segment.
-- [[pd_send_request_msg, pd_request_vconn_swap], set_state, [usb_mux_set, pd_power_supply_reset]]
-# It is equivalent to the following four lines,
-# - [pd_send_request_msg, set_state, usb_mux_set]
-# - [pd_send_request_msg, set_state, pd_power_supply_reset]
-# - [pd_request_vconn_swap, set_state, usb_mux_set]
-# - [pd_request_vconn_swap, set_state, pd_power_supply_reset]
diff --git a/extra/stack_analyzer/run_tests.sh b/extra/stack_analyzer/run_tests.sh
deleted file mode 100755
index 5662f60b8b..0000000000
--- a/extra/stack_analyzer/run_tests.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2017 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# Discover all the unit tests in extra/stack_analyzer directory and run them.
-python3 -m unittest discover -b -s extra/stack_analyzer -p "*_unittest.py" \
- && touch extra/stack_analyzer/.tests-passed
diff --git a/extra/stack_analyzer/stack_analyzer.py b/extra/stack_analyzer/stack_analyzer.py
deleted file mode 100755
index 77d16d5450..0000000000
--- a/extra/stack_analyzer/stack_analyzer.py
+++ /dev/null
@@ -1,1872 +0,0 @@
-#!/usr/bin/env python3
-# Copyright 2017 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-# Ignore indention messages, since legacy scripts use 2 spaces instead of 4.
-# pylint: disable=bad-indentation,docstring-section-indent
-# pylint: disable=docstring-trailing-quotes
-
-"""Statically analyze stack usage of EC firmware.
-
- Example:
- extra/stack_analyzer/stack_analyzer.py \
- --export_taskinfo ./build/elm/util/export_taskinfo.so \
- --section RW \
- ./build/elm/RW/ec.RW.elf
-
-"""
-
-from __future__ import print_function
-
-import argparse
-import collections
-import ctypes
-import os
-import re
-import subprocess
-import yaml
-
-
-SECTION_RO = 'RO'
-SECTION_RW = 'RW'
-# Default size of extra stack frame needed by exception context switch.
-# This value is for cortex-m with FPU enabled.
-DEFAULT_EXCEPTION_FRAME_SIZE = 224
-
-
-class StackAnalyzerError(Exception):
- """Exception class for stack analyzer utility."""
-
-
-class TaskInfo(ctypes.Structure):
- """Taskinfo ctypes structure.
-
- The structure definition is corresponding to the "struct taskinfo"
- in "util/export_taskinfo.so.c".
- """
- _fields_ = [('name', ctypes.c_char_p),
- ('routine', ctypes.c_char_p),
- ('stack_size', ctypes.c_uint32)]
-
-
-class Task(object):
- """Task information.
-
- Attributes:
- name: Task name.
- routine_name: Routine function name.
- stack_max_size: Max stack size.
- routine_address: Resolved routine address. None if it hasn't been resolved.
- """
-
- def __init__(self, name, routine_name, stack_max_size, routine_address=None):
- """Constructor.
-
- Args:
- name: Task name.
- routine_name: Routine function name.
- stack_max_size: Max stack size.
- routine_address: Resolved routine address.
- """
- self.name = name
- self.routine_name = routine_name
- self.stack_max_size = stack_max_size
- self.routine_address = routine_address
-
- def __eq__(self, other):
- """Task equality.
-
- Args:
- other: The compared object.
-
- Returns:
- True if equal, False if not.
- """
- if not isinstance(other, Task):
- return False
-
- return (self.name == other.name and
- self.routine_name == other.routine_name and
- self.stack_max_size == other.stack_max_size and
- self.routine_address == other.routine_address)
-
-
-class Symbol(object):
- """Symbol information.
-
- Attributes:
- address: Symbol address.
- symtype: Symbol type, 'O' (data, object) or 'F' (function).
- size: Symbol size.
- name: Symbol name.
- """
-
- def __init__(self, address, symtype, size, name):
- """Constructor.
-
- Args:
- address: Symbol address.
- symtype: Symbol type.
- size: Symbol size.
- name: Symbol name.
- """
- assert symtype in ['O', 'F']
- self.address = address
- self.symtype = symtype
- self.size = size
- self.name = name
-
- def __eq__(self, other):
- """Symbol equality.
-
- Args:
- other: The compared object.
-
- Returns:
- True if equal, False if not.
- """
- if not isinstance(other, Symbol):
- return False
-
- return (self.address == other.address and
- self.symtype == other.symtype and
- self.size == other.size and
- self.name == other.name)
-
-
-class Callsite(object):
- """Function callsite.
-
- Attributes:
- address: Address of callsite location. None if it is unknown.
- target: Callee address. None if it is unknown.
- is_tail: A bool indicates that it is a tailing call.
- callee: Resolved callee function. None if it hasn't been resolved.
- """
-
- def __init__(self, address, target, is_tail, callee=None):
- """Constructor.
-
- Args:
- address: Address of callsite location. None if it is unknown.
- target: Callee address. None if it is unknown.
- is_tail: A bool indicates that it is a tailing call. (function jump to
- another function without restoring the stack frame)
- callee: Resolved callee function.
- """
- # It makes no sense that both address and target are unknown.
- assert not (address is None and target is None)
- self.address = address
- self.target = target
- self.is_tail = is_tail
- self.callee = callee
-
- def __eq__(self, other):
- """Callsite equality.
-
- Args:
- other: The compared object.
-
- Returns:
- True if equal, False if not.
- """
- if not isinstance(other, Callsite):
- return False
-
- if not (self.address == other.address and
- self.target == other.target and
- self.is_tail == other.is_tail):
- return False
-
- if self.callee is None:
- return other.callee is None
- elif other.callee is None:
- return False
-
- # Assume the addresses of functions are unique.
- return self.callee.address == other.callee.address
-
-
-class Function(object):
- """Function.
-
- Attributes:
- address: Address of function.
- name: Name of function from its symbol.
- stack_frame: Size of stack frame.
- callsites: Callsite list.
- stack_max_usage: Max stack usage. None if it hasn't been analyzed.
- stack_max_path: Max stack usage path. None if it hasn't been analyzed.
- """
-
- def __init__(self, address, name, stack_frame, callsites):
- """Constructor.
-
- Args:
- address: Address of function.
- name: Name of function from its symbol.
- stack_frame: Size of stack frame.
- callsites: Callsite list.
- """
- self.address = address
- self.name = name
- self.stack_frame = stack_frame
- self.callsites = callsites
- self.stack_max_usage = None
- self.stack_max_path = None
-
- def __eq__(self, other):
- """Function equality.
-
- Args:
- other: The compared object.
-
- Returns:
- True if equal, False if not.
- """
- if not isinstance(other, Function):
- return False
-
- if not (self.address == other.address and
- self.name == other.name and
- self.stack_frame == other.stack_frame and
- self.callsites == other.callsites and
- self.stack_max_usage == other.stack_max_usage):
- return False
-
- if self.stack_max_path is None:
- return other.stack_max_path is None
- elif other.stack_max_path is None:
- return False
-
- if len(self.stack_max_path) != len(other.stack_max_path):
- return False
-
- for self_func, other_func in zip(self.stack_max_path, other.stack_max_path):
- # Assume the addresses of functions are unique.
- if self_func.address != other_func.address:
- return False
-
- return True
-
- def __hash__(self):
- return id(self)
-
-class AndesAnalyzer(object):
- """Disassembly analyzer for Andes architecture.
-
- Public Methods:
- AnalyzeFunction: Analyze stack frame and callsites of the function.
- """
-
- GENERAL_PURPOSE_REGISTER_SIZE = 4
-
- # Possible condition code suffixes.
- CONDITION_CODES = [ 'eq', 'eqz', 'gez', 'gtz', 'lez', 'ltz', 'ne', 'nez',
- 'eqc', 'nec', 'nezs', 'nes', 'eqs']
- CONDITION_CODES_RE = '({})'.format('|'.join(CONDITION_CODES))
-
- IMM_ADDRESS_RE = r'([0-9A-Fa-f]+)\s+<([^>]+)>'
- # Branch instructions.
- JUMP_OPCODE_RE = re.compile(r'^(b{0}|j|jr|jr.|jrnez)(\d?|\d\d)$' \
- .format(CONDITION_CODES_RE))
- # Call instructions.
- CALL_OPCODE_RE = re.compile \
- (r'^(jal|jral|jral.|jralnez|beqzal|bltzal|bgezal)(\d)?$')
- CALL_OPERAND_RE = re.compile(r'^{}$'.format(IMM_ADDRESS_RE))
- # Ignore lp register because it's for return.
- INDIRECT_CALL_OPERAND_RE = re.compile \
- (r'^\$r\d{1,}$|\$fp$|\$gp$|\$ta$|\$sp$|\$pc$')
- # TODO: Handle other kinds of store instructions.
- PUSH_OPCODE_RE = re.compile(r'^push(\d{1,})$')
- PUSH_OPERAND_RE = re.compile(r'^\$r\d{1,}, \#\d{1,} \! \{([^\]]+)\}')
- SMW_OPCODE_RE = re.compile(r'^smw(\.\w\w|\.\w\w\w)$')
- SMW_OPERAND_RE = re.compile(r'^(\$r\d{1,}|\$\wp), \[\$\wp\], '
- r'(\$r\d{1,}|\$\wp), \#\d\w\d \! \{([^\]]+)\}')
- OPERANDGROUP_RE = re.compile(r'^\$r\d{1,}\~\$r\d{1,}')
-
- LWI_OPCODE_RE = re.compile(r'^lwi(\.\w\w)$')
- LWI_PC_OPERAND_RE = re.compile(r'^\$pc, \[([^\]]+)\]')
- # Example: "34280: 3f c8 0f ec addi.gp $fp, #0xfec"
- # Assume there is always a "\t" after the hex data.
- DISASM_REGEX_RE = re.compile(r'^(?P<address>[0-9A-Fa-f]+):\s+'
- r'(?P<words>[0-9A-Fa-f ]+)'
- r'\t\s*(?P<opcode>\S+)(\s+(?P<operand>[^;]*))?')
-
- def ParseInstruction(self, line, function_end):
- """Parse the line of instruction.
-
- Args:
- line: Text of disassembly.
- function_end: End address of the current function. None if unknown.
-
- Returns:
- (address, words, opcode, operand_text): The instruction address, words,
- opcode, and the text of operands.
- None if it isn't an instruction line.
- """
- result = self.DISASM_REGEX_RE.match(line)
- if result is None:
- return None
-
- address = int(result.group('address'), 16)
- # Check if it's out of bound.
- if function_end is not None and address >= function_end:
- return None
-
- opcode = result.group('opcode').strip()
- operand_text = result.group('operand')
- words = result.group('words')
- if operand_text is None:
- operand_text = ''
- else:
- operand_text = operand_text.strip()
-
- return (address, words, opcode, operand_text)
-
- def AnalyzeFunction(self, function_symbol, instructions):
-
- stack_frame = 0
- callsites = []
- for address, words, opcode, operand_text in instructions:
- is_jump_opcode = self.JUMP_OPCODE_RE.match(opcode) is not None
- is_call_opcode = self.CALL_OPCODE_RE.match(opcode) is not None
-
- if is_jump_opcode or is_call_opcode:
- is_tail = is_jump_opcode
-
- result = self.CALL_OPERAND_RE.match(operand_text)
-
- if result is None:
- if (self.INDIRECT_CALL_OPERAND_RE.match(operand_text) is not None):
- # Found an indirect call.
- callsites.append(Callsite(address, None, is_tail))
-
- else:
- target_address = int(result.group(1), 16)
- # Filter out the in-function target (branches and in-function calls,
- # which are actually branches).
- if not (function_symbol.size > 0 and
- function_symbol.address < target_address <
- (function_symbol.address + function_symbol.size)):
- # Maybe it is a callsite.
- callsites.append(Callsite(address, target_address, is_tail))
-
- elif self.LWI_OPCODE_RE.match(opcode) is not None:
- result = self.LWI_PC_OPERAND_RE.match(operand_text)
- if result is not None:
- # Ignore "lwi $pc, [$sp], xx" because it's usually a return.
- if result.group(1) != '$sp':
- # Found an indirect call.
- callsites.append(Callsite(address, None, True))
-
- elif self.PUSH_OPCODE_RE.match(opcode) is not None:
- # Example: fc 20 push25 $r8, #0 ! {$r6~$r8, $fp, $gp, $lp}
- if self.PUSH_OPERAND_RE.match(operand_text) is not None:
- # capture fc 20
- imm5u = int(words.split(' ')[1], 16)
- # sp = sp - (imm5u << 3)
- imm8u = (imm5u<<3) & 0xff
- stack_frame += imm8u
-
- result = self.PUSH_OPERAND_RE.match(operand_text)
- operandgroup_text = result.group(1)
- # capture $rx~$ry
- if self.OPERANDGROUP_RE.match(operandgroup_text) is not None:
- # capture number & transfer string to integer
- oprandgrouphead = operandgroup_text.split(',')[0]
- rx=int(''.join(filter(str.isdigit, oprandgrouphead.split('~')[0])))
- ry=int(''.join(filter(str.isdigit, oprandgrouphead.split('~')[1])))
-
- stack_frame += ((len(operandgroup_text.split(','))+ry-rx) *
- self.GENERAL_PURPOSE_REGISTER_SIZE)
- else:
- stack_frame += (len(operandgroup_text.split(',')) *
- self.GENERAL_PURPOSE_REGISTER_SIZE)
-
- elif self.SMW_OPCODE_RE.match(opcode) is not None:
- # Example: smw.adm $r6, [$sp], $r10, #0x2 ! {$r6~$r10, $lp}
- if self.SMW_OPERAND_RE.match(operand_text) is not None:
- result = self.SMW_OPERAND_RE.match(operand_text)
- operandgroup_text = result.group(3)
- # capture $rx~$ry
- if self.OPERANDGROUP_RE.match(operandgroup_text) is not None:
- # capture number & transfer string to integer
- oprandgrouphead = operandgroup_text.split(',')[0]
- rx=int(''.join(filter(str.isdigit, oprandgrouphead.split('~')[0])))
- ry=int(''.join(filter(str.isdigit, oprandgrouphead.split('~')[1])))
-
- stack_frame += ((len(operandgroup_text.split(','))+ry-rx) *
- self.GENERAL_PURPOSE_REGISTER_SIZE)
- else:
- stack_frame += (len(operandgroup_text.split(',')) *
- self.GENERAL_PURPOSE_REGISTER_SIZE)
-
- return (stack_frame, callsites)
-
-class ArmAnalyzer(object):
- """Disassembly analyzer for ARM architecture.
-
- Public Methods:
- AnalyzeFunction: Analyze stack frame and callsites of the function.
- """
-
- GENERAL_PURPOSE_REGISTER_SIZE = 4
-
- # Possible condition code suffixes.
- CONDITION_CODES = ['', 'eq', 'ne', 'cs', 'hs', 'cc', 'lo', 'mi', 'pl', 'vs',
- 'vc', 'hi', 'ls', 'ge', 'lt', 'gt', 'le']
- CONDITION_CODES_RE = '({})'.format('|'.join(CONDITION_CODES))
- # Assume there is no function name containing ">".
- IMM_ADDRESS_RE = r'([0-9A-Fa-f]+)\s+<([^>]+)>'
-
- # Fuzzy regular expressions for instruction and operand parsing.
- # Branch instructions.
- JUMP_OPCODE_RE = re.compile(
- r'^(b{0}|bx{0})(\.\w)?$'.format(CONDITION_CODES_RE))
- # Call instructions.
- CALL_OPCODE_RE = re.compile(
- r'^(bl{0}|blx{0})(\.\w)?$'.format(CONDITION_CODES_RE))
- CALL_OPERAND_RE = re.compile(r'^{}$'.format(IMM_ADDRESS_RE))
- CBZ_CBNZ_OPCODE_RE = re.compile(r'^(cbz|cbnz)(\.\w)?$')
- # Example: "r0, 1009bcbe <host_cmd_motion_sense+0x1d2>"
- CBZ_CBNZ_OPERAND_RE = re.compile(r'^[^,]+,\s+{}$'.format(IMM_ADDRESS_RE))
- # Ignore lr register because it's for return.
- INDIRECT_CALL_OPERAND_RE = re.compile(r'^r\d+|sb|sl|fp|ip|sp|pc$')
- # TODO(cheyuw): Handle conditional versions of following
- # instructions.
- # TODO(cheyuw): Handle other kinds of pc modifying instructions (e.g. mov pc).
- LDR_OPCODE_RE = re.compile(r'^ldr(\.\w)?$')
- # Example: "pc, [sp], #4"
- LDR_PC_OPERAND_RE = re.compile(r'^pc, \[([^\]]+)\]')
- # TODO(cheyuw): Handle other kinds of stm instructions.
- PUSH_OPCODE_RE = re.compile(r'^push$')
- STM_OPCODE_RE = re.compile(r'^stmdb$')
- # Stack subtraction instructions.
- SUB_OPCODE_RE = re.compile(r'^sub(s|w)?(\.\w)?$')
- SUB_OPERAND_RE = re.compile(r'^sp[^#]+#(\d+)')
- # Example: "44d94: f893 0068 ldrb.w r0, [r3, #104] ; 0x68"
- # Assume there is always a "\t" after the hex data.
- DISASM_REGEX_RE = re.compile(r'^(?P<address>[0-9A-Fa-f]+):\s+[0-9A-Fa-f ]+'
- r'\t\s*(?P<opcode>\S+)(\s+(?P<operand>[^;]*))?')
-
- def ParseInstruction(self, line, function_end):
- """Parse the line of instruction.
-
- Args:
- line: Text of disassembly.
- function_end: End address of the current function. None if unknown.
-
- Returns:
- (address, opcode, operand_text): The instruction address, opcode,
- and the text of operands. None if it
- isn't an instruction line.
- """
- result = self.DISASM_REGEX_RE.match(line)
- if result is None:
- return None
-
- address = int(result.group('address'), 16)
- # Check if it's out of bound.
- if function_end is not None and address >= function_end:
- return None
-
- opcode = result.group('opcode').strip()
- operand_text = result.group('operand')
- if operand_text is None:
- operand_text = ''
- else:
- operand_text = operand_text.strip()
-
- return (address, opcode, operand_text)
-
- def AnalyzeFunction(self, function_symbol, instructions):
- """Analyze function, resolve the size of stack frame and callsites.
-
- Args:
- function_symbol: Function symbol.
- instructions: Instruction list.
-
- Returns:
- (stack_frame, callsites): Size of stack frame, callsite list.
- """
- stack_frame = 0
- callsites = []
- for address, opcode, operand_text in instructions:
- is_jump_opcode = self.JUMP_OPCODE_RE.match(opcode) is not None
- is_call_opcode = self.CALL_OPCODE_RE.match(opcode) is not None
- is_cbz_cbnz_opcode = self.CBZ_CBNZ_OPCODE_RE.match(opcode) is not None
- if is_jump_opcode or is_call_opcode or is_cbz_cbnz_opcode:
- is_tail = is_jump_opcode or is_cbz_cbnz_opcode
-
- if is_cbz_cbnz_opcode:
- result = self.CBZ_CBNZ_OPERAND_RE.match(operand_text)
- else:
- result = self.CALL_OPERAND_RE.match(operand_text)
-
- if result is None:
- # Failed to match immediate address, maybe it is an indirect call.
- # CBZ and CBNZ can't be indirect calls.
- if (not is_cbz_cbnz_opcode and
- self.INDIRECT_CALL_OPERAND_RE.match(operand_text) is not None):
- # Found an indirect call.
- callsites.append(Callsite(address, None, is_tail))
-
- else:
- target_address = int(result.group(1), 16)
- # Filter out the in-function target (branches and in-function calls,
- # which are actually branches).
- if not (function_symbol.size > 0 and
- function_symbol.address < target_address <
- (function_symbol.address + function_symbol.size)):
- # Maybe it is a callsite.
- callsites.append(Callsite(address, target_address, is_tail))
-
- elif self.LDR_OPCODE_RE.match(opcode) is not None:
- result = self.LDR_PC_OPERAND_RE.match(operand_text)
- if result is not None:
- # Ignore "ldr pc, [sp], xx" because it's usually a return.
- if result.group(1) != 'sp':
- # Found an indirect call.
- callsites.append(Callsite(address, None, True))
-
- elif self.PUSH_OPCODE_RE.match(opcode) is not None:
- # Example: "{r4, r5, r6, r7, lr}"
- stack_frame += (len(operand_text.split(',')) *
- self.GENERAL_PURPOSE_REGISTER_SIZE)
- elif self.SUB_OPCODE_RE.match(opcode) is not None:
- result = self.SUB_OPERAND_RE.match(operand_text)
- if result is not None:
- stack_frame += int(result.group(1))
- else:
- # Unhandled stack register subtraction.
- assert not operand_text.startswith('sp')
-
- elif self.STM_OPCODE_RE.match(opcode) is not None:
- if operand_text.startswith('sp!'):
- # Subtract and writeback to stack register.
- # Example: "sp!, {r4, r5, r6, r7, r8, r9, lr}"
- # Get the text of pushed register list.
- unused_sp, unused_sep, parameter_text = operand_text.partition(',')
- stack_frame += (len(parameter_text.split(',')) *
- self.GENERAL_PURPOSE_REGISTER_SIZE)
-
- return (stack_frame, callsites)
-
-class RiscvAnalyzer(object):
- """Disassembly analyzer for RISC-V architecture.
-
- Public Methods:
- AnalyzeFunction: Analyze stack frame and callsites of the function.
- """
-
- # Possible condition code suffixes.
- CONDITION_CODES = [ 'eqz', 'nez', 'lez', 'gez', 'ltz', 'gtz', 'gt', 'le',
- 'gtu', 'leu', 'eq', 'ne', 'ge', 'lt', 'ltu', 'geu']
- CONDITION_CODES_RE = '({})'.format('|'.join(CONDITION_CODES))
- # Branch instructions.
- JUMP_OPCODE_RE = re.compile(r'^(b{0}|j|jr)$'.format(CONDITION_CODES_RE))
- # Call instructions.
- CALL_OPCODE_RE = re.compile(r'^(jal|jalr)$')
- # Example: "j 8009b318 <set_state_prl_hr>" or
- # "jal ra,800a4394 <power_get_signals>" or
- # "bltu t0,t1,80080300 <data_loop>"
- JUMP_ADDRESS_RE = r'((\w(\w|\d\d),){0,2})([0-9A-Fa-f]+)\s+<([^>]+)>'
- CALL_OPERAND_RE = re.compile(r'^{}$'.format(JUMP_ADDRESS_RE))
- # Capture address, Example: 800a4394
- CAPTURE_ADDRESS = re.compile(r'[0-9A-Fa-f]{8}')
- # Indirect jump, Example: jalr a5
- INDIRECT_CALL_OPERAND_RE = re.compile(r'^t\d+|s\d+|a\d+$')
- # Example: addi
- ADDI_OPCODE_RE = re.compile(r'^addi$')
- # Allocate stack instructions.
- ADDI_OPERAND_RE = re.compile(r'^(sp,sp,-\d+)$')
- # Example: "800804b6: 1101 addi sp,sp,-32"
- DISASM_REGEX_RE = re.compile(r'^(?P<address>[0-9A-Fa-f]+):\s+[0-9A-Fa-f ]+'
- r'\t\s*(?P<opcode>\S+)(\s+(?P<operand>[^;]*))?')
-
- def ParseInstruction(self, line, function_end):
- """Parse the line of instruction.
-
- Args:
- line: Text of disassembly.
- function_end: End address of the current function. None if unknown.
-
- Returns:
- (address, opcode, operand_text): The instruction address, opcode,
- and the text of operands. None if it
- isn't an instruction line.
- """
- result = self.DISASM_REGEX_RE.match(line)
- if result is None:
- return None
-
- address = int(result.group('address'), 16)
- # Check if it's out of bound.
- if function_end is not None and address >= function_end:
- return None
-
- opcode = result.group('opcode').strip()
- operand_text = result.group('operand')
- if operand_text is None:
- operand_text = ''
- else:
- operand_text = operand_text.strip()
-
- return (address, opcode, operand_text)
-
- def AnalyzeFunction(self, function_symbol, instructions):
-
- stack_frame = 0
- callsites = []
- for address, opcode, operand_text in instructions:
- is_jump_opcode = self.JUMP_OPCODE_RE.match(opcode) is not None
- is_call_opcode = self.CALL_OPCODE_RE.match(opcode) is not None
-
- if is_jump_opcode or is_call_opcode:
- is_tail = is_jump_opcode
-
- result = self.CALL_OPERAND_RE.match(operand_text)
- if result is None:
- if (self.INDIRECT_CALL_OPERAND_RE.match(operand_text) is not None):
- # Found an indirect call.
- callsites.append(Callsite(address, None, is_tail))
-
- else:
- # Capture address form operand_text and then convert to string
- address_str = "".join(self.CAPTURE_ADDRESS.findall(operand_text))
- # String to integer
- target_address = int(address_str, 16)
- # Filter out the in-function target (branches and in-function calls,
- # which are actually branches).
- if not (function_symbol.size > 0 and
- function_symbol.address < target_address <
- (function_symbol.address + function_symbol.size)):
- # Maybe it is a callsite.
- callsites.append(Callsite(address, target_address, is_tail))
-
- elif self.ADDI_OPCODE_RE.match(opcode) is not None:
- # Example: sp,sp,-32
- if self.ADDI_OPERAND_RE.match(operand_text) is not None:
- stack_frame += abs(int(operand_text.split(",")[2]))
-
- return (stack_frame, callsites)
-
-class StackAnalyzer(object):
- """Class to analyze stack usage.
-
- Public Methods:
- Analyze: Run the stack analysis.
- """
-
- C_FUNCTION_NAME = r'_A-Za-z0-9'
-
- # Assume there is no ":" in the path.
- # Example: "driver/accel_kionix.c:321 (discriminator 3)"
- ADDRTOLINE_RE = re.compile(
- r'^(?P<path>[^:]+):(?P<linenum>\d+)(\s+\(discriminator\s+\d+\))?$')
- # To eliminate the suffix appended by compilers, try to extract the
- # C function name from the prefix of symbol name.
- # Example: "SHA256_transform.constprop.28"
- FUNCTION_PREFIX_NAME_RE = re.compile(
- r'^(?P<name>[{0}]+)([^{0}].*)?$'.format(C_FUNCTION_NAME))
-
- # Errors of annotation resolving.
- ANNOTATION_ERROR_INVALID = 'invalid signature'
- ANNOTATION_ERROR_NOTFOUND = 'function is not found'
- ANNOTATION_ERROR_AMBIGUOUS = 'signature is ambiguous'
-
- def __init__(self, options, symbols, rodata, tasklist, annotation):
- """Constructor.
-
- Args:
- options: Namespace from argparse.parse_args().
- symbols: Symbol list.
- rodata: Content of .rodata section (offset, data)
- tasklist: Task list.
- annotation: Annotation config.
- """
- self.options = options
- self.symbols = symbols
- self.rodata_offset = rodata[0]
- self.rodata = rodata[1]
- self.tasklist = tasklist
- self.annotation = annotation
- self.address_to_line_cache = {}
-
- def AddressToLine(self, address, resolve_inline=False):
- """Convert address to line.
-
- Args:
- address: Target address.
- resolve_inline: Output the stack of inlining.
-
- Returns:
- lines: List of the corresponding lines.
-
- Raises:
- StackAnalyzerError: If addr2line is failed.
- """
- cache_key = (address, resolve_inline)
- if cache_key in self.address_to_line_cache:
- return self.address_to_line_cache[cache_key]
-
- try:
- args = [self.options.addr2line,
- '-f',
- '-e',
- self.options.elf_path,
- '{:x}'.format(address)]
- if resolve_inline:
- args.append('-i')
-
- line_text = subprocess.check_output(args, encoding='utf-8')
- except subprocess.CalledProcessError:
- raise StackAnalyzerError('addr2line failed to resolve lines.')
- except OSError:
- raise StackAnalyzerError('Failed to run addr2line.')
-
- lines = [line.strip() for line in line_text.splitlines()]
- # Assume the output has at least one pair like "function\nlocation\n", and
- # they always show up in pairs.
- # Example: "handle_request\n
- # common/usb_pd_protocol.c:1191\n"
- assert len(lines) >= 2 and len(lines) % 2 == 0
-
- line_infos = []
- for index in range(0, len(lines), 2):
- (function_name, line_text) = lines[index:index + 2]
- if line_text in ['??:0', ':?']:
- line_infos.append(None)
- else:
- result = self.ADDRTOLINE_RE.match(line_text)
- # Assume the output is always well-formed.
- assert result is not None
- line_infos.append((function_name.strip(),
- os.path.realpath(result.group('path').strip()),
- int(result.group('linenum'))))
-
- self.address_to_line_cache[cache_key] = line_infos
- return line_infos
-
- def AnalyzeDisassembly(self, disasm_text):
- """Parse the disassembly text, analyze, and build a map of all functions.
-
- Args:
- disasm_text: Disassembly text.
-
- Returns:
- function_map: Dict of functions.
- """
- disasm_lines = [line.strip() for line in disasm_text.splitlines()]
-
- if 'nds' in disasm_lines[1]:
- analyzer = AndesAnalyzer()
- elif 'arm' in disasm_lines[1]:
- analyzer = ArmAnalyzer()
- elif 'riscv' in disasm_lines[1]:
- analyzer = RiscvAnalyzer()
- else:
- raise StackAnalyzerError('Unsupported architecture.')
-
- # Example: "08028c8c <motion_lid_calc>:"
- function_signature_regex = re.compile(
- r'^(?P<address>[0-9A-Fa-f]+)\s+<(?P<name>[^>]+)>:$')
-
- def DetectFunctionHead(line):
- """Check if the line is a function head.
-
- Args:
- line: Text of disassembly.
-
- Returns:
- symbol: Function symbol. None if it isn't a function head.
- """
- result = function_signature_regex.match(line)
- if result is None:
- return None
-
- address = int(result.group('address'), 16)
- symbol = symbol_map.get(address)
-
- # Check if the function exists and matches.
- if symbol is None or symbol.symtype != 'F':
- return None
-
- return symbol
-
- # Build symbol map, indexed by symbol address.
- symbol_map = {}
- for symbol in self.symbols:
- # If there are multiple symbols with same address, keeping any of them is
- # good enough.
- symbol_map[symbol.address] = symbol
-
- # Parse the disassembly text. We update the variable "line" to next line
- # when needed. There are two steps of parser:
- #
- # Step 1: Searching for the function head. Once reach the function head,
- # move to the next line, which is the first line of function body.
- #
- # Step 2: Parsing each instruction line of function body. Once reach a
- # non-instruction line, stop parsing and analyze the parsed instructions.
- #
- # Finally turn back to the step 1 without updating the line, because the
- # current non-instruction line can be another function head.
- function_map = {}
- # The following three variables are the states of the parsing processing.
- # They will be initialized properly during the state changes.
- function_symbol = None
- function_end = None
- instructions = []
-
- # Remove heading and tailing spaces for each line.
- line_index = 0
- while line_index < len(disasm_lines):
- # Get the current line.
- line = disasm_lines[line_index]
-
- if function_symbol is None:
- # Step 1: Search for the function head.
-
- function_symbol = DetectFunctionHead(line)
- if function_symbol is not None:
- # Assume there is no empty function. If the function head is followed
- # by EOF, it is an empty function.
- assert line_index + 1 < len(disasm_lines)
-
- # Found the function head, initialize and turn to the step 2.
- instructions = []
- # If symbol size exists, use it as a hint of function size.
- if function_symbol.size > 0:
- function_end = function_symbol.address + function_symbol.size
- else:
- function_end = None
-
- else:
- # Step 2: Parse the function body.
-
- instruction = analyzer.ParseInstruction(line, function_end)
- if instruction is not None:
- instructions.append(instruction)
-
- if instruction is None or line_index + 1 == len(disasm_lines):
- # Either the invalid instruction or EOF indicates the end of the
- # function, finalize the function analysis.
-
- # Assume there is no empty function.
- assert len(instructions) > 0
-
- (stack_frame, callsites) = analyzer.AnalyzeFunction(function_symbol,
- instructions)
- # Assume the function addresses are unique in the disassembly.
- assert function_symbol.address not in function_map
- function_map[function_symbol.address] = Function(
- function_symbol.address,
- function_symbol.name,
- stack_frame,
- callsites)
-
- # Initialize and turn back to the step 1.
- function_symbol = None
-
- # If the current line isn't an instruction, it can be another function
- # head, skip moving to the next line.
- if instruction is None:
- continue
-
- # Move to the next line.
- line_index += 1
-
- # Resolve callees of functions.
- for function in function_map.values():
- for callsite in function.callsites:
- if callsite.target is not None:
- # Remain the callee as None if we can't resolve it.
- callsite.callee = function_map.get(callsite.target)
-
- return function_map
-
- def MapAnnotation(self, function_map, signature_set):
- """Map annotation signatures to functions.
-
- Args:
- function_map: Function map.
- signature_set: Set of annotation signatures.
-
- Returns:
- Map of signatures to functions, map of signatures which can't be resolved.
- """
- # Build the symbol map indexed by symbol name. If there are multiple symbols
- # with the same name, add them into a set. (e.g. symbols of static function
- # with the same name)
- symbol_map = collections.defaultdict(set)
- for symbol in self.symbols:
- if symbol.symtype == 'F':
- # Function symbol.
- result = self.FUNCTION_PREFIX_NAME_RE.match(symbol.name)
- if result is not None:
- function = function_map.get(symbol.address)
- # Ignore the symbol not in disassembly.
- if function is not None:
- # If there are multiple symbol with the same name and point to the
- # same function, the set will deduplicate them.
- symbol_map[result.group('name').strip()].add(function)
-
- # Build the signature map indexed by annotation signature.
- signature_map = {}
- sig_error_map = {}
- symbol_path_map = {}
- for sig in signature_set:
- (name, path, _) = sig
-
- functions = symbol_map.get(name)
- if functions is None:
- sig_error_map[sig] = self.ANNOTATION_ERROR_NOTFOUND
- continue
-
- if name not in symbol_path_map:
- # Lazy symbol path resolving. Since the addr2line isn't fast, only
- # resolve needed symbol paths.
- group_map = collections.defaultdict(list)
- for function in functions:
- line_info = self.AddressToLine(function.address)[0]
- if line_info is None:
- continue
-
- (_, symbol_path, _) = line_info
-
- # Group the functions with the same symbol signature (symbol name +
- # symbol path). Assume they are the same copies and do the same
- # annotation operations of them because we don't know which copy is
- # indicated by the users.
- group_map[symbol_path].append(function)
-
- symbol_path_map[name] = group_map
-
- # Symbol matching.
- function_group = None
- group_map = symbol_path_map[name]
- if len(group_map) > 0:
- if path is None:
- if len(group_map) > 1:
- # There is ambiguity but the path isn't specified.
- sig_error_map[sig] = self.ANNOTATION_ERROR_AMBIGUOUS
- continue
-
- # No path signature but all symbol signatures of functions are same.
- # Assume they are the same functions, so there is no ambiguity.
- (function_group,) = group_map.values()
- else:
- function_group = group_map.get(path)
-
- if function_group is None:
- sig_error_map[sig] = self.ANNOTATION_ERROR_NOTFOUND
- continue
-
- # The function_group is a list of all the same functions (according to
- # our assumption) which should be annotated together.
- signature_map[sig] = function_group
-
- return (signature_map, sig_error_map)
-
- def LoadAnnotation(self):
- """Load annotation rules.
-
- Returns:
- Map of add rules, set of remove rules, set of text signatures which can't
- be parsed.
- """
- # Assume there is no ":" in the path.
- # Example: "get_range.lto.2501[driver/accel_kionix.c:327]"
- annotation_signature_regex = re.compile(
- r'^(?P<name>[^\[]+)(\[(?P<path>[^:]+)(:(?P<linenum>\d+))?\])?$')
-
- def NormalizeSignature(signature_text):
- """Parse and normalize the annotation signature.
-
- Args:
- signature_text: Text of the annotation signature.
-
- Returns:
- (function name, path, line number) of the signature. The path and line
- number can be None if not exist. None if failed to parse.
- """
- result = annotation_signature_regex.match(signature_text.strip())
- if result is None:
- return None
-
- name_result = self.FUNCTION_PREFIX_NAME_RE.match(
- result.group('name').strip())
- if name_result is None:
- return None
-
- path = result.group('path')
- if path is not None:
- path = os.path.realpath(path.strip())
-
- linenum = result.group('linenum')
- if linenum is not None:
- linenum = int(linenum.strip())
-
- return (name_result.group('name').strip(), path, linenum)
-
- def ExpandArray(dic):
- """Parse and expand a symbol array
-
- Args:
- dic: Dictionary for the array annotation
-
- Returns:
- array of (symbol name, None, None).
- """
- # TODO(drinkcat): This function is quite inefficient, as it goes through
- # the symbol table multiple times.
-
- begin_name = dic['name']
- end_name = dic['name'] + "_end"
- offset = dic['offset'] if 'offset' in dic else 0
- stride = dic['stride']
-
- begin_address = None
- end_address = None
-
- for symbol in self.symbols:
- if (symbol.name == begin_name):
- begin_address = symbol.address
- if (symbol.name == end_name):
- end_address = symbol.address
-
- if (not begin_address or not end_address):
- return None
-
- output = []
- # TODO(drinkcat): This is inefficient as we go from address to symbol
- # object then to symbol name, and later on we'll go back from symbol name
- # to symbol object.
- for addr in range(begin_address+offset, end_address, stride):
- # TODO(drinkcat): Not all architectures need to drop the first bit.
- val = self.rodata[(addr-self.rodata_offset) // 4] & 0xfffffffe
- name = None
- for symbol in self.symbols:
- if (symbol.address == val):
- result = self.FUNCTION_PREFIX_NAME_RE.match(symbol.name)
- name = result.group('name')
- break
-
- if not name:
- raise StackAnalyzerError('Cannot find function for address %s.',
- hex(val))
-
- output.append((name, None, None))
-
- return output
-
- add_rules = collections.defaultdict(set)
- remove_rules = list()
- invalid_sigtxts = set()
-
- if 'add' in self.annotation and self.annotation['add'] is not None:
- for src_sigtxt, dst_sigtxts in self.annotation['add'].items():
- src_sig = NormalizeSignature(src_sigtxt)
- if src_sig is None:
- invalid_sigtxts.add(src_sigtxt)
- continue
-
- for dst_sigtxt in dst_sigtxts:
- if isinstance(dst_sigtxt, dict):
- dst_sig = ExpandArray(dst_sigtxt)
- if dst_sig is None:
- invalid_sigtxts.add(str(dst_sigtxt))
- else:
- add_rules[src_sig].update(dst_sig)
- else:
- dst_sig = NormalizeSignature(dst_sigtxt)
- if dst_sig is None:
- invalid_sigtxts.add(dst_sigtxt)
- else:
- add_rules[src_sig].add(dst_sig)
-
- if 'remove' in self.annotation and self.annotation['remove'] is not None:
- for sigtxt_path in self.annotation['remove']:
- if isinstance(sigtxt_path, str):
- # The path has only one vertex.
- sigtxt_path = [sigtxt_path]
-
- if len(sigtxt_path) == 0:
- continue
-
- # Generate multiple remove paths from all the combinations of the
- # signatures of each vertex.
- sig_paths = [[]]
- broken_flag = False
- for sigtxt_node in sigtxt_path:
- if isinstance(sigtxt_node, str):
- # The vertex has only one signature.
- sigtxt_set = {sigtxt_node}
- elif isinstance(sigtxt_node, list):
- # The vertex has multiple signatures.
- sigtxt_set = set(sigtxt_node)
- else:
- # Assume the format of annotation is verified. There should be no
- # invalid case.
- assert False
-
- sig_set = set()
- for sigtxt in sigtxt_set:
- sig = NormalizeSignature(sigtxt)
- if sig is None:
- invalid_sigtxts.add(sigtxt)
- broken_flag = True
- elif not broken_flag:
- sig_set.add(sig)
-
- if broken_flag:
- continue
-
- # Append each signature of the current node to the all previous
- # remove paths.
- sig_paths = [path + [sig] for path in sig_paths for sig in sig_set]
-
- if not broken_flag:
- # All signatures are normalized. The remove path has no error.
- remove_rules.extend(sig_paths)
-
- return (add_rules, remove_rules, invalid_sigtxts)
-
- def ResolveAnnotation(self, function_map):
- """Resolve annotation.
-
- Args:
- function_map: Function map.
-
- Returns:
- Set of added call edges, list of remove paths, set of eliminated
- callsite addresses, set of annotation signatures which can't be resolved.
- """
- def StringifySignature(signature):
- """Stringify the tupled signature.
-
- Args:
- signature: Tupled signature.
-
- Returns:
- Signature string.
- """
- (name, path, linenum) = signature
- bracket_text = ''
- if path is not None:
- path = os.path.relpath(path)
- if linenum is None:
- bracket_text = '[{}]'.format(path)
- else:
- bracket_text = '[{}:{}]'.format(path, linenum)
-
- return name + bracket_text
-
- (add_rules, remove_rules, invalid_sigtxts) = self.LoadAnnotation()
-
- signature_set = set()
- for src_sig, dst_sigs in add_rules.items():
- signature_set.add(src_sig)
- signature_set.update(dst_sigs)
-
- for remove_sigs in remove_rules:
- signature_set.update(remove_sigs)
-
- # Map signatures to functions.
- (signature_map, sig_error_map) = self.MapAnnotation(function_map,
- signature_set)
-
- # Build the indirect callsite map indexed by callsite signature.
- indirect_map = collections.defaultdict(set)
- for function in function_map.values():
- for callsite in function.callsites:
- if callsite.target is not None:
- continue
-
- # Found an indirect callsite.
- line_info = self.AddressToLine(callsite.address)[0]
- if line_info is None:
- continue
-
- (name, path, linenum) = line_info
- result = self.FUNCTION_PREFIX_NAME_RE.match(name)
- if result is None:
- continue
-
- indirect_map[(result.group('name').strip(), path, linenum)].add(
- (function, callsite.address))
-
- # Generate the annotation sets.
- add_set = set()
- remove_list = list()
- eliminated_addrs = set()
-
- for src_sig, dst_sigs in add_rules.items():
- src_funcs = set(signature_map.get(src_sig, []))
- # Try to match the source signature to the indirect callsites. Even if it
- # can't be found in disassembly.
- indirect_calls = indirect_map.get(src_sig)
- if indirect_calls is not None:
- for function, callsite_address in indirect_calls:
- # Add the caller of the indirect callsite to the source functions.
- src_funcs.add(function)
- # Assume each callsite can be represented by a unique address.
- eliminated_addrs.add(callsite_address)
-
- if src_sig in sig_error_map:
- # Assume the error is always the not found error. Since the signature
- # found in indirect callsite map must be a full signature, it can't
- # happen the ambiguous error.
- assert sig_error_map[src_sig] == self.ANNOTATION_ERROR_NOTFOUND
- # Found in inline stack, remove the not found error.
- del sig_error_map[src_sig]
-
- for dst_sig in dst_sigs:
- dst_funcs = signature_map.get(dst_sig)
- if dst_funcs is None:
- continue
-
- # Duplicate the call edge for all the same source and destination
- # functions.
- for src_func in src_funcs:
- for dst_func in dst_funcs:
- add_set.add((src_func, dst_func))
-
- for remove_sigs in remove_rules:
- # Since each signature can be mapped to multiple functions, generate
- # multiple remove paths from all the combinations of these functions.
- remove_paths = [[]]
- skip_flag = False
- for remove_sig in remove_sigs:
- # Transform each signature to the corresponding functions.
- remove_funcs = signature_map.get(remove_sig)
- if remove_funcs is None:
- # There is an unresolved signature in the remove path. Ignore the
- # whole broken remove path.
- skip_flag = True
- break
- else:
- # Append each function of the current signature to the all previous
- # remove paths.
- remove_paths = [p + [f] for p in remove_paths for f in remove_funcs]
-
- if skip_flag:
- # Ignore the broken remove path.
- continue
-
- for remove_path in remove_paths:
- # Deduplicate the remove paths.
- if remove_path not in remove_list:
- remove_list.append(remove_path)
-
- # Format the error messages.
- failed_sigtxts = set()
- for sigtxt in invalid_sigtxts:
- failed_sigtxts.add((sigtxt, self.ANNOTATION_ERROR_INVALID))
-
- for sig, error in sig_error_map.items():
- failed_sigtxts.add((StringifySignature(sig), error))
-
- return (add_set, remove_list, eliminated_addrs, failed_sigtxts)
-
- def PreprocessAnnotation(self, function_map, add_set, remove_list,
- eliminated_addrs):
- """Preprocess the annotation and callgraph.
-
- Add the missing call edges, and delete simple remove paths (the paths have
- one or two vertices) from the function_map.
-
- Eliminate the annotated indirect callsites.
-
- Return the remaining remove list.
-
- Args:
- function_map: Function map.
- add_set: Set of missing call edges.
- remove_list: List of remove paths.
- eliminated_addrs: Set of eliminated callsite addresses.
-
- Returns:
- List of remaining remove paths.
- """
- def CheckEdge(path):
- """Check if all edges of the path are on the callgraph.
-
- Args:
- path: Path.
-
- Returns:
- True or False.
- """
- for index in range(len(path) - 1):
- if (path[index], path[index + 1]) not in edge_set:
- return False
-
- return True
-
- for src_func, dst_func in add_set:
- # TODO(cheyuw): Support tailing call annotation.
- src_func.callsites.append(
- Callsite(None, dst_func.address, False, dst_func))
-
- # Delete simple remove paths.
- remove_simple = set(tuple(p) for p in remove_list if len(p) <= 2)
- edge_set = set()
- for function in function_map.values():
- cleaned_callsites = []
- for callsite in function.callsites:
- if ((callsite.callee,) in remove_simple or
- (function, callsite.callee) in remove_simple):
- continue
-
- if callsite.target is None and callsite.address in eliminated_addrs:
- continue
-
- cleaned_callsites.append(callsite)
- if callsite.callee is not None:
- edge_set.add((function, callsite.callee))
-
- function.callsites = cleaned_callsites
-
- return [p for p in remove_list if len(p) >= 3 and CheckEdge(p)]
-
- def AnalyzeCallGraph(self, function_map, remove_list):
- """Analyze callgraph.
-
- It will update the max stack size and path for each function.
-
- Args:
- function_map: Function map.
- remove_list: List of remove paths.
-
- Returns:
- List of function cycles.
- """
- def Traverse(curr_state):
- """Traverse the callgraph and calculate the max stack usages of functions.
-
- Args:
- curr_state: Current state.
-
- Returns:
- SCC lowest link.
- """
- scc_index = scc_index_counter[0]
- scc_index_counter[0] += 1
- scc_index_map[curr_state] = scc_index
- scc_lowlink = scc_index
- scc_stack.append(curr_state)
- # Push the current state in the stack. We can use a set to maintain this
- # because the stacked states are unique; otherwise we will find a cycle
- # first.
- stacked_states.add(curr_state)
-
- (curr_address, curr_positions) = curr_state
- curr_func = function_map[curr_address]
-
- invalid_flag = False
- new_positions = list(curr_positions)
- for index, position in enumerate(curr_positions):
- remove_path = remove_list[index]
-
- # The position of each remove path in the state is the length of the
- # longest matching path between the prefix of the remove path and the
- # suffix of the current traversing path. We maintain this length when
- # appending the next callee to the traversing path. And it can be used
- # to check if the remove path appears in the traversing path.
-
- # TODO(cheyuw): Implement KMP algorithm to match remove paths
- # efficiently.
- if remove_path[position] is curr_func:
- # Matches the current function, extend the length.
- new_positions[index] = position + 1
- if new_positions[index] == len(remove_path):
- # The length of the longest matching path is equal to the length of
- # the remove path, which means the suffix of the current traversing
- # path matches the remove path.
- invalid_flag = True
- break
-
- else:
- # We can't get the new longest matching path by extending the previous
- # one directly. Fallback to search the new longest matching path.
-
- # If we can't find any matching path in the following search, reset
- # the matching length to 0.
- new_positions[index] = 0
-
- # We want to find the new longest matching prefix of remove path with
- # the suffix of the current traversing path. Because the new longest
- # matching path won't be longer than the prevous one now, and part of
- # the suffix matches the prefix of remove path, we can get the needed
- # suffix from the previous matching prefix of the invalid path.
- suffix = remove_path[:position] + [curr_func]
- for offset in range(1, len(suffix)):
- length = position - offset
- if remove_path[:length] == suffix[offset:]:
- new_positions[index] = length
- break
-
- new_positions = tuple(new_positions)
-
- # If the current suffix is invalid, set the max stack usage to 0.
- max_stack_usage = 0
- max_callee_state = None
- self_loop = False
-
- if not invalid_flag:
- # Max stack usage is at least equal to the stack frame.
- max_stack_usage = curr_func.stack_frame
- for callsite in curr_func.callsites:
- callee = callsite.callee
- if callee is None:
- continue
-
- callee_state = (callee.address, new_positions)
- if callee_state not in scc_index_map:
- # Unvisited state.
- scc_lowlink = min(scc_lowlink, Traverse(callee_state))
- elif callee_state in stacked_states:
- # The state is shown in the stack. There is a cycle.
- sub_stack_usage = 0
- scc_lowlink = min(scc_lowlink, scc_index_map[callee_state])
- if callee_state == curr_state:
- self_loop = True
-
- done_result = done_states.get(callee_state)
- if done_result is not None:
- # Already done this state and use its result. If the state reaches a
- # cycle, reusing the result will cause inaccuracy (the stack usage
- # of cycle depends on where the entrance is). But it's fine since we
- # can't get accurate stack usage under this situation, and we rely
- # on user-provided annotations to break the cycle, after which the
- # result will be accurate again.
- (sub_stack_usage, _) = done_result
-
- if callsite.is_tail:
- # For tailing call, since the callee reuses the stack frame of the
- # caller, choose the larger one directly.
- stack_usage = max(curr_func.stack_frame, sub_stack_usage)
- else:
- stack_usage = curr_func.stack_frame + sub_stack_usage
-
- if stack_usage > max_stack_usage:
- max_stack_usage = stack_usage
- max_callee_state = callee_state
-
- if scc_lowlink == scc_index:
- group = []
- while scc_stack[-1] != curr_state:
- scc_state = scc_stack.pop()
- stacked_states.remove(scc_state)
- group.append(scc_state)
-
- scc_stack.pop()
- stacked_states.remove(curr_state)
-
- # If the cycle is not empty, record it.
- if len(group) > 0 or self_loop:
- group.append(curr_state)
- cycle_groups.append(group)
-
- # Store the done result.
- done_states[curr_state] = (max_stack_usage, max_callee_state)
-
- if curr_positions == initial_positions:
- # If the current state is initial state, we traversed the callgraph by
- # using the current function as start point. Update the stack usage of
- # the function.
- # If the function matches a single vertex remove path, this will set its
- # max stack usage to 0, which is not expected (we still calculate its
- # max stack usage, but prevent any function from calling it). However,
- # all the single vertex remove paths have been preprocessed and removed.
- curr_func.stack_max_usage = max_stack_usage
-
- # Reconstruct the max stack path by traversing the state transitions.
- max_stack_path = [curr_func]
- callee_state = max_callee_state
- while callee_state is not None:
- # The first element of state tuple is function address.
- max_stack_path.append(function_map[callee_state[0]])
- done_result = done_states.get(callee_state)
- # All of the descendants should be done.
- assert done_result is not None
- (_, callee_state) = done_result
-
- curr_func.stack_max_path = max_stack_path
-
- return scc_lowlink
-
- # The state is the concatenation of the current function address and the
- # state of matching position.
- initial_positions = (0,) * len(remove_list)
- done_states = {}
- stacked_states = set()
- scc_index_counter = [0]
- scc_index_map = {}
- scc_stack = []
- cycle_groups = []
- for function in function_map.values():
- if function.stack_max_usage is None:
- Traverse((function.address, initial_positions))
-
- cycle_functions = []
- for group in cycle_groups:
- cycle = set(function_map[state[0]] for state in group)
- if cycle not in cycle_functions:
- cycle_functions.append(cycle)
-
- return cycle_functions
-
- def Analyze(self):
- """Run the stack analysis.
-
- Raises:
- StackAnalyzerError: If disassembly fails.
- """
- def OutputInlineStack(address, prefix=''):
- """Output beautiful inline stack.
-
- Args:
- address: Address.
- prefix: Prefix of each line.
-
- Returns:
- Key for sorting, output text
- """
- line_infos = self.AddressToLine(address, True)
-
- if line_infos[0] is None:
- order_key = (None, None)
- else:
- (_, path, linenum) = line_infos[0]
- order_key = (linenum, path)
-
- line_texts = []
- for line_info in reversed(line_infos):
- if line_info is None:
- (function_name, path, linenum) = ('??', '??', 0)
- else:
- (function_name, path, linenum) = line_info
-
- line_texts.append('{}[{}:{}]'.format(function_name,
- os.path.relpath(path),
- linenum))
-
- output = '{}-> {} {:x}\n'.format(prefix, line_texts[0], address)
- for depth, line_text in enumerate(line_texts[1:]):
- output += '{} {}- {}\n'.format(prefix, ' ' * depth, line_text)
-
- # Remove the last newline character.
- return (order_key, output.rstrip('\n'))
-
- # Analyze disassembly.
- try:
- disasm_text = subprocess.check_output([self.options.objdump,
- '-d',
- self.options.elf_path],
- encoding='utf-8')
- except subprocess.CalledProcessError:
- raise StackAnalyzerError('objdump failed to disassemble.')
- except OSError:
- raise StackAnalyzerError('Failed to run objdump.')
-
- function_map = self.AnalyzeDisassembly(disasm_text)
- result = self.ResolveAnnotation(function_map)
- (add_set, remove_list, eliminated_addrs, failed_sigtxts) = result
- remove_list = self.PreprocessAnnotation(function_map,
- add_set,
- remove_list,
- eliminated_addrs)
- cycle_functions = self.AnalyzeCallGraph(function_map, remove_list)
-
- # Print the results of task-aware stack analysis.
- extra_stack_frame = self.annotation.get('exception_frame_size',
- DEFAULT_EXCEPTION_FRAME_SIZE)
- for task in self.tasklist:
- routine_func = function_map[task.routine_address]
- print('Task: {}, Max size: {} ({} + {}), Allocated size: {}'.format(
- task.name,
- routine_func.stack_max_usage + extra_stack_frame,
- routine_func.stack_max_usage,
- extra_stack_frame,
- task.stack_max_size))
-
- print('Call Trace:')
- max_stack_path = routine_func.stack_max_path
- # Assume the routine function is resolved.
- assert max_stack_path is not None
- for depth, curr_func in enumerate(max_stack_path):
- line_info = self.AddressToLine(curr_func.address)[0]
- if line_info is None:
- (path, linenum) = ('??', 0)
- else:
- (_, path, linenum) = line_info
-
- print(' {} ({}) [{}:{}] {:x}'.format(curr_func.name,
- curr_func.stack_frame,
- os.path.relpath(path),
- linenum,
- curr_func.address))
-
- if depth + 1 < len(max_stack_path):
- succ_func = max_stack_path[depth + 1]
- text_list = []
- for callsite in curr_func.callsites:
- if callsite.callee is succ_func:
- indent_prefix = ' '
- if callsite.address is None:
- order_text = (None, '{}-> [annotation]'.format(indent_prefix))
- else:
- order_text = OutputInlineStack(callsite.address, indent_prefix)
-
- text_list.append(order_text)
-
- for _, text in sorted(text_list, key=lambda item: item[0]):
- print(text)
-
- print('Unresolved indirect callsites:')
- for function in function_map.values():
- indirect_callsites = []
- for callsite in function.callsites:
- if callsite.target is None:
- indirect_callsites.append(callsite.address)
-
- if len(indirect_callsites) > 0:
- print(' In function {}:'.format(function.name))
- text_list = []
- for address in indirect_callsites:
- text_list.append(OutputInlineStack(address, ' '))
-
- for _, text in sorted(text_list, key=lambda item: item[0]):
- print(text)
-
- print('Unresolved annotation signatures:')
- for sigtxt, error in failed_sigtxts:
- print(' {}: {}'.format(sigtxt, error))
-
- if len(cycle_functions) > 0:
- print('There are cycles in the following function sets:')
- for functions in cycle_functions:
- print('[{}]'.format(', '.join(function.name for function in functions)))
-
-
-def ParseArgs():
- """Parse commandline arguments.
-
- Returns:
- options: Namespace from argparse.parse_args().
- """
- parser = argparse.ArgumentParser(description="EC firmware stack analyzer.")
- parser.add_argument('elf_path', help="the path of EC firmware ELF")
- parser.add_argument('--export_taskinfo', required=True,
- help="the path of export_taskinfo.so utility")
- parser.add_argument('--section', required=True, help='the section.',
- choices=[SECTION_RO, SECTION_RW])
- parser.add_argument('--objdump', default='objdump',
- help='the path of objdump')
- parser.add_argument('--addr2line', default='addr2line',
- help='the path of addr2line')
- parser.add_argument('--annotation', default=None,
- help='the path of annotation file')
-
- # TODO(cheyuw): Add an option for dumping stack usage of all functions.
-
- return parser.parse_args()
-
-
-def ParseSymbolText(symbol_text):
- """Parse the content of the symbol text.
-
- Args:
- symbol_text: Text of the symbols.
-
- Returns:
- symbols: Symbol list.
- """
- # Example: "10093064 g F .text 0000015c .hidden hook_task"
- symbol_regex = re.compile(r'^(?P<address>[0-9A-Fa-f]+)\s+[lwg]\s+'
- r'((?P<type>[OF])\s+)?\S+\s+'
- r'(?P<size>[0-9A-Fa-f]+)\s+'
- r'(\S+\s+)?(?P<name>\S+)$')
-
- symbols = []
- for line in symbol_text.splitlines():
- line = line.strip()
- result = symbol_regex.match(line)
- if result is not None:
- address = int(result.group('address'), 16)
- symtype = result.group('type')
- if symtype is None:
- symtype = 'O'
-
- size = int(result.group('size'), 16)
- name = result.group('name')
- symbols.append(Symbol(address, symtype, size, name))
-
- return symbols
-
-
-def ParseRoDataText(rodata_text):
- """Parse the content of rodata
-
- Args:
- symbol_text: Text of the rodata dump.
-
- Returns:
- symbols: Symbol list.
- """
- # Examples: 8018ab0 00040048 00010000 10020000 4b8e0108 ...H........K...
- # 100a7294 00000000 00000000 01000000 ............
-
- base_offset = None
- offset = None
- rodata = []
- for line in rodata_text.splitlines():
- line = line.strip()
- space = line.find(' ')
- if space < 0:
- continue
- try:
- address = int(line[0:space], 16)
- except ValueError:
- continue
-
- if not base_offset:
- base_offset = address
- offset = address
- elif address != offset:
- raise StackAnalyzerError('objdump of rodata not contiguous.')
-
- for i in range(0, 4):
- num = line[(space + 1 + i*9):(space + 9 + i*9)]
- if len(num.strip()) > 0:
- val = int(num, 16)
- else:
- val = 0
- # TODO(drinkcat): Not all platforms are necessarily big-endian
- rodata.append((val & 0x000000ff) << 24 |
- (val & 0x0000ff00) << 8 |
- (val & 0x00ff0000) >> 8 |
- (val & 0xff000000) >> 24)
-
- offset = offset + 4*4
-
- return (base_offset, rodata)
-
-
-def LoadTasklist(section, export_taskinfo, symbols):
- """Load the task information.
-
- Args:
- section: Section (RO | RW).
- export_taskinfo: Handle of export_taskinfo.so.
- symbols: Symbol list.
-
- Returns:
- tasklist: Task list.
- """
-
- TaskInfoPointer = ctypes.POINTER(TaskInfo)
- taskinfos = TaskInfoPointer()
- if section == SECTION_RO:
- get_taskinfos_func = export_taskinfo.get_ro_taskinfos
- else:
- get_taskinfos_func = export_taskinfo.get_rw_taskinfos
-
- taskinfo_num = get_taskinfos_func(ctypes.pointer(taskinfos))
-
- tasklist = []
- for index in range(taskinfo_num):
- taskinfo = taskinfos[index]
- tasklist.append(Task(taskinfo.name.decode('utf-8'),
- taskinfo.routine.decode('utf-8'),
- taskinfo.stack_size))
-
- # Resolve routine address for each task. It's more efficient to resolve all
- # routine addresses of tasks together.
- routine_map = dict((task.routine_name, None) for task in tasklist)
-
- for symbol in symbols:
- # Resolve task routine address.
- if symbol.name in routine_map:
- # Assume the symbol of routine is unique.
- assert routine_map[symbol.name] is None
- routine_map[symbol.name] = symbol.address
-
- for task in tasklist:
- address = routine_map[task.routine_name]
- # Assume we have resolved all routine addresses.
- assert address is not None
- task.routine_address = address
-
- return tasklist
-
-
-def main():
- """Main function."""
- try:
- options = ParseArgs()
-
- # Load annotation config.
- if options.annotation is None:
- annotation = {}
- elif not os.path.exists(options.annotation):
- print('Warning: Annotation file {} does not exist.'
- .format(options.annotation))
- annotation = {}
- else:
- try:
- with open(options.annotation, 'r') as annotation_file:
- annotation = yaml.safe_load(annotation_file)
-
- except yaml.YAMLError:
- raise StackAnalyzerError('Failed to parse annotation file {}.'
- .format(options.annotation))
- except IOError:
- raise StackAnalyzerError('Failed to open annotation file {}.'
- .format(options.annotation))
-
- # TODO(cheyuw): Do complete annotation format verification.
- if not isinstance(annotation, dict):
- raise StackAnalyzerError('Invalid annotation file {}.'
- .format(options.annotation))
-
- # Generate and parse the symbols.
- try:
- symbol_text = subprocess.check_output([options.objdump,
- '-t',
- options.elf_path],
- encoding='utf-8')
- rodata_text = subprocess.check_output([options.objdump,
- '-s',
- '-j', '.rodata',
- options.elf_path],
- encoding='utf-8')
- except subprocess.CalledProcessError:
- raise StackAnalyzerError('objdump failed to dump symbol table or rodata.')
- except OSError:
- raise StackAnalyzerError('Failed to run objdump.')
-
- symbols = ParseSymbolText(symbol_text)
- rodata = ParseRoDataText(rodata_text)
-
- # Load the tasklist.
- try:
- export_taskinfo = ctypes.CDLL(options.export_taskinfo)
- except OSError:
- raise StackAnalyzerError('Failed to load export_taskinfo.')
-
- tasklist = LoadTasklist(options.section, export_taskinfo, symbols)
-
- analyzer = StackAnalyzer(options, symbols, rodata, tasklist, annotation)
- analyzer.Analyze()
- except StackAnalyzerError as e:
- print('Error: {}'.format(e))
-
-
-if __name__ == '__main__':
- main()
diff --git a/extra/stack_analyzer/stack_analyzer_unittest.py b/extra/stack_analyzer/stack_analyzer_unittest.py
deleted file mode 100755
index c36fa9da45..0000000000
--- a/extra/stack_analyzer/stack_analyzer_unittest.py
+++ /dev/null
@@ -1,830 +0,0 @@
-#!/usr/bin/env python3
-# Copyright 2017 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-# Ignore indention messages, since legacy scripts use 2 spaces instead of 4.
-# pylint: disable=bad-indentation,docstring-section-indent
-# pylint: disable=docstring-trailing-quotes
-
-"""Tests for Stack Analyzer classes and functions."""
-
-from __future__ import print_function
-
-import mock
-import os
-import subprocess
-import unittest
-
-import stack_analyzer as sa
-
-
-class ObjectTest(unittest.TestCase):
- """Tests for classes of basic objects."""
-
- def testTask(self):
- task_a = sa.Task('a', 'a_task', 1234)
- task_b = sa.Task('b', 'b_task', 5678, 0x1000)
- self.assertEqual(task_a, task_a)
- self.assertNotEqual(task_a, task_b)
- self.assertNotEqual(task_a, None)
-
- def testSymbol(self):
- symbol_a = sa.Symbol(0x1234, 'F', 32, 'a')
- symbol_b = sa.Symbol(0x234, 'O', 42, 'b')
- self.assertEqual(symbol_a, symbol_a)
- self.assertNotEqual(symbol_a, symbol_b)
- self.assertNotEqual(symbol_a, None)
-
- def testCallsite(self):
- callsite_a = sa.Callsite(0x1002, 0x3000, False)
- callsite_b = sa.Callsite(0x1002, 0x3000, True)
- self.assertEqual(callsite_a, callsite_a)
- self.assertNotEqual(callsite_a, callsite_b)
- self.assertNotEqual(callsite_a, None)
-
- def testFunction(self):
- func_a = sa.Function(0x100, 'a', 0, [])
- func_b = sa.Function(0x200, 'b', 0, [])
- self.assertEqual(func_a, func_a)
- self.assertNotEqual(func_a, func_b)
- self.assertNotEqual(func_a, None)
-
-
-class ArmAnalyzerTest(unittest.TestCase):
- """Tests for class ArmAnalyzer."""
-
- def AppendConditionCode(self, opcodes):
- rets = []
- for opcode in opcodes:
- rets.extend(opcode + cc for cc in sa.ArmAnalyzer.CONDITION_CODES)
-
- return rets
-
- def testInstructionMatching(self):
- jump_list = self.AppendConditionCode(['b', 'bx'])
- jump_list += (list(opcode + '.n' for opcode in jump_list) +
- list(opcode + '.w' for opcode in jump_list))
- for opcode in jump_list:
- self.assertIsNotNone(sa.ArmAnalyzer.JUMP_OPCODE_RE.match(opcode))
-
- self.assertIsNone(sa.ArmAnalyzer.JUMP_OPCODE_RE.match('bl'))
- self.assertIsNone(sa.ArmAnalyzer.JUMP_OPCODE_RE.match('blx'))
-
- cbz_list = ['cbz', 'cbnz', 'cbz.n', 'cbnz.n', 'cbz.w', 'cbnz.w']
- for opcode in cbz_list:
- self.assertIsNotNone(sa.ArmAnalyzer.CBZ_CBNZ_OPCODE_RE.match(opcode))
-
- self.assertIsNone(sa.ArmAnalyzer.CBZ_CBNZ_OPCODE_RE.match('cbn'))
-
- call_list = self.AppendConditionCode(['bl', 'blx'])
- call_list += list(opcode + '.n' for opcode in call_list)
- for opcode in call_list:
- self.assertIsNotNone(sa.ArmAnalyzer.CALL_OPCODE_RE.match(opcode))
-
- self.assertIsNone(sa.ArmAnalyzer.CALL_OPCODE_RE.match('ble'))
-
- result = sa.ArmAnalyzer.CALL_OPERAND_RE.match('53f90 <get_time+0x18>')
- self.assertIsNotNone(result)
- self.assertEqual(result.group(1), '53f90')
- self.assertEqual(result.group(2), 'get_time+0x18')
-
- result = sa.ArmAnalyzer.CBZ_CBNZ_OPERAND_RE.match('r6, 53f90 <get+0x0>')
- self.assertIsNotNone(result)
- self.assertEqual(result.group(1), '53f90')
- self.assertEqual(result.group(2), 'get+0x0')
-
- self.assertIsNotNone(sa.ArmAnalyzer.PUSH_OPCODE_RE.match('push'))
- self.assertIsNone(sa.ArmAnalyzer.PUSH_OPCODE_RE.match('pushal'))
- self.assertIsNotNone(sa.ArmAnalyzer.STM_OPCODE_RE.match('stmdb'))
- self.assertIsNone(sa.ArmAnalyzer.STM_OPCODE_RE.match('lstm'))
- self.assertIsNotNone(sa.ArmAnalyzer.SUB_OPCODE_RE.match('sub'))
- self.assertIsNotNone(sa.ArmAnalyzer.SUB_OPCODE_RE.match('subs'))
- self.assertIsNotNone(sa.ArmAnalyzer.SUB_OPCODE_RE.match('subw'))
- self.assertIsNotNone(sa.ArmAnalyzer.SUB_OPCODE_RE.match('sub.w'))
- self.assertIsNotNone(sa.ArmAnalyzer.SUB_OPCODE_RE.match('subs.w'))
-
- result = sa.ArmAnalyzer.SUB_OPERAND_RE.match('sp, sp, #1668 ; 0x684')
- self.assertIsNotNone(result)
- self.assertEqual(result.group(1), '1668')
- result = sa.ArmAnalyzer.SUB_OPERAND_RE.match('sp, #1668')
- self.assertIsNotNone(result)
- self.assertEqual(result.group(1), '1668')
- self.assertIsNone(sa.ArmAnalyzer.SUB_OPERAND_RE.match('sl, #1668'))
-
- def testAnalyzeFunction(self):
- analyzer = sa.ArmAnalyzer()
- symbol = sa.Symbol(0x10, 'F', 0x100, 'foo')
- instructions = [
- (0x10, 'push', '{r4, r5, r6, r7, lr}'),
- (0x12, 'subw', 'sp, sp, #16 ; 0x10'),
- (0x16, 'movs', 'lr, r1'),
- (0x18, 'beq.n', '26 <foo+0x26>'),
- (0x1a, 'bl', '30 <foo+0x30>'),
- (0x1e, 'bl', 'deadbeef <bar>'),
- (0x22, 'blx', '0 <woo>'),
- (0x26, 'push', '{r1}'),
- (0x28, 'stmdb', 'sp!, {r4, r5, r6, r7, r8, r9, lr}'),
- (0x2c, 'stmdb', 'sp!, {r4}'),
- (0x30, 'stmdb', 'sp, {r4}'),
- (0x34, 'bx.n', '10 <foo>'),
- (0x36, 'bx.n', 'r3'),
- (0x38, 'ldr', 'pc, [r10]'),
- ]
- (size, callsites) = analyzer.AnalyzeFunction(symbol, instructions)
- self.assertEqual(size, 72)
- expect_callsites = [sa.Callsite(0x1e, 0xdeadbeef, False),
- sa.Callsite(0x22, 0x0, False),
- sa.Callsite(0x34, 0x10, True),
- sa.Callsite(0x36, None, True),
- sa.Callsite(0x38, None, True)]
- self.assertEqual(callsites, expect_callsites)
-
-
-class StackAnalyzerTest(unittest.TestCase):
- """Tests for class StackAnalyzer."""
-
- def setUp(self):
- symbols = [sa.Symbol(0x1000, 'F', 0x15C, 'hook_task'),
- sa.Symbol(0x2000, 'F', 0x51C, 'console_task'),
- sa.Symbol(0x3200, 'O', 0x124, '__just_data'),
- sa.Symbol(0x4000, 'F', 0x11C, 'touchpad_calc'),
- sa.Symbol(0x5000, 'F', 0x12C, 'touchpad_calc.constprop.42'),
- sa.Symbol(0x12000, 'F', 0x13C, 'trackpad_range'),
- sa.Symbol(0x13000, 'F', 0x200, 'inlined_mul'),
- sa.Symbol(0x13100, 'F', 0x200, 'inlined_mul'),
- sa.Symbol(0x13100, 'F', 0x200, 'inlined_mul_alias'),
- sa.Symbol(0x20000, 'O', 0x0, '__array'),
- sa.Symbol(0x20010, 'O', 0x0, '__array_end'),
- ]
- tasklist = [sa.Task('HOOKS', 'hook_task', 2048, 0x1000),
- sa.Task('CONSOLE', 'console_task', 460, 0x2000)]
- # Array at 0x20000 that contains pointers to hook_task and console_task,
- # with stride=8, offset=4
- rodata = (0x20000, [ 0xDEAD1000, 0x00001000, 0xDEAD2000, 0x00002000 ])
- options = mock.MagicMock(elf_path='./ec.RW.elf',
- export_taskinfo='fake',
- section='RW',
- objdump='objdump',
- addr2line='addr2line',
- annotation=None)
- self.analyzer = sa.StackAnalyzer(options, symbols, rodata, tasklist, {})
-
- def testParseSymbolText(self):
- symbol_text = (
- '0 g F .text e8 Foo\n'
- '0000dead w F .text 000000e8 .hidden Bar\n'
- 'deadbeef l O .bss 00000004 .hidden Woooo\n'
- 'deadbee g O .rodata 00000008 __Hooo_ooo\n'
- 'deadbee g .rodata 00000000 __foo_doo_coo_end\n'
- )
- symbols = sa.ParseSymbolText(symbol_text)
- expect_symbols = [sa.Symbol(0x0, 'F', 0xe8, 'Foo'),
- sa.Symbol(0xdead, 'F', 0xe8, 'Bar'),
- sa.Symbol(0xdeadbeef, 'O', 0x4, 'Woooo'),
- sa.Symbol(0xdeadbee, 'O', 0x8, '__Hooo_ooo'),
- sa.Symbol(0xdeadbee, 'O', 0x0, '__foo_doo_coo_end')]
- self.assertEqual(symbols, expect_symbols)
-
- def testParseRoData(self):
- rodata_text = (
- '\n'
- 'Contents of section .rodata:\n'
- ' 20000 dead1000 00100000 dead2000 00200000 He..f.He..s.\n'
- )
- rodata = sa.ParseRoDataText(rodata_text)
- expect_rodata = (0x20000,
- [ 0x0010adde, 0x00001000, 0x0020adde, 0x00002000 ])
- self.assertEqual(rodata, expect_rodata)
-
- def testLoadTasklist(self):
- def tasklist_to_taskinfos(pointer, tasklist):
- taskinfos = []
- for task in tasklist:
- taskinfos.append(sa.TaskInfo(name=task.name.encode('utf-8'),
- routine=task.routine_name.encode('utf-8'),
- stack_size=task.stack_max_size))
-
- TaskInfoArray = sa.TaskInfo * len(taskinfos)
- pointer.contents.contents = TaskInfoArray(*taskinfos)
- return len(taskinfos)
-
- def ro_taskinfos(pointer):
- return tasklist_to_taskinfos(pointer, expect_ro_tasklist)
-
- def rw_taskinfos(pointer):
- return tasklist_to_taskinfos(pointer, expect_rw_tasklist)
-
- expect_ro_tasklist = [
- sa.Task('HOOKS', 'hook_task', 2048, 0x1000),
- ]
-
- expect_rw_tasklist = [
- sa.Task('HOOKS', 'hook_task', 2048, 0x1000),
- sa.Task('WOOKS', 'hook_task', 4096, 0x1000),
- sa.Task('CONSOLE', 'console_task', 460, 0x2000),
- ]
-
- export_taskinfo = mock.MagicMock(
- get_ro_taskinfos=mock.MagicMock(side_effect=ro_taskinfos),
- get_rw_taskinfos=mock.MagicMock(side_effect=rw_taskinfos))
-
- tasklist = sa.LoadTasklist('RO', export_taskinfo, self.analyzer.symbols)
- self.assertEqual(tasklist, expect_ro_tasklist)
- tasklist = sa.LoadTasklist('RW', export_taskinfo, self.analyzer.symbols)
- self.assertEqual(tasklist, expect_rw_tasklist)
-
- def testResolveAnnotation(self):
- self.analyzer.annotation = {}
- (add_rules, remove_rules, invalid_sigtxts) = self.analyzer.LoadAnnotation()
- self.assertEqual(add_rules, {})
- self.assertEqual(remove_rules, [])
- self.assertEqual(invalid_sigtxts, set())
-
- self.analyzer.annotation = {'add': None, 'remove': None}
- (add_rules, remove_rules, invalid_sigtxts) = self.analyzer.LoadAnnotation()
- self.assertEqual(add_rules, {})
- self.assertEqual(remove_rules, [])
- self.assertEqual(invalid_sigtxts, set())
-
- self.analyzer.annotation = {
- 'add': None,
- 'remove': [
- [['a', 'b'], ['0', '[', '2'], 'x'],
- [['a', 'b[x:3]'], ['0', '1', '2'], 'x'],
- ],
- }
- (add_rules, remove_rules, invalid_sigtxts) = self.analyzer.LoadAnnotation()
- self.assertEqual(add_rules, {})
- self.assertEqual(list.sort(remove_rules), list.sort([
- [('a', None, None), ('1', None, None), ('x', None, None)],
- [('a', None, None), ('0', None, None), ('x', None, None)],
- [('a', None, None), ('2', None, None), ('x', None, None)],
- [('b', os.path.abspath('x'), 3), ('1', None, None), ('x', None, None)],
- [('b', os.path.abspath('x'), 3), ('0', None, None), ('x', None, None)],
- [('b', os.path.abspath('x'), 3), ('2', None, None), ('x', None, None)],
- ]))
- self.assertEqual(invalid_sigtxts, {'['})
-
- self.analyzer.annotation = {
- 'add': {
- 'touchpad_calc': [ dict(name='__array', stride=8, offset=4) ],
- }
- }
- (add_rules, remove_rules, invalid_sigtxts) = self.analyzer.LoadAnnotation()
- self.assertEqual(add_rules, {
- ('touchpad_calc', None, None):
- set([('console_task', None, None), ('hook_task', None, None)])})
-
- funcs = {
- 0x1000: sa.Function(0x1000, 'hook_task', 0, []),
- 0x2000: sa.Function(0x2000, 'console_task', 0, []),
- 0x4000: sa.Function(0x4000, 'touchpad_calc', 0, []),
- 0x5000: sa.Function(0x5000, 'touchpad_calc.constprop.42', 0, []),
- 0x13000: sa.Function(0x13000, 'inlined_mul', 0, []),
- 0x13100: sa.Function(0x13100, 'inlined_mul', 0, []),
- }
- funcs[0x1000].callsites = [
- sa.Callsite(0x1002, None, False, None)]
- # Set address_to_line_cache to fake the results of addr2line.
- self.analyzer.address_to_line_cache = {
- (0x1000, False): [('hook_task', os.path.abspath('a.c'), 10)],
- (0x1002, False): [('toot_calc', os.path.abspath('t.c'), 1234)],
- (0x2000, False): [('console_task', os.path.abspath('b.c'), 20)],
- (0x4000, False): [('toudhpad_calc', os.path.abspath('a.c'), 20)],
- (0x5000, False): [
- ('touchpad_calc.constprop.42', os.path.abspath('b.c'), 40)],
- (0x12000, False): [('trackpad_range', os.path.abspath('t.c'), 10)],
- (0x13000, False): [('inlined_mul', os.path.abspath('x.c'), 12)],
- (0x13100, False): [('inlined_mul', os.path.abspath('x.c'), 12)],
- }
- self.analyzer.annotation = {
- 'add': {
- 'hook_task.lto.573': ['touchpad_calc.lto.2501[a.c]'],
- 'console_task': ['touchpad_calc[b.c]', 'inlined_mul_alias'],
- 'hook_task[q.c]': ['hook_task'],
- 'inlined_mul[x.c]': ['inlined_mul'],
- 'toot_calc[t.c:1234]': ['hook_task'],
- },
- 'remove': [
- ['touchpad?calc['],
- 'touchpad_calc',
- ['touchpad_calc[a.c]'],
- ['task_unk[a.c]'],
- ['touchpad_calc[x/a.c]'],
- ['trackpad_range'],
- ['inlined_mul'],
- ['inlined_mul', 'console_task', 'touchpad_calc[a.c]'],
- ['inlined_mul', 'inlined_mul_alias', 'console_task'],
- ['inlined_mul', 'inlined_mul_alias', 'console_task'],
- ],
- }
- (add_rules, remove_rules, invalid_sigtxts) = self.analyzer.LoadAnnotation()
- self.assertEqual(invalid_sigtxts, {'touchpad?calc['})
-
- signature_set = set()
- for src_sig, dst_sigs in add_rules.items():
- signature_set.add(src_sig)
- signature_set.update(dst_sigs)
-
- for remove_sigs in remove_rules:
- signature_set.update(remove_sigs)
-
- (signature_map, failed_sigs) = self.analyzer.MapAnnotation(funcs,
- signature_set)
- result = self.analyzer.ResolveAnnotation(funcs)
- (add_set, remove_list, eliminated_addrs, failed_sigs) = result
-
- expect_signature_map = {
- ('hook_task', None, None): {funcs[0x1000]},
- ('touchpad_calc', os.path.abspath('a.c'), None): {funcs[0x4000]},
- ('touchpad_calc', os.path.abspath('b.c'), None): {funcs[0x5000]},
- ('console_task', None, None): {funcs[0x2000]},
- ('inlined_mul_alias', None, None): {funcs[0x13100]},
- ('inlined_mul', os.path.abspath('x.c'), None): {funcs[0x13000],
- funcs[0x13100]},
- ('inlined_mul', None, None): {funcs[0x13000], funcs[0x13100]},
- }
- self.assertEqual(len(signature_map), len(expect_signature_map))
- for sig, funclist in signature_map.items():
- self.assertEqual(set(funclist), expect_signature_map[sig])
-
- self.assertEqual(add_set, {
- (funcs[0x1000], funcs[0x4000]),
- (funcs[0x1000], funcs[0x1000]),
- (funcs[0x2000], funcs[0x5000]),
- (funcs[0x2000], funcs[0x13100]),
- (funcs[0x13000], funcs[0x13000]),
- (funcs[0x13000], funcs[0x13100]),
- (funcs[0x13100], funcs[0x13000]),
- (funcs[0x13100], funcs[0x13100]),
- })
- expect_remove_list = [
- [funcs[0x4000]],
- [funcs[0x13000]],
- [funcs[0x13100]],
- [funcs[0x13000], funcs[0x2000], funcs[0x4000]],
- [funcs[0x13100], funcs[0x2000], funcs[0x4000]],
- [funcs[0x13000], funcs[0x13100], funcs[0x2000]],
- [funcs[0x13100], funcs[0x13100], funcs[0x2000]],
- ]
- self.assertEqual(len(remove_list), len(expect_remove_list))
- for remove_path in remove_list:
- self.assertTrue(remove_path in expect_remove_list)
-
- self.assertEqual(eliminated_addrs, {0x1002})
- self.assertEqual(failed_sigs, {
- ('touchpad?calc[', sa.StackAnalyzer.ANNOTATION_ERROR_INVALID),
- ('touchpad_calc', sa.StackAnalyzer.ANNOTATION_ERROR_AMBIGUOUS),
- ('hook_task[q.c]', sa.StackAnalyzer.ANNOTATION_ERROR_NOTFOUND),
- ('task_unk[a.c]', sa.StackAnalyzer.ANNOTATION_ERROR_NOTFOUND),
- ('touchpad_calc[x/a.c]', sa.StackAnalyzer.ANNOTATION_ERROR_NOTFOUND),
- ('trackpad_range', sa.StackAnalyzer.ANNOTATION_ERROR_NOTFOUND),
- })
-
- def testPreprocessAnnotation(self):
- funcs = {
- 0x1000: sa.Function(0x1000, 'hook_task', 0, []),
- 0x2000: sa.Function(0x2000, 'console_task', 0, []),
- 0x4000: sa.Function(0x4000, 'touchpad_calc', 0, []),
- }
- funcs[0x1000].callsites = [
- sa.Callsite(0x1002, 0x1000, False, funcs[0x1000])]
- funcs[0x2000].callsites = [
- sa.Callsite(0x2002, 0x1000, False, funcs[0x1000]),
- sa.Callsite(0x2006, None, True, None),
- ]
- add_set = {
- (funcs[0x2000], funcs[0x2000]),
- (funcs[0x2000], funcs[0x4000]),
- (funcs[0x4000], funcs[0x1000]),
- (funcs[0x4000], funcs[0x2000]),
- }
- remove_list = [
- [funcs[0x1000]],
- [funcs[0x2000], funcs[0x2000]],
- [funcs[0x4000], funcs[0x1000]],
- [funcs[0x2000], funcs[0x4000], funcs[0x2000]],
- [funcs[0x4000], funcs[0x1000], funcs[0x4000]],
- ]
- eliminated_addrs = {0x2006}
-
- remaining_remove_list = self.analyzer.PreprocessAnnotation(funcs,
- add_set,
- remove_list,
- eliminated_addrs)
-
- expect_funcs = {
- 0x1000: sa.Function(0x1000, 'hook_task', 0, []),
- 0x2000: sa.Function(0x2000, 'console_task', 0, []),
- 0x4000: sa.Function(0x4000, 'touchpad_calc', 0, []),
- }
- expect_funcs[0x2000].callsites = [
- sa.Callsite(None, 0x4000, False, expect_funcs[0x4000])]
- expect_funcs[0x4000].callsites = [
- sa.Callsite(None, 0x2000, False, expect_funcs[0x2000])]
- self.assertEqual(funcs, expect_funcs)
- self.assertEqual(remaining_remove_list, [
- [funcs[0x2000], funcs[0x4000], funcs[0x2000]],
- ])
-
- def testAndesAnalyzeDisassembly(self):
- disasm_text = (
- '\n'
- 'build/{BOARD}/RW/ec.RW.elf: file format elf32-nds32le'
- '\n'
- 'Disassembly of section .text:\n'
- '\n'
- '00000900 <wook_task>:\n'
- ' ...\n'
- '00001000 <hook_task>:\n'
- ' 1000: fc 42\tpush25 $r10, #16 ! {$r6~$r10, $fp, $gp, $lp}\n'
- ' 1004: 47 70\t\tmovi55 $r0, #1\n'
- ' 1006: b1 13\tbnezs8 100929de <flash_command_write>\n'
- ' 1008: 00 01 5c fc\tbne $r6, $r0, 2af6a\n'
- '00002000 <console_task>:\n'
- ' 2000: fc 00\t\tpush25 $r6, #0 ! {$r6, $fp, $gp, $lp} \n'
- ' 2002: f0 0e fc c5\tjal 1000 <hook_task>\n'
- ' 2006: f0 0e bd 3b\tj 53968 <get_program_memory_addr>\n'
- ' 200a: de ad be ef\tswi.gp $r0, [ + #-11036]\n'
- '00004000 <touchpad_calc>:\n'
- ' 4000: 47 70\t\tmovi55 $r0, #1\n'
- '00010000 <look_task>:'
- )
- function_map = self.analyzer.AnalyzeDisassembly(disasm_text)
- func_hook_task = sa.Function(0x1000, 'hook_task', 48, [
- sa.Callsite(0x1006, 0x100929de, True, None)])
- expect_funcmap = {
- 0x1000: func_hook_task,
- 0x2000: sa.Function(0x2000, 'console_task', 16,
- [sa.Callsite(0x2002, 0x1000, False, func_hook_task),
- sa.Callsite(0x2006, 0x53968, True, None)]),
- 0x4000: sa.Function(0x4000, 'touchpad_calc', 0, []),
- }
- self.assertEqual(function_map, expect_funcmap)
-
- def testArmAnalyzeDisassembly(self):
- disasm_text = (
- '\n'
- 'build/{BOARD}/RW/ec.RW.elf: file format elf32-littlearm'
- '\n'
- 'Disassembly of section .text:\n'
- '\n'
- '00000900 <wook_task>:\n'
- ' ...\n'
- '00001000 <hook_task>:\n'
- ' 1000: dead beef\tfake\n'
- ' 1004: 4770\t\tbx lr\n'
- ' 1006: b113\tcbz r3, 100929de <flash_command_write>\n'
- ' 1008: 00015cfc\t.word 0x00015cfc\n'
- '00002000 <console_task>:\n'
- ' 2000: b508\t\tpush {r3, lr} ; malformed comments,; r0, r1 \n'
- ' 2002: f00e fcc5\tbl 1000 <hook_task>\n'
- ' 2006: f00e bd3b\tb.w 53968 <get_program_memory_addr>\n'
- ' 200a: dead beef\tfake\n'
- '00004000 <touchpad_calc>:\n'
- ' 4000: 4770\t\tbx lr\n'
- '00010000 <look_task>:'
- )
- function_map = self.analyzer.AnalyzeDisassembly(disasm_text)
- func_hook_task = sa.Function(0x1000, 'hook_task', 0, [
- sa.Callsite(0x1006, 0x100929de, True, None)])
- expect_funcmap = {
- 0x1000: func_hook_task,
- 0x2000: sa.Function(0x2000, 'console_task', 8,
- [sa.Callsite(0x2002, 0x1000, False, func_hook_task),
- sa.Callsite(0x2006, 0x53968, True, None)]),
- 0x4000: sa.Function(0x4000, 'touchpad_calc', 0, []),
- }
- self.assertEqual(function_map, expect_funcmap)
-
- def testAnalyzeCallGraph(self):
- funcs = {
- 0x1000: sa.Function(0x1000, 'hook_task', 0, []),
- 0x2000: sa.Function(0x2000, 'console_task', 8, []),
- 0x3000: sa.Function(0x3000, 'task_a', 12, []),
- 0x4000: sa.Function(0x4000, 'task_b', 96, []),
- 0x5000: sa.Function(0x5000, 'task_c', 32, []),
- 0x6000: sa.Function(0x6000, 'task_d', 100, []),
- 0x7000: sa.Function(0x7000, 'task_e', 24, []),
- 0x8000: sa.Function(0x8000, 'task_f', 20, []),
- 0x9000: sa.Function(0x9000, 'task_g', 20, []),
- 0x10000: sa.Function(0x10000, 'task_x', 16, []),
- }
- funcs[0x1000].callsites = [
- sa.Callsite(0x1002, 0x3000, False, funcs[0x3000]),
- sa.Callsite(0x1006, 0x4000, False, funcs[0x4000])]
- funcs[0x2000].callsites = [
- sa.Callsite(0x2002, 0x5000, False, funcs[0x5000]),
- sa.Callsite(0x2006, 0x2000, False, funcs[0x2000]),
- sa.Callsite(0x200a, 0x10000, False, funcs[0x10000])]
- funcs[0x3000].callsites = [
- sa.Callsite(0x3002, 0x4000, False, funcs[0x4000]),
- sa.Callsite(0x3006, 0x1000, False, funcs[0x1000])]
- funcs[0x4000].callsites = [
- sa.Callsite(0x4002, 0x6000, True, funcs[0x6000]),
- sa.Callsite(0x4006, 0x7000, False, funcs[0x7000]),
- sa.Callsite(0x400a, 0x8000, False, funcs[0x8000])]
- funcs[0x5000].callsites = [
- sa.Callsite(0x5002, 0x4000, False, funcs[0x4000])]
- funcs[0x7000].callsites = [
- sa.Callsite(0x7002, 0x7000, False, funcs[0x7000])]
- funcs[0x8000].callsites = [
- sa.Callsite(0x8002, 0x9000, False, funcs[0x9000])]
- funcs[0x9000].callsites = [
- sa.Callsite(0x9002, 0x4000, False, funcs[0x4000])]
- funcs[0x10000].callsites = [
- sa.Callsite(0x10002, 0x2000, False, funcs[0x2000])]
-
- cycles = self.analyzer.AnalyzeCallGraph(funcs, [
- [funcs[0x2000]] * 2,
- [funcs[0x10000], funcs[0x2000]] * 3,
- [funcs[0x1000], funcs[0x3000], funcs[0x1000]]
- ])
-
- expect_func_stack = {
- 0x1000: (268, [funcs[0x1000],
- funcs[0x3000],
- funcs[0x4000],
- funcs[0x8000],
- funcs[0x9000],
- funcs[0x4000],
- funcs[0x7000]]),
- 0x2000: (208, [funcs[0x2000],
- funcs[0x10000],
- funcs[0x2000],
- funcs[0x10000],
- funcs[0x2000],
- funcs[0x5000],
- funcs[0x4000],
- funcs[0x7000]]),
- 0x3000: (280, [funcs[0x3000],
- funcs[0x1000],
- funcs[0x3000],
- funcs[0x4000],
- funcs[0x8000],
- funcs[0x9000],
- funcs[0x4000],
- funcs[0x7000]]),
- 0x4000: (120, [funcs[0x4000], funcs[0x7000]]),
- 0x5000: (152, [funcs[0x5000], funcs[0x4000], funcs[0x7000]]),
- 0x6000: (100, [funcs[0x6000]]),
- 0x7000: (24, [funcs[0x7000]]),
- 0x8000: (160, [funcs[0x8000],
- funcs[0x9000],
- funcs[0x4000],
- funcs[0x7000]]),
- 0x9000: (140, [funcs[0x9000], funcs[0x4000], funcs[0x7000]]),
- 0x10000: (200, [funcs[0x10000],
- funcs[0x2000],
- funcs[0x10000],
- funcs[0x2000],
- funcs[0x5000],
- funcs[0x4000],
- funcs[0x7000]]),
- }
- expect_cycles = [
- {funcs[0x4000], funcs[0x8000], funcs[0x9000]},
- {funcs[0x7000]},
- ]
- for func in funcs.values():
- (stack_max_usage, stack_max_path) = expect_func_stack[func.address]
- self.assertEqual(func.stack_max_usage, stack_max_usage)
- self.assertEqual(func.stack_max_path, stack_max_path)
-
- self.assertEqual(len(cycles), len(expect_cycles))
- for cycle in cycles:
- self.assertTrue(cycle in expect_cycles)
-
- @mock.patch('subprocess.check_output')
- def testAddressToLine(self, checkoutput_mock):
- checkoutput_mock.return_value = 'fake_func\n/test.c:1'
- self.assertEqual(self.analyzer.AddressToLine(0x1234),
- [('fake_func', '/test.c', 1)])
- checkoutput_mock.assert_called_once_with(
- ['addr2line', '-f', '-e', './ec.RW.elf', '1234'], encoding='utf-8')
- checkoutput_mock.reset_mock()
-
- checkoutput_mock.return_value = 'fake_func\n/a.c:1\nbake_func\n/b.c:2\n'
- self.assertEqual(self.analyzer.AddressToLine(0x1234, True),
- [('fake_func', '/a.c', 1), ('bake_func', '/b.c', 2)])
- checkoutput_mock.assert_called_once_with(
- ['addr2line', '-f', '-e', './ec.RW.elf', '1234', '-i'],
- encoding='utf-8')
- checkoutput_mock.reset_mock()
-
- checkoutput_mock.return_value = 'fake_func\n/test.c:1 (discriminator 128)'
- self.assertEqual(self.analyzer.AddressToLine(0x12345),
- [('fake_func', '/test.c', 1)])
- checkoutput_mock.assert_called_once_with(
- ['addr2line', '-f', '-e', './ec.RW.elf', '12345'], encoding='utf-8')
- checkoutput_mock.reset_mock()
-
- checkoutput_mock.return_value = '??\n:?\nbake_func\n/b.c:2\n'
- self.assertEqual(self.analyzer.AddressToLine(0x123456),
- [None, ('bake_func', '/b.c', 2)])
- checkoutput_mock.assert_called_once_with(
- ['addr2line', '-f', '-e', './ec.RW.elf', '123456'], encoding='utf-8')
- checkoutput_mock.reset_mock()
-
- with self.assertRaisesRegexp(sa.StackAnalyzerError,
- 'addr2line failed to resolve lines.'):
- checkoutput_mock.side_effect = subprocess.CalledProcessError(1, '')
- self.analyzer.AddressToLine(0x5678)
-
- with self.assertRaisesRegexp(sa.StackAnalyzerError,
- 'Failed to run addr2line.'):
- checkoutput_mock.side_effect = OSError()
- self.analyzer.AddressToLine(0x9012)
-
- @mock.patch('subprocess.check_output')
- @mock.patch('stack_analyzer.StackAnalyzer.AddressToLine')
- def testAndesAnalyze(self, addrtoline_mock, checkoutput_mock):
- disasm_text = (
- '\n'
- 'build/{BOARD}/RW/ec.RW.elf: file format elf32-nds32le'
- '\n'
- 'Disassembly of section .text:\n'
- '\n'
- '00000900 <wook_task>:\n'
- ' ...\n'
- '00001000 <hook_task>:\n'
- ' 1000: fc 00\t\tpush25 $r10, #16 ! {$r6~$r10, $fp, $gp, $lp}\n'
- ' 1002: 47 70\t\tmovi55 $r0, #1\n'
- ' 1006: 00 01 5c fc\tbne $r6, $r0, 2af6a\n'
- '00002000 <console_task>:\n'
- ' 2000: fc 00\t\tpush25 $r6, #0 ! {$r6, $fp, $gp, $lp} \n'
- ' 2002: f0 0e fc c5\tjal 1000 <hook_task>\n'
- ' 2006: f0 0e bd 3b\tj 53968 <get_program_memory_addr>\n'
- ' 200a: 12 34 56 78\tjral5 $r0\n'
- )
-
- addrtoline_mock.return_value = [('??', '??', 0)]
- self.analyzer.annotation = {
- 'exception_frame_size': 64,
- 'remove': [['fake_func']],
- }
-
- with mock.patch('builtins.print') as print_mock:
- checkoutput_mock.return_value = disasm_text
- self.analyzer.Analyze()
- print_mock.assert_has_calls([
- mock.call(
- 'Task: HOOKS, Max size: 96 (32 + 64), Allocated size: 2048'),
- mock.call('Call Trace:'),
- mock.call(' hook_task (32) [??:0] 1000'),
- mock.call(
- 'Task: CONSOLE, Max size: 112 (48 + 64), Allocated size: 460'),
- mock.call('Call Trace:'),
- mock.call(' console_task (16) [??:0] 2000'),
- mock.call(' -> ??[??:0] 2002'),
- mock.call(' hook_task (32) [??:0] 1000'),
- mock.call('Unresolved indirect callsites:'),
- mock.call(' In function console_task:'),
- mock.call(' -> ??[??:0] 200a'),
- mock.call('Unresolved annotation signatures:'),
- mock.call(' fake_func: function is not found'),
- ])
-
- with self.assertRaisesRegexp(sa.StackAnalyzerError,
- 'Failed to run objdump.'):
- checkoutput_mock.side_effect = OSError()
- self.analyzer.Analyze()
-
- with self.assertRaisesRegexp(sa.StackAnalyzerError,
- 'objdump failed to disassemble.'):
- checkoutput_mock.side_effect = subprocess.CalledProcessError(1, '')
- self.analyzer.Analyze()
-
- @mock.patch('subprocess.check_output')
- @mock.patch('stack_analyzer.StackAnalyzer.AddressToLine')
- def testArmAnalyze(self, addrtoline_mock, checkoutput_mock):
- disasm_text = (
- '\n'
- 'build/{BOARD}/RW/ec.RW.elf: file format elf32-littlearm'
- '\n'
- 'Disassembly of section .text:\n'
- '\n'
- '00000900 <wook_task>:\n'
- ' ...\n'
- '00001000 <hook_task>:\n'
- ' 1000: b508\t\tpush {r3, lr}\n'
- ' 1002: 4770\t\tbx lr\n'
- ' 1006: 00015cfc\t.word 0x00015cfc\n'
- '00002000 <console_task>:\n'
- ' 2000: b508\t\tpush {r3, lr}\n'
- ' 2002: f00e fcc5\tbl 1000 <hook_task>\n'
- ' 2006: f00e bd3b\tb.w 53968 <get_program_memory_addr>\n'
- ' 200a: 1234 5678\tb.w sl\n'
- )
-
- addrtoline_mock.return_value = [('??', '??', 0)]
- self.analyzer.annotation = {
- 'exception_frame_size': 64,
- 'remove': [['fake_func']],
- }
-
- with mock.patch('builtins.print') as print_mock:
- checkoutput_mock.return_value = disasm_text
- self.analyzer.Analyze()
- print_mock.assert_has_calls([
- mock.call(
- 'Task: HOOKS, Max size: 72 (8 + 64), Allocated size: 2048'),
- mock.call('Call Trace:'),
- mock.call(' hook_task (8) [??:0] 1000'),
- mock.call(
- 'Task: CONSOLE, Max size: 80 (16 + 64), Allocated size: 460'),
- mock.call('Call Trace:'),
- mock.call(' console_task (8) [??:0] 2000'),
- mock.call(' -> ??[??:0] 2002'),
- mock.call(' hook_task (8) [??:0] 1000'),
- mock.call('Unresolved indirect callsites:'),
- mock.call(' In function console_task:'),
- mock.call(' -> ??[??:0] 200a'),
- mock.call('Unresolved annotation signatures:'),
- mock.call(' fake_func: function is not found'),
- ])
-
- with self.assertRaisesRegexp(sa.StackAnalyzerError,
- 'Failed to run objdump.'):
- checkoutput_mock.side_effect = OSError()
- self.analyzer.Analyze()
-
- with self.assertRaisesRegexp(sa.StackAnalyzerError,
- 'objdump failed to disassemble.'):
- checkoutput_mock.side_effect = subprocess.CalledProcessError(1, '')
- self.analyzer.Analyze()
-
- @mock.patch('subprocess.check_output')
- @mock.patch('stack_analyzer.ParseArgs')
- def testMain(self, parseargs_mock, checkoutput_mock):
- symbol_text = ('1000 g F .text 0000015c .hidden hook_task\n'
- '2000 g F .text 0000051c .hidden console_task\n')
- rodata_text = (
- '\n'
- 'Contents of section .rodata:\n'
- ' 20000 dead1000 00100000 dead2000 00200000 He..f.He..s.\n'
- )
-
- args = mock.MagicMock(elf_path='./ec.RW.elf',
- export_taskinfo='fake',
- section='RW',
- objdump='objdump',
- addr2line='addr2line',
- annotation='fake')
- parseargs_mock.return_value = args
-
- with mock.patch('os.path.exists') as path_mock:
- path_mock.return_value = False
- with mock.patch('builtins.print') as print_mock:
- with mock.patch('builtins.open', mock.mock_open()) as open_mock:
- sa.main()
- print_mock.assert_any_call(
- 'Warning: Annotation file fake does not exist.')
-
- with mock.patch('os.path.exists') as path_mock:
- path_mock.return_value = True
- with mock.patch('builtins.print') as print_mock:
- with mock.patch('builtins.open', mock.mock_open()) as open_mock:
- open_mock.side_effect = IOError()
- sa.main()
- print_mock.assert_called_once_with(
- 'Error: Failed to open annotation file fake.')
-
- with mock.patch('builtins.print') as print_mock:
- with mock.patch('builtins.open', mock.mock_open()) as open_mock:
- open_mock.return_value.read.side_effect = ['{', '']
- sa.main()
- open_mock.assert_called_once_with('fake', 'r')
- print_mock.assert_called_once_with(
- 'Error: Failed to parse annotation file fake.')
-
- with mock.patch('builtins.print') as print_mock:
- with mock.patch('builtins.open',
- mock.mock_open(read_data='')) as open_mock:
- sa.main()
- print_mock.assert_called_once_with(
- 'Error: Invalid annotation file fake.')
-
- args.annotation = None
-
- with mock.patch('builtins.print') as print_mock:
- checkoutput_mock.side_effect = [symbol_text, rodata_text]
- sa.main()
- print_mock.assert_called_once_with(
- 'Error: Failed to load export_taskinfo.')
-
- with mock.patch('builtins.print') as print_mock:
- checkoutput_mock.side_effect = subprocess.CalledProcessError(1, '')
- sa.main()
- print_mock.assert_called_once_with(
- 'Error: objdump failed to dump symbol table or rodata.')
-
- with mock.patch('builtins.print') as print_mock:
- checkoutput_mock.side_effect = OSError()
- sa.main()
- print_mock.assert_called_once_with('Error: Failed to run objdump.')
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/extra/tigertool/README.md b/extra/tigertool/README.md
deleted file mode 100644
index 407a58a751..0000000000
--- a/extra/tigertool/README.md
+++ /dev/null
@@ -1,28 +0,0 @@
-# tigertool
-
-tigertool.py is a commandline utility to control the tigertail USB-C mux. It
-supports changing the mux status to port A, B, or off. You can set a serial
-number to use multiple tigertails at once.
-
-## Usage
-
-Typical usage to set the mux port
-
-```
-./tigertail.py -m [A|B|off] -s [serialno]
-```
-
-Reboot the tigertail<br>
-
-```
-./tigertail.py --reboot
-```
-
-Set the serial number, when only one tigertail is plugged
-
-```
-./tigertail.py --setserialno=[serialno]
-```
-
-Tigertail can support up to 20V 3A on the mux and passes through all USB-C lines
-except SBU.
diff --git a/extra/tigertool/ecusb/__init__.py b/extra/tigertool/ecusb/__init__.py
deleted file mode 100644
index fe4dbc6749..0000000000
--- a/extra/tigertool/ecusb/__init__.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright 2017 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-# Ignore indention messages, since legacy scripts use 2 spaces instead of 4.
-# pylint: disable=bad-indentation,docstring-section-indent
-# pylint: disable=docstring-trailing-quotes
-
-__all__ = ['tiny_servo_common', 'stm32usb', 'stm32uart', 'pty_driver']
diff --git a/extra/tigertool/ecusb/pty_driver.py b/extra/tigertool/ecusb/pty_driver.py
deleted file mode 100644
index 137cbc149b..0000000000
--- a/extra/tigertool/ecusb/pty_driver.py
+++ /dev/null
@@ -1,304 +0,0 @@
-# Copyright 2017 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-# Ignore indention messages, since legacy scripts use 2 spaces instead of 4.
-# pylint: disable=bad-indentation,docstring-section-indent
-# pylint: disable=docstring-trailing-quotes
-
-"""ptyDriver class
-
-This class takes a pty interface and can send commands and expect results
-as regex. This is useful for automating console based interfaces, such as
-the CrOS EC console commands.
-"""
-
-import ast
-import errno
-import fcntl
-import os
-import pexpect
-import time
-from pexpect import fdpexpect
-
-# Expecting a result in 3 seconds is plenty even for slow platforms.
-DEFAULT_UART_TIMEOUT = 3
-FLUSH_UART_TIMEOUT = 1
-
-
-class ptyError(Exception):
- """Exception class for pty errors."""
-
-
-UART_PARAMS = {
- 'uart_cmd': None,
- 'uart_multicmd': None,
- 'uart_regexp': None,
- 'uart_timeout': DEFAULT_UART_TIMEOUT,
-}
-
-
-class ptyDriver(object):
- """Automate interactive commands on a pty interface."""
- def __init__(self, interface, params, fast=False):
- """Init class variables."""
- self._child = None
- self._fd = None
- self._interface = interface
- self._pty_path = self._interface.get_pty()
- self._dict = UART_PARAMS.copy()
- self._fast = fast
-
- def __del__(self):
- self.close()
-
- def close(self):
- """Close any open files and interfaces."""
- if self._fd:
- self._close()
- self._interface.close()
-
- def _open(self):
- """Connect to serial device and create pexpect interface."""
- assert self._fd is None
- self._fd = os.open(self._pty_path, os.O_RDWR | os.O_NONBLOCK)
- # Don't allow forked processes to access.
- fcntl.fcntl(self._fd, fcntl.F_SETFD,
- fcntl.fcntl(self._fd, fcntl.F_GETFD) | fcntl.FD_CLOEXEC)
- self._child = fdpexpect.fdspawn(self._fd)
- # pexpect defaults to a 100ms delay before sending characters, to
- # work around race conditions in ssh. We don't need this feature
- # so we'll change delaybeforesend from 0.1 to 0.001 to speed things up.
- if self._fast:
- self._child.delaybeforesend = 0.001
-
- def _close(self):
- """Close serial device connection."""
- os.close(self._fd)
- self._fd = None
- self._child = None
-
- def _flush(self):
- """Flush device output to prevent previous messages interfering."""
- if self._child.sendline('') != 1:
- raise ptyError('Failed to send newline.')
- # Have a maximum timeout for the flush operation. We should have cleared
- # all data from the buffer, but if data is regularly being generated, we
- # can't guarantee it will ever stop.
- flush_end_time = time.time() + FLUSH_UART_TIMEOUT
- while time.time() <= flush_end_time:
- try:
- self._child.expect('.', timeout=0.01)
- except (pexpect.TIMEOUT, pexpect.EOF):
- break
- except OSError as e:
- # EAGAIN indicates no data available, maybe we didn't wait long enough.
- if e.errno != errno.EAGAIN:
- raise
- break
-
- def _send(self, cmds):
- """Send command to EC.
-
- This function always flushes serial device before sending, and is used as
- a wrapper function to make sure the channel is always flushed before
- sending commands.
-
- Args:
- cmds: The commands to send to the device, either a list or a string.
-
- Raises:
- ptyError: Raised when writing to the device fails.
- """
- self._flush()
- if not isinstance(cmds, list):
- cmds = [cmds]
- for cmd in cmds:
- if self._child.sendline(cmd) != len(cmd) + 1:
- raise ptyError('Failed to send command.')
-
- def _issue_cmd(self, cmds):
- """Send command to the device and do not wait for response.
-
- Args:
- cmds: The commands to send to the device, either a list or a string.
- """
- self._issue_cmd_get_results(cmds, [])
-
- def _issue_cmd_get_results(self, cmds,
- regex_list, timeout=DEFAULT_UART_TIMEOUT):
- """Send command to the device and wait for response.
-
- This function waits for response message matching a regular
- expressions.
-
- Args:
- cmds: The commands issued, either a list or a string.
- regex_list: List of Regular expressions used to match response message.
- Note1, list must be ordered.
- Note2, empty list sends and returns.
- timeout: time to wait for matching results before failing.
-
- Returns:
- List of tuples, each of which contains the entire matched string and
- all the subgroups of the match. None if not matched.
- For example:
- response of the given command:
- High temp: 37.2
- Low temp: 36.4
- regex_list:
- ['High temp: (\d+)\.(\d+)', 'Low temp: (\d+)\.(\d+)']
- returns:
- [('High temp: 37.2', '37', '2'), ('Low temp: 36.4', '36', '4')]
-
- Raises:
- ptyError: If timed out waiting for a response
- """
- result_list = []
- self._open()
- try:
- self._send(cmds)
- for regex in regex_list:
- self._child.expect(regex, timeout)
- match = self._child.match
- lastindex = match.lastindex if match and match.lastindex else 0
- # Create a tuple which contains the entire matched string and all
- # the subgroups of the match.
- result = match.group(*range(lastindex + 1)) if match else None
- if result:
- result = tuple(res.decode('utf-8') for res in result)
- result_list.append(result)
- except pexpect.TIMEOUT:
- raise ptyError('Timeout waiting for response.')
- finally:
- self._close()
- return result_list
-
- def _issue_cmd_get_multi_results(self, cmd, regex):
- """Send command to the device and wait for multiple response.
-
- This function waits for arbitrary number of response message
- matching a regular expression.
-
- Args:
- cmd: The command issued.
- regex: Regular expression used to match response message.
-
- Returns:
- List of tuples, each of which contains the entire matched string and
- all the subgroups of the match. None if not matched.
- """
- result_list = []
- self._open()
- try:
- self._send(cmd)
- while True:
- try:
- self._child.expect(regex, timeout=0.1)
- match = self._child.match
- lastindex = match.lastindex if match and match.lastindex else 0
- # Create a tuple which contains the entire matched string and all
- # the subgroups of the match.
- result = match.group(*range(lastindex + 1)) if match else None
- if result:
- result = tuple(res.decode('utf-8') for res in result)
- result_list.append(result)
- except pexpect.TIMEOUT:
- break
- finally:
- self._close()
- return result_list
-
- def _Set_uart_timeout(self, timeout):
- """Set timeout value for waiting for the device response.
-
- Args:
- timeout: Timeout value in second.
- """
- self._dict['uart_timeout'] = timeout
-
- def _Get_uart_timeout(self):
- """Get timeout value for waiting for the device response.
-
- Returns:
- Timeout value in second.
- """
- return self._dict['uart_timeout']
-
- def _Set_uart_regexp(self, regexp):
- """Set the list of regular expressions which matches the command response.
-
- Args:
- regexp: A string which contains a list of regular expressions.
- """
- if not isinstance(regexp, str):
- raise ptyError('The argument regexp should be a string.')
- self._dict['uart_regexp'] = ast.literal_eval(regexp)
-
- def _Get_uart_regexp(self):
- """Get the list of regular expressions which matches the command response.
-
- Returns:
- A string which contains a list of regular expressions.
- """
- return str(self._dict['uart_regexp'])
-
- def _Set_uart_cmd(self, cmd):
- """Set the UART command and send it to the device.
-
- If ec_uart_regexp is 'None', the command is just sent and it doesn't care
- about its response.
-
- If ec_uart_regexp is not 'None', the command is send and its response,
- which matches the regular expression of ec_uart_regexp, will be kept.
- Use its getter to obtain this result. If no match after ec_uart_timeout
- seconds, a timeout error will be raised.
-
- Args:
- cmd: A string of UART command.
- """
- if self._dict['uart_regexp']:
- self._dict['uart_cmd'] = self._issue_cmd_get_results(
- cmd, self._dict['uart_regexp'], self._dict['uart_timeout'])
- else:
- self._dict['uart_cmd'] = None
- self._issue_cmd(cmd)
-
- def _Set_uart_multicmd(self, cmds):
- """Set multiple UART commands and send them to the device.
-
- Note that ec_uart_regexp is not supported to match the results.
-
- Args:
- cmds: A semicolon-separated string of UART commands.
- """
- self._issue_cmd(cmds.split(';'))
-
- def _Get_uart_cmd(self):
- """Get the result of the latest UART command.
-
- Returns:
- A string which contains a list of tuples, each of which contains the
- entire matched string and all the subgroups of the match. 'None' if
- the ec_uart_regexp is 'None'.
- """
- return str(self._dict['uart_cmd'])
-
- def _Set_uart_capture(self, cmd):
- """Set UART capture mode (on or off).
-
- Once capture is enabled, UART output could be collected periodically by
- invoking _Get_uart_stream() below.
-
- Args:
- cmd: True for on, False for off
- """
- self._interface.set_capture_active(cmd)
-
- def _Get_uart_capture(self):
- """Get the UART capture mode (on or off)."""
- return self._interface.get_capture_active()
-
- def _Get_uart_stream(self):
- """Get uart stream generated since last time."""
- return self._interface.get_stream()
diff --git a/extra/tigertool/ecusb/stm32uart.py b/extra/tigertool/ecusb/stm32uart.py
deleted file mode 100644
index 95219455a9..0000000000
--- a/extra/tigertool/ecusb/stm32uart.py
+++ /dev/null
@@ -1,248 +0,0 @@
-# Copyright 2017 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-# Ignore indention messages, since legacy scripts use 2 spaces instead of 4.
-# pylint: disable=bad-indentation,docstring-section-indent
-# pylint: disable=docstring-trailing-quotes
-
-"""Allow creation of uart/console interface via stm32 usb endpoint."""
-
-from __future__ import print_function
-
-import os
-import select
-import sys
-import termios
-import threading
-import time
-import tty
-import usb
-
-from . import stm32usb
-
-
-class SuartError(Exception):
- """Class for exceptions of Suart."""
- def __init__(self, msg, value=0):
- """SuartError constructor.
-
- Args:
- msg: string, message describing error in detail
- value: integer, value of error when non-zero status returned. Default=0
- """
- super(SuartError, self).__init__(msg, value)
- self.msg = msg
- self.value = value
-
-
-class Suart(object):
- """Provide interface to stm32 serial usb endpoint."""
- def __init__(self, vendor=0x18d1, product=0x501a, interface=0,
- serialname=None, debuglog=False):
- """Suart contstructor.
-
- Initializes stm32 USB stream interface.
-
- Args:
- vendor: usb vendor id of stm32 device
- product: usb product id of stm32 device
- interface: interface number of stm32 device to use
- serialname: serial name to target. Defaults to None.
- debuglog: chatty output. Defaults to False.
-
- Raises:
- SuartError: If init fails
- """
- self._ptym = None
- self._ptys = None
- self._ptyname = None
- self._rx_thread = None
- self._tx_thread = None
- self._debuglog = debuglog
- self._susb = stm32usb.Susb(vendor=vendor, product=product,
- interface=interface, serialname=serialname)
- self._running = False
-
- def __del__(self):
- """Suart destructor."""
- self.close()
-
- def close(self):
- """Stop all running threads."""
- self._running = False
- if self._rx_thread:
- self._rx_thread.join(2)
- self._rx_thread = None
- if self._tx_thread:
- self._tx_thread.join(2)
- self._tx_thread = None
- self._susb.close()
-
- def run_rx_thread(self):
- """Background loop to pass data from USB to pty."""
- ep = select.epoll()
- ep.register(self._ptym, select.EPOLLHUP)
- try:
- while self._running:
- events = ep.poll(0)
- # Check if the pty is connected to anything, or hungup.
- if not events:
- try:
- r = self._susb._read_ep.read(64, self._susb.TIMEOUT_MS)
- if r:
- if self._debuglog:
- print(''.join([chr(x) for x in r]), end='')
- os.write(self._ptym, r)
-
- # If we miss some characters on pty disconnect, that's fine.
- # ep.read() also throws USBError on timeout, which we discard.
- except OSError:
- pass
- except usb.core.USBError:
- pass
- else:
- time.sleep(.1)
- except Exception as e:
- raise e
-
- def run_tx_thread(self):
- """Background loop to pass data from pty to USB."""
- ep = select.epoll()
- ep.register(self._ptym, select.EPOLLHUP)
- try:
- while self._running:
- events = ep.poll(0)
- # Check if the pty is connected to anything, or hungup.
- if not events:
- try:
- r = os.read(self._ptym, 64)
- # TODO(crosbug.com/936182): Remove when the servo v4/micro console
- # issues are fixed.
- time.sleep(0.001)
- if r:
- self._susb._write_ep.write(r, self._susb.TIMEOUT_MS)
-
- except OSError:
- pass
- except usb.core.USBError:
- pass
- else:
- time.sleep(.1)
- except Exception as e:
- raise e
-
- def run(self):
- """Creates pthreads to poll stm32 & PTY for data."""
- m, s = os.openpty()
- self._ptyname = os.ttyname(s)
-
- self._ptym = m
- self._ptys = s
-
- os.fchmod(s, 0o660)
-
- # Change the owner and group of the PTY to the user who started servod.
- try:
- uid = int(os.environ.get('SUDO_UID', -1))
- except TypeError:
- uid = -1
-
- try:
- gid = int(os.environ.get('SUDO_GID', -1))
- except TypeError:
- gid = -1
- os.fchown(s, uid, gid)
-
- tty.setraw(self._ptym, termios.TCSADRAIN)
-
- # Generate a HUP flag on pty slave fd.
- os.fdopen(s).close()
-
- self._running = True
-
- self._rx_thread = threading.Thread(target=self.run_rx_thread, args=[])
- self._rx_thread.daemon = True
- self._rx_thread.start()
-
- self._tx_thread = threading.Thread(target=self.run_tx_thread, args=[])
- self._tx_thread.daemon = True
- self._tx_thread.start()
-
- def get_uart_props(self):
- """Get the uart's properties.
-
- Returns:
- dict where:
- baudrate: integer of uarts baudrate
- bits: integer, number of bits of data Can be 5|6|7|8 inclusive
- parity: integer, parity of 0-2 inclusive where:
- 0: no parity
- 1: odd parity
- 2: even parity
- sbits: integer, number of stop bits. Can be 0|1|2 inclusive where:
- 0: 1 stop bit
- 1: 1.5 stop bits
- 2: 2 stop bits
- """
- return {
- 'baudrate': 115200,
- 'bits': 8,
- 'parity': 0,
- 'sbits': 1,
- }
-
- def set_uart_props(self, line_props):
- """Set the uart's properties.
-
- Note that Suart cannot set properties
- and will fail if the properties are not the default 115200,8n1.
-
- Args:
- line_props: dict where:
- baudrate: integer of uarts baudrate
- bits: integer, number of bits of data ( prior to stop bit)
- parity: integer, parity of 0-2 inclusive where
- 0: no parity
- 1: odd parity
- 2: even parity
- sbits: integer, number of stop bits. Can be 0|1|2 inclusive where:
- 0: 1 stop bit
- 1: 1.5 stop bits
- 2: 2 stop bits
-
- Raises:
- SuartError: If requested line properties are not the default.
- """
- curr_props = self.get_uart_props()
- for prop in line_props:
- if line_props[prop] != curr_props[prop]:
- raise SuartError('Line property %s cannot be set from %s to %s' % (
- prop, curr_props[prop], line_props[prop]))
- return True
-
- def get_pty(self):
- """Gets path to pty for communication to/from uart.
-
- Returns:
- String path to the pty connected to the uart
- """
- return self._ptyname
-
-
-def main():
- """Run a suart test with the default parameters."""
- try:
- sobj = Suart()
- sobj.run()
-
- # run() is a thread so just busy wait to mimic server.
- while True:
- # Ours sleeps to eleven!
- time.sleep(11)
- except KeyboardInterrupt:
- sys.exit(0)
-
-
-if __name__ == '__main__':
- main()
diff --git a/extra/tigertool/ecusb/stm32usb.py b/extra/tigertool/ecusb/stm32usb.py
deleted file mode 100644
index bfd5fbb1fb..0000000000
--- a/extra/tigertool/ecusb/stm32usb.py
+++ /dev/null
@@ -1,119 +0,0 @@
-# Copyright 2017 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-# Ignore indention messages, since legacy scripts use 2 spaces instead of 4.
-# pylint: disable=bad-indentation,docstring-section-indent
-# pylint: disable=docstring-trailing-quotes
-
-"""Allows creation of an interface via stm32 usb."""
-
-import usb
-
-
-class SusbError(Exception):
- """Class for exceptions of Susb."""
- def __init__(self, msg, value=0):
- """SusbError constructor.
-
- Args:
- msg: string, message describing error in detail
- value: integer, value of error when non-zero status returned. Default=0
- """
- super(SusbError, self).__init__(msg, value)
- self.msg = msg
- self.value = value
-
-
-class Susb(object):
- """Provide stm32 USB functionality.
-
- Instance Variables:
- _read_ep: pyUSB read endpoint for this interface
- _write_ep: pyUSB write endpoint for this interface
- """
- READ_ENDPOINT = 0x81
- WRITE_ENDPOINT = 0x1
- TIMEOUT_MS = 100
-
- def __init__(self, vendor=0x18d1,
- product=0x5027, interface=1, serialname=None, logger=None):
- """Susb constructor.
-
- Discovers and connects to stm32 USB endpoints.
-
- Args:
- vendor: usb vendor id of stm32 device.
- product: usb product id of stm32 device.
- interface: interface number ( 1 - 4 ) of stm32 device to use.
- serialname: string of device serialname.
- logger: none
-
- Raises:
- SusbError: An error accessing Susb object
- """
- self._vendor = vendor
- self._product = product
- self._interface = interface
- self._serialname = serialname
- self._find_device()
-
- def _find_device(self):
- """Set up the usb endpoint"""
- # Find the stm32.
- dev_g = usb.core.find(idVendor=self._vendor, idProduct=self._product,
- find_all=True)
- dev_list = list(dev_g)
-
- if not dev_list:
- raise SusbError('USB device not found')
-
- # Check if we have multiple stm32s and we've specified the serial.
- dev = None
- if self._serialname:
- for d in dev_list:
- dev_serial = usb.util.get_string(d, d.iSerialNumber)
- if dev_serial == self._serialname:
- dev = d
- break
- if dev is None:
- raise SusbError('USB device(%s) not found' % self._serialname)
- else:
- try:
- dev = dev_list[0]
- except StopIteration:
- raise SusbError('USB device %04x:%04x not found' % (
- self._vendor, self._product))
-
- # If we can't set configuration, it's already been set.
- try:
- dev.set_configuration()
- except usb.core.USBError:
- pass
-
- self._dev = dev
-
- # Get an endpoint instance.
- cfg = dev.get_active_configuration()
- intf = usb.util.find_descriptor(cfg, bInterfaceNumber=self._interface)
- self._intf = intf
- if not intf:
- raise SusbError('Interface %04x:%04x - 0x%x not found' % (
- self._vendor, self._product, self._interface))
-
- # Detach raiden.ko if it is loaded. CCD endpoints support either a kernel
- # module driver that produces a ttyUSB, or direct endpoint access, but
- # can't do both at the same time.
- if dev.is_kernel_driver_active(intf.bInterfaceNumber) is True:
- dev.detach_kernel_driver(intf.bInterfaceNumber)
-
- read_ep_number = intf.bInterfaceNumber + self.READ_ENDPOINT
- read_ep = usb.util.find_descriptor(intf, bEndpointAddress=read_ep_number)
- self._read_ep = read_ep
-
- write_ep_number = intf.bInterfaceNumber + self.WRITE_ENDPOINT
- write_ep = usb.util.find_descriptor(intf, bEndpointAddress=write_ep_number)
- self._write_ep = write_ep
-
- def close(self):
- usb.util.dispose_resources(self._dev)
diff --git a/extra/tigertool/ecusb/tiny_servo_common.py b/extra/tigertool/ecusb/tiny_servo_common.py
deleted file mode 100644
index 152c238bdf..0000000000
--- a/extra/tigertool/ecusb/tiny_servo_common.py
+++ /dev/null
@@ -1,174 +0,0 @@
-# Copyright 2017 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-# Ignore indention messages, since legacy scripts use 2 spaces instead of 4.
-# pylint: disable=bad-indentation,docstring-section-indent
-# pylint: disable=docstring-trailing-quotes
-
-"""Utilities for using lightweight console functions."""
-
-# Note: This is a py2/3 compatible file.
-
-import datetime
-import errno
-import os
-import re
-import subprocess
-import sys
-import time
-
-import six
-
-from . import pty_driver
-from . import stm32uart
-
-
-def get_subprocess_args():
- if six.PY3:
- return {'encoding': 'utf-8'}
- return {}
-
-
-class TinyServoError(Exception):
- """Exceptions."""
-
-
-def log(output):
- """Print output to console, logfiles can be added here.
-
- Args:
- output: string to output.
- """
- sys.stdout.write(output)
- sys.stdout.write('\n')
- sys.stdout.flush()
-
-def check_usb(vidpid, serialname=None):
- """Check if |vidpid| is present on the system's USB.
-
- Args:
- vidpid: string representation of the usb vid:pid, eg. '18d1:2001'
- serialname: serialname if specified.
-
- Returns: True if found, False, otherwise.
- """
- if serialname:
- output = subprocess.check_output(['lsusb', '-v', '-d', vidpid],
- **get_subprocess_args())
- m = re.search(r'^\s*iSerial\s+\d+\s+%s$' % serialname, output, flags=re.M)
- if m:
- return True
-
- return False
- else:
- if subprocess.call(['lsusb', '-d', vidpid], stdout=open('/dev/null', 'w')):
- return False
- return True
-
-def check_usb_sn(vidpid):
- """Return the serial number
-
- Return the serial number of the first USB device with VID:PID vidpid,
- or None if no device is found. This will not work well with two of
- the same device attached.
-
- Args:
- vidpid: string representation of the usb vid:pid, eg. '18d1:2001'
-
- Returns: string serial number if found, None otherwise.
- """
- output = subprocess.check_output(['lsusb', '-v', '-d', vidpid],
- **get_subprocess_args())
- m = re.search(r'^\s*iSerial\s+(.*)$', output, flags=re.M)
- if m:
- return m.group(1)
-
- return None
-
-def wait_for_usb_remove(vidpid, serialname=None, timeout=None):
- """Wait for USB device with vidpid to be removed.
-
- Wrapper for wait_for_usb below
- """
- wait_for_usb(vidpid, serialname=serialname,
- timeout=timeout, desiredpresence=False)
-
-def wait_for_usb(vidpid, serialname=None, timeout=None, desiredpresence=True):
- """Wait for usb device with vidpid to be present/absent.
-
- Args:
- vidpid: string representation of the usb vid:pid, eg. '18d1:2001'
- serialname: serialname if specificed.
- timeout: timeout in seconds, None for no timeout.
- desiredpresence: True for present, False for not present.
-
- Raises:
- TinyServoError: on timeout.
- """
- if timeout:
- finish = datetime.datetime.now() + datetime.timedelta(seconds=timeout)
- while check_usb(vidpid, serialname) != desiredpresence:
- time.sleep(.1)
- if timeout:
- if datetime.datetime.now() > finish:
- raise TinyServoError('Timeout', 'Timeout waiting for USB %s' % vidpid)
-
-def do_serialno(serialno, pty):
- """Set serialnumber 'serialno' via ec console 'pty'.
-
- Commands are:
- # > serialno set 1234
- # Saving serial number
- # Serial number: 1234
-
- Args:
- serialno: string serial number to set.
- pty: tinyservo console to send commands.
-
- Raises:
- TinyServoError: on failure to set.
- ptyError: on command interface error.
- """
- cmd = 'serialno set %s' % serialno
- regex = 'Serial number:\s+(\S+)'
-
- results = pty._issue_cmd_get_results(cmd, [regex])[0]
- sn = results[1].strip().strip('\n\r')
-
- if sn == serialno:
- log('Success !')
- log('Serial set to %s' % sn)
- else:
- log('Serial number set to %s but saved as %s.' % (serialno, sn))
- raise TinyServoError(
- 'Serial Number',
- 'Serial number set to %s but saved as %s.' % (serialno, sn))
-
-def setup_tinyservod(vidpid, interface, serialname=None, debuglog=False):
- """Set up a pty
-
- Set up a pty to the ec console in order
- to send commands. Returns a pty_driver object.
-
- Args:
- vidpid: string vidpid of device to access.
- interface: not used.
- serialname: string serial name of device requested, optional.
- debuglog: chatty printout (boolean)
-
- Returns: pty object
-
- Raises:
- UsbError, SusbError: on device not found
- """
- vidstr, pidstr = vidpid.split(':')
- vid = int(vidstr, 16)
- pid = int(pidstr, 16)
- suart = stm32uart.Suart(vendor=vid, product=pid,
- interface=interface, serialname=serialname,
- debuglog=debuglog)
- suart.run()
- pty = pty_driver.ptyDriver(suart, [])
-
- return pty
diff --git a/extra/tigertool/ecusb/tiny_servod.py b/extra/tigertool/ecusb/tiny_servod.py
deleted file mode 100644
index 632d9c3a20..0000000000
--- a/extra/tigertool/ecusb/tiny_servod.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-# Ignore indention messages, since legacy scripts use 2 spaces instead of 4.
-# pylint: disable=bad-indentation,docstring-section-indent
-# pylint: disable=docstring-trailing-quotes
-
-"""Helper class to facilitate communication to servo ec console."""
-
-from ecusb import pty_driver
-from ecusb import stm32uart
-
-
-class TinyServod(object):
- """Helper class to wrap a pty_driver with interface."""
-
- def __init__(self, vid, pid, interface, serialname=None, debug=False):
- """Build the driver and interface.
-
- Args:
- vid: servo device vid
- pid: servo device pid
- interface: which usb interface the servo console is on
- serialname: the servo device serial (if available)
- """
- self._vid = vid
- self._pid = pid
- self._interface = interface
- self._serial = serialname
- self._debug = debug
- self._init()
-
- def _init(self):
- self.suart = stm32uart.Suart(vendor=self._vid,
- product=self._pid,
- interface=self._interface,
- serialname=self._serial,
- debuglog=self._debug)
- self.suart.run()
- self.pty = pty_driver.ptyDriver(self.suart, [])
-
- def reinitialize(self):
- """Reinitialize the connect after a reset/disconnect/etc."""
- self.close()
- self._init()
-
- def close(self):
- """Close out the connection and release resources.
-
- Note: if another TinyServod process or servod itself needs the same device
- it's necessary to call this to ensure the usb device is available.
- """
- self.suart.close()
diff --git a/extra/tigertool/flash_dfu.sh b/extra/tigertool/flash_dfu.sh
deleted file mode 100755
index 7aa6c24f09..0000000000
--- a/extra/tigertool/flash_dfu.sh
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/bin/bash
-# Copyright 2017 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-FLAGS_timeout=600
-IMG=${1:-tigertail.bin}
-
-echo "Flashing ${IMG}"
-
-error() {
- printf "%s\n" "$*" >&2
-}
-
-die() {
- [[ "$#*" == "0" ]] || error "$@"
- exit 1
-}
-
-flash_stm32_dfu() {
- local DFU_DEVICE=0483:df11
- local ADDR=0x08000000
-
- [[ -e "${IMG}" ]] || die "File ${IMG} not found!"
-
- # Check for a suitable local dfu-util
- local LOCAL_DFU_UTIL=$(which dfu-util)
- if [[ -n "${LOCAL_DFU_UTIL}" ]]; then
- DFU_VERSION=$("${LOCAL_DFU_UTIL}" -V | head -n1 | cut -d' ' -f2)
- if [[ "${DFU_VERSION}" < "0.7" ]]; then
- LOCAL_DFU_UTIL=""
- fi
- fi
- local DFU_UTIL=${LOCAL_DFU_UTIL:-'./dfu-util'}
-
- which "${DFU_UTIL}" &> /dev/null || die \
- "no dfu-util util found. Did you 'sudo emerge dfu-util'."
-
- local dev_cnt=$(lsusb -d "${DFU_DEVICE}" | wc -l)
- if [ $dev_cnt -eq 0 ] ; then
- die "unable to locate dfu device at ${DFU_DEVICE}."
- elif [ $dev_cnt -ne 1 ] ; then
- die "too many dfu devices (${dev_cnt}). Disconnect all but one."
- fi
-
- local SIZE=$(wc -c "${IMG}" | cut -d' ' -f1)
- # Remove read protection.
- sudo timeout -k 10 -s 9 "${FLAGS_timeout}" \
- ${DFU_UTIL} -a 0 -s ${ADDR}:${SIZE}:force:unprotect -D "${IMG}"
- # Wait for mass-erase and reboot after unprotection.
- sleep 1
- # Actual image flashing.
- sudo timeout -k 10 -s 9 "${FLAGS_timeout}" \
- $DFU_UTIL -a 0 -s ${ADDR}:${SIZE} -D "${IMG}"
-}
-
-flash_stm32_dfu
diff --git a/extra/tigertool/make_pkg.sh b/extra/tigertool/make_pkg.sh
deleted file mode 100755
index d2860f64c1..0000000000
--- a/extra/tigertool/make_pkg.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/bash
-# Copyright 2017 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# Make sure we are in the correct dir.
-cd "$( dirname "${BASH_SOURCE[0]}" )" || exit
-
-# Clean and previous cruft.
-rm -rf build
-
-DEST=build/tigertool
-DATE=$(date +"%Y%m%d")
-
-mkdir -p "${DEST}"
-cp ../usb_serial/console.py "${DEST}"
-cp ../../../../../chroot/usr/bin/dfu-util "${DEST}"
-cp flash_dfu.sh "${DEST}"
-cp tigertool.py "${DEST}"
-
-cp -r ecusb "${DEST}"
-cp -r ../../../../../chroot/usr/lib64/python2.7/site-packages/usb "${DEST}"
-find "${DEST}" -name "*.py[co]" -delete
-cp -r ../usb_serial "${DEST}"
-
-(cd build; tar -czf tigertool_${DATE}.tgz tigertool)
-
-echo "Done packaging tigertool_${DATE}.tgz"
diff --git a/extra/tigertool/tigertool.py b/extra/tigertool/tigertool.py
deleted file mode 100755
index 79aa30c3a4..0000000000
--- a/extra/tigertool/tigertool.py
+++ /dev/null
@@ -1,266 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2017 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-# Ignore indention messages, since legacy scripts use 2 spaces instead of 4.
-# pylint: disable=bad-indentation,docstring-section-indent
-# pylint: disable=docstring-trailing-quotes
-
-"""Script to control tigertail USB-C Mux board."""
-
-# Note: This is a py2/3 compatible file.
-
-import argparse
-import sys
-import time
-
-import ecusb.tiny_servo_common as c
-
-STM_VIDPID = '18d1:5027'
-serialno = 'Uninitialized'
-
-def do_mux(mux, pty):
- """Set mux via ec console 'pty'.
-
- Args:
- mux: mux to connect to DUT, 'A', 'B', or 'off'
- pty: a pty object connected to tigertail
-
- Commands are:
- # > mux A
- # TYPE-C mux is A
- """
- validmux = ['A', 'B', 'off']
- if mux not in validmux:
- c.log('Mux setting %s invalid, try one of %s' % (mux, validmux))
- return False
-
- cmd = '\r\nmux %s\r\n' % mux
- regex = 'TYPE\-C mux is ([^\s\r\n]*)\r'
-
- results = pty._issue_cmd_get_results(cmd, [regex])[0]
- result = results[1].strip().strip('\n\r')
-
- if result != mux:
- c.log('Mux set to %s but saved as %s.' % (mux, result))
- return False
- c.log('Mux set to %s' % result)
- return True
-
-def do_version(pty):
- """Check version via ec console 'pty'.
-
- Args:
- pty: a pty object connected to tigertail
-
- Commands are:
- # > version
- # Chip: stm stm32f07x
- # Board: 0
- # RO: tigertail_v1.1.6749-74d1a312e
- # RW: tigertail_v1.1.6749-74d1a312e
- # Build: tigertail_v1.1.6749-74d1a312e
- # 2017-07-25 20:08:34 nsanders@meatball.mtv.corp.google.com
-
- """
- cmd = '\r\nversion\r\n'
- regex = 'RO:\s+(\S+)\s+RW:\s+(\S+)\s+Build:\s+(\S+)\s+' \
- '(\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d) (\S+)'
-
- results = pty._issue_cmd_get_results(cmd, [regex])[0]
- c.log('Version is %s' % results[3])
- c.log('RO: %s' % results[1])
- c.log('RW: %s' % results[2])
- c.log('Date: %s' % results[4])
- c.log('Src: %s' % results[5])
-
- return True
-
-def do_check_serial(pty):
- """Check serial via ec console 'pty'.
-
- Args:
- pty: a pty object connected to tigertail
-
- Commands are:
- # > serialno
- # Serial number: number
- """
- cmd = '\r\nserialno\r\n'
- regex = 'Serial number: ([^\n\r]+)'
-
- results = pty._issue_cmd_get_results(cmd, [regex])[0]
- c.log('Serial is %s' % results[1])
-
- return True
-
-
-def do_power(count, bus, pty):
- """Check power usage via ec console 'pty'.
-
- Args:
- count: number of samples to capture
- bus: rail to monitor, 'vbus', 'cc1', or 'cc2'
- pty: a pty object connected to tigertail
-
- Commands are:
- # > ina 0
- # Configuration: 4127
- # Shunt voltage: 02c4 => 1770 uV
- # Bus voltage : 1008 => 5130 mV
- # Power : 0019 => 625 mW
- # Current : 0082 => 130 mA
- # Calibration : 0155
- # Mask/Enable : 0008
- # Alert limit : 0000
- """
- if bus == 'vbus':
- ina = 0
- if bus == 'cc1':
- ina = 4
- if bus == 'cc2':
- ina = 1
-
- start = time.time()
-
- c.log('time,\tmV,\tmW,\tmA')
-
- cmd = '\r\nina %s\r\n' % ina
- regex = 'Bus voltage : \S+ \S+ (\d+) mV\s+' \
- 'Power : \S+ \S+ (\d+) mW\s+' \
- 'Current : \S+ \S+ (\d+) mA'
-
- for i in range(0, count):
- results = pty._issue_cmd_get_results(cmd, [regex])[0]
- c.log('%.2f,\t%s,\t%s\t%s' % (time.time() - start,
- results[1], results[2], results[3]))
-
- return True
-
-def do_reboot(pty):
- """Reboot via ec console pty
-
- Args:
- pty: a pty object connected to tigertail
-
- Command is: reboot.
- """
- cmd = '\r\nreboot\r\n'
- regex = 'Rebooting'
-
- try:
- results = pty._issue_cmd_get_results(cmd, [regex])[0]
- time.sleep(1)
- c.log(results)
- except Exception as e:
- c.log(e)
- return False
-
- return True
-
-def do_sysjump(region, pty):
- """Set region via ec console 'pty'.
-
- Args:
- region: ec code region to execute, 'ro' or 'rw'
- pty: a pty object connected to tigertail
-
- Commands are:
- # > sysjump rw
- """
- validregion = ['ro', 'rw']
- if region not in validregion:
- c.log('Region setting %s invalid, try one of %s' % (
- region, validregion))
- return False
-
- cmd = '\r\nsysjump %s\r\n' % region
- try:
- pty._issue_cmd(cmd)
- time.sleep(1)
- except Exception as e:
- c.log(e)
- return False
-
- c.log('Region requested %s' % region)
- return True
-
-def get_parser():
- parser = argparse.ArgumentParser(
- description=__doc__)
- parser.add_argument('-s', '--serialno', type=str, default=None,
- help='serial number of board to use')
- parser.add_argument('-b', '--bus', type=str, default='vbus',
- help='Which rail to log: [vbus|cc1|cc2]')
- group = parser.add_mutually_exclusive_group()
- group.add_argument('--setserialno', type=str, default=None,
- help='serial number to set on the board.')
- group.add_argument('--check_serial', action='store_true',
- help='check serial number set on the board.')
- group.add_argument('-m', '--mux', type=str, default=None,
- help='mux selection')
- group.add_argument('-p', '--power', action='store_true',
- help='check VBUS')
- group.add_argument('-l', '--powerlog', type=int, default=None,
- help='log VBUS')
- group.add_argument('-r', '--sysjump', type=str, default=None,
- help='region selection')
- group.add_argument('--reboot', action='store_true',
- help='reboot tigertail')
- group.add_argument('--check_version', action='store_true',
- help='check tigertail version')
- return parser
-
-def main(argv):
- parser = get_parser()
- opts = parser.parse_args(argv)
-
- result = True
-
- # Let's make sure there's a tigertail
- # If nothing found in 5 seconds, fail.
- c.wait_for_usb(STM_VIDPID, timeout=5., serialname=opts.serialno)
-
- pty = c.setup_tinyservod(STM_VIDPID, 0, serialname=opts.serialno)
-
- if opts.bus not in ('vbus', 'cc1', 'cc2'):
- c.log('Try --bus [vbus|cc1|cc2]')
- result = False
-
- elif opts.setserialno:
- try:
- c.do_serialno(opts.setserialno, pty)
- except Exception:
- result = False
-
- elif opts.mux:
- result &= do_mux(opts.mux, pty)
-
- elif opts.sysjump:
- result &= do_sysjump(opts.sysjump, pty)
-
- elif opts.reboot:
- result &= do_reboot(pty)
-
- elif opts.check_version:
- result &= do_version(pty)
-
- elif opts.check_serial:
- result &= do_check_serial(pty)
-
- elif opts.power:
- result &= do_power(1, opts.bus, pty)
-
- elif opts.powerlog:
- result &= do_power(opts.powerlog, opts.bus, pty)
-
- if result:
- c.log('PASS')
- else:
- c.log('FAIL')
- exit(-1)
-
-
-if __name__ == '__main__':
- sys.exit(main(sys.argv[1:]))
diff --git a/extra/touchpad_updater/Makefile b/extra/touchpad_updater/Makefile
deleted file mode 100644
index ebf9c3212d..0000000000
--- a/extra/touchpad_updater/Makefile
+++ /dev/null
@@ -1,36 +0,0 @@
-# Copyright 2017 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-CC ?= gcc
-PKG_CONFIG ?= pkg-config
-PROGRAM := touchpad_updater
-SOURCE := $(PROGRAM).c
-LIBS :=
-LFLAGS :=
-CFLAGS := -std=gnu99 \
- -g3 \
- -O3 \
- -Wall \
- -Werror \
- -Wpointer-arith \
- -Wcast-align \
- -Wcast-qual \
- -Wundef \
- -Wsign-compare \
- -Wredundant-decls \
- -Wmissing-declarations
-
-#
-# Add libusb-1.0 required flags
-#
-LIBS += $(shell $(PKG_CONFIG) --libs libusb-1.0)
-CFLAGS += $(shell $(PKG_CONFIG) --cflags libusb-1.0)
-
-$(PROGRAM): $(SOURCE) Makefile
- $(CC) $(CFLAGS) $(SOURCE) $(LFLAGS) $(LIBS) -o $@
-
-.PHONY: clean
-
-clean:
- rm -rf $(PROGRAM) *~
diff --git a/extra/touchpad_updater/touchpad_updater.c b/extra/touchpad_updater/touchpad_updater.c
deleted file mode 100644
index 716ded00f5..0000000000
--- a/extra/touchpad_updater/touchpad_updater.c
+++ /dev/null
@@ -1,669 +0,0 @@
-/*
- * Copyright 2017 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include <errno.h>
-#include <getopt.h>
-#include <poll.h>
-#include <signal.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/select.h>
-#include <unistd.h>
-
-#include <libusb.h>
-
-/* Command line options */
-static uint16_t vid = 0x18d1; /* Google */
-static uint16_t pid = 0x5022; /* Hammer */
-static uint8_t ep_num = 4; /* console endpoint */
-static uint8_t extended_i2c_exercise; /* non-zero to exercise */
-static char *firmware_binary = "144.0_2.0.bin"; /* firmware blob */
-
-/* Firmware binary blob related */
-#define MAX_FW_PAGE_SIZE 512
-#define MAX_FW_PAGE_COUNT 1024
-#define MAX_FW_SIZE (128 * 1024)
-
-static uint8_t fw_data[MAX_FW_SIZE];
-int fw_page_count;
-int fw_page_size;
-int fw_size;
-uint8_t ic_type;
-int iap_version;
-
-/* Utility functions */
-static int le_bytes_to_int(uint8_t *buf)
-{
- return buf[0] + (int)(buf[1] << 8);
-}
-
-/* Command line parsing related */
-static char *progname;
-static char *short_opts = ":f:v:p:e:hd";
-static const struct option long_opts[] = {
- /* name hasarg *flag val */
- {"file", 1, NULL, 'f'},
- {"vid", 1, NULL, 'v'},
- {"pid", 1, NULL, 'p'},
- {"ep", 1, NULL, 'e'},
- {"help", 0, NULL, 'h'},
- {"debug", 0, NULL, 'd'},
- {NULL, 0, NULL, 0},
-};
-
-static void usage(int errs)
-{
- printf("\nUsage: %s [options]\n"
- "\n"
- "Firmware updater over USB for trackpad under hammer\n"
- "\n"
- "Options:\n"
- "\n"
- " -f,--file STR Firmware binary (default %s)\n"
- " -v,--vid HEXVAL Vendor ID (default %04x)\n"
- " -p,--pid HEXVAL Product ID (default %04x)\n"
- " -e,--ep NUM Endpoint (default %d)\n"
- " -d,--debug Exercise extended read I2C over USB\n"
- " and print verbose debug messages.\n"
- " -h,--help Show this message\n"
- "\n", progname, firmware_binary, vid, pid, ep_num);
-
- exit(!!errs);
-}
-
-static void parse_cmdline(int argc, char *argv[])
-{
- char *e = 0;
- int i, errorcnt = 0;
-
- progname = strrchr(argv[0], '/');
- if (progname)
- progname++;
- else
- progname = argv[0];
-
- opterr = 0; /* quiet, you */
- while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) {
- switch (i) {
- case 'f':
- firmware_binary = optarg;
- break;
- case 'p':
- pid = (uint16_t) strtoull(optarg, &e, 16);
- if (!*optarg || (e && *e)) {
- printf("Invalid argument: \"%s\"\n", optarg);
- errorcnt++;
- }
- break;
- case 'v':
- vid = (uint16_t) strtoull(optarg, &e, 16);
- if (!*optarg || (e && *e)) {
- printf("Invalid argument: \"%s\"\n", optarg);
- errorcnt++;
- }
- break;
- case 'e':
- ep_num = (uint8_t) strtoull(optarg, &e, 0);
- if (!*optarg || (e && *e)) {
- printf("Invalid argument: \"%s\"\n", optarg);
- errorcnt++;
- }
- break;
- case 'd':
- extended_i2c_exercise = 1;
- break;
- case 'h':
- usage(errorcnt);
- break;
- case 0: /* auto-handled option */
- break;
- case '?':
- if (optopt)
- printf("Unrecognized option: -%c\n", optopt);
- else
- printf("Unrecognized option: %s\n",
- argv[optind - 1]);
- errorcnt++;
- break;
- case ':':
- printf("Missing argument to %s\n", argv[optind - 1]);
- errorcnt++;
- break;
- default:
- printf("Internal error at %s:%d\n", __FILE__, __LINE__);
- exit(1);
- }
- }
-
- if (errorcnt)
- usage(errorcnt);
-
-}
-
-/* USB transfer related */
-static uint8_t rx_buf[1024];
-static uint8_t tx_buf[1024];
-
-static struct libusb_device_handle *devh;
-static struct libusb_transfer *rx_transfer;
-static struct libusb_transfer *tx_transfer;
-
-static int claimed_iface;
-static int iface_num = -1;
-static int do_exit;
-
-static void request_exit(const char *format, ...)
-{
- va_list ap;
- va_start(ap, format);
- vfprintf(stderr, format, ap);
- va_end(ap);
- do_exit++; /* Why need this ? */
-
- if (tx_transfer)
- libusb_free_transfer(tx_transfer);
- if (rx_transfer)
- libusb_free_transfer(rx_transfer);
- if (devh) {
- if (claimed_iface)
- libusb_release_interface(devh, iface_num);
- libusb_close(devh);
- }
- libusb_exit(NULL);
- exit(1);
-}
-
-#define DIE(msg, r) \
- request_exit("%s: line %d, %s\n", msg, __LINE__, \
- libusb_error_name(r))
-
-static void sighandler(int signum)
-{
- request_exit("caught signal %d: %s\n", signum, strsignal(signum));
-}
-
-static int find_interface_with_endpoint(int want_ep_num)
-{
- int iface_num = -1;
- int r, i, j, k;
- struct libusb_device *dev;
- struct libusb_config_descriptor *conf = 0;
- const struct libusb_interface *iface0;
- const struct libusb_interface_descriptor *iface;
- const struct libusb_endpoint_descriptor *ep;
-
- dev = libusb_get_device(devh);
- r = libusb_get_active_config_descriptor(dev, &conf);
- if (r < 0) {
- DIE("get_active_config", r);
- return -1;
- }
-
- for (i = 0; i < conf->bNumInterfaces; i++) {
- iface0 = &conf->interface[i];
- for (j = 0; j < iface0->num_altsetting; j++) {
- iface = &iface0->altsetting[j];
- for (k = 0; k < iface->bNumEndpoints; k++) {
- ep = &iface->endpoint[k];
- if (ep->bEndpointAddress == want_ep_num) {
- iface_num = i;
- break;
- }
- }
- }
- }
-
- libusb_free_config_descriptor(conf);
- return iface_num;
-}
-
-static void init_with_libusb(void)
-{
- int r = 1;
-
- printf("init usb interface\n");
- r = libusb_init(NULL);
- if (r < 0)
- DIE("init", r);
-
- printf("open_device %04x:%04x\n", vid, pid);
- devh = libusb_open_device_with_vid_pid(NULL, vid, pid);
- if (!devh)
- request_exit("can't find device\n");
-
- iface_num = find_interface_with_endpoint(ep_num);
- if (iface_num < 0)
- request_exit("can't find interface owning EP %d\n", ep_num);
-
- printf("claim_interface %d to use endpoint %d\n", iface_num, ep_num);
- r = libusb_claim_interface(devh, iface_num);
- if (r < 0)
- DIE("claim interface", r);
- claimed_iface = 1;
-}
-
-static void register_sigaction(void)
-{
- struct sigaction sigact;
- sigact.sa_handler = sighandler;
- sigemptyset(&sigact.sa_mask);
- sigact.sa_flags = 0;
- sigaction(SIGINT, &sigact, NULL);
- sigaction(SIGTERM, &sigact, NULL);
- sigaction(SIGQUIT, &sigact, NULL);
-}
-
-/* Transfer over libusb */
-#define I2C_PORT_ON_HAMMER 0x00
-#define I2C_ADDRESS_ON_HAMMER 0x15
-
-static int check_read_status(int r, int expected, int actual)
-{
- int i;
- if (r)
- printf("Warning: libusb_bulk_transfer return error : %d\n", r);
- if (actual != (expected + 4)) {
- printf("Warning: Not reading back %d bytes.\n", expected);
- r = 1;
- }
-
- /* Check transaction status as defined in usb_i2c.h */
- for (i = 0; i < 4; ++i)
- if (rx_buf[i] != 0)
- break;
-
- if (i != 4) {
- r = le_bytes_to_int(rx_buf);
- printf("Warning: Defined error code (%d) returned.\n", r);
- }
-
- if (r || extended_i2c_exercise) {
- printf("\nDumping the receive buffer:\n");
- printf(" Recv %d bytes from USB hosts.\n", actual);
- for (i = 0; i < actual; ++i)
- printf(" [%2d]bytes: 0x%0x\n", i, rx_buf[i]);
- }
- return r;
-}
-
-#define MAX_USB_PACKET_SIZE 64
-#define PRIMITIVE_READING_SIZE 60
-
-static int libusb_single_write_and_read(
- const uint8_t *to_write, uint16_t write_length,
- uint8_t *to_read, uint16_t read_length)
-{
- int r;
- int tx_ready;
- int remains;
- int sent_bytes = 0;
- int actual_length = -1;
- int offset = read_length > PRIMITIVE_READING_SIZE ? 6 : 4;
- tx_transfer = rx_transfer = 0;
-
- memmove(tx_buf + offset, to_write, write_length);
- tx_buf[0] = I2C_PORT_ON_HAMMER | ((write_length >> 8) << 4);
- tx_buf[1] = I2C_ADDRESS_ON_HAMMER;
- tx_buf[2] = write_length & 0xff;
- if (read_length > PRIMITIVE_READING_SIZE) {
- tx_buf[3] = (read_length & 0x7f) | (1 << 7);
- tx_buf[4] = read_length >> 7;
- if (extended_i2c_exercise) {
- printf("Triggering extended reading."
- "rc:%0x, rc1:%0x\n",
- tx_buf[3], tx_buf[4]);
- printf("Expecting %d Bytes.\n",
- (tx_buf[3] & 0x7f) | (tx_buf[4] << 7));
- }
- } else {
- tx_buf[3] = read_length;
- }
-
- /*
- * TODO: This loop is probably not required as we write the whole block
- * in one transaction.
- */
- while (sent_bytes < (offset + write_length)) {
- tx_ready = remains = (offset + write_length) - sent_bytes;
-
- r = libusb_bulk_transfer(devh,
- (ep_num | LIBUSB_ENDPOINT_OUT),
- tx_buf + sent_bytes, tx_ready,
- &actual_length, 5000);
- if (r == 0 && actual_length == tx_ready) {
- r = libusb_bulk_transfer(devh,
- (ep_num | LIBUSB_ENDPOINT_IN),
- rx_buf, sizeof(rx_buf),
- &actual_length, 5000);
- }
- r = check_read_status(
- r, (remains == tx_ready) ? read_length : 0,
- actual_length);
- if (r)
- break;
- sent_bytes += tx_ready;
- }
- return r;
-}
-
-/* Control Elan trackpad I2C over USB */
-#define ETP_I2C_INF_LENGTH 2
-
-static int elan_write_and_read(
- int reg, uint8_t *buf, int read_length,
- int with_cmd, int cmd)
-{
-
- tx_buf[0] = (reg >> 0) & 0xff;
- tx_buf[1] = (reg >> 8) & 0xff;
- if (with_cmd) {
- tx_buf[2] = (cmd >> 0) & 0xff;
- tx_buf[3] = (cmd >> 8) & 0xff;
- }
- return libusb_single_write_and_read(
- tx_buf, with_cmd ? 4 : 2, rx_buf, read_length);
-}
-
-static int elan_read_block(int reg, uint8_t *buf, int read_length)
-{
- return elan_write_and_read(reg, buf, read_length, 0, 0);
-}
-
-static int elan_read_cmd(int reg)
-{
- return elan_read_block(reg, rx_buf, ETP_I2C_INF_LENGTH);
-}
-
-static int elan_write_cmd(int reg, int cmd)
-{
- return elan_write_and_read(reg, rx_buf, 0, 1, cmd);
-}
-
-/* Elan trackpad firmware information related */
-#define ETP_I2C_IAP_VERSION_CMD 0x0110
-#define ETP_I2C_FW_VERSION_CMD 0x0102
-#define ETP_I2C_IAP_CHECKSUM_CMD 0x0315
-#define ETP_I2C_FW_CHECKSUM_CMD 0x030F
-#define ETP_I2C_OSM_VERSION_CMD 0x0103
-
-static int elan_get_version(int is_iap)
-{
- elan_read_cmd(
- is_iap ? ETP_I2C_IAP_VERSION_CMD : ETP_I2C_FW_VERSION_CMD);
- return le_bytes_to_int(rx_buf + 4);
-}
-
-static void elan_get_ic_page_count(void)
-{
- elan_read_cmd(ETP_I2C_OSM_VERSION_CMD);
-
- ic_type = rx_buf[5];
- printf("ic_type: %02x\n", ic_type);
-
- switch (ic_type) {
- case 0x09:
- fw_page_count = 768;
- break;
- case 0x0D:
- fw_page_count = 896;
- break;
- case 0x00:
- case 0x10:
- case 0x14:
- fw_page_count = 1024;
- break;
- default:
- request_exit("The IC type is not supported.\n");
- }
-
- iap_version = elan_get_version(1);
- if (ic_type == 0x14 && iap_version >= 2) {
- fw_page_count /= 8;
- fw_page_size = 512;
- } else if (ic_type >= 0x0D && iap_version >= 1) {
- fw_page_count /= 2;
- fw_page_size = 128;
- } else {
- fw_page_size = 64;
- }
-}
-
-static int elan_get_checksum(int is_iap)
-{
- elan_read_cmd(
- is_iap ? ETP_I2C_IAP_CHECKSUM_CMD : ETP_I2C_FW_CHECKSUM_CMD);
- return le_bytes_to_int(rx_buf + 4);
-}
-
-static uint16_t elan_get_fw_info(void)
-{
- int fw_version = -1;
- uint16_t iap_checksum = 0xffff;
- uint16_t fw_checksum = 0xffff;
-
- printf("Querying device info...\n");
- fw_checksum = elan_get_checksum(0);
- iap_checksum = elan_get_checksum(1);
- fw_version = elan_get_version(0);
- iap_version = elan_get_version(1);
- printf("IAP version: %4x, FW version: %4x\n",
- iap_version, fw_version);
- printf("IAP checksum: %4x, FW checksum: %4x\n",
- iap_checksum, fw_checksum);
- return fw_checksum;
-}
-
-/* Update preparation */
-#define ETP_I2C_IAP_RESET_CMD 0x0314
-#define ETP_I2C_IAP_RESET 0xF0F0
-#define ETP_I2C_IAP_CTRL_CMD 0x0310
-#define ETP_I2C_MAIN_MODE_ON (1 << 9)
-#define ETP_I2C_IAP_CMD 0x0311
-#define ETP_I2C_IAP_PASSWORD 0x1EA5
-#define ETP_I2C_IAP_TYPE_CMD 0x0304
-
-static int elan_in_main_mode(void)
-{
- elan_read_cmd(ETP_I2C_IAP_CTRL_CMD);
- return le_bytes_to_int(rx_buf + 4) & ETP_I2C_MAIN_MODE_ON;
-}
-
-static int elan_read_write_iap_type(void)
-{
- for (int retry = 0; retry < 3; ++retry) {
- uint16_t val;
-
- if (elan_write_cmd(ETP_I2C_IAP_TYPE_CMD,
- fw_page_size / 2))
- return -1;
-
- if (elan_read_cmd(ETP_I2C_IAP_TYPE_CMD))
- return -1;
-
- val = le_bytes_to_int(rx_buf + 4);
- if (val == fw_page_size / 2) {
- printf("%s: OK\n", __func__);
- return 0;
- }
-
- }
- return -1;
-}
-
-static void elan_prepare_for_update(void)
-{
- printf("%s\n", __func__);
-
- int initial_mode = elan_in_main_mode();
- if (!initial_mode) {
- printf("In IAP mode, reset IC.\n");
- elan_write_cmd(ETP_I2C_IAP_RESET_CMD, ETP_I2C_IAP_RESET);
- usleep(30 * 1000);
- }
-
- /* Send the passphrase */
- elan_write_cmd(ETP_I2C_IAP_CMD, ETP_I2C_IAP_PASSWORD);
- usleep((initial_mode ? 100 : 30) * 1000);
-
- /* We should be in the IAP mode now */
- if (elan_in_main_mode())
- request_exit("Failure to enter IAP mode, still in main mode\n");
-
- if (ic_type >= 0x0D && iap_version >= 1) {
- if (elan_read_write_iap_type())
- request_exit("Failure to set IAP mode\n");
- }
-
- /* Send the passphrase again */
- elan_write_cmd(ETP_I2C_IAP_CMD, ETP_I2C_IAP_PASSWORD);
- usleep(30 * 1000);
-
- /* Verify the password */
- if (elan_read_cmd(ETP_I2C_IAP_CMD))
- request_exit("cannot read iap password.\n");
- if (le_bytes_to_int(rx_buf + 4) != ETP_I2C_IAP_PASSWORD)
- request_exit("Got an unexpected IAP password %4x\n",
- le_bytes_to_int(rx_buf + 4));
-}
-
-/* Firmware block update */
-#define ETP_IAP_START_ADDR 0x0083
-
-static uint16_t elan_calc_checksum(uint8_t *data, int length)
-{
- uint16_t checksum = 0;
- for (int i = 0; i < length; i += 2)
- checksum += ((uint16_t)(data[i+1]) << 8) | (data[i]);
- return checksum;
-}
-
-static int elan_get_iap_addr(void)
-{
- return le_bytes_to_int(fw_data + ETP_IAP_START_ADDR * 2) * 2;
-}
-
-#define ETP_I2C_IAP_REG_L 0x01
-#define ETP_I2C_IAP_REG_H 0x06
-
-#define ETP_FW_IAP_PAGE_ERR (1 << 5)
-#define ETP_FW_IAP_INTF_ERR (1 << 4)
-
-static int elan_write_fw_block(uint8_t *raw_data, uint16_t checksum)
-{
- uint8_t page_store[MAX_FW_PAGE_SIZE + 4];
- int rv;
-
- page_store[0] = ETP_I2C_IAP_REG_L;
- page_store[1] = ETP_I2C_IAP_REG_H;
- memcpy(page_store + 2, raw_data, fw_page_size);
- page_store[fw_page_size + 2 + 0] = (checksum >> 0) & 0xff;
- page_store[fw_page_size + 2 + 1] = (checksum >> 8) & 0xff;
-
- rv = libusb_single_write_and_read(
- page_store, fw_page_size + 4, rx_buf, 0);
- if (rv)
- return rv;
- usleep((fw_page_size >= 512 ? 50 : 35) * 1000);
- elan_read_cmd(ETP_I2C_IAP_CTRL_CMD);
- rv = le_bytes_to_int(rx_buf + 4);
- if (rv & (ETP_FW_IAP_PAGE_ERR | ETP_FW_IAP_INTF_ERR)) {
- printf("IAP reports failed write : %x\n", rv);
- return rv;
- }
- return 0;
-}
-
-
-static uint16_t elan_update_firmware(void)
-{
- uint16_t checksum = 0, block_checksum;
- int rv;
-
- printf("%s\n", __func__);
-
- for (int i = elan_get_iap_addr(); i < fw_size; i += fw_page_size) {
- printf("\rUpdating page %3d...", i / fw_page_size);
- fflush(stdout);
- block_checksum = elan_calc_checksum(fw_data + i, fw_page_size);
- rv = elan_write_fw_block(fw_data + i, block_checksum);
- if (rv)
- request_exit("Failed to update.\n");
- checksum += block_checksum;
- printf(" Updated, checksum: %d", checksum);
- fflush(stdout);
- }
- return checksum;
-}
-
-static void pretty_print_buffer(uint8_t *buf, int len)
-{
- int i;
-
- printf("Buffer = 0x");
- for (i = 0; i < len; ++i)
- printf("%02X", buf[i]);
- printf("\n");
-}
-
-int main(int argc, char *argv[])
-{
- uint16_t local_checksum;
- uint16_t remote_checksum;
-
- parse_cmdline(argc, argv);
- init_with_libusb();
- register_sigaction();
-
- /*
- * Judge IC type and get page count first.
- * Then check the FW file.
- */
- elan_get_ic_page_count();
- fw_size = fw_page_count * fw_page_size;
- printf("FW has %d bytes x %d pages\n", fw_page_size, fw_page_count);
-
- /* Read the FW file */
- FILE *f = fopen(firmware_binary, "rb");
- if (!f)
- request_exit("Cannot find binary: %s\n", firmware_binary);
- if (fread(fw_data, 1, fw_size, f) != (unsigned int)fw_size)
- request_exit("binary size mismatch, expect %d\n", fw_size);
-
- /*
- * It is possible that you are not able to get firmware info. This
- * might due to an incomplete update last time
- */
- elan_get_fw_info();
-
- /* Trigger an I2C transaction of expecting reading of 633 bytes. */
- if (extended_i2c_exercise) {
- tx_buf[0] = 0x05;
- tx_buf[1] = 0x00;
- tx_buf[2] = 0x3C;
- tx_buf[3] = 0x02;
- tx_buf[4] = 0x06;
- tx_buf[5] = 0x00;
- libusb_single_write_and_read(tx_buf, 6, rx_buf, 633);
- pretty_print_buffer(rx_buf, 637);
- }
-
- /* Get the trackpad ready for receiving update */
- elan_prepare_for_update();
-
- local_checksum = elan_update_firmware();
- /* Wait for a reset */
- usleep(600 * 1000);
- remote_checksum = elan_get_checksum(1);
- if (remote_checksum != local_checksum)
- printf("checksum diff local=[%04X], remote=[%04X]\n",
- local_checksum, remote_checksum);
-
- /* Print the updated firmware information */
- elan_get_fw_info();
- return 0;
-}
diff --git a/extra/usb_console/.gitignore b/extra/usb_console/.gitignore
deleted file mode 100644
index efee63e87e..0000000000
--- a/extra/usb_console/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-usb_console
diff --git a/extra/usb_console/Makefile b/extra/usb_console/Makefile
deleted file mode 100644
index bddca1d0a2..0000000000
--- a/extra/usb_console/Makefile
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright 2015 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-PROGRAM := usb_console
-SOURCE := $(PROGRAM).c
-LIBS :=
-LFLAGS :=
-CFLAGS := -std=gnu99 \
- -g3 \
- -O3 \
- -Wall \
- -Werror \
- -Wpointer-arith \
- -Wcast-align \
- -Wcast-qual \
- -Wundef \
- -Wsign-compare \
- -Wredundant-decls \
- -Wmissing-declarations
-
-#
-# Add libusb-1.0 required flags
-#
-LIBS += $(shell pkg-config --libs libusb-1.0)
-CFLAGS += $(shell pkg-config --cflags libusb-1.0)
-
-$(PROGRAM): $(SOURCE) Makefile
- gcc $(CFLAGS) $(SOURCE) $(LFLAGS) $(LIBS) -o $@
-
-.PHONY: clean
-
-clean:
- rm -rf $(PROGRAM) *~
diff --git a/extra/usb_console/usb_console.c b/extra/usb_console/usb_console.c
deleted file mode 100644
index e4f8ea504f..0000000000
--- a/extra/usb_console/usb_console.c
+++ /dev/null
@@ -1,457 +0,0 @@
-/*
- * Copyright 2015 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include <errno.h>
-#include <getopt.h>
-#include <poll.h>
-#include <signal.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/select.h>
-#include <unistd.h>
-
-#include <libusb.h>
-
-/* Options */
-static uint16_t vid = 0x18d1; /* Google */
-static uint16_t pid = 0x500f; /* discovery-stm32f072 */
-static uint8_t ep_num = 4; /* console endpoint */
-
-static unsigned char rx_buf[1024]; /* much too big */
-static unsigned char tx_buf[1024]; /* much too big */
-static const struct libusb_pollfd **usb_fds;
-static struct libusb_device_handle *devh;
-static struct libusb_transfer *rx_transfer;
-static struct libusb_transfer *tx_transfer;
-static int tx_ready;
-static int do_exit;
-
-static void request_exit(const char *format, ...)
-{
- va_list ap;
- va_start(ap, format);
- vfprintf(stderr, format, ap);
- va_end(ap);
- do_exit++;
-}
-
-#define BOO(msg, r) \
- request_exit("%s: line %d, %s\n", msg, __LINE__, \
- libusb_error_name(r))
-
-static void sighandler(int signum)
-{
- request_exit("caught signal %d: %s\n", signum, strsignal(signum));
-}
-
-#if 0
-static void show_xfer(const char *msg, struct libusb_transfer *t)
-{
- printf("%s: f=%02x ep=%02x type=%d status=%d len=%d actlen=%d\n", msg,
- t->flags,
- t->endpoint, t->type, t->status, t->length, t->actual_length);
-}
-#endif
-
-static void LIBUSB_CALL cb_rx(struct libusb_transfer *transfer)
-{
- int r;
-
- if (transfer->actual_length) {
- transfer->buffer[transfer->actual_length] = '\0';
- fputs((char *)transfer->buffer, stdout);
- fflush(stdout);
- }
-
- if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
- printf("rx_transfer cancelled\n");
- if (rx_transfer)
- libusb_free_transfer(rx_transfer);
- rx_transfer = NULL;
- return;
- }
-
- /* Try again */
- if (!do_exit) {
- r = libusb_submit_transfer(rx_transfer);
- if (r < 0)
- BOO("resubmit rx_transfer failed", r);
- }
-}
-
-static void LIBUSB_CALL cb_tx(struct libusb_transfer *transfer)
-{
- if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
- if (tx_transfer)
- libusb_free_transfer(tx_transfer);
- tx_transfer = NULL;
- request_exit("tx_transfer cancelled\n");
- return;
- }
-
- if (tx_ready != transfer->actual_length)
- printf("%s: only sent %d/%d bytes\n", __func__,
- transfer->actual_length, tx_ready);
-
- tx_ready = 0;
-}
-
-static void send_tx(int len)
-{
- int r;
-
- libusb_fill_bulk_transfer(tx_transfer, devh,
- ep_num, tx_buf, len, cb_tx, NULL, 0);
-
- r = libusb_submit_transfer(tx_transfer);
- if (r < 0)
- BOO("submit tx_transfer failed", r);
-}
-
-static void handle_stdin(void)
-{
- static unsigned int i;
- int n;
-
- for (; i < sizeof(tx_buf) - 1; i++) {
- n = read(0, tx_buf + i, 1);
- if (n == 0) {
- request_exit("EOF on stdin\n");
- return;
- }
- if (n < 0) {
- request_exit("stdin: %s\n", strerror(errno));
- return;
- }
-
- if (tx_buf[i] == '\n') {
- i++;
- tx_buf[i] = '\0';
- break;
- }
- }
-
- tx_ready = strlen((char *)tx_buf) + 1;
- send_tx(tx_ready);
- i = 0;
-}
-
-static void handle_libusb(void)
-{
- struct timeval tv = { 0, 0 };
- int r;
-
- r = libusb_handle_events_timeout_completed(NULL, &tv, &do_exit);
- if (r < 0)
- BOO("libusb event problem", r);
-}
-
-static int wait_for_stuff_to_happen(void)
-{
- int i, r, nfds = 0;
- fd_set readset, writeset;
- struct timeval tv = { 1, 0 };
-
- if (!usb_fds) {
- request_exit("No usb_fds to watch\n");
- return -1;
- }
-
- FD_ZERO(&readset);
- FD_ZERO(&writeset);
- /* always watch stdin */
- FD_SET(0, &readset);
-
- for (i = 0; usb_fds[i]; i++) {
- int fd = usb_fds[i]->fd;
- short events = usb_fds[i]->events;
- if (fd > nfds)
- nfds = fd;
-
- if (events & POLLIN)
- FD_SET(fd, &readset);
- if (events & POLLOUT)
- FD_SET(fd, &writeset);
- }
-
- r = select(nfds + 1, &readset, &writeset, NULL, &tv);
- if (r < 0) {
- request_exit("select: %s\n", strerror(errno));
- return -1;
- }
-
- if (r == 0) /* timed out */
- return 0;
-
- /* Ignore stdin until we've finished sending the current line */
- if (!tx_ready && FD_ISSET(0, &readset))
- return 1;
-
- /* libusb, then */
- return 2;
-}
-
-static int find_interface_with_endpoint(int want_ep_num)
-{
- int iface_num = -1;
- int r, i, j, k;
- struct libusb_device *dev;
- struct libusb_config_descriptor *conf = 0;
- const struct libusb_interface *iface0;
- const struct libusb_interface_descriptor *iface;
- const struct libusb_endpoint_descriptor *ep;
-
- dev = libusb_get_device(devh);
- r = libusb_get_active_config_descriptor(dev, &conf);
- if (r < 0) {
- BOO("get_active_config", r);
- return -1;
- }
-
- for (i = 0; i < conf->bNumInterfaces; i++) {
- iface0 = &conf->interface[i];
- for (j = 0; j < iface0->num_altsetting; j++) {
- iface = &iface0->altsetting[j];
- for (k = 0; k < iface->bNumEndpoints; k++) {
- ep = &iface->endpoint[k];
- if (ep->bEndpointAddress == want_ep_num) {
- iface_num = i;
- break;
- }
- }
- }
- }
-
- libusb_free_config_descriptor(conf);
- return iface_num;
-}
-
-static char *progname;
-static char *short_opts = ":v:p:e:h";
-static const struct option long_opts[] = {
- /* name hasarg *flag val */
- {"vid", 1, NULL, 'v'},
- {"pid", 1, NULL, 'p'},
- {"ep", 1, NULL, 'e'},
- {"help", 0, NULL, 'h'},
- {NULL, 0, NULL, 0},
-};
-
-static void usage(int errs)
-{
- printf("\nUsage: %s [options]\n"
- "\n"
- "A very simple serial console emulator\n"
- "\n"
- "Options:\n"
- "\n"
- " -v,--vid HEXVAL Vendor ID (default %04x)\n"
- " -p,--pid HEXVAL Product ID (default %04x)\n"
- " -e,--ep NUM Endpoint (default %d)\n"
- " -h,--help Show this message\n"
- "\n", progname, vid, pid, ep_num);
-
- exit(!!errs);
-}
-
-int main(int argc, char *argv[])
-{
- struct sigaction sigact;
- int iface_num;
- int claimed_iface = 0;
- int r = 1;
- int errorcnt = 0;
- char *e = 0;
- int i;
-
- progname = strrchr(argv[0], '/');
- if (progname)
- progname++;
- else
- progname = argv[0];
-
- opterr = 0; /* quiet, you */
- while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) {
- switch (i) {
- case 'p':
- pid = (uint16_t) strtoull(optarg, &e, 16);
- if (!*optarg || (e && *e)) {
- printf("Invalid argument: \"%s\"\n", optarg);
- errorcnt++;
- }
- break;
- case 'v':
- vid = (uint16_t) strtoull(optarg, &e, 16);
- if (!*optarg || (e && *e)) {
- printf("Invalid argument: \"%s\"\n", optarg);
- errorcnt++;
- }
- break;
- case 'e':
- ep_num = (uint8_t) strtoull(optarg, &e, 0);
- if (!*optarg || (e && *e)) {
- printf("Invalid argument: \"%s\"\n", optarg);
- errorcnt++;
- }
- break;
- case 'h':
- usage(errorcnt);
- break;
- case 0: /* auto-handled option */
- break;
- case '?':
- if (optopt)
- printf("Unrecognized option: -%c\n", optopt);
- else
- printf("Unrecognized option: %s\n",
- argv[optind - 1]);
- errorcnt++;
- break;
- case ':':
- printf("Missing argument to %s\n", argv[optind - 1]);
- errorcnt++;
- break;
- default:
- printf("Internal error at %s:%d\n", __FILE__, __LINE__);
- exit(1);
- }
- }
-
- if (errorcnt)
- usage(errorcnt);
-
- printf("init\n");
- r = libusb_init(NULL);
- if (r < 0) {
- BOO("init", r);
- exit(1);
- }
-
- printf("open_device %04x:%04x\n", vid, pid);
- devh = libusb_open_device_with_vid_pid(NULL, vid, pid);
- if (!devh) {
- printf("can't find device\n");
- goto out;
- }
-
- iface_num = find_interface_with_endpoint(ep_num);
- if (iface_num < 0) {
- printf("can't find interface owning EP %d\n", ep_num);
- goto out;
- }
- /* NOTE: The EP might be on an alternate interface. We should switch
- * to the correct one. */
-
- printf("claim_interface %d to use endpoint %d\n", iface_num, ep_num);
- r = libusb_claim_interface(devh, iface_num);
- if (r < 0) {
- BOO("claim interface", r);
- goto out;
- }
- claimed_iface = 1;
-
- sigact.sa_handler = sighandler;
- sigemptyset(&sigact.sa_mask);
- sigact.sa_flags = 0;
- sigaction(SIGINT, &sigact, NULL);
- sigaction(SIGTERM, &sigact, NULL);
- sigaction(SIGQUIT, &sigact, NULL);
-
- printf("alloc_transfers\n");
- rx_transfer = libusb_alloc_transfer(0);
- if (!rx_transfer) {
- printf("can't alloc rx_transfer");
- goto out;
- }
- libusb_fill_bulk_transfer(rx_transfer, devh,
- 0x80 | ep_num,
- rx_buf, sizeof(rx_buf), cb_rx, NULL, 0);
-
- tx_transfer = libusb_alloc_transfer(0);
- if (!tx_transfer) {
- printf("can't alloc tx_transfer");
- goto out;
- }
-
- printf("get_pollfds\n");
- usb_fds = libusb_get_pollfds(NULL);
- if (!usb_fds) {
- printf("can't get usb_fds\n");
- goto out;
- }
-
- printf("submit rx_transfer\n");
- r = libusb_submit_transfer(rx_transfer);
- if (r < 0) {
- BOO("submit rx_transfer", r);
- goto out;
- }
-
- printf("READY\n-------\n");
- while (!do_exit) {
- r = wait_for_stuff_to_happen();
- switch (r) {
- case 0: /* timed out */
- /* printf("."); */
- /* fflush(stdout); */
- break;
- case 1: /* stdin ready */
- handle_stdin();
- break;
- case 2: /* libusb ready */
- handle_libusb();
- break;
- }
- }
-
- printf("-------\nshutting down\n");
-
- r = libusb_cancel_transfer(rx_transfer);
- if (r < 0) {
- BOO("cancel rx_transfer", r);
- if (rx_transfer)
- libusb_free_transfer(rx_transfer);
- rx_transfer = 0;
- }
-
- if (tx_ready) {
- r = libusb_cancel_transfer(tx_transfer);
- if (r < 0) {
- BOO("cancel tx_transfer", r);
- if (tx_transfer)
- libusb_free_transfer(tx_transfer);
- tx_transfer = 0;
- }
- }
-
- while (rx_transfer) {
- printf("draining events...\n");
- r = libusb_handle_events(NULL);
- if (r < 0) {
- printf("Huh: %s\n", libusb_error_name(r));
- break;
- }
- }
-
- printf("bye\n");
- r = 0;
- out:
- if (tx_transfer)
- libusb_free_transfer(tx_transfer);
- if (rx_transfer)
- libusb_free_transfer(rx_transfer);
-
- if (devh) {
- if (claimed_iface)
- libusb_release_interface(devh, iface_num);
- libusb_close(devh);
- }
- libusb_exit(NULL);
-
- return r;
-}
diff --git a/extra/usb_gpio/.gitignore b/extra/usb_gpio/.gitignore
deleted file mode 100644
index 239f1ed4d8..0000000000
--- a/extra/usb_gpio/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-usb_gpio
diff --git a/extra/usb_gpio/Makefile b/extra/usb_gpio/Makefile
deleted file mode 100644
index 644e3ee70f..0000000000
--- a/extra/usb_gpio/Makefile
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright 2014 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-PROGRAM := usb_gpio
-SOURCE := $(PROGRAM).c
-LIBS :=
-LFLAGS :=
-CFLAGS := -std=gnu99 \
- -g3 \
- -O3 \
- -Wall \
- -Werror \
- -Wpointer-arith \
- -Wcast-align \
- -Wcast-qual \
- -Wundef \
- -Wsign-compare \
- -Wredundant-decls \
- -Wmissing-declarations
-
-#
-# Add libusb-1.0 required flags
-#
-LIBS += $(shell pkg-config --libs libusb-1.0)
-CFLAGS += $(shell pkg-config --cflags libusb-1.0)
-
-$(PROGRAM): $(SOURCE) Makefile
- gcc $(CFLAGS) $(SOURCE) $(LFLAGS) $(LIBS) -o $@
-
-.PHONY: clean
-
-clean:
- rm -rf $(PROGRAM) *~
diff --git a/extra/usb_gpio/usb_gpio.c b/extra/usb_gpio/usb_gpio.c
deleted file mode 100644
index 8973f3d304..0000000000
--- a/extra/usb_gpio/usb_gpio.c
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright 2014 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include <libusb.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#define CHECK(expression) \
- ({ \
- int error__ = (expression); \
- \
- if (error__ != 0) { \
- fprintf(stderr, \
- "libusb error: %s:%d %s\n", \
- __FILE__, \
- __LINE__, \
- libusb_error_name(error__)); \
- return error__; \
- } \
- \
- error__; \
- })
-
-#define TRANSFER_TIMEOUT_MS 100
-
-static int gpio_write(libusb_device_handle *device,
- uint32_t set_mask,
- uint32_t clear_mask)
-{
- uint8_t command[8];
- int transferred;
-
- command[0] = (set_mask >> 0) & 0xff;
- command[1] = (set_mask >> 8) & 0xff;
- command[2] = (set_mask >> 16) & 0xff;
- command[3] = (set_mask >> 24) & 0xff;
-
- command[4] = (clear_mask >> 0) & 0xff;
- command[5] = (clear_mask >> 8) & 0xff;
- command[6] = (clear_mask >> 16) & 0xff;
- command[7] = (clear_mask >> 24) & 0xff;
-
- CHECK(libusb_bulk_transfer(device,
- LIBUSB_ENDPOINT_OUT | 2,
- command,
- sizeof(command),
- &transferred,
- TRANSFER_TIMEOUT_MS));
-
- if (transferred != sizeof(command)) {
- fprintf(stderr,
- "Failed to transfer full command "
- "(sent %d of %d bytes)\n",
- transferred,
- (int)sizeof(command));
- return LIBUSB_ERROR_OTHER;
- }
-
- return 0;
-}
-
-static int gpio_read(libusb_device_handle *device, uint32_t *mask)
-{
- uint8_t response[4];
- int transferred;
-
- /*
- * The first query does triggers the sampling of the GPIO values, the
- * second query reads them back.
- */
- CHECK(libusb_bulk_transfer(device,
- LIBUSB_ENDPOINT_IN | 2,
- response,
- sizeof(response),
- &transferred,
- TRANSFER_TIMEOUT_MS));
-
- CHECK(libusb_bulk_transfer(device,
- LIBUSB_ENDPOINT_IN | 2,
- response,
- sizeof(response),
- &transferred,
- TRANSFER_TIMEOUT_MS));
-
- if (transferred != sizeof(response)) {
- fprintf(stderr,
- "Failed to transfer full response "
- "(read %d of %d bytes)\n",
- transferred,
- (int)sizeof(response));
- return LIBUSB_ERROR_OTHER;
- }
-
- *mask = (response[0] << 0 |
- response[1] << 8 |
- response[2] << 16 |
- response[3] << 24);
-
- return 0;
-}
-
-int main(int argc, char **argv)
-{
- libusb_context *context;
- libusb_device_handle *device;
- uint16_t vendor_id = 0x18d1; /* Google */
- uint16_t product_id = 0x500f; /* discovery-stm32f072 */
- int interface = 1; /* gpio interface */
-
- if (!(argc == 2 && strcmp(argv[1], "read") == 0) &&
- !(argc == 4 && strcmp(argv[1], "write") == 0)) {
- puts("Usage: usb_gpio read\n"
- " usb_gpio write <set_mask> <clear_mask>\n");
- return 1;
- }
-
- CHECK(libusb_init(&context));
-
- device = libusb_open_device_with_vid_pid(context,
- vendor_id,
- product_id);
-
- if (device == NULL) {
- fprintf(stderr,
- "Unable to find device 0x%04x:0x%04x\n",
- vendor_id,
- product_id);
- return 1;
- }
-
- CHECK(libusb_set_auto_detach_kernel_driver(device, 1));
- CHECK(libusb_claim_interface(device, interface));
-
- if (argc == 2 && strcmp(argv[1], "read") == 0) {
- uint32_t mask;
-
- CHECK(gpio_read(device, &mask));
-
- printf("GPIO mask: 0x%08x\n", mask);
- }
-
- if (argc == 4 && strcmp(argv[1], "write") == 0) {
- uint32_t set_mask = strtol(argv[2], NULL, 0);
- uint32_t clear_mask = strtol(argv[3], NULL, 0);
-
- CHECK(gpio_write(device, set_mask, clear_mask));
- }
-
- libusb_close(device);
- libusb_exit(context);
-
- return 0;
-}
diff --git a/extra/usb_power/__init__.py b/extra/usb_power/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
--- a/extra/usb_power/__init__.py
+++ /dev/null
diff --git a/extra/usb_power/board/kevin/kevin.board b/extra/usb_power/board/kevin/kevin.board
deleted file mode 100644
index 8ab59573c6..0000000000
--- a/extra/usb_power/board/kevin/kevin.board
+++ /dev/null
@@ -1,18 +0,0 @@
-[
-{"name": "pp5000", "rs": 0.01, "sweetberry": "A", "channel": 0},
-{"name": "ppvar_gpu", "rs": 0.01, "sweetberry": "A", "channel": 1},
-{"name": "pp3300_wifi_bt", "rs": 0.01, "sweetberry": "A", "channel": 2},
-{"name": "pp1500_ap_io", "rs": 0.01, "sweetberry": "A", "channel": 3},
-{"name": "pp3300_alw", "rs": 0.01, "sweetberry": "A", "channel": 4},
-{"name": "ppvar_litcpu", "rs": 0.01, "sweetberry": "A", "channel": 5},
-{"name": "pp1800_s0", "rs": 0.01, "sweetberry": "A", "channel": 6},
-{"name": "pp3300_haven", "rs": 0.1, "sweetberry": "A", "channel": 7},
-{"name": "ppvar_bigcpu", "rs": 0.01, "sweetberry": "A", "channel": 8},
-{"name": "pp900_ap", "rs": 0.01, "sweetberry": "A", "channel": 9},
-{"name": "pp1800_ec", "rs": 0.1, "sweetberry": "A", "channel": 10},
-{"name": "pp1800_sensor", "rs": 0.01, "sweetberry": "A", "channel": 11},
-{"name": "pp1800_alw", "rs": 0.01, "sweetberry": "A", "channel": 12},
-{"name": "pp1200_lpddr", "rs": 0.01, "sweetberry": "A", "channel": 13},
-{"name": "pp3300_ec", "rs": 0.1, "sweetberry": "A", "channel": 14},
-{"name": "pp3300_s0", "rs": 0.01, "sweetberry": "A", "channel": 15}
-]
diff --git a/extra/usb_power/board/kevin/kevin_all.scenario b/extra/usb_power/board/kevin/kevin_all.scenario
deleted file mode 100644
index dbc3953364..0000000000
--- a/extra/usb_power/board/kevin/kevin_all.scenario
+++ /dev/null
@@ -1,18 +0,0 @@
-[
-"pp5000",
-"ppvar_gpu",
-"pp3300_wifi_bt",
-"pp1500_ap_io",
-"pp3300_alw",
-"ppvar_litcpu",
-"pp1800_s0",
-"pp3300_haven",
-"ppvar_bigcpu",
-"pp900_ap",
-"pp1800_ec",
-"pp1800_sensor",
-"pp1800_alw",
-"pp1200_lpddr",
-"pp3300_ec",
-"pp3300_s0"
-]
diff --git a/extra/usb_power/board/marlin/marlin.board b/extra/usb_power/board/marlin/marlin.board
deleted file mode 100644
index dc4cdad258..0000000000
--- a/extra/usb_power/board/marlin/marlin.board
+++ /dev/null
@@ -1,74 +0,0 @@
-[
-{"name": "VBAT", "rs": 0.01, "sweetberry": "A", "net": "", "channel": 0},
-{"name": "VBAT_", "rs": 0.01, "sweetberry": "B", "net": "", "channel": 0},
-{"name": "VDD_MEM", "rs": 0.05, "sweetberry": "A", "net": "V_MEM_0V875", "channel": 1},
-{"name": "VDD_EBI_PHY", "rs": 0.1, "sweetberry": "A", "net": "V_EBI_0V875", "channel": 2},
-{"name": "VDD_PCIE_1P8", "rs": 0.5, "sweetberry": "A", "net": "V_USB_1V8", "channel": 3},
-{"name": "VDD_PCIE_CORE", "rs": 0.1, "sweetberry": "A", "net": "V_PCIE_0V925", "channel": 4},
-{"name": "VDD_MIPI_CSI", "rs": 0.1, "sweetberry": "A", "net": "V_CSI_DSI_1V25", "channel": 5},
-{"name": "VDD_A1", "rs": 0.1, "sweetberry": "A", "net": "V_MSMA1_1V225", "channel": 6},
-{"name": "VDD_A2", "rs": 1.0, "sweetberry": "A", "net": "V_MSMA2_1V8", "channel": 7},
-{"name": "VDD_P2", "rs": 0.02, "sweetberry": "A", "net": "V_IO_1V8", "channel": 8},
-{"name": "VDD_P3", "rs": 0.5, "sweetberry": "B", "net": "V_IO_1V8", "channel": 1},
-{"name": "VDD_P5", "rs": 0.5, "sweetberry": "A", "net": "V_RUIM1", "channel": 9},
-{"name": "VDD_P6", "rs": 0.02, "sweetberry": "A", "net": "V_IO_1V8", "channel": 46},
-{"name": "VDD_P10", "rs": 0.1, "sweetberry": "A", "net": "V_UFS_1V2", "channel": 10},
-{"name": "VDD_P12", "rs": 0.1, "sweetberry": "A", "net": "V_SRIO_1V8", "channel": 11},
-{"name": "VDD_USB_HS_3P1", "rs": 0.5, "sweetberry": "A", "net": "V_USB_3V075", "channel": 12},
-{"name": "VDD_CORE", "rs": 0.02, "sweetberry": "A", "net": "V_VDDCORE_0V8", "channel": 13},
-{"name": "VDD_GFX", "rs": 0.05, "sweetberry": "A", "net": "V_GFX_0V98", "channel": 14},
-{"name": "VDD_MODEM", "rs": 0.05, "sweetberry": "A", "net": "V_MODEM_1V0", "channel": 15},
-{"name": "VDD_APC", "rs": 0.01, "sweetberry": "A", "net": "V_APC_0V8", "channel": 16},
-{"name": "VDD_P1", "rs": 0.1, "sweetberry": "A", "net": "V_DDRCORE_1V1", "channel": 17},
-{"name": "VDD_DDR_CORE_1P8", "rs": 0.1, "sweetberry": "B", "net": "V_IO_1V8", "channel": 3},
-{"name": "VDD_SSC_CORE", "rs": 0.05, "sweetberry": "A", "net": "V_SSCCORE_0V8", "channel": 18},
-{"name": "VDD_SSC_MEM", "rs": 0.02, "sweetberry": "A", "net": "V_SSCMEM_0V875", "channel": 19},
-{"name": "V_EMMC_2V95", "rs": 0.1, "sweetberry": "A", "net": "V_EMMC_2V95", "channel": 20},
-{"name": "VCCQ2", "rs": 0.1, "sweetberry": "B", "net": "V_IO_1V8", "channel": 4},
-{"name": "VDD/VDDIO", "rs": 1.0, "sweetberry": "B", "net": "V_SRIO_1V8", "channel": 5},
-{"name": "V_LED_3V3", "rs": 0.1, "sweetberry": "A", "net": "V_LED_3V3", "channel": 21},
-{"name": "VDD/VIO", "rs": 1.0, "sweetberry": "B", "net": "V_SRIO_1V8", "channel": 6},
-{"name": "V_SRIO_1V8", "rs": 1.0, "sweetberry": "B", "net": "V_SRIO_1V8", "channel": 7},
-{"name": "V_SRIO_1V8_", "rs": 1.0, "sweetberry": "B", "net": "V_SRIO_1V8", "channel": 8},
-{"name": "VBAT/VDD/VDDA", "rs": 0.1, "sweetberry": "B", "net": "V_SRIO_1V8", "channel": 9},
-{"name": "V_SRIO_1V8__", "rs": 1.0, "sweetberry": "B", "net": "V_SRIO_1V8", "channel": 10},
-{"name": "V_SR_2V85", "rs": 0.1, "sweetberry": "A", "net": "V_SR_2V85", "channel": 22},
-{"name": "", "rs": 0.1, "sweetberry": "B", "net": "", "channel": 11},
-{"name": "V_USBSS_SW_1V8", "rs": 0.1, "sweetberry": "A", "net": "V_USBSS_SW_1V8", "channel": 23},
-{"name": "V_RF_2V7", "rs": 0.5, "sweetberry": "A", "net": "V_RF_2V7", "channel": 24},
-{"name": "V_TP_3V3", "rs": 0.5, "sweetberry": "A", "net": "V_TP_3V3", "channel": 25},
-{"name": "V_ELVDD", "rs": 0.1, "sweetberry": "B", "net": "V_ELVDD", "channel": 16},
-{"name": "V_AVDD", "rs": 1.0, "sweetberry": "B", "net": "V_AVDD", "channel": 17},
-{"name": "VCI_3V", "rs": 0.1, "sweetberry": "A", "net": "VCI_3V", "channel": 26},
-{"name": "VDD_1V8_PANEL", "rs": 0.5, "sweetberry": "A", "net": "VDD_1V8_PANEL", "channel": 27},
-{"name": "V_TP_1V8", "rs": 0.1, "sweetberry": "A", "net": "V_TP_1V8", "channel": 28},
-{"name": "V_CAM2_D1V2", "rs": 0.1, "sweetberry": "A", "net": "V_CAM2_D1V2", "channel": 31},
-{"name": "V_CAMIO_1V8", "rs": 0.1, "sweetberry": "A", "net": "V_CAMIO_1V8", "channel": 32},
-{"name": "V_CAM1_VCM2V85", "rs": 0.5, "sweetberry": "B", "net": "V_CAM1_VCM2V85", "channel": 18},
-{"name": "V_CAM1_A2V85", "rs": 1.0, "sweetberry": "B", "net": "V_CAM1_A2V85", "channel": 19},
-{"name": "V_CAM1_D1V0", "rs": 0.02, "sweetberry": "A", "net": "V_CAM1_D1V0", "channel": 33},
-{"name": "V_CAMIO_1V8_", "rs": 1.0, "sweetberry": "B", "net": "V_CAMIO_1V8", "channel": 20},
-{"name": "VBAT_ADC_IN", "rs": 0.01, "sweetberry": "A", "net": "V_DCIN", "channel": 34},
-{"name": "VDD_RX", "rs": 1.0, "sweetberry": "B", "net": "V_IO_1V8", "channel": 21},
-{"name": "VDD_MIC_BIAS", "rs": 0.01, "sweetberry": "A", "net": "V_BOOST_BYPASS", "channel": 35},
-{"name": "PVDD/VDD", "rs": 0.1, "sweetberry": "A", "net": "V_AUD_AMP_3V3", "channel": 36},
-{"name": "V_DCIN", "rs": 0.01, "sweetberry": "A", "net": "V_DCIN", "channel": 47},
-{"name": "V_AUDIO_2V15", "rs": 0.02, "sweetberry": "A", "net": "V_AUDIO_2V15", "channel": 38},
-{"name": "V_AUDIO_1V3", "rs": 0.02, "sweetberry": "A", "net": "V_AUDIO_1V3", "channel": 39},
-{"name": "PVIN/AVIN", "rs": 0.02, "sweetberry": "A", "net": "V_DCIN", "channel": 40},
-{"name": "VBATT", "rs": 0.5, "sweetberry": "B", "net": "VPA_BATT", "channel": 24},
-{"name": "VCC_GSM", "rs": 0.01, "sweetberry": "B", "net": "VPA_APT", "channel": 25},
-{"name": "VCC1_3G", "rs": 0.01, "sweetberry": "B", "net": "VPA", "channel": 26},
-{"name": "VAPT", "rs": 0.01, "sweetberry": "B", "net": "VPA_APT", "channel": 27},
-{"name": "VCC1", "rs": 0.01, "sweetberry": "B", "net": "VPA", "channel": 28},
-{"name": "VPA_BATT", "rs": 0.5, "sweetberry": "B", "net": "VPA_BATT", "channel": 29},
-{"name": "VDD_RF1_TVCO", "rs": 0.1, "sweetberry": "A", "net": "VREG_RF_1P0", "channel": 42},
-{"name": "V_GPS_1V8", "rs": 1.0, "sweetberry": "A", "net": "V_GPS_1V8", "channel": 43},
-{"name": "VDDIO_XTAL", "rs": 1.0, "sweetberry": "A", "net": "VDDIO_XTAL_1V8", "channel": 44},
-{"name": "VDD_FEM", "rs": 0.05, "sweetberry": "A", "net": "V_DCIN", "channel": 45},
-{"name": "VDD33", "rs": 0.1, "sweetberry": "B", "net": "V_VDDRF_3V2", "channel": 31},
-{"name": "DVDD11", "rs": 0.1, "sweetberry": "B", "net": "V_VDDRF_1V1", "channel": 32},
-{"name": "VDD(PAD)", "rs": 0.5, "sweetberry": "B", "net": "V_NFC_1V8", "channel": 33},
-{"name": "VBAT/VBAT2/VDD(UP)", "rs": 0.1, "sweetberry": "B", "net": "V_MBAT", "channel": 34},
-{"name": "NFC_5V_BOOST", "rs": 0.1, "sweetberry": "B", "net": "NFC_5V_BOOST", "channel": 35}
-]
diff --git a/extra/usb_power/board/marlin/marlin_all_A.scenario b/extra/usb_power/board/marlin/marlin_all_A.scenario
deleted file mode 100644
index a024b698d7..0000000000
--- a/extra/usb_power/board/marlin/marlin_all_A.scenario
+++ /dev/null
@@ -1,42 +0,0 @@
-[ "VBAT",
-"VDD_MEM",
-"VDD_EBI_PHY",
-"VDD_PCIE_1P8",
-"VDD_PCIE_CORE",
-"VDD_MIPI_CSI",
-"VDD_A1",
-"VDD_A2",
-"VDD_P2",
-"VDD_P5",
-"VDD_P6",
-"VDD_P10",
-"VDD_P12",
-"VDD_USB_HS_3P1",
-"VDD_CORE",
-"VDD_GFX",
-"VDD_MODEM",
-"VDD_APC",
-"VDD_P1",
-"VDD_SSC_CORE",
-"VDD_SSC_MEM",
-"V_EMMC_2V95",
-"V_LED_3V3",
-"V_SR_2V85",
-"V_USBSS_SW_1V8",
-"V_RF_2V7",
-"V_TP_3V3",
-"VCI_3V",
-"VDD_1V8_PANEL",
-"V_TP_1V8",
-"V_CAM2_D1V2",
-"V_CAMIO_1V8",
-"V_CAM1_D1V0",
-"VBAT_ADC_IN",
-"VDD_MIC_BIAS",
-"PVDD/VDD",
-"V_DCIN",
-"V_AUDIO_2V15",
-"V_AUDIO_1V3",
-"VDD_RF1_TVCO",
-"V_GPS_1V8",
-"VDD_FEM"]
diff --git a/extra/usb_power/board/marlin/marlin_all_B.scenario b/extra/usb_power/board/marlin/marlin_all_B.scenario
deleted file mode 100644
index 876d2dbfbd..0000000000
--- a/extra/usb_power/board/marlin/marlin_all_B.scenario
+++ /dev/null
@@ -1,28 +0,0 @@
-[ "VBAT_",
-"VDD_P3",
-"VDD_DDR_CORE_1P8",
-"VCCQ2",
-"VDD/VDDIO",
-"VDD/VIO",
-"V_SRIO_1V8",
-"V_SRIO_1V8_",
-"VBAT/VDD/VDDA",
-"V_SRIO_1V8__",
-"",
-"V_ELVDD",
-"V_AVDD",
-"V_CAM1_VCM2V85",
-"V_CAM1_A2V85",
-"V_CAMIO_1V8_",
-"VDD_RX",
-"VBATT",
-"VCC_GSM",
-"VCC1_3G",
-"VAPT",
-"VCC1",
-"VPA_BATT",
-"VDD33",
-"DVDD11",
-"VDD(PAD)",
-"VBAT/VBAT2/VDD(UP)",
-"NFC_5V_BOOST" ]
diff --git a/extra/usb_power/board/marlin/marlin_common.scenario b/extra/usb_power/board/marlin/marlin_common.scenario
deleted file mode 100644
index 7e20236c34..0000000000
--- a/extra/usb_power/board/marlin/marlin_common.scenario
+++ /dev/null
@@ -1 +0,0 @@
-["VBAT", "VDD_MEM", "VDD_EBI_PHY", "VDD_PCIE_1P8", "VDD_PCIE_CORE", "VDD_MIPI_CSI", "VDD_A1", "VDD_CORE", "VDD_GFX", "VDD_MODEM", "VDD_APC", "VDD_P1", "VDD_SSC_CORE", "VDD_SSC_MEM", "VDD_1V8_PANEL", "V_CAM2_D1V2", "VBAT_ADC_IN", "VDD_FEM"]
diff --git a/extra/usb_power/board/marlin/marlin_pvc.scenario b/extra/usb_power/board/marlin/marlin_pvc.scenario
deleted file mode 100644
index 426cd1479c..0000000000
--- a/extra/usb_power/board/marlin/marlin_pvc.scenario
+++ /dev/null
@@ -1 +0,0 @@
-["VBAT", ["VBAT", "BUSV"], ["VBAT", "CURRENT"], ["VBAT", "SHUNTV"]]
diff --git a/extra/usb_power/board/marlin/marlin_short.scenario b/extra/usb_power/board/marlin/marlin_short.scenario
deleted file mode 100644
index 2cfc8b0f9a..0000000000
--- a/extra/usb_power/board/marlin/marlin_short.scenario
+++ /dev/null
@@ -1 +0,0 @@
-["VBAT", "VDD_MEM", "VDD_CORE", "VDD_GFX", "VDD_1V8_PANEL"]
diff --git a/extra/usb_power/board/marlin/marlin_vbat.scenario b/extra/usb_power/board/marlin/marlin_vbat.scenario
deleted file mode 100644
index f1c18ca202..0000000000
--- a/extra/usb_power/board/marlin/marlin_vbat.scenario
+++ /dev/null
@@ -1 +0,0 @@
-["VBAT"]
diff --git a/extra/usb_power/convert_power_log_board.py b/extra/usb_power/convert_power_log_board.py
deleted file mode 100644
index 8aab77ee4c..0000000000
--- a/extra/usb_power/convert_power_log_board.py
+++ /dev/null
@@ -1,92 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2018 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-# Ignore indention messages, since legacy scripts use 2 spaces instead of 4.
-# pylint: disable=bad-indentation,docstring-section-indent
-# pylint: disable=docstring-trailing-quotes
-
-"""
-Program to convert sweetberry config to servod config template.
-"""
-
-# Note: This is a py2/3 compatible file.
-
-from __future__ import print_function
-import json
-import os
-import sys
-
-from powerlog import Spower
-
-
-def fetch_records(board_file):
- """Import records from servo_ina file.
-
- board files are json files, and have a list of tuples with
- the INA data.
- (name, rs, swetberry_num, net_name, channel)
-
- Args:
- board_file: board file
-
- Returns:
- list of tuples as described above.
- """
- data = None
- with open(board_file) as f:
- data = json.load(f)
- return data
-
-
-def write_to_file(file, sweetberry, inas):
- """Writes records of |sweetberry| to |file|
- Args:
- file: file to write to.
- sweetberry: sweetberry type. A or B.
- inas: list of inas read from board file.
- """
-
- with open(file, 'w') as pyfile:
-
- pyfile.write('inas = [\n')
-
- for rec in inas:
- if rec['sweetberry'] != sweetberry:
- continue
-
- # EX : ('sweetberry', 0x40, 'SB_FW_CAM_2P8', 5.0, 1.000, 3, False),
- channel, i2c_addr = Spower.CHMAP[rec['channel']]
- record = (" ('sweetberry', 0x%02x, '%s', 5.0, %f, %d, 'True')"
- ",\n" % (i2c_addr, rec['name'], rec['rs'], channel))
- pyfile.write(record)
-
- pyfile.write(']\n')
-
-
-def main(argv):
- if len(argv) != 2:
- print("usage:")
- print(" %s input.board" % argv[0])
- return
-
- inputf = argv[1]
- basename = os.path.splitext(inputf)[0]
-
- inas = fetch_records(inputf)
-
- sweetberry = set(rec['sweetberry'] for rec in inas)
-
- if len(sweetberry) == 2:
- print("Converting %s to %s and %s" % (inputf, basename + '_a.py',
- basename + '_b.py'))
- write_to_file(basename + '_a.py', 'A', inas)
- write_to_file(basename + '_b.py', 'B', inas)
- else:
- print("Converting %s to %s" % (inputf, basename + '.py'))
- write_to_file(basename + '.py', sweetberry.pop(), inas)
-
-
-if __name__ == "__main__":
- main(sys.argv)
diff --git a/extra/usb_power/convert_servo_ina.py b/extra/usb_power/convert_servo_ina.py
deleted file mode 100755
index 1c70f31aeb..0000000000
--- a/extra/usb_power/convert_servo_ina.py
+++ /dev/null
@@ -1,80 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2017 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-# Ignore indention messages, since legacy scripts use 2 spaces instead of 4.
-# pylint: disable=bad-indentation,docstring-section-indent
-# pylint: disable=docstring-trailing-quotes
-
-"""Program to convert power logging config from a servo_ina device
- to a sweetberry config.
-"""
-
-# Note: This is a py2/3 compatible file.
-
-from __future__ import print_function
-import os
-import sys
-
-
-def fetch_records(basename):
- """Import records from servo_ina file.
-
- servo_ina files are python imports, and have a list of tuples with
- the INA data.
- (inatype, i2caddr, rail name, bus voltage, shunt ohms, mux, True)
-
- Args:
- basename: python import name (filename -.py)
-
- Returns:
- list of tuples as described above.
- """
- ina_desc = __import__(basename)
- return ina_desc.inas
-
-
-def main(argv):
- if len(argv) != 2:
- print("usage:")
- print(" %s input.py" % argv[0])
- return
-
- inputf = argv[1]
- basename = os.path.splitext(inputf)[0]
- outputf = basename + '.board'
- outputs = basename + '.scenario'
-
- print("Converting %s to %s, %s" % (inputf, outputf, outputs))
-
- inas = fetch_records(basename)
-
-
- boardfile = open(outputf, 'w')
- scenario = open(outputs, 'w')
-
- boardfile.write('[\n')
- scenario.write('[\n')
- start = True
-
- for rec in inas:
- if start:
- start = False
- else:
- boardfile.write(',\n')
- scenario.write(',\n')
-
- record = ' {"name": "%s", "rs": %f, "sweetberry": "A", "channel": %d}' % (
- rec[2], rec[4], rec[1] - 64)
- boardfile.write(record)
- scenario.write('"%s"' % rec[2])
-
- boardfile.write('\n')
- boardfile.write(']')
-
- scenario.write('\n')
- scenario.write(']')
-
-if __name__ == "__main__":
- main(sys.argv)
diff --git a/extra/usb_power/marlin_v.scenario b/extra/usb_power/marlin_v.scenario
deleted file mode 100644
index 99c132cb27..0000000000
--- a/extra/usb_power/marlin_v.scenario
+++ /dev/null
@@ -1 +0,0 @@
-[["VBAT", "BUSV"], ["VDD_1V8_PANEL", "BUSV"], ["V_EMMC_2V95", "BUSV"], ["V_SR_2V85", "BUSV"], ["V_USBSS_SW_1V8", "BUSV"], ["V_AUDIO_2V15", "BUSV"]]
diff --git a/extra/usb_power/powerlog.README.md b/extra/usb_power/powerlog.README.md
deleted file mode 100644
index 105516330a..0000000000
--- a/extra/usb_power/powerlog.README.md
+++ /dev/null
@@ -1,210 +0,0 @@
-# Sweetberry USB power monitoring
-
-This tool allows high speed monitoring of power rails via a special USB
-endpoint. Currently this is implemented for the Sweetberry board.
-
-To use on a board, you'll need two config files, one describing the board, a
-`.board` file, and one describing the particular rails you want to monitor in
-this session, a `.scenario` file.
-
-## Converting from servo_ina configs
-
-- Method 1 (not limited to chroot)
-
- Many configs can be found for the servo_ina_board in `hdctools/servo/data/`.
- Sweetberry is plug compatible with servo_ina headers, and config files can
- be converted with the following tool:
-
- ```
- ./convert_servo_ina.py <board>_r0_loc.py
- ```
-
- This will generate `<board>_r0_loc.board` and `<board>_r0_loc.scenario`
- locally, which can be used with `powerlog.py`.
-
-- Method 2 (recommended for Chrome OS developers, requires chroot)
-
- If you are using `powerlog.py` within the chroot, copy `<board>_r0_loc.py`
- to `src/third_party/hdctools/servo/data`, then add this line to file:
-
- ```python
- config_type = 'sweetberry'
- ```
-
- And run command in chroot:
-
- ```
- (Anywhere in chroot, just ONCE) cros_workon --host start dev-util/hdctools
- ```
-
- Then every time you make a change to `<board>_r0_loc.py`, run:
-
- ```
- (Anywhere in chroot) sudo emerge dev-util/hdctools
- ```
-
- The command will install the corresponding `.board` and `.scenario` file in
- the chroot. To use `powerlog.py` use the command:
-
- ```
- (Anywhere in chroot) powerlog -b <board>_r0_loc.board -c <board>_r0_loc.scenario
- ```
-
- There is no need to specify the absolute path to the `.board` and
- `.scenario` file, once they are installed into the chroot. If there is any
- changes to `<board>_r0_loc.py`, you need to `sudo emerge dev-util/hdctools`
- again.
-
-## Board files
-
-Board files contain a list of rails, supporting 48 channels each on up to two
-Sweetberries. For each rail you must specify a name, sense resistor value, and
-channel number. You can optionally list expected voltage and net name. The
-format is as follows, in json:
-
-example.board:
-
-```json
-[
-{ "name": "railname",
- "rs": <sense resistor value in ohms>,
- "sweetberry": <"A" for main Sweetberry, "B" for a secondary Sweetberry>,
- "channel": <0-47 according to board schematic>,
- "v": <optional expected bus voltage in volts>,
- "net": <optional schematic net name>
-},
-{...}
-]
-```
-
-## Scenario files
-
-Scenario files contain the set of rails to monitor in this session. The file
-format is simply a list of rail names from the board file.
-
-Optionally, you can specify the type of measurement, from the set of `"POWER"`,
-`"BUSV"`, `"CURRENT"`, `"SHUNTV"`. If not specified, the default is power.
-
-example.scenario:
-
-```json
-[
-"railname",
-"another_railname",
-["railname", "BUSV"],
-["railname", "CURRENT"],
-...
-]
-```
-
-## Output
-
-`powerlog.py` will output a csv formatted log to stdout, at timing intervals
-specified on the command line. Currently values below `-t 10000` do not work
-reliably but further updates should allow faster updating.
-
-An example run of:
-
-```
-./powerlog.py -b board/marlin/marlin.board -c board/marlin/marlin_short.scenario -t 100000
-```
-
-Will result in: `ts:32976us, VBAT uW, VDD_MEM uW, VDD_CORE uW, VDD_GFX uW,
-VDD_1V8_PANEL uW 0.033004, 12207.03, 4882.81, 9155.27, 2441.41, 0.00 0.066008,
-12207.03, 3662.11, 9155.27, 2441.41, 0.00 0.099012, 12207.03, 3662.11, 9155.27,
-2441.41, 0.00 ...`
-
-The output format is as follows:
-
-- `ts:32976us`
-
- Timestamps either zero based or synced to system clock, in seconds. The
- column header indicates the selected sampling interval. Since the INA231 has
- specific hardware defines sampling options, this will be the closest
- supported option lower than the requested `-t` value on the command line.
-
-- `VBAT uW`
-
- Microwatt reading from this rail, generated on the INA by integrating the
- voltage/amperage on the sense resistor over the sampling time, and
- multiplying by the sampled bus voltage.
-
-- `... uW`
-
- Further microwatt entry columns for each rail specified in your scenario
- file.
-
-- `... xX`
-
- Measurement in uW, mW, mV, uA, uV as per config.
-
-## Calculate stats and store data and stats
-
-When appropriate flag is set, powerlog.py is capable of calculating statistics
-and storing statistics and raw data.
-
-- Example 1
-
- ```
- ./powerlog.py -b board/eve_dvt2_loc/eve_dvt2_loc.board -c board/eve_dvt2_loc/eve_dvt2_loc.scenario --save_stats [<directory>]
- ```
-
- If `<directory>` is specified, this will save stats as:
- `<directory>/sweetberry<timestamp>/summary.txt`. If `<directory>` does not
- exist, it will be created.
-
- If `<directory>` is not specified but the flag is set, this will save stats
- under the directory which `powerlog.py` is in: `<directory of
- powerlog.py>/sweetberry<timestamp>/summary.txt`.
-
- If `--save_stats` flag is not set, stats will not be saved.
-
-- Example 2
-
- ```
- ./powerlog.py -b board/eve_dvt2_loc/eve_dvt2_loc.board -c board/eve_dvt2_loc/eve_dvt2_loc.scenario --save_raw_data [<directory>]
- ```
-
- If `<directory>` is specified, this will save raw data in:
- `<directory>/sweetberry<timestamp>/raw_data/`. If `<directory>` does not
- exist, it will be created.
-
- If `<directory>` is not specified but the flag is set, this will save raw
- data under the directory which `powerlog.py` is in: `<directory of
- powerlog.py>/sweetberry<timestamp>/raw_data/`.
-
- If `--save_raw_data` flag is not set, raw data will not be saved.
-
-- Example 3:
-
- ```
- ./powerlog.py -b board/eve_dvt2_loc/eve_dvt2_loc.board -c board/eve_dvt2_loc/eve_dvt2_loc.scenario --save_stats_json [<directory>]
- ```
-
- If `<directory>` is specified, this will save MEANS in json as:
- `<directory>/sweetberry<timestamp>/summary.json`. If `<directory>` does not
- exist, it will be created.
-
- If `<directory>` is not specified but the flag is set, this will save MEANS
- in json under the directory which `powerlog.py` is in: `<directory of
- powerlog.py>/sweetberry<timestamp>/summary.json`.
-
- If `--save_stats` flag is not set, stats will not be saved.
-
- `--save_stats_json` is designed for `power_telemetry_logger` for easy
- reading and writing.
-
-## Making developer changes to `powerlog.py`
-
-`powerlog.py` is installed in chroot, and the developer can import `powerlog` or
-use `powerlog` directly anywhere within chroot. Anytime the developer makes a
-change to `powerlog.py`, the developer needs to re-install `powerlog.py` so that
-anything that imports `powerlog` does not break. The following is how the
-developer installs `powerlog.py` during development.
-
-Run command in chroot:
-
-```
-(Anywhere in chroot, just ONCE) cros_workon --host start chromeos-base/ec-devutils
-(Anywhere in chroot, every time powerlog.py is changed) sudo emerge chromeos-base/ec-devutils
-```
diff --git a/extra/usb_power/powerlog.py b/extra/usb_power/powerlog.py
deleted file mode 100755
index 82cce3daed..0000000000
--- a/extra/usb_power/powerlog.py
+++ /dev/null
@@ -1,908 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2016 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-# Ignore indention messages, since legacy scripts use 2 spaces instead of 4.
-# pylint: disable=bad-indentation,docstring-section-indent
-# pylint: disable=docstring-trailing-quotes
-
-"""Program to fetch power logging data from a sweetberry device
- or other usb device that exports a USB power logging interface.
-"""
-
-# Note: This is a py2/3 compatible file.
-
-from __future__ import print_function
-import argparse
-import array
-from distutils import sysconfig
-import json
-import logging
-import os
-import pprint
-import struct
-import sys
-import time
-import traceback
-
-import usb
-
-from stats_manager import StatsManager
-
-# Directory where hdctools installs configuration files into.
-LIB_DIR = os.path.join(sysconfig.get_python_lib(standard_lib=False), 'servo',
- 'data')
-
-# Potential config file locations: current working directory, the same directory
-# as powerlog.py file or LIB_DIR.
-CONFIG_LOCATIONS = [os.getcwd(), os.path.dirname(os.path.realpath(__file__)),
- LIB_DIR]
-
-def logoutput(msg):
- print(msg)
- sys.stdout.flush()
-
-def process_filename(filename):
- """Find the file path from the filename.
-
- If filename is already the complete path, return that directly. If filename is
- just the short name, look for the file in the current working directory, in
- the directory of the current .py file, and then in the directory installed by
- hdctools. If the file is found, return the complete path of the file.
-
- Args:
- filename: complete file path or short file name.
-
- Returns:
- a complete file path.
-
- Raises:
- IOError if filename does not exist.
- """
- # Check if filename is absolute path.
- if os.path.isabs(filename) and os.path.isfile(filename):
- return filename
- # Check if filename is relative to a known config location.
- for dirname in CONFIG_LOCATIONS:
- file_at_dir = os.path.join(dirname, filename)
- if os.path.isfile(file_at_dir):
- return file_at_dir
- raise IOError('No such file or directory: \'%s\'' % filename)
-
-
-class Spower(object):
- """Power class to access devices on the bus.
-
- Usage:
- bus = Spower()
-
- Instance Variables:
- _dev: pyUSB device object
- _read_ep: pyUSB read endpoint for this interface
- _write_ep: pyUSB write endpoint for this interface
- """
-
- # INA interface type.
- INA_POWER = 1
- INA_BUSV = 2
- INA_CURRENT = 3
- INA_SHUNTV = 4
- # INA_SUFFIX is used to differentiate multiple ina types for the same power
- # rail. No suffix for when ina type is 0 (non-existent) and when ina type is 1
- # (power, no suffix for backward compatibility).
- INA_SUFFIX = ['', '', '_busv', '_cur', '_shuntv']
-
- # usb power commands
- CMD_RESET = 0x0000
- CMD_STOP = 0x0001
- CMD_ADDINA = 0x0002
- CMD_START = 0x0003
- CMD_NEXT = 0x0004
- CMD_SETTIME = 0x0005
-
- # Map between header channel number (0-47)
- # and INA I2C bus/addr on sweetberry.
- CHMAP = {
- 0: (3, 0x40),
- 1: (1, 0x40),
- 2: (2, 0x40),
- 3: (0, 0x40),
- 4: (3, 0x41),
- 5: (1, 0x41),
- 6: (2, 0x41),
- 7: (0, 0x41),
- 8: (3, 0x42),
- 9: (1, 0x42),
- 10: (2, 0x42),
- 11: (0, 0x42),
- 12: (3, 0x43),
- 13: (1, 0x43),
- 14: (2, 0x43),
- 15: (0, 0x43),
- 16: (3, 0x44),
- 17: (1, 0x44),
- 18: (2, 0x44),
- 19: (0, 0x44),
- 20: (3, 0x45),
- 21: (1, 0x45),
- 22: (2, 0x45),
- 23: (0, 0x45),
- 24: (3, 0x46),
- 25: (1, 0x46),
- 26: (2, 0x46),
- 27: (0, 0x46),
- 28: (3, 0x47),
- 29: (1, 0x47),
- 30: (2, 0x47),
- 31: (0, 0x47),
- 32: (3, 0x48),
- 33: (1, 0x48),
- 34: (2, 0x48),
- 35: (0, 0x48),
- 36: (3, 0x49),
- 37: (1, 0x49),
- 38: (2, 0x49),
- 39: (0, 0x49),
- 40: (3, 0x4a),
- 41: (1, 0x4a),
- 42: (2, 0x4a),
- 43: (0, 0x4a),
- 44: (3, 0x4b),
- 45: (1, 0x4b),
- 46: (2, 0x4b),
- 47: (0, 0x4b),
- }
-
- def __init__(self, board, vendor=0x18d1,
- product=0x5020, interface=1, serialname=None):
- self._logger = logging.getLogger(__name__)
- self._board = board
-
- # Find the stm32.
- dev_g = usb.core.find(idVendor=vendor, idProduct=product, find_all=True)
- dev_list = list(dev_g)
- if dev_list is None:
- raise Exception("Power", "USB device not found")
-
- # Check if we have multiple stm32s and we've specified the serial.
- dev = None
- if serialname:
- for d in dev_list:
- dev_serial = "PyUSB dioesn't have a stable interface"
- try:
- dev_serial = usb.util.get_string(d, 256, d.iSerialNumber)
- except ValueError:
- # Incompatible pyUsb version.
- dev_serial = usb.util.get_string(d, d.iSerialNumber)
- if dev_serial == serialname:
- dev = d
- break
- if dev is None:
- raise Exception("Power", "USB device(%s) not found" % serialname)
- else:
- try:
- dev = dev_list[0]
- except TypeError:
- # Incompatible pyUsb version.
- dev = dev_list.next()
-
- self._logger.debug("Found USB device: %04x:%04x", vendor, product)
- self._dev = dev
-
- # Get an endpoint instance.
- try:
- dev.set_configuration()
- except usb.USBError:
- pass
- cfg = dev.get_active_configuration()
-
- intf = usb.util.find_descriptor(cfg, custom_match=lambda i: \
- i.bInterfaceClass==255 and i.bInterfaceSubClass==0x54)
-
- self._intf = intf
- self._logger.debug("InterfaceNumber: %s", intf.bInterfaceNumber)
-
- read_ep = usb.util.find_descriptor(
- intf,
- # match the first IN endpoint
- custom_match = \
- lambda e: \
- usb.util.endpoint_direction(e.bEndpointAddress) == \
- usb.util.ENDPOINT_IN
- )
-
- self._read_ep = read_ep
- self._logger.debug("Reader endpoint: 0x%x", read_ep.bEndpointAddress)
-
- write_ep = usb.util.find_descriptor(
- intf,
- # match the first OUT endpoint
- custom_match = \
- lambda e: \
- usb.util.endpoint_direction(e.bEndpointAddress) == \
- usb.util.ENDPOINT_OUT
- )
-
- self._write_ep = write_ep
- self._logger.debug("Writer endpoint: 0x%x", write_ep.bEndpointAddress)
-
- self.clear_ina_struct()
-
- self._logger.debug("Found power logging USB endpoint.")
-
- def clear_ina_struct(self):
- """ Clear INA description struct."""
- self._inas = []
-
- def append_ina_struct(self, name, rs, port, addr,
- data=None, ina_type=INA_POWER):
- """Add an INA descriptor into the list of active INAs.
-
- Args:
- name: Readable name of this channel.
- rs: Sense resistor value in ohms, floating point.
- port: I2C channel this INA is connected to.
- addr: I2C addr of this INA.
- data: Misc data for special handling, board specific.
- ina_type: INA function to use, power, voltage, etc.
- """
- ina = {}
- ina['name'] = name
- ina['rs'] = rs
- ina['port'] = port
- ina['addr'] = addr
- ina['type'] = ina_type
- # Calculate INA231 Calibration register
- # (see INA231 spec p.15)
- # CurrentLSB = uA per div = 80mV / (Rsh * 2^15)
- # CurrentLSB uA = 80000000nV / (Rsh mOhm * 0x8000)
- ina['uAscale'] = 80000000. / (rs * 0x8000);
- ina['uWscale'] = 25. * ina['uAscale'];
- ina['mVscale'] = 1.25
- ina['uVscale'] = 2.5
- ina['data'] = data
- self._inas.append(ina)
-
- def wr_command(self, write_list, read_count=1, wtimeout=100, rtimeout=1000):
- """Write command to logger logic.
-
- This function writes byte command values list to stm, then reads
- byte status.
-
- Args:
- write_list: list of command byte values [0~255].
- read_count: number of status byte values to read.
-
- Interface:
- write: [command, data ... ]
- read: [status ]
-
- Returns:
- bytes read, or None on failure.
- """
- self._logger.debug("Spower.wr_command(write_list=[%s] (%d), read_count=%s)",
- list(bytearray(write_list)), len(write_list), read_count)
-
- # Clean up args from python style to correct types.
- write_length = 0
- if write_list:
- write_length = len(write_list)
- if not read_count:
- read_count = 0
-
- # Send command to stm32.
- if write_list:
- cmd = write_list
- ret = self._write_ep.write(cmd, wtimeout)
-
- self._logger.debug("RET: %s ", ret)
-
- # Read back response if necessary.
- if read_count:
- bytesread = self._read_ep.read(512, rtimeout)
- self._logger.debug("BYTES: [%s]", bytesread)
-
- if len(bytesread) != read_count:
- pass
-
- self._logger.debug("STATUS: 0x%02x", int(bytesread[0]))
- if read_count == 1:
- return bytesread[0]
- else:
- return bytesread
-
- return None
-
- def clear(self):
- """Clear pending reads on the stm32"""
- try:
- while True:
- ret = self.wr_command(b"", read_count=512, rtimeout=100, wtimeout=50)
- self._logger.debug("Try Clear: read %s",
- "success" if ret == 0 else "failure")
- except:
- pass
-
- def send_reset(self):
- """Reset the power interface on the stm32"""
- cmd = struct.pack("<H", self.CMD_RESET)
- ret = self.wr_command(cmd, rtimeout=50, wtimeout=50)
- self._logger.debug("Command RESET: %s",
- "success" if ret == 0 else "failure")
-
- def reset(self):
- """Try resetting the USB interface until success.
-
- Use linear back off strategy when encounter the error with 10ms increment.
-
- Raises:
- Exception on failure.
- """
- max_reset_retry = 100
- for count in range(1, max_reset_retry + 1):
- self.clear()
- try:
- self.send_reset()
- return
- except Exception as e:
- self.clear()
- self.clear()
- self._logger.debug("TRY %d of %d: %s", count, max_reset_retry, e)
- time.sleep(count * 0.01)
- raise Exception("Power", "Failed to reset")
-
- def stop(self):
- """Stop any active data acquisition."""
- cmd = struct.pack("<H", self.CMD_STOP)
- ret = self.wr_command(cmd)
- self._logger.debug("Command STOP: %s",
- "success" if ret == 0 else "failure")
-
- def start(self, integration_us):
- """Start data acquisition.
-
- Args:
- integration_us: int, how many us between samples, and
- how often the data block must be read.
-
- Returns:
- actual sampling interval in ms.
- """
- cmd = struct.pack("<HI", self.CMD_START, integration_us)
- read = self.wr_command(cmd, read_count=5)
- actual_us = 0
- if len(read) == 5:
- ret, actual_us = struct.unpack("<BI", read)
- self._logger.debug("Command START: %s %dus",
- "success" if ret == 0 else "failure", actual_us)
- else:
- self._logger.debug("Command START: FAIL")
-
- return actual_us
-
- def add_ina_name(self, name_tuple):
- """Add INA from board config.
-
- Args:
- name_tuple: name and type of power rail in board config.
-
- Returns:
- True if INA added, False if the INA is not on this board.
-
- Raises:
- Exception on unexpected failure.
- """
- name, ina_type = name_tuple
-
- for datum in self._brdcfg:
- if datum["name"] == name:
- rs = int(float(datum["rs"]) * 1000.)
- board = datum["sweetberry"]
-
- if board == self._board:
- if 'port' in datum and 'addr' in datum:
- port = datum['port']
- addr = datum['addr']
- else:
- channel = int(datum["channel"])
- port, addr = self.CHMAP[channel]
- self.add_ina(port, ina_type, addr, 0, rs, data=datum)
- return True
- else:
- return False
- raise Exception("Power", "Failed to find INA %s" % name)
-
- def set_time(self, timestamp_us):
- """Set sweetberry time to match host time.
-
- Args:
- timestamp_us: host timestmap in us.
- """
- # 0x0005 , 8 byte timestamp
- cmd = struct.pack("<HQ", self.CMD_SETTIME, timestamp_us)
- ret = self.wr_command(cmd)
-
- self._logger.debug("Command SETTIME: %s",
- "success" if ret == 0 else "failure")
-
- def add_ina(self, bus, ina_type, addr, extra, resistance, data=None):
- """Add an INA to the data acquisition list.
-
- Args:
- bus: which i2c bus the INA is on. Same ordering as Si2c.
- ina_type: Ina interface: INA_POWER/BUSV/etc.
- addr: 7 bit i2c addr of this INA
- extra: extra data for nonstandard configs.
- resistance: int, shunt resistance in mOhm
- """
- # 0x0002, 1B: bus, 1B:INA type, 1B: INA addr, 1B: extra, 4B: Rs
- cmd = struct.pack("<HBBBBI", self.CMD_ADDINA,
- bus, ina_type, addr, extra, resistance)
- ret = self.wr_command(cmd)
- if ret == 0:
- if data:
- name = data['name']
- else:
- name = "ina%d_%02x" % (bus, addr)
- self.append_ina_struct(name, resistance, bus, addr,
- data=data, ina_type=ina_type)
- self._logger.debug("Command ADD_INA: %s",
- "success" if ret == 0 else "failure")
-
- def report_header_size(self):
- """Helper function to calculate power record header size."""
- result = 2
- timestamp = 8
- return result + timestamp
-
- def report_size(self, ina_count):
- """Helper function to calculate full power record size."""
- record = 2
-
- datasize = self.report_header_size() + ina_count * record
- # Round to multiple of 4 bytes.
- datasize = int(((datasize + 3) // 4) * 4)
-
- return datasize
-
- def read_line(self):
- """Read a line of data from the setup INAs
-
- Returns:
- list of dicts of the values read by ina/type tuple, otherwise None.
- [{ts:100, (vbat, power):450}, {ts:200, (vbat, power):440}]
- """
- try:
- expected_bytes = self.report_size(len(self._inas))
- cmd = struct.pack("<H", self.CMD_NEXT)
- bytesread = self.wr_command(cmd, read_count=expected_bytes)
- except usb.core.USBError as e:
- self._logger.error("READ LINE FAILED %s", e)
- return None
-
- if len(bytesread) == 1:
- if bytesread[0] != 0x6:
- self._logger.debug("READ LINE FAILED bytes: %d ret: %02x",
- len(bytesread), bytesread[0])
- return None
-
- if len(bytesread) % expected_bytes != 0:
- self._logger.debug("READ LINE WARNING: expected %d, got %d",
- expected_bytes, len(bytesread))
-
- packet_count = len(bytesread) // expected_bytes
-
- values = []
- for i in range(0, packet_count):
- start = i * expected_bytes
- end = (i + 1) * expected_bytes
- record = self.interpret_line(bytesread[start:end])
- values.append(record)
-
- return values
-
- def interpret_line(self, data):
- """Interpret a power record from INAs
-
- Args:
- data: one single record of bytes.
-
- Output:
- stdout of the record in csv format.
-
- Returns:
- dict containing name, value of recorded data.
- """
- status, size = struct.unpack("<BB", data[0:2])
- if len(data) != self.report_size(size):
- self._logger.error("READ LINE FAILED st:%d size:%d expected:%d len:%d",
- status, size, self.report_size(size), len(data))
- else:
- pass
-
- timestamp = struct.unpack("<Q", data[2:10])[0]
- self._logger.debug("READ LINE: st:%d size:%d time:%dus", status, size,
- timestamp)
- ftimestamp = float(timestamp) / 1000000.
-
- record = {"ts": ftimestamp, "status": status, "berry":self._board}
-
- for i in range(0, size):
- idx = self.report_header_size() + 2*i
- name = self._inas[i]['name']
- name_tuple = (self._inas[i]['name'], self._inas[i]['type'])
-
- raw_val = struct.unpack("<h", data[idx:idx+2])[0]
-
- if self._inas[i]['type'] == Spower.INA_POWER:
- val = raw_val * self._inas[i]['uWscale']
- elif self._inas[i]['type'] == Spower.INA_BUSV:
- val = raw_val * self._inas[i]['mVscale']
- elif self._inas[i]['type'] == Spower.INA_CURRENT:
- val = raw_val * self._inas[i]['uAscale']
- elif self._inas[i]['type'] == Spower.INA_SHUNTV:
- val = raw_val * self._inas[i]['uVscale']
-
- self._logger.debug("READ %d %s: %fs: 0x%04x %f", i, name, ftimestamp,
- raw_val, val)
- record[name_tuple] = val
-
- return record
-
- def load_board(self, brdfile):
- """Load a board config.
-
- Args:
- brdfile: Filename of a json file decribing the INA wiring of this board.
- """
- with open(process_filename(brdfile)) as data_file:
- data = json.load(data_file)
-
- #TODO: validate this.
- self._brdcfg = data;
- self._logger.debug(pprint.pformat(data))
-
-
-class powerlog(object):
- """Power class to log aggregated power.
-
- Usage:
- obj = powerlog()
-
- Instance Variables:
- _data: a StatsManager object that records sweetberry readings and calculates
- statistics.
- _pwr[]: Spower objects for individual sweetberries.
- """
-
- def __init__(self, brdfile, cfgfile, serial_a=None, serial_b=None,
- sync_date=False, use_ms=False, use_mW=False, print_stats=False,
- stats_dir=None, stats_json_dir=None, print_raw_data=True,
- raw_data_dir=None):
- """Init the powerlog class and set the variables.
-
- Args:
- brdfile: string name of json file containing board layout.
- cfgfile: string name of json containing list of rails to read.
- serial_a: serial number of sweetberry A.
- serial_b: serial number of sweetberry B.
- sync_date: report timestamps synced with host datetime.
- use_ms: report timestamps in ms rather than us.
- use_mW: report power as milliwatts, otherwise default to microwatts.
- print_stats: print statistics for sweetberry readings at the end.
- stats_dir: directory to save sweetberry readings statistics; if None then
- do not save the statistics.
- stats_json_dir: directory to save means of sweetberry readings in json
- format; if None then do not save the statistics.
- print_raw_data: print sweetberry readings raw data in real time, default
- is to print.
- raw_data_dir: directory to save sweetberry readings raw data; if None then
- do not save the raw data.
- """
- self._logger = logging.getLogger(__name__)
- self._data = StatsManager()
- self._pwr = {}
- self._use_ms = use_ms
- self._use_mW = use_mW
- self._print_stats = print_stats
- self._stats_dir = stats_dir
- self._stats_json_dir = stats_json_dir
- self._print_raw_data = print_raw_data
- self._raw_data_dir = raw_data_dir
-
- if not serial_a and not serial_b:
- self._pwr['A'] = Spower('A')
- if serial_a:
- self._pwr['A'] = Spower('A', serialname=serial_a)
- if serial_b:
- self._pwr['B'] = Spower('B', serialname=serial_b)
-
- with open(process_filename(cfgfile)) as data_file:
- names = json.load(data_file)
- self._names = self.process_scenario(names)
-
- for key in self._pwr:
- self._pwr[key].load_board(brdfile)
- self._pwr[key].reset()
-
- # Allocate the rails to the appropriate boards.
- used_boards = []
- for name in self._names:
- success = False
- for key in self._pwr.keys():
- if self._pwr[key].add_ina_name(name):
- success = True
- if key not in used_boards:
- used_boards.append(key)
- if not success:
- raise Exception("Failed to add %s (maybe missing "
- "sweetberry, or bad board file?)" % name)
-
- # Evict unused boards.
- for key in list(self._pwr.keys()):
- if key not in used_boards:
- self._pwr.pop(key)
-
- for key in self._pwr.keys():
- if sync_date:
- self._pwr[key].set_time(time.time() * 1000000)
- else:
- self._pwr[key].set_time(0)
-
- def process_scenario(self, name_list):
- """Return list of tuples indicating name and type.
-
- Args:
- json originated list of names, or [name, type]
- Returns:
- list of tuples of (name, type) defaulting to type "POWER"
- Raises: exception, invalid INA type.
- """
- names = []
- for entry in name_list:
- if isinstance(entry, list):
- name = entry[0]
- if entry[1] == "POWER":
- type = Spower.INA_POWER
- elif entry[1] == "BUSV":
- type = Spower.INA_BUSV
- elif entry[1] == "CURRENT":
- type = Spower.INA_CURRENT
- elif entry[1] == "SHUNTV":
- type = Spower.INA_SHUNTV
- else:
- raise Exception("Invalid INA type", "Type of %s [%s] not recognized,"
- " try one of POWER, BUSV, CURRENT" % (entry[0], entry[1]))
- else:
- name = entry
- type = Spower.INA_POWER
-
- names.append((name, type))
- return names
-
- def start(self, integration_us_request, seconds, sync_speed=.8):
- """Starts sampling.
-
- Args:
- integration_us_request: requested interval between sample values.
- seconds: time until exit, or None to run until cancel.
- sync_speed: A usb request is sent every [.8] * integration_us.
- """
- # We will get back the actual integration us.
- # It should be the same for all devices.
- integration_us = None
- for key in self._pwr:
- integration_us_new = self._pwr[key].start(integration_us_request)
- if integration_us:
- if integration_us != integration_us_new:
- raise Exception("FAIL",
- "Integration on A: %dus != integration on B %dus" % (
- integration_us, integration_us_new))
- integration_us = integration_us_new
-
- # CSV header
- title = "ts:%dus" % integration_us
- for name_tuple in self._names:
- name, ina_type = name_tuple
-
- if ina_type == Spower.INA_POWER:
- unit = "mW" if self._use_mW else "uW"
- elif ina_type == Spower.INA_BUSV:
- unit = "mV"
- elif ina_type == Spower.INA_CURRENT:
- unit = "uA"
- elif ina_type == Spower.INA_SHUNTV:
- unit = "uV"
-
- title += ", %s %s" % (name, unit)
- name_type = name + Spower.INA_SUFFIX[ina_type]
- self._data.SetUnit(name_type, unit)
- title += ", status"
- if self._print_raw_data:
- logoutput(title)
-
- forever = False
- if not seconds:
- forever = True
- end_time = time.time() + seconds
- try:
- pending_records = []
- while forever or end_time > time.time():
- if (integration_us > 5000):
- time.sleep((integration_us / 1000000.) * sync_speed)
- for key in self._pwr:
- records = self._pwr[key].read_line()
- if not records:
- continue
-
- for record in records:
- pending_records.append(record)
-
- pending_records.sort(key=lambda r: r['ts'])
-
- aggregate_record = {"boards": set()}
- for record in pending_records:
- if record["berry"] not in aggregate_record["boards"]:
- for rkey in record.keys():
- aggregate_record[rkey] = record[rkey]
- aggregate_record["boards"].add(record["berry"])
- else:
- self._logger.info("break %s, %s", record["berry"],
- aggregate_record["boards"])
- break
-
- if aggregate_record["boards"] == set(self._pwr.keys()):
- csv = "%f" % aggregate_record["ts"]
- for name in self._names:
- if name in aggregate_record:
- multiplier = 0.001 if (self._use_mW and
- name[1]==Spower.INA_POWER) else 1
- value = aggregate_record[name] * multiplier
- csv += ", %.2f" % value
- name_type = name[0] + Spower.INA_SUFFIX[name[1]]
- self._data.AddSample(name_type, value)
- else:
- csv += ", "
- csv += ", %d" % aggregate_record["status"]
- if self._print_raw_data:
- logoutput(csv)
-
- aggregate_record = {"boards": set()}
- for r in range(0, len(self._pwr)):
- pending_records.pop(0)
-
- except KeyboardInterrupt:
- self._logger.info('\nCTRL+C caught.')
-
- finally:
- for key in self._pwr:
- self._pwr[key].stop()
- self._data.CalculateStats()
- if self._print_stats:
- print(self._data.SummaryToString())
- save_dir = 'sweetberry%s' % time.time()
- if self._stats_dir:
- stats_dir = os.path.join(self._stats_dir, save_dir)
- self._data.SaveSummary(stats_dir)
- if self._stats_json_dir:
- stats_json_dir = os.path.join(self._stats_json_dir, save_dir)
- self._data.SaveSummaryJSON(stats_json_dir)
- if self._raw_data_dir:
- raw_data_dir = os.path.join(self._raw_data_dir, save_dir)
- self._data.SaveRawData(raw_data_dir)
-
-
-def main(argv=None):
- if argv is None:
- argv = sys.argv[1:]
- # Command line argument description.
- parser = argparse.ArgumentParser(
- description="Gather CSV data from sweetberry")
- parser.add_argument('-b', '--board', type=str,
- help="Board configuration file, eg. my.board", default="")
- parser.add_argument('-c', '--config', type=str,
- help="Rail config to monitor, eg my.scenario", default="")
- parser.add_argument('-A', '--serial', type=str,
- help="Serial number of sweetberry A", default="")
- parser.add_argument('-B', '--serial_b', type=str,
- help="Serial number of sweetberry B", default="")
- parser.add_argument('-t', '--integration_us', type=int,
- help="Target integration time for samples", default=100000)
- parser.add_argument('-s', '--seconds', type=float,
- help="Seconds to run capture", default=0.)
- parser.add_argument('--date', default=False,
- help="Sync logged timestamp to host date", action="store_true")
- parser.add_argument('--ms', default=False,
- help="Print timestamp as milliseconds", action="store_true")
- parser.add_argument('--mW', default=False,
- help="Print power as milliwatts, otherwise default to microwatts",
- action="store_true")
- parser.add_argument('--slow', default=False,
- help="Intentionally overflow", action="store_true")
- parser.add_argument('--print_stats', default=False, action="store_true",
- help="Print statistics for sweetberry readings at the end")
- parser.add_argument('--save_stats', type=str, nargs='?',
- dest='stats_dir', metavar='STATS_DIR',
- const=os.path.dirname(os.path.abspath(__file__)), default=None,
- help="Save statistics for sweetberry readings to %(metavar)s if "
- "%(metavar)s is specified, %(metavar)s will be created if it does "
- "not exist; if %(metavar)s is not specified but the flag is set, "
- "stats will be saved to where %(prog)s is located; if this flag is "
- "not set, then do not save stats")
- parser.add_argument('--save_stats_json', type=str, nargs='?',
- dest='stats_json_dir', metavar='STATS_JSON_DIR',
- const=os.path.dirname(os.path.abspath(__file__)), default=None,
- help="Save means for sweetberry readings in json to %(metavar)s if "
- "%(metavar)s is specified, %(metavar)s will be created if it does "
- "not exist; if %(metavar)s is not specified but the flag is set, "
- "stats will be saved to where %(prog)s is located; if this flag is "
- "not set, then do not save stats")
- parser.add_argument('--no_print_raw_data',
- dest='print_raw_data', default=True, action="store_false",
- help="Not print raw sweetberry readings at real time, default is to "
- "print")
- parser.add_argument('--save_raw_data', type=str, nargs='?',
- dest='raw_data_dir', metavar='RAW_DATA_DIR',
- const=os.path.dirname(os.path.abspath(__file__)), default=None,
- help="Save raw data for sweetberry readings to %(metavar)s if "
- "%(metavar)s is specified, %(metavar)s will be created if it does "
- "not exist; if %(metavar)s is not specified but the flag is set, "
- "raw data will be saved to where %(prog)s is located; if this flag "
- "is not set, then do not save raw data")
- parser.add_argument('-v', '--verbose', default=False,
- help="Very chatty printout", action="store_true")
-
- args = parser.parse_args(argv)
-
- root_logger = logging.getLogger(__name__)
- if args.verbose:
- root_logger.setLevel(logging.DEBUG)
- else:
- root_logger.setLevel(logging.INFO)
-
- # if powerlog is used through main, log to sys.stdout
- if __name__ == "__main__":
- stdout_handler = logging.StreamHandler(sys.stdout)
- stdout_handler.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
- root_logger.addHandler(stdout_handler)
-
- integration_us_request = args.integration_us
- if not args.board:
- raise Exception("Power", "No board file selected, see board.README")
- if not args.config:
- raise Exception("Power", "No config file selected, see board.README")
-
- brdfile = args.board
- cfgfile = args.config
- seconds = args.seconds
- serial_a = args.serial
- serial_b = args.serial_b
- sync_date = args.date
- use_ms = args.ms
- use_mW = args.mW
- print_stats = args.print_stats
- stats_dir = args.stats_dir
- stats_json_dir = args.stats_json_dir
- print_raw_data = args.print_raw_data
- raw_data_dir = args.raw_data_dir
-
- boards = []
-
- sync_speed = .8
- if args.slow:
- sync_speed = 1.2
-
- # Set up logging interface.
- powerlogger = powerlog(brdfile, cfgfile, serial_a=serial_a, serial_b=serial_b,
- sync_date=sync_date, use_ms=use_ms, use_mW=use_mW,
- print_stats=print_stats, stats_dir=stats_dir,
- stats_json_dir=stats_json_dir,
- print_raw_data=print_raw_data,raw_data_dir=raw_data_dir)
-
- # Start logging.
- powerlogger.start(integration_us_request, seconds, sync_speed=sync_speed)
-
-
-if __name__ == "__main__":
- main()
diff --git a/extra/usb_power/powerlog_unittest.py b/extra/usb_power/powerlog_unittest.py
deleted file mode 100644
index 1d0718530e..0000000000
--- a/extra/usb_power/powerlog_unittest.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright 2018 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-# Ignore indention messages, since legacy scripts use 2 spaces instead of 4.
-# pylint: disable=bad-indentation,docstring-section-indent
-# pylint: disable=docstring-trailing-quotes
-
-"""Unit tests for powerlog."""
-
-import os
-import shutil
-import tempfile
-import unittest
-
-import powerlog
-
-class TestPowerlog(unittest.TestCase):
- """Test to verify powerlog util methods work as expected."""
-
- def setUp(self):
- """Set up data and create a temporary directory to save data and stats."""
- self.tempdir = tempfile.mkdtemp()
- self.filename = 'testfile'
- self.filepath = os.path.join(self.tempdir, self.filename)
- with open(self.filepath, 'w') as f:
- f.write('')
-
- def tearDown(self):
- """Delete the temporary directory and its content."""
- shutil.rmtree(self.tempdir)
-
- def test_ProcessFilenameAbsoluteFilePath(self):
- """Absolute file path is returned unchanged."""
- processed_fname = powerlog.process_filename(self.filepath)
- self.assertEqual(self.filepath, processed_fname)
-
- def test_ProcessFilenameRelativeFilePath(self):
- """Finds relative file path inside a known config location."""
- original = powerlog.CONFIG_LOCATIONS
- powerlog.CONFIG_LOCATIONS = [self.tempdir]
- processed_fname = powerlog.process_filename(self.filename)
- try:
- self.assertEqual(self.filepath, processed_fname)
- finally:
- powerlog.CONFIG_LOCATIONS = original
-
- def test_ProcessFilenameInvalid(self):
- """IOError is raised when file cannot be found by any of the four ways."""
- with self.assertRaises(IOError):
- powerlog.process_filename(self.filename)
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/extra/usb_power/stats_manager.py b/extra/usb_power/stats_manager.py
deleted file mode 100644
index 0f8c3fcb15..0000000000
--- a/extra/usb_power/stats_manager.py
+++ /dev/null
@@ -1,401 +0,0 @@
-# Copyright 2017 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-# Ignore indention messages, since legacy scripts use 2 spaces instead of 4.
-# pylint: disable=bad-indentation,docstring-section-indent
-# pylint: disable=docstring-trailing-quotes
-
-"""Calculates statistics for lists of data and pretty print them."""
-
-# Note: This is a py2/3 compatible file.
-
-from __future__ import print_function
-
-import collections
-import json
-import logging
-import math
-import os
-
-import numpy
-
-STATS_PREFIX = '@@'
-NAN_TAG = '*'
-NAN_DESCRIPTION = '%s domains contain NaN samples' % NAN_TAG
-
-LONG_UNIT = {
- '': 'N/A',
- 'mW': 'milliwatt',
- 'uW': 'microwatt',
- 'mV': 'millivolt',
- 'uA': 'microamp',
- 'uV': 'microvolt'
-}
-
-
-class StatsManagerError(Exception):
- """Errors in StatsManager class."""
- pass
-
-
-class StatsManager(object):
- """Calculates statistics for several lists of data(float).
-
- Example usage:
-
- >>> stats = StatsManager(title='Title Banner')
- >>> stats.AddSample(TIME_KEY, 50.0)
- >>> stats.AddSample(TIME_KEY, 25.0)
- >>> stats.AddSample(TIME_KEY, 40.0)
- >>> stats.AddSample(TIME_KEY, 10.0)
- >>> stats.AddSample(TIME_KEY, 10.0)
- >>> stats.AddSample('frobnicate', 11.5)
- >>> stats.AddSample('frobnicate', 9.0)
- >>> stats.AddSample('foobar', 11111.0)
- >>> stats.AddSample('foobar', 22222.0)
- >>> stats.CalculateStats()
- >>> print(stats.SummaryToString())
- ` @@--------------------------------------------------------------
- ` @@ Title Banner
- @@--------------------------------------------------------------
- @@ NAME COUNT MEAN STDDEV MAX MIN
- @@ sample_msecs 4 31.25 15.16 50.00 10.00
- @@ foobar 2 16666.50 5555.50 22222.00 11111.00
- @@ frobnicate 2 10.25 1.25 11.50 9.00
- ` @@--------------------------------------------------------------
-
- Attributes:
- _data: dict of list of readings for each domain(key)
- _unit: dict of unit for each domain(key)
- _smid: id supplied to differentiate data output to other StatsManager
- instances that potentially save to the same directory
- if smid all output files will be named |smid|_|fname|
- _title: title to add as banner to formatted summary. If no title,
- no banner gets added
- _order: list of formatting order for domains. Domains not listed are
- displayed in sorted order
- _hide_domains: collection of domains to hide when formatting summary string
- _accept_nan: flag to indicate if NaN samples are acceptable
- _nan_domains: set to keep track of which domains contain NaN samples
- _summary: dict of stats per domain (key): min, max, count, mean, stddev
- _logger = StatsManager logger
-
- Note:
- _summary is empty until CalculateStats() is called, and is updated when
- CalculateStats() is called.
- """
-
- # pylint: disable=W0102
- def __init__(self, smid='', title='', order=[], hide_domains=[],
- accept_nan=True):
- """Initialize infrastructure for data and their statistics."""
- self._title = title
- self._data = collections.defaultdict(list)
- self._unit = collections.defaultdict(str)
- self._smid = smid
- self._order = order
- self._hide_domains = hide_domains
- self._accept_nan = accept_nan
- self._nan_domains = set()
- self._summary = {}
- self._logger = logging.getLogger(type(self).__name__)
-
- def AddSample(self, domain, sample):
- """Add one sample for a domain.
-
- Args:
- domain: the domain name for the sample.
- sample: one time sample for domain, expect type float.
-
- Raises:
- StatsManagerError: if trying to add NaN and |_accept_nan| is false
- """
- try:
- sample = float(sample)
- except ValueError:
- # if we don't accept nan this will be caught below
- self._logger.debug('sample %s for domain %s is not a number. Making NaN',
- sample, domain)
- sample = float('NaN')
- if not self._accept_nan and math.isnan(sample):
- raise StatsManagerError('accept_nan is false. Cannot add NaN sample.')
- self._data[domain].append(sample)
- if math.isnan(sample):
- self._nan_domains.add(domain)
-
- def SetUnit(self, domain, unit):
- """Set the unit for a domain.
-
- There can be only one unit for each domain. Setting unit twice will
- overwrite the original unit.
-
- Args:
- domain: the domain name.
- unit: unit of the domain.
- """
- if domain in self._unit:
- self._logger.warning('overwriting the unit of %s, old unit is %s, new '
- 'unit is %s.', domain, self._unit[domain], unit)
- self._unit[domain] = unit
-
- def CalculateStats(self):
- """Calculate stats for all domain-data pairs.
-
- First erases all previous stats, then calculate stats for all data.
- """
- self._summary = {}
- for domain, data in self._data.items():
- data_np = numpy.array(data)
- self._summary[domain] = {
- 'mean': numpy.nanmean(data_np),
- 'min': numpy.nanmin(data_np),
- 'max': numpy.nanmax(data_np),
- 'stddev': numpy.nanstd(data_np),
- 'count': data_np.size,
- }
-
- @property
- def DomainsToDisplay(self):
- """List of domains that the manager will output in summaries."""
- return set(self._summary.keys()) - set(self._hide_domains)
-
- @property
- def NanInOutput(self):
- """Return whether any of the domains to display have NaN values."""
- return bool(len(set(self._nan_domains) & self.DomainsToDisplay))
-
- def _SummaryTable(self):
- """Generate the matrix to output as a summary.
-
- Returns:
- A 2d matrix of headers and their data for each domain
- e.g.
- [[NAME, COUNT, MEAN, STDDEV, MAX, MIN],
- [pp5000_mw, 10, 50, 0, 50, 50]]
- """
- headers = ('NAME', 'COUNT', 'MEAN', 'STDDEV', 'MAX', 'MIN')
- table = [headers]
- # determine what domains to display & and the order
- domains_to_display = self.DomainsToDisplay
- display_order = [key for key in self._order if key in domains_to_display]
- domains_to_display -= set(display_order)
- display_order.extend(sorted(domains_to_display))
- for domain in display_order:
- stats = self._summary[domain]
- if not domain.endswith(self._unit[domain]):
- domain = '%s_%s' % (domain, self._unit[domain])
- if domain in self._nan_domains:
- domain = '%s%s' % (domain, NAN_TAG)
- row = [domain]
- row.append(str(stats['count']))
- for entry in headers[2:]:
- row.append('%.2f' % stats[entry.lower()])
- table.append(row)
- return table
-
- def SummaryToMarkdownString(self):
- """Format the summary into a b/ compatible markdown table string.
-
- This requires this sort of output format
-
- | header1 | header2 | header3 | ...
- | --------- | --------- | --------- | ...
- | sample1h1 | sample1h2 | sample1h3 | ...
- .
- .
- .
-
- Returns:
- formatted summary string.
- """
- # All we need to do before processing is insert a row of '-' between
- # the headers, and the data
- table = self._SummaryTable()
- columns = len(table[0])
- # Using '-:' to allow the numbers to be right aligned
- sep_row = ['-'] + ['-:'] * (columns - 1)
- table.insert(1, sep_row)
- text_rows = ['|'.join(r) for r in table]
- body = '\n'.join(['|%s|' % r for r in text_rows])
- if self._title:
- title_section = '**%s** \n\n' % self._title
- body = title_section + body
- # Make sure that the body is terminated with a newline.
- return body + '\n'
-
- def SummaryToString(self, prefix=STATS_PREFIX):
- """Format summary into a string, ready for pretty print.
-
- See class description for format example.
-
- Args:
- prefix: start every row in summary string with prefix, for easier reading.
-
- Returns:
- formatted summary string.
- """
- table = self._SummaryTable()
- max_col_width = []
- for col_idx in range(len(table[0])):
- col_item_widths = [len(row[col_idx]) for row in table]
- max_col_width.append(max(col_item_widths))
-
- formatted_lines = []
- for row in table:
- formatted_row = prefix + ' '
- for i in range(len(row)):
- formatted_row += row[i].rjust(max_col_width[i] + 2)
- formatted_lines.append(formatted_row)
- if self.NanInOutput:
- formatted_lines.append('%s %s' % (prefix, NAN_DESCRIPTION))
-
- if self._title:
- line_length = len(formatted_lines[0])
- dec_length = len(prefix)
- # trim title to be at most as long as the longest line without the prefix
- title = self._title[:(line_length - dec_length)]
- # line is a seperator line consisting of -----
- line = '%s%s' % (prefix, '-' * (line_length - dec_length))
- # prepend the prefix to the centered title
- padded_title = '%s%s' % (prefix, title.center(line_length)[dec_length:])
- formatted_lines = [line, padded_title, line] + formatted_lines + [line]
- formatted_output = '\n'.join(formatted_lines)
- return formatted_output
-
- def GetSummary(self):
- """Getter for summary."""
- return self._summary
-
- def _MakeUniqueFName(self, fname):
- """prepend |_smid| to fname & rotate fname to ensure uniqueness.
-
- Before saving a file through the StatsManager, make sure that the filename
- is unique, first by prepending the smid if any and otherwise by appending
- increasing integer suffixes until the filename is unique.
-
- If |smid| is defined /path/to/example/file.txt becomes
- /path/to/example/{smid}_file.txt.
-
- The rotation works by changing /path/to/example/somename.txt to
- /path/to/example/somename1.txt if the first one already exists on the
- system.
-
- Note: this is not thread-safe. While it makes sense to use StatsManager
- in a threaded data-collection, the data retrieval should happen in a
- single threaded environment to ensure files don't get potentially clobbered.
-
- Args:
- fname: filename to ensure uniqueness.
-
- Returns:
- {smid_}fname{tag}.[b].ext
- the smid portion gets prepended if |smid| is defined
- the tag portion gets appended if necessary to ensure unique fname
- """
- fdir = os.path.dirname(fname)
- base, ext = os.path.splitext(os.path.basename(fname))
- if self._smid:
- base = '%s_%s' % (self._smid, base)
- unique_fname = os.path.join(fdir, '%s%s' % (base, ext))
- tag = 0
- while os.path.exists(unique_fname):
- old_fname = unique_fname
- unique_fname = os.path.join(fdir, '%s%d%s' % (base, tag, ext))
- self._logger.warning('Attempted to store stats information at %s, but '
- 'file already exists. Attempting to store at %s '
- 'now.', old_fname, unique_fname)
- tag += 1
- return unique_fname
-
- def SaveSummary(self, directory, fname='summary.txt', prefix=STATS_PREFIX):
- """Save summary to file.
-
- Args:
- directory: directory to save the summary in.
- fname: filename to save summary under.
- prefix: start every row in summary string with prefix, for easier reading.
-
- Returns:
- full path of summary save location
- """
- summary_str = self.SummaryToString(prefix=prefix) + '\n'
- return self._SaveSummary(summary_str, directory, fname)
-
- def SaveSummaryJSON(self, directory, fname='summary.json'):
- """Save summary (only MEAN) into a JSON file.
-
- Args:
- directory: directory to save the JSON summary in.
- fname: filename to save summary under.
-
- Returns:
- full path of summary save location
- """
- data = {}
- for domain in self._summary:
- unit = LONG_UNIT.get(self._unit[domain], self._unit[domain])
- data_entry = {'mean': self._summary[domain]['mean'], 'unit': unit}
- data[domain] = data_entry
- summary_str = json.dumps(data, indent=2)
- return self._SaveSummary(summary_str, directory, fname)
-
- def SaveSummaryMD(self, directory, fname='summary.md'):
- """Save summary into a MD file to paste into b/.
-
- Args:
- directory: directory to save the MD summary in.
- fname: filename to save summary under.
-
- Returns:
- full path of summary save location
- """
- summary_str = self.SummaryToMarkdownString()
- return self._SaveSummary(summary_str, directory, fname)
-
- def _SaveSummary(self, output_str, directory, fname):
- """Wrote |output_str| to |fname|.
-
- Args:
- output_str: formatted output string
- directory: directory to save the summary in.
- fname: filename to save summary under.
-
- Returns:
- full path of summary save location
- """
- if not os.path.exists(directory):
- os.makedirs(directory)
- fname = self._MakeUniqueFName(os.path.join(directory, fname))
- with open(fname, 'w') as f:
- f.write(output_str)
- return fname
-
- def GetRawData(self):
- """Getter for all raw_data."""
- return self._data
-
- def SaveRawData(self, directory, dirname='raw_data'):
- """Save raw data to file.
-
- Args:
- directory: directory to create the raw data folder in.
- dirname: folder in which raw data live.
-
- Returns:
- list of full path of each domain's raw data save location
- """
- if not os.path.exists(directory):
- os.makedirs(directory)
- dirname = os.path.join(directory, dirname)
- if not os.path.exists(dirname):
- os.makedirs(dirname)
- fnames = []
- for domain, data in self._data.items():
- if not domain.endswith(self._unit[domain]):
- domain = '%s_%s' % (domain, self._unit[domain])
- fname = self._MakeUniqueFName(os.path.join(dirname, '%s.txt' % domain))
- with open(fname, 'w') as f:
- f.write('\n'.join('%.2f' % sample for sample in data) + '\n')
- fnames.append(fname)
- return fnames
diff --git a/extra/usb_power/stats_manager_unittest.py b/extra/usb_power/stats_manager_unittest.py
deleted file mode 100644
index beb9984b93..0000000000
--- a/extra/usb_power/stats_manager_unittest.py
+++ /dev/null
@@ -1,315 +0,0 @@
-# Copyright 2017 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-# Ignore indention messages, since legacy scripts use 2 spaces instead of 4.
-# pylint: disable=bad-indentation,docstring-section-indent
-# pylint: disable=docstring-trailing-quotes
-
-"""Unit tests for StatsManager."""
-
-from __future__ import print_function
-import json
-import os
-import re
-import shutil
-import tempfile
-import unittest
-
-import stats_manager
-
-
-class TestStatsManager(unittest.TestCase):
- """Test to verify StatsManager methods work as expected.
-
- StatsManager should collect raw data, calculate their statistics, and save
- them in expected format.
- """
-
- def _populate_mock_stats(self):
- """Create a populated & processed StatsManager to test data retrieval."""
- self.data.AddSample('A', 99999.5)
- self.data.AddSample('A', 100000.5)
- self.data.SetUnit('A', 'uW')
- self.data.SetUnit('A', 'mW')
- self.data.AddSample('B', 1.5)
- self.data.AddSample('B', 2.5)
- self.data.AddSample('B', 3.5)
- self.data.SetUnit('B', 'mV')
- self.data.CalculateStats()
-
- def _populate_mock_stats_no_unit(self):
- self.data.AddSample('B', 1000)
- self.data.AddSample('A', 200)
- self.data.SetUnit('A', 'blue')
-
- def setUp(self):
- """Set up StatsManager and create a temporary directory for test."""
- self.tempdir = tempfile.mkdtemp()
- self.data = stats_manager.StatsManager()
-
- def tearDown(self):
- """Delete the temporary directory and its content."""
- shutil.rmtree(self.tempdir)
-
- def test_AddSample(self):
- """Adding a sample successfully adds a sample."""
- self.data.AddSample('Test', 1000)
- self.data.SetUnit('Test', 'test')
- self.data.CalculateStats()
- summary = self.data.GetSummary()
- self.assertEqual(1, summary['Test']['count'])
-
- def test_AddSampleNoFloatAcceptNaN(self):
- """Adding a non-number adds 'NaN' and doesn't raise an exception."""
- self.data.AddSample('Test', 10)
- self.data.AddSample('Test', 20)
- # adding a fake NaN: one that gets converted into NaN internally
- self.data.AddSample('Test', 'fiesta')
- # adding a real NaN
- self.data.AddSample('Test', float('NaN'))
- self.data.SetUnit('Test', 'test')
- self.data.CalculateStats()
- summary = self.data.GetSummary()
- # assert that 'NaN' as added.
- self.assertEqual(4, summary['Test']['count'])
- # assert that mean, min, and max calculatings ignore the 'NaN'
- self.assertEqual(10, summary['Test']['min'])
- self.assertEqual(20, summary['Test']['max'])
- self.assertEqual(15, summary['Test']['mean'])
-
- def test_AddSampleNoFloatNotAcceptNaN(self):
- """Adding a non-number raises a StatsManagerError if accept_nan is False."""
- self.data = stats_manager.StatsManager(accept_nan=False)
- with self.assertRaisesRegexp(stats_manager.StatsManagerError,
- 'accept_nan is false. Cannot add NaN sample.'):
- # adding a fake NaN: one that gets converted into NaN internally
- self.data.AddSample('Test', 'fiesta')
- with self.assertRaisesRegexp(stats_manager.StatsManagerError,
- 'accept_nan is false. Cannot add NaN sample.'):
- # adding a real NaN
- self.data.AddSample('Test', float('NaN'))
-
- def test_AddSampleNoUnit(self):
- """Not adding a unit does not cause an exception on CalculateStats()."""
- self.data.AddSample('Test', 17)
- self.data.CalculateStats()
- summary = self.data.GetSummary()
- self.assertEqual(1, summary['Test']['count'])
-
- def test_UnitSuffix(self):
- """Unit gets appended as a suffix in the displayed summary."""
- self.data.AddSample('test', 250)
- self.data.SetUnit('test', 'mw')
- self.data.CalculateStats()
- summary_str = self.data.SummaryToString()
- self.assertIn('test_mw', summary_str)
-
- def test_DoubleUnitSuffix(self):
- """If domain already ends in unit, verify that unit doesn't get appended."""
- self.data.AddSample('test_mw', 250)
- self.data.SetUnit('test_mw', 'mw')
- self.data.CalculateStats()
- summary_str = self.data.SummaryToString()
- self.assertIn('test_mw', summary_str)
- self.assertNotIn('test_mw_mw', summary_str)
-
- def test_GetRawData(self):
- """GetRawData returns exact same data as fed in."""
- self._populate_mock_stats()
- raw_data = self.data.GetRawData()
- self.assertListEqual([99999.5, 100000.5], raw_data['A'])
- self.assertListEqual([1.5, 2.5, 3.5], raw_data['B'])
-
- def test_GetSummary(self):
- """GetSummary returns expected stats about the data fed in."""
- self._populate_mock_stats()
- summary = self.data.GetSummary()
- self.assertEqual(2, summary['A']['count'])
- self.assertAlmostEqual(100000.5, summary['A']['max'])
- self.assertAlmostEqual(99999.5, summary['A']['min'])
- self.assertAlmostEqual(0.5, summary['A']['stddev'])
- self.assertAlmostEqual(100000.0, summary['A']['mean'])
- self.assertEqual(3, summary['B']['count'])
- self.assertAlmostEqual(3.5, summary['B']['max'])
- self.assertAlmostEqual(1.5, summary['B']['min'])
- self.assertAlmostEqual(0.81649658092773, summary['B']['stddev'])
- self.assertAlmostEqual(2.5, summary['B']['mean'])
-
- def test_SaveRawData(self):
- """SaveRawData stores same data as fed in."""
- self._populate_mock_stats()
- dirname = 'unittest_raw_data'
- expected_files = set(['A_mW.txt', 'B_mV.txt'])
- fnames = self.data.SaveRawData(self.tempdir, dirname)
- files_returned = set([os.path.basename(f) for f in fnames])
- # Assert that only the expected files got returned.
- self.assertEqual(expected_files, files_returned)
- # Assert that only the returned files are in the outdir.
- self.assertEqual(set(os.listdir(os.path.join(self.tempdir, dirname))),
- files_returned)
- for fname in fnames:
- with open(fname, 'r') as f:
- if 'A_mW' in fname:
- self.assertEqual('99999.50', f.readline().strip())
- self.assertEqual('100000.50', f.readline().strip())
- if 'B_mV' in fname:
- self.assertEqual('1.50', f.readline().strip())
- self.assertEqual('2.50', f.readline().strip())
- self.assertEqual('3.50', f.readline().strip())
-
- def test_SaveRawDataNoUnit(self):
- """SaveRawData appends no unit suffix if the unit is not specified."""
- self._populate_mock_stats_no_unit()
- self.data.CalculateStats()
- outdir = 'unittest_raw_data'
- files = self.data.SaveRawData(self.tempdir, outdir)
- files = [os.path.basename(f) for f in files]
- # Verify nothing gets appended to domain for filename if no unit exists.
- self.assertIn('B.txt', files)
-
- def test_SaveRawDataSMID(self):
- """SaveRawData uses the smid when creating output filename."""
- identifier = 'ec'
- self.data = stats_manager.StatsManager(smid=identifier)
- self._populate_mock_stats()
- files = self.data.SaveRawData(self.tempdir)
- for fname in files:
- self.assertTrue(os.path.basename(fname).startswith(identifier))
-
- def test_SummaryToStringNaNHelp(self):
- """NaN containing row gets tagged with *, help banner gets added."""
- help_banner_exp = '%s %s' % (stats_manager.STATS_PREFIX,
- stats_manager.NAN_DESCRIPTION)
- nan_domain = 'A-domain'
- nan_domain_exp = '%s%s' % (nan_domain, stats_manager.NAN_TAG)
- # NaN helper banner is added when a NaN domain is found & domain gets tagged
- data = stats_manager.StatsManager()
- data.AddSample(nan_domain, float('NaN'))
- data.AddSample(nan_domain, 17)
- data.AddSample('B-domain', 17)
- data.CalculateStats()
- summarystr = data.SummaryToString()
- self.assertIn(help_banner_exp, summarystr)
- self.assertIn(nan_domain_exp, summarystr)
- # NaN helper banner is not added when no NaN domain output, no tagging
- data = stats_manager.StatsManager()
- # nan_domain in this scenario does not contain any NaN
- data.AddSample(nan_domain, 19)
- data.AddSample('B-domain', 17)
- data.CalculateStats()
- summarystr = data.SummaryToString()
- self.assertNotIn(help_banner_exp, summarystr)
- self.assertNotIn(nan_domain_exp, summarystr)
-
- def test_SummaryToStringTitle(self):
- """Title shows up in SummaryToString if title specified."""
- title = 'titulo'
- data = stats_manager.StatsManager(title=title)
- self._populate_mock_stats()
- summary_str = data.SummaryToString()
- self.assertIn(title, summary_str)
-
- def test_SummaryToStringHideDomains(self):
- """Keys indicated in hide_domains are not printed in the summary."""
- data = stats_manager.StatsManager(hide_domains=['A-domain'])
- data.AddSample('A-domain', 17)
- data.AddSample('B-domain', 17)
- data.CalculateStats()
- summary_str = data.SummaryToString()
- self.assertIn('B-domain', summary_str)
- self.assertNotIn('A-domain', summary_str)
-
- def test_SummaryToStringOrder(self):
- """Order passed into StatsManager is honoured when formatting summary."""
- # StatsManager that should print D & B first, and the subsequent elements
- # are sorted.
- d_b_a_c_regexp = re.compile('D-domain.*B-domain.*A-domain.*C-domain',
- re.DOTALL)
- data = stats_manager.StatsManager(order=['D-domain', 'B-domain'])
- data.AddSample('A-domain', 17)
- data.AddSample('B-domain', 17)
- data.AddSample('C-domain', 17)
- data.AddSample('D-domain', 17)
- data.CalculateStats()
- summary_str = data.SummaryToString()
- self.assertRegexpMatches(summary_str, d_b_a_c_regexp)
-
- def test_MakeUniqueFName(self):
- data = stats_manager.StatsManager()
- testfile = os.path.join(self.tempdir, 'testfile.txt')
- with open(testfile, 'w') as f:
- f.write('')
- expected_fname = os.path.join(self.tempdir, 'testfile0.txt')
- self.assertEqual(expected_fname, data._MakeUniqueFName(testfile))
-
- def test_SaveSummary(self):
- """SaveSummary properly dumps the summary into a file."""
- self._populate_mock_stats()
- fname = 'unittest_summary.txt'
- expected_fname = os.path.join(self.tempdir, fname)
- fname = self.data.SaveSummary(self.tempdir, fname)
- # Assert the reported fname is the same as the expected fname
- self.assertEqual(expected_fname, fname)
- # Assert only the reported fname is output (in the tempdir)
- self.assertEqual(set([os.path.basename(fname)]),
- set(os.listdir(self.tempdir)))
- with open(fname, 'r') as f:
- self.assertEqual(
- '@@ NAME COUNT MEAN STDDEV MAX MIN\n',
- f.readline())
- self.assertEqual(
- '@@ A_mW 2 100000.00 0.50 100000.50 99999.50\n',
- f.readline())
- self.assertEqual(
- '@@ B_mV 3 2.50 0.82 3.50 1.50\n',
- f.readline())
-
- def test_SaveSummarySMID(self):
- """SaveSummary uses the smid when creating output filename."""
- identifier = 'ec'
- self.data = stats_manager.StatsManager(smid=identifier)
- self._populate_mock_stats()
- fname = os.path.basename(self.data.SaveSummary(self.tempdir))
- self.assertTrue(fname.startswith(identifier))
-
- def test_SaveSummaryJSON(self):
- """SaveSummaryJSON saves the added data properly in JSON format."""
- self._populate_mock_stats()
- fname = 'unittest_summary.json'
- expected_fname = os.path.join(self.tempdir, fname)
- fname = self.data.SaveSummaryJSON(self.tempdir, fname)
- # Assert the reported fname is the same as the expected fname
- self.assertEqual(expected_fname, fname)
- # Assert only the reported fname is output (in the tempdir)
- self.assertEqual(set([os.path.basename(fname)]),
- set(os.listdir(self.tempdir)))
- with open(fname, 'r') as f:
- summary = json.load(f)
- self.assertAlmostEqual(100000.0, summary['A']['mean'])
- self.assertEqual('milliwatt', summary['A']['unit'])
- self.assertAlmostEqual(2.5, summary['B']['mean'])
- self.assertEqual('millivolt', summary['B']['unit'])
-
- def test_SaveSummaryJSONSMID(self):
- """SaveSummaryJSON uses the smid when creating output filename."""
- identifier = 'ec'
- self.data = stats_manager.StatsManager(smid=identifier)
- self._populate_mock_stats()
- fname = os.path.basename(self.data.SaveSummaryJSON(self.tempdir))
- self.assertTrue(fname.startswith(identifier))
-
- def test_SaveSummaryJSONNoUnit(self):
- """SaveSummaryJSON marks unknown units properly as N/A."""
- self._populate_mock_stats_no_unit()
- self.data.CalculateStats()
- fname = 'unittest_summary.json'
- fname = self.data.SaveSummaryJSON(self.tempdir, fname)
- with open(fname, 'r') as f:
- summary = json.load(f)
- self.assertEqual('blue', summary['A']['unit'])
- # if no unit is specified, JSON should save 'N/A' as the unit.
- self.assertEqual('N/A', summary['B']['unit'])
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/extra/usb_serial/.gitignore b/extra/usb_serial/.gitignore
deleted file mode 100644
index 77fee262b1..0000000000
--- a/extra/usb_serial/.gitignore
+++ /dev/null
@@ -1,12 +0,0 @@
-.built-in.o.cmd
-.raiden.ko.cmd
-.raiden.mod.o.cmd
-.raiden.o.cmd
-.tmp_versions/
-Module.symvers
-built-in.o
-modules.order
-raiden.ko
-raiden.mod.c
-raiden.mod.o
-raiden.o
diff --git a/extra/usb_serial/51-google-serial-fallback.rules b/extra/usb_serial/51-google-serial-fallback.rules
deleted file mode 100644
index 5f43e58e30..0000000000
--- a/extra/usb_serial/51-google-serial-fallback.rules
+++ /dev/null
@@ -1,6 +0,0 @@
-#
-# Add USB VID/PID for usb-serial compatible CCD devices. This is a fallback
-# rule that can be used if the raiden module can't be built, or used for some
-# reason.
-#
-SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="18d1", ENV{ID_USB_INTERFACES}=="*:ff5001:*", RUN+="add_usb_serial_id $attr{idVendor} $attr{idProduct}"
diff --git a/extra/usb_serial/51-google-serial.rules b/extra/usb_serial/51-google-serial.rules
deleted file mode 100644
index 3dedac1b1f..0000000000
--- a/extra/usb_serial/51-google-serial.rules
+++ /dev/null
@@ -1,33 +0,0 @@
-#
-# Rules for Google Case Closed Debugging devices.
-#
-# The first rule matches the google VID and records the product name, USB bus
-# number and USB device path (the device path is the list of hub ports between
-# the root and the device). This becomes a unique directory name under which
-# later rules can create symlinks. If this rule doesn't match udev will skip
-# the rest of this files rules.
-#
-# This rule intentionally matches using SUBSYSTEMS and ATTRS instead of
-# SUBSYSTEM and ATTR so that the GOOGLE_CCD_NAME is available to all nodes
-# that descend from a Google USB device (this includes all USB interface nodes
-# as well as all of the TTY nodes derived from CCD USB interfaces).
-#
-SUBSYSTEMS=="usb", ATTRS{idVendor}=="18d1", ENV{GOOGLE_CCD_NAME}="$attr{product}-$attr{busnum}-$attr{devpath}"
-
-#
-# Force ModemManager to ignore all Google case closed debug devices. It would
-# be better to just ignore the case closed debug serial console interfaces, but
-# ModemManager doesn't look at the usb_interface udev node, it looks at the
-# usb_device node, so you have to mark the entire device as incompatible with
-# ModemManager.
-#
-# This node could lose the match against the usb_device DEVTYPE and still work,
-# it would just add extraneous ID_MM_DEVICE_IGNORE tags to the TTY and USB
-# interface nodes.
-#
-SUBSYSTEM=="usb", ENV{GOOGLE_CCD_NAME}!="", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:ff5001:*", ENV{ID_MM_DEVICE_IGNORE}="1"
-
-#
-# Construct a symlink to a TTY generated from a CCD USB serial interface.
-#
-SUBSYSTEM=="tty", ENV{GOOGLE_CCD_NAME}!="", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="50", ATTRS{bInterfaceProtocol}=="01", OPTIONS+="string_escape=replace", SYMLINK+="google/$env{GOOGLE_CCD_NAME}/serial/$attr{interface}"
diff --git a/extra/usb_serial/Makefile b/extra/usb_serial/Makefile
deleted file mode 100644
index 9c478050cc..0000000000
--- a/extra/usb_serial/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-obj-m := raiden.o
-
-.PHONY: all
-
-all: modules
-
-.DEFAULT:
- $(MAKE) -C /lib/modules/$(shell uname -r)/build \
- M=$(shell pwd) \
- $(MAKECMDGOALS)
diff --git a/extra/usb_serial/README.md b/extra/usb_serial/README.md
deleted file mode 100644
index 7cc6030d0a..0000000000
--- a/extra/usb_serial/README.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Case Closed Debugging Serial Consoles over USB
-
-Please see the documentation in [case_closed_debugging doc][1]
-
-[1]:https://chromium.googlesource.com/chromiumos/platform/ec/+/cr50_stab/docs/case_closed_debugging.md
diff --git a/extra/usb_serial/add_usb_serial_id b/extra/usb_serial/add_usb_serial_id
deleted file mode 100755
index ef8336afdc..0000000000
--- a/extra/usb_serial/add_usb_serial_id
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/bin/sh -e
-#
-# Copyright 2016 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-# Add a USB VID:PID device ID to the usbserial list of handled devices
-
-if [ $# -ne 2 ]; then
- echo ""
- echo "Usage: $0 <VID> <PID>"
- echo ""
- echo "Add a USB VID:PID device identification pair to the list of devices"
- echo "that usbserial will recognize. This script ensures that a device is"
- echo "not added more than once."
- echo ""
- exit 1
-fi
-
-#
-# Firts ensure that the usbserial module is loaded. This is required as there
-# may be no other USB serial adaptor connected yet.
-#
-modprobe usbserial
-
-device_id="$1 $2"
-file="/sys/bus/usb-serial/drivers/generic/new_id"
-
-#
-# Only add the device ID pair if it isn't already in the ID list.
-#
-grep -q "$device_id" $file || echo $device_id > $file
diff --git a/extra/usb_serial/console.py b/extra/usb_serial/console.py
deleted file mode 100755
index 7b3bacd903..0000000000
--- a/extra/usb_serial/console.py
+++ /dev/null
@@ -1,298 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2016 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-# Ignore indention messages, since legacy scripts use 2 spaces instead of 4.
-# pylint: disable=bad-indentation,docstring-section-indent
-# pylint: disable=docstring-trailing-quotes
-
-"""Allow creation of uart/console interface via usb google serial endpoint."""
-
-# Note: This is a py2/3 compatible file.
-
-from __future__ import print_function
-import argparse
-import array
-import os
-import sys
-import termios
-import threading
-import time
-import traceback
-import tty
-try:
- import usb
-except:
- print("import usb failed")
- print("try running these commands:")
- print(" sudo apt-get install python-pip")
- print(" sudo pip install --pre pyusb")
- print()
- sys.exit(-1)
-
-import six
-
-
-def GetBuffer(stream):
- if six.PY3:
- return stream.buffer
- return stream
-
-
-"""Class Susb covers USB device discovery and initialization.
-
- It can find a particular endpoint by vid:pid, serial number,
- and interface number.
-"""
-
-class SusbError(Exception):
- """Class for exceptions of Susb."""
- def __init__(self, msg, value=0):
- """SusbError constructor.
-
- Args:
- msg: string, message describing error in detail
- value: integer, value of error when non-zero status returned. Default=0
- """
- super(SusbError, self).__init__(msg, value)
- self.msg = msg
- self.value = value
-
-class Susb():
- """Provide USB functionality.
-
- Instance Variables:
- _read_ep: pyUSB read endpoint for this interface
- _write_ep: pyUSB write endpoint for this interface
- """
- READ_ENDPOINT = 0x81
- WRITE_ENDPOINT = 0x1
- TIMEOUT_MS = 100
-
- def __init__(self, vendor=0x18d1,
- product=0x500f, interface=1, serialname=None):
- """Susb constructor.
-
- Discovers and connects to USB endpoints.
-
- Args:
- vendor : usb vendor id of device
- product : usb product id of device
- interface : interface number ( 1 - 8 ) of device to use
- serialname: string of device serialnumber.
-
- Raises:
- SusbError: An error accessing Susb object
- """
- # Find the device.
- dev_g = usb.core.find(idVendor=vendor, idProduct=product, find_all=True)
- dev_list = list(dev_g)
- if dev_list is None:
- raise SusbError("USB device not found")
-
- # Check if we have multiple devices.
- dev = None
- if serialname:
- for d in dev_list:
- dev_serial = "PyUSB doesn't have a stable interface"
- try:
- dev_serial = usb.util.get_string(d, 256, d.iSerialNumber)
- except:
- dev_serial = usb.util.get_string(d, d.iSerialNumber)
- if dev_serial == serialname:
- dev = d
- break
- if dev is None:
- raise SusbError("USB device(%s) not found" % (serialname,))
- else:
- try:
- dev = dev_list[0]
- except:
- try:
- dev = dev_list.next()
- except:
- raise SusbError("USB device %04x:%04x not found" % (vendor, product))
-
- # If we can't set configuration, it's already been set.
- try:
- dev.set_configuration()
- except usb.core.USBError:
- pass
-
- # Get an endpoint instance.
- cfg = dev.get_active_configuration()
- intf = usb.util.find_descriptor(cfg, bInterfaceNumber=interface)
- self._intf = intf
-
- if not intf:
- raise SusbError("Interface not found")
-
- # Detach raiden.ko if it is loaded.
- if dev.is_kernel_driver_active(intf.bInterfaceNumber) is True:
- dev.detach_kernel_driver(intf.bInterfaceNumber)
-
- read_ep_number = intf.bInterfaceNumber + self.READ_ENDPOINT
- read_ep = usb.util.find_descriptor(intf, bEndpointAddress=read_ep_number)
- self._read_ep = read_ep
-
- write_ep_number = intf.bInterfaceNumber + self.WRITE_ENDPOINT
- write_ep = usb.util.find_descriptor(intf, bEndpointAddress=write_ep_number)
- self._write_ep = write_ep
-
-
-"""Suart class implements a stream interface, to access Google's USB class.
-
- This creates a send and receive thread that monitors USB and console input
- and forwards them across. This particular class is hardcoded to stdin/out.
-"""
-
-class SuartError(Exception):
- """Class for exceptions of Suart."""
- def __init__(self, msg, value=0):
- """SuartError constructor.
-
- Args:
- msg: string, message describing error in detail
- value: integer, value of error when non-zero status returned. Default=0
- """
- super(SuartError, self).__init__(msg, value)
- self.msg = msg
- self.value = value
-
-
-class Suart():
- """Provide interface to serial usb endpoint."""
-
- def __init__(self, vendor=0x18d1, product=0x501c, interface=0,
- serialname=None):
- """Suart contstructor.
-
- Initializes USB stream interface.
-
- Args:
- vendor: usb vendor id of device
- product: usb product id of device
- interface: interface number of device to use
- serialname: Defaults to None.
-
- Raises:
- SuartError: If init fails
- """
- self._done = threading.Event()
- self._susb = Susb(vendor=vendor, product=product,
- interface=interface, serialname=serialname)
-
- def wait_until_done(self, timeout=None):
- return self._done.wait(timeout=timeout)
-
- def run_rx_thread(self):
- try:
- while True:
- try:
- r = self._susb._read_ep.read(64, self._susb.TIMEOUT_MS)
- if r:
- GetBuffer(sys.stdout).write(r.tostring())
- GetBuffer(sys.stdout).flush()
-
- except Exception as e:
- # If we miss some characters on pty disconnect, that's fine.
- # ep.read() also throws USBError on timeout, which we discard.
- if not isinstance(e, (OSError, usb.core.USBError)):
- print("rx %s" % e)
- finally:
- self._done.set()
-
- def run_tx_thread(self):
- try:
- while True:
- try:
- r = GetBuffer(sys.stdin).read(1)
- if not r or r == b"\x03":
- break
- if r:
- self._susb._write_ep.write(array.array('B', r),
- self._susb.TIMEOUT_MS)
- except Exception as e:
- print("tx %s" % e)
- finally:
- self._done.set()
-
- def run(self):
- """Creates pthreads to poll USB & PTY for data.
- """
- self._exit = False
-
- self._rx_thread = threading.Thread(target=self.run_rx_thread)
- self._rx_thread.daemon = True
- self._rx_thread.start()
-
- self._tx_thread = threading.Thread(target=self.run_tx_thread)
- self._tx_thread.daemon = True
- self._tx_thread.start()
-
-
-
-"""Command line functionality
-
- Allows specifying vid:pid, serialnumber, interface.
- Ctrl-C exits.
-"""
-
-parser = argparse.ArgumentParser(description="Open a console to a USB device")
-parser.add_argument('-d', '--device', type=str,
- help="vid:pid of target device", default="18d1:501c")
-parser.add_argument('-i', '--interface', type=int,
- help="interface number of console", default=0)
-parser.add_argument('-s', '--serialno', type=str,
- help="serial number of device", default="")
-parser.add_argument('-S', '--notty-exit-sleep', type=float, default=0.2,
- help="When stdin is *not* a TTY, wait this many seconds after EOF from "
- "stdin before exiting, to give time for receiving a reply from the USB "
- "device.")
-
-
-def runconsole():
- """Run the usb console code
-
- Starts the pty thread, and idles until a ^C is caught.
- """
- args = parser.parse_args()
-
- vidstr, pidstr = args.device.split(':')
- vid = int(vidstr, 16)
- pid = int(pidstr, 16)
-
- serialno = args.serialno
- interface = args.interface
-
- sobj = Suart(vendor=vid, product=pid, interface=interface,
- serialname=serialno)
- if sys.stdin.isatty():
- tty.setraw(sys.stdin.fileno())
- sobj.run()
- sobj.wait_until_done()
- if not sys.stdin.isatty() and args.notty_exit_sleep > 0:
- time.sleep(args.notty_exit_sleep)
-
-
-def main():
- stdin_isatty = sys.stdin.isatty()
- if stdin_isatty:
- fd = sys.stdin.fileno()
- os.system("stty -echo")
- old_settings = termios.tcgetattr(fd)
-
- try:
- runconsole()
- finally:
- if stdin_isatty:
- termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
- os.system("stty echo")
- # Avoid having the user's shell prompt start mid-line after the final output
- # from this program.
- print()
-
-
-if __name__ == '__main__':
- main()
diff --git a/extra/usb_serial/install b/extra/usb_serial/install
deleted file mode 100755
index eba1d2ac83..0000000000
--- a/extra/usb_serial/install
+++ /dev/null
@@ -1,88 +0,0 @@
-#!/bin/sh -e
-#
-# Copyright 2016 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-# Build and install raiden module and udev rules
-
-bold=$(tput bold)
-normal=$(tput sgr0)
-
-error() {
- echo "${bold}Install failed${normal}"
-}
-
-trap "error $LINENO" ERR
-
-fallback=false
-
-if [ "$1" = "--fallback" ]; then
- fallback=true
- shift
-fi
-
-if [ $# -ne 0 ]; then
- echo ""
- echo "Usage: ${bold}$0${normal} [--fallback]"
- echo ""
- echo "Install Raiden kernel module and udev rules for working with Case"
- echo "Closed Debug enabled devices."
- echo ""
- echo "${bold}--fallback${normal}"
- echo " Install udev rules to use usbserial directly without installing"
- echo " the raiden module. This can be used when the raiden module fails"
- echo " to build, or can not be used for other reasons."
- echo ""
- echo " The fallback solution will generate extra /dev/ttyUSB? entries"
- echo " for the SPI and possibly other CCD bridges. These should be"
- echo " ignored by you. Flashrom is smart enough to detach the kernel"
- echo " driver from the SPI bridge, so they will not interfere with"
- echo " flashing new firmware images over CCD."
- echo ""
- exit 1
-fi
-
-if [ "$fallback" = "false" ]; then
- #
- # The normal path builds and installs the raiden module
- #
- {
- #
- # Don't build the module as root so it's easier to clean up after
- #
- make modules &&
-
- #
- # Install the new module and update dependency and alias information
- #
- sudo make modules_install &&
- sudo depmod -a
- } || {
- echo $bold
- echo "Building and/or installing the raiden module failed, you may"
- echo "want to use the --fallback option."
- echo $normal
- exit 1;
- }
-else
- #
- # The fallback path installs the fallback udev rule and its helper script.
- #
- sudo install -m644 51-google-serial-fallback.rules /etc/udev/rules.d
- sudo install add_usb_serial_id /lib/udev
-fi
-
-#
-# Install the udev rule for creating /dev/google symlinks.
-#
-sudo install -m644 51-google-serial.rules /etc/udev/rules.d
-
-#
-# Trigger udev to create the symlinks for any attached devices that have the
-# Google Vendor ID. Limiting triggering like this prevents unwanted resetting
-# of some device state, even with the change action specified.
-#
-for syspath in $(dirname $(grep -rxl --include=idVendor 18d1 /sys/devices)); do
- sudo udevadm trigger --action=change --parent-match=${syspath}
-done
diff --git a/extra/usb_serial/raiden.c b/extra/usb_serial/raiden.c
deleted file mode 100644
index e4720b4357..0000000000
--- a/extra/usb_serial/raiden.c
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * USB Serial module for Raiden USB debug serial console forwarding.
- * SubClass and Protocol allocated in go/usb-ids
- *
- * Copyright 2014 The Chromium OS Authors <chromium-os-dev@chromium.org>
- * Author: Anton Staaf <robotboy@chromium.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/tty.h>
-#include <linux/usb.h>
-#include <linux/usb/serial.h>
-
-MODULE_LICENSE("GPL");
-
-#define USB_VENDOR_ID_GOOGLE 0x18d1
-#define USB_SUBCLASS_GOOGLE_SERIAL 0x50
-#define USB_PROTOCOL_GOOGLE_SERIAL 0x01
-
-static struct usb_device_id const ids[] = {
- { USB_VENDOR_AND_INTERFACE_INFO(USB_VENDOR_ID_GOOGLE,
- USB_CLASS_VENDOR_SPEC,
- USB_SUBCLASS_GOOGLE_SERIAL,
- USB_PROTOCOL_GOOGLE_SERIAL) },
- { 0 }
-};
-
-MODULE_DEVICE_TABLE(usb, ids);
-
-static struct usb_serial_driver device =
-{
- .driver = { .owner = THIS_MODULE,
- .name = "Google" },
- .id_table = ids,
- .num_ports = 1,
-};
-
-static struct usb_serial_driver * const drivers[] = { &device, NULL };
-
-module_usb_serial_driver(drivers, ids);
diff --git a/extra/usb_updater/.gitignore b/extra/usb_updater/.gitignore
deleted file mode 100644
index 37c3bd3808..0000000000
--- a/extra/usb_updater/.gitignore
+++ /dev/null
@@ -1,6 +0,0 @@
-generated_version.h
-gsctool
-usb_updater2
-*.d
-*.o
-dp \ No newline at end of file
diff --git a/extra/usb_updater/Makefile b/extra/usb_updater/Makefile
deleted file mode 100644
index 1dfbc55645..0000000000
--- a/extra/usb_updater/Makefile
+++ /dev/null
@@ -1,55 +0,0 @@
-# Copyright 2015 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-CC ?= gcc
-PKG_CONFIG ?= pkg-config
-PROGRAMS := usb_updater2
-LIBS :=
-LFLAGS :=
-CFLAGS := -std=gnu99 \
- -g \
- -Wall \
- -Werror \
- -Wpointer-arith \
- -Wcast-align \
- -Wcast-qual \
- -Wundef \
- -Wsign-compare \
- -Wredundant-decls \
- -Wmissing-declarations
-
-ifeq (DEBUG,)
-CFLAGS += -O3
-else
-CFLAGS += -O0
-endif
-
-#
-# Add libusb-1.0 required flags
-#
-LIBS += $(shell $(PKG_CONFIG) --libs libusb-1.0)
-CFLAGS += $(shell $(PKG_CONFIG) --cflags libusb-1.0)
-CFLAGS += -I../../include -I../../util -I../../fuzz -I../../test
-
-VPATH = ../../util
-
-LIBS_common = -lfmap
-
-all: $(PROGRAMS)
-
-%.o: %.c
- $(CC) $(CFLAGS) -c -MMD -MF $(basename $@).d -o $@ $<
-
-# common EC code USB updater
-usb_updater2: usb_updater2.c Makefile
- $(CC) $(CFLAGS) $< $(LFLAGS) $(LIBS) $(LIBS_common) -o $@
-
-.PHONY: clean
-
-clean:
- rm -rf $(PROGRAMS) *~ *.o *.d dp
-
-parser_debug: desc_parser.c
- gcc -g -O0 -DTEST_PARSER desc_parser.c -o dp
-
diff --git a/extra/usb_updater/c2d2.json b/extra/usb_updater/c2d2.json
deleted file mode 100644
index 79fc6f0992..0000000000
--- a/extra/usb_updater/c2d2.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "Comment": "This file describes the updateable sections of the flash.",
- "board": "c2d2",
- "vid": "0x18d1",
- "pid": "0x5041",
- "console": "3",
- "Comment on flash": "This is the base address of writeable flash",
- "flash": "0x8000000",
- "Comment on region format": "name: [baseoffset, length]",
- "regions": {
- "RW": ["0x10000", "0x10000"],
- "PSTATE": ["0xf000", "0x1000"],
- "RO": ["0x0000", "0xf000"]
- }
-}
diff --git a/extra/usb_updater/desc_parser.c b/extra/usb_updater/desc_parser.c
deleted file mode 100644
index 5bd996bdda..0000000000
--- a/extra/usb_updater/desc_parser.c
+++ /dev/null
@@ -1,377 +0,0 @@
-/*
- * Copyright 2018 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include <ctype.h>
-#include <errno.h>
-#include <malloc.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-
-#include "desc_parser.h"
-
-static FILE *hash_file_;
-static int line_count_;
-static int section_count_;
-
-/*
- * This is used to verify consistency of the description database, namely that
- * all hash sections include the same number of hash variants.
- */
-static size_t variant_count;
-
-/* Size of the retrieved string or negative OS error value. */
-static ssize_t get_next_line(char *next_line, size_t line_size)
-{
- size_t index = 0;
-
- while (fgets(next_line + index, line_size - index, hash_file_)) {
- line_count_++;
-
- if (next_line[index] == '#')
- continue; /* Skip the comment */
-
- if (next_line[index] == '\n') {
- /*
- * This is an empty line, return all collected data,
- * pontintially an array of size zero if this is a
- * repeated empty line.
- */
- next_line[index] = '\0';
- return index;
- }
-
- /* Make sure next string overwrites this string's newline. */
- index += strlen(next_line + index) - 1;
-
- if (index >= (line_size - 1)) {
- fprintf(stderr, "%s: Input overflow in line %d\n",
- __func__, line_count_);
- return -EOVERFLOW;
- }
- }
-
- if (index) {
- /*
- * This must be the last line in the file with no empty line
- * after it. Drop the closing newline, if it is there.
- */
- if (next_line[index] == '\n')
- next_line[index--] = '\0';
-
- return index;
- }
- return errno ? -errno : -ENODATA;
-}
-
-static int get_next_token(char *input, size_t expected_size, char **output)
-{
- char *next_colon;
-
- next_colon = strchr(input, ':');
- if (next_colon)
- *next_colon = '\0';
- if (!next_colon || (expected_size &&
- strlen(input) != expected_size)) {
- fprintf(stderr, "Invalid entry in section %d\n",
- section_count_);
- return -EINVAL;
- }
-
- *output = next_colon + 1;
- return 0;
-}
-
-static int get_hex_value(char *input, char **output)
-{
- char *e;
- long int value;
-
- if (strchr(input, ':'))
- get_next_token(input, 0, output);
- else
- *output = NULL;
-
- value = strtol(input, &e, 16);
- if ((e && *e) || (strlen(input) > 8)) {
- fprintf(stderr, "Invalid hex value %s in section %d\n",
- input, section_count_);
- return -EINVAL;
- }
-
- return value;
-}
-
-static int parse_range(char *next_line,
- size_t line_len,
- struct addr_range *parsed_range)
-{
- char *line_cursor;
- char *next_token;
- int is_a_hash_range;
- struct result_node *node;
- int value;
-
- section_count_++;
- line_cursor = next_line;
-
- /* Range type. */
- if (get_next_token(line_cursor, 1, &next_token))
- return -EINVAL;
-
- switch (*line_cursor) {
- case 'a':
- parsed_range->range_type = AP_RANGE;
- break;
- case 'e':
- parsed_range->range_type = EC_RANGE;
- break;
- case 'g':
- parsed_range->range_type = EC_GANG_RANGE;
- break;
- default:
- fprintf(stderr, "Invalid range type %c in section %d\n",
- *line_cursor, section_count_);
- return -EINVAL;
- }
- line_cursor = next_token;
-
- /* Hash or dump? */
- if (get_next_token(line_cursor, 1, &next_token))
- return -EINVAL;
-
- switch (*line_cursor) {
- case 'd':
- is_a_hash_range = 0;
- break;
- case 'h':
- is_a_hash_range = 1;
- break;
- default:
- fprintf(stderr, "Invalid entry kind %c in section %d\n",
- *line_cursor, section_count_);
- return -EINVAL;
- }
- line_cursor = next_token;
-
- /* Range base address. */
- value = get_hex_value(line_cursor, &next_token);
- if (value < 0)
- return -EINVAL;
- parsed_range->base_addr = value;
-
- /* Range size. */
- line_cursor = next_token;
- value = get_hex_value(line_cursor, &next_token);
- if (value < 0)
- return -EINVAL;
- parsed_range->range_size = value;
-
- if (!next_token && is_a_hash_range) {
- fprintf(stderr, "Missing hash in section %d\n", section_count_);
- return -EINVAL;
- }
-
- if (next_token && !is_a_hash_range) {
- fprintf(stderr, "Unexpected data in section %d\n",
- section_count_);
- return -EINVAL;
- }
-
- parsed_range->variant_count = 0;
- if (!is_a_hash_range)
- return 0; /* No more input for dump ranges. */
-
- node = parsed_range->variants;
- do { /* While line is not over. */
- char c;
- int i = 0;
-
- line_cursor = next_token;
- next_token = strchr(line_cursor, ':');
- if (next_token)
- *next_token++ = '\0';
- if (strlen(line_cursor) != (2 * sizeof(*node))) {
- fprintf(stderr,
- "Invalid hash %zd size %zd in section %d\n",
- parsed_range->variant_count + 1,
- strlen(line_cursor), section_count_);
- return -EINVAL;
- }
-
- while ((c = *line_cursor++) != 0) {
- uint8_t nibble;
-
- if (!isxdigit(c)) {
- fprintf(stderr,
- "Invalid hash %zd value in section %d\n",
- parsed_range->variant_count + 1,
- section_count_);
- return -EINVAL;
- }
-
- if (c <= '9')
- nibble = c - '0';
- else if (c >= 'a')
- nibble = c - 'a' + 10;
- else
- nibble = c - 'A' + 10;
-
- if (i & 1)
- node->expected_result[i / 2] |= nibble;
- else
- node->expected_result[i / 2] = nibble << 4;
-
- i++;
- }
-
- node++;
- parsed_range->variant_count++;
-
- } while (next_token);
-
- return 0;
-}
-
-int parser_get_next_range(struct addr_range **range)
-{
- char next_line[1000]; /* Should be enough for the largest descriptor. */
- ssize_t entry_size;
- struct addr_range *new_range;
- int rv;
-
- /*
- * We come here after hash descriptor database file was opened and the
- * current board's section has been found. Just in case check if the
- * file has been opened.
- */
- if (!hash_file_ || !range)
- return -EIO;
-
- *range = NULL;
- do {
- entry_size = get_next_line(next_line, sizeof(next_line));
- if (entry_size < 0)
- return entry_size;
- } while (!entry_size); /* Skip empty lines. */
-
- if (entry_size == 4) /* Next board's entry must have been reached. */
- return -ENODATA;
-
- /* This sure will be enough to fit parsed structure contents. */
- new_range = malloc(sizeof(*new_range) + entry_size);
- if (!new_range) {
- fprintf(stderr, "Failed to allocate %zd bytes\n",
- sizeof(*new_range) + entry_size);
- return -ENOMEM;
- }
-
- /* This must be a new descriptor section, lets parse it. */
- rv = parse_range(next_line, entry_size, new_range);
-
- if (rv) {
- free(new_range);
- return rv;
- }
-
- if (new_range->variant_count) {
- /*
- * A new range was found, if this is the first hash range we
- * encountered, save its dimensions for future reference.
- *
- * If this is not the first one - verify that it has the same
- * number of hash variants as all previous hash blocks.
- */
- if (!variant_count) {
- variant_count = new_range->variant_count;
- } else if (variant_count != new_range->variant_count) {
- fprintf(stderr,
- "Unexpected number of variants in section %d\n",
- section_count_);
- free(new_range);
- return -EINVAL;
- }
- }
-
- *range = new_range;
- return 0;
-
-}
-
-int parser_find_board(const char *hash_file_name, const char *board_id)
-{
- char next_line[1000]; /* Should be enough for the largest descriptor. */
- ssize_t id_len = strlen(board_id);
-
- if (!hash_file_) {
- hash_file_ = fopen(hash_file_name, "r");
- if (!hash_file_) {
- fprintf(stderr, "Error:%s can not open file '%s'\n",
- strerror(errno), hash_file_name);
- return errno;
- }
- }
-
- while (1) {
- ssize_t entry_size;
-
- entry_size = get_next_line(next_line, sizeof(next_line));
- if (entry_size < 0) {
- return entry_size;
- }
-
- if ((entry_size == id_len) &&
- !memcmp(next_line, board_id, id_len)) {
- variant_count = 0;
- return 0;
- }
- }
-
- return -ENODATA;
-}
-
-void parser_done(void)
-{
- if (!hash_file_)
- return;
-
- fclose(hash_file_);
- hash_file_ = NULL;
-}
-
-#ifdef TEST_PARSER
-int main(int argc, char **argv)
-{
- const char *board_name = "QZUX";
- char next_line[1000]; /* Should be enough for the largest descriptor. */
- int rv;
- int count;
-
- if (argc < 2) {
- fprintf(stderr, "Name of the file to parse is required.\n");
- return -1;
- }
-
- if (parser_find_board(argv[1], board_name)) {
- fprintf(stderr, "Board %s NOT found\n", board_name);
- return -1;
- }
-
- count = 0;
- do {
- struct addr_range *range;
-
- rv = parser_get_next_range(&range);
- count++;
- printf("Section %d, rv %d\n", count, rv);
- free(range); /* Freeing NULL is OK. */
-
- } while (rv != -ENODATA);
-
- return 0;
-}
-#endif
diff --git a/extra/usb_updater/desc_parser.h b/extra/usb_updater/desc_parser.h
deleted file mode 100644
index faa80d1a63..0000000000
--- a/extra/usb_updater/desc_parser.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2018 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#ifndef __EXTRA_USB_UPDATER_DESC_PARSER_H
-#define __EXTRA_USB_UPDATER_DESC_PARSER_H
-
-#include <stddef.h>
-#include <stdint.h>
-
-struct result_node {
- uint8_t expected_result[32];
-};
-
-enum range_type_t {
- NOT_A_RANGE,
- AP_RANGE,
- EC_RANGE,
- EC_GANG_RANGE,
-};
-
-struct addr_range {
- enum range_type_t range_type;
- uint32_t base_addr;
- uint32_t range_size;
- size_t variant_count; /* Set to zero for dump ranges. */
- struct result_node variants[0];
-};
-
-/* Board description retrieval API includes the following functions. */
-
-/*
- * In the given hash database file find board by its ID. Return zero on
- * success, or OS error of error. In particular ENODATA is returned if the
- * section for the required board ID is not found in the file.
- */
-int parser_find_board(const char *hash_file_name, const char board_id[4]);
-
-/*
- * Find next range for the previousely defined board, parse it into the
- * addr_range structure and return pointer to the parsed structure to the
- * caller, set pointer to NULL if no more entries are available or in case of
- * error.
- *
- * Caller of this function is responsible for returning memory allocated for
- * the entry.
- *
- * Return value set to zero on success, or to OS error if one occurs. EIO is
- * used if an attmept to get next range is made before hash database file was
- * opened and board entry in it was found.
- */
-int parser_get_next_range(struct addr_range **range);
-
-/* Close the hash database file. */
-void parser_done(void);
-
-#endif // __EXTRA_USB_UPDATER_DESC_PARSER_H
diff --git a/extra/usb_updater/ecusb b/extra/usb_updater/ecusb
deleted file mode 120000
index c06ee0f51b..0000000000
--- a/extra/usb_updater/ecusb
+++ /dev/null
@@ -1 +0,0 @@
-../tigertool/ecusb/ \ No newline at end of file
diff --git a/extra/usb_updater/fw_update.py b/extra/usb_updater/fw_update.py
deleted file mode 100755
index 0d7a570fc3..0000000000
--- a/extra/usb_updater/fw_update.py
+++ /dev/null
@@ -1,426 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2016 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-# Ignore indention messages, since legacy scripts use 2 spaces instead of 4.
-# pylint: disable=bad-indentation,docstring-section-indent
-# pylint: disable=docstring-trailing-quotes
-
-# Upload firmware over USB
-# Note: This is a py2/3 compatible file.
-
-from __future__ import print_function
-
-import argparse
-import array
-import json
-import os
-import struct
-import sys
-import time
-from pprint import pprint
-import usb
-
-
-debug = False
-def debuglog(msg):
- if debug:
- print(msg)
-
-def log(msg):
- print(msg)
- sys.stdout.flush()
-
-
-"""Sends firmware update to CROS EC usb endpoint."""
-
-class Supdate(object):
- """Class to access firmware update endpoints.
-
- Usage:
- d = Supdate()
-
- Instance Variables:
- _dev: pyUSB device object
- _read_ep: pyUSB read endpoint for this interface
- _write_ep: pyUSB write endpoint for this interface
- """
- USB_SUBCLASS_GOOGLE_UPDATE = 0x53
- USB_CLASS_VENDOR = 0xFF
-
- def __init__(self):
- pass
-
-
- def connect_usb(self, serialname=None):
- """Initial discovery and connection to USB endpoint.
-
- This searches for a USB device matching the VID:PID specified
- in the config file, optionally matching a specified serialname.
-
- Args:
- serialname: Find the device with this serial, in case multiple
- devices are attached.
-
- Returns:
- True on success.
- Raises:
- Exception on error.
- """
- # Find the stm32.
- vendor = self._brdcfg['vid']
- product = self._brdcfg['pid']
-
- dev_g = usb.core.find(idVendor=vendor, idProduct=product, find_all=True)
- dev_list = list(dev_g)
- if dev_list is None:
- raise Exception("Update", "USB device not found")
-
- # Check if we have multiple stm32s and we've specified the serial.
- dev = None
- if serialname:
- for d in dev_list:
- if usb.util.get_string(d, d.iSerialNumber) == serialname:
- dev = d
- break
- if dev is None:
- raise SusbError("USB device(%s) not found" % serialname)
- else:
- try:
- dev = dev_list[0]
- except:
- dev = dev_list.next()
-
- debuglog("Found stm32: %04x:%04x" % (vendor, product))
- self._dev = dev
-
- # Get an endpoint instance.
- try:
- dev.set_configuration()
- except:
- pass
- cfg = dev.get_active_configuration()
-
- intf = usb.util.find_descriptor(cfg, custom_match=lambda i: \
- i.bInterfaceClass==self.USB_CLASS_VENDOR and \
- i.bInterfaceSubClass==self.USB_SUBCLASS_GOOGLE_UPDATE)
-
- self._intf = intf
- debuglog("Interface: %s" % intf)
- debuglog("InterfaceNumber: %s" % intf.bInterfaceNumber)
-
- read_ep = usb.util.find_descriptor(
- intf,
- # match the first IN endpoint
- custom_match = \
- lambda e: \
- usb.util.endpoint_direction(e.bEndpointAddress) == \
- usb.util.ENDPOINT_IN
- )
-
- self._read_ep = read_ep
- debuglog("Reader endpoint: 0x%x" % read_ep.bEndpointAddress)
-
- write_ep = usb.util.find_descriptor(
- intf,
- # match the first OUT endpoint
- custom_match = \
- lambda e: \
- usb.util.endpoint_direction(e.bEndpointAddress) == \
- usb.util.ENDPOINT_OUT
- )
-
- self._write_ep = write_ep
- debuglog("Writer endpoint: 0x%x" % write_ep.bEndpointAddress)
-
- return True
-
-
- def wr_command(self, write_list, read_count=1, wtimeout=100, rtimeout=2000):
- """Write command to logger logic..
-
- This function writes byte command values list to stm, then reads
- byte status.
-
- Args:
- write_list: list of command byte values [0~255].
- read_count: number of status byte values to read.
- wtimeout: mS to wait for write success
- rtimeout: mS to wait for read success
-
- Returns:
- status byte, if one byte is read,
- byte list, if multiple bytes are read,
- None, if no bytes are read.
-
- Interface:
- write: [command, data ... ]
- read: [status ]
- """
- debuglog("wr_command(write_list=[%s] (%d), read_count=%s)" % (
- list(bytearray(write_list)), len(write_list), read_count))
-
- # Clean up args from python style to correct types.
- write_length = 0
- if write_list:
- write_length = len(write_list)
- if not read_count:
- read_count = 0
-
- # Send command to stm32.
- if write_list:
- cmd = write_list
- ret = self._write_ep.write(cmd, wtimeout)
- debuglog("RET: %s " % ret)
-
- # Read back response if necessary.
- if read_count:
- bytesread = self._read_ep.read(512, rtimeout)
- debuglog("BYTES: [%s]" % bytesread)
-
- if len(bytesread) != read_count:
- debuglog("Unexpected bytes read: %d, expected: %d" % (len(bytesread), read_count))
- pass
-
- debuglog("STATUS: 0x%02x" % int(bytesread[0]))
- if read_count == 1:
- return bytesread[0]
- else:
- return bytesread
-
- return None
-
- def stop(self):
- """Finalize system flash and exit."""
- cmd = struct.pack(">I", 0xB007AB1E)
- read = self.wr_command(cmd, read_count=4)
-
- if len(read) == 4:
- log("Finished flashing")
- return
-
- raise Exception("Update", "Stop failed [%s]" % read)
-
-
- def write_file(self):
- """Write the update region packet by packet to USB
-
- This sends write packets of size 128B out, in 32B chunks.
- Overall, this will write all data in the inactive code region.
-
- Raises:
- Exception if write failed or address out of bounds.
- """
- region = self._region
- flash_base = self._brdcfg["flash"]
- offset = self._base - flash_base
- if offset != self._brdcfg['regions'][region][0]:
- raise Exception("Update", "Region %s offset 0x%x != available offset 0x%x" % (
- region, self._brdcfg['regions'][region][0], offset))
-
- length = self._brdcfg['regions'][region][1]
- log("Sending")
-
- # Go to the correct region in the ec.bin file.
- self._binfile.seek(offset)
-
- # Send 32 bytes at a time. Must be less than the endpoint's max packet size.
- maxpacket = 32
-
- # While data is left, create update packets.
- while length > 0:
- # Update packets are 128B. We can use any number
- # but the micro must malloc this memory.
- pagesize = min(length, 128)
-
- # Packet is:
- # packet size: page bytes transferred plus 3 x 32b values header.
- # cmd: n/a
- # base: flash address to write this packet.
- # data: 128B of data to write into flash_base
- cmd = struct.pack(">III", pagesize + 12, 0, offset + flash_base)
- read = self.wr_command(cmd, read_count=0)
-
- # Push 'todo' bytes out the pipe.
- todo = pagesize
- while todo > 0:
- packetsize = min(maxpacket, todo)
- data = self._binfile.read(packetsize)
- if len(data) != packetsize:
- raise Exception("Update", "No more data from file")
- for i in range(0, 10):
- try:
- self.wr_command(data, read_count=0)
- break
- except:
- log("Timeout fail")
- todo -= packetsize
- # Done with this packet, move to the next one.
- length -= pagesize
- offset += pagesize
-
- # Validate that the micro thinks it successfully wrote the data.
- read = self.wr_command(''.encode(), read_count=4)
- result = struct.unpack("<I", read)
- result = result[0]
- if result != 0:
- raise Exception("Update", "Upload failed with rc: 0x%x" % result)
-
-
- def start(self):
- """Start a transaction and erase currently inactive region.
-
- This function sends a start command, and receives the base of the
- preferred inactive region. This could be RW, RW_B,
- or RO (if there's no RW_B)
-
- Note that the region is erased here, so you'd better program the RO if
- you just erased it. TODO(nsanders): Modify the protocol to allow active
- region select or query before erase.
- """
-
- # Size is 3 uint32 fields
- # packet: [packetsize, cmd, base]
- size = 4 + 4 + 4
- # Return value is [status, base_addr]
- expected = 4 + 4
-
- cmd = struct.pack("<III", size, 0, 0)
- read = self.wr_command(cmd, read_count=expected)
-
- if len(read) == 4:
- raise Exception("Update", "Protocol version 0 not supported")
- elif len(read) == expected:
- base, version = struct.unpack(">II", read)
- log("Update protocol v. %d" % version)
- log("Available flash region base: %x" % base)
- else:
- raise Exception("Update", "Start command returned %d bytes" % len(read))
-
- if base < 256:
- raise Exception("Update", "Start returned error code 0x%x" % base)
-
- self._base = base
- flash_base = self._brdcfg["flash"]
- self._offset = self._base - flash_base
-
- # Find our active region.
- for region in self._brdcfg['regions']:
- if (self._offset >= self._brdcfg['regions'][region][0]) and \
- (self._offset < (self._brdcfg['regions'][region][0] + \
- self._brdcfg['regions'][region][1])):
- log("Active region: %s" % region)
- self._region = region
-
-
- def load_board(self, brdfile):
- """Load firmware layout file.
-
- example as follows:
- {
- "board": "servo micro",
- "vid": 6353,
- "pid": 20506,
- "flash": 134217728,
- "regions": {
- "RW": [65536, 65536],
- "PSTATE": [61440, 4096],
- "RO": [0, 61440]
- }
- }
-
- Args:
- brdfile: path to board description file.
- """
- with open(brdfile) as data_file:
- data = json.load(data_file)
-
- # TODO(nsanders): validate this data before moving on.
- self._brdcfg = data;
- if debug:
- pprint(data)
-
- log("Board is %s" % self._brdcfg['board'])
- # Cast hex strings to int.
- self._brdcfg['flash'] = int(self._brdcfg['flash'], 0)
- self._brdcfg['vid'] = int(self._brdcfg['vid'], 0)
- self._brdcfg['pid'] = int(self._brdcfg['pid'], 0)
-
- log("Flash Base is %x" % self._brdcfg['flash'])
- self._flashsize = 0
- for region in self._brdcfg['regions']:
- base = int(self._brdcfg['regions'][region][0], 0)
- length = int(self._brdcfg['regions'][region][1], 0)
- log("region %s\tbase:0x%08x size:0x%08x" % (
- region, base, length))
- self._flashsize += length
-
- # Convert these to int because json doesn't support hex.
- self._brdcfg['regions'][region][0] = base
- self._brdcfg['regions'][region][1] = length
-
- log("Flash Size: 0x%x" % self._flashsize)
-
- def load_file(self, binfile):
- """Open and verify size of the target ec.bin file.
-
- Args:
- binfile: path to ec.bin
-
- Raises:
- Exception on file not found or filesize not matching.
- """
- self._filesize = os.path.getsize(binfile)
- self._binfile = open(binfile, 'rb')
-
- if self._filesize != self._flashsize:
- raise Exception("Update", "Flash size 0x%x != file size 0x%x" % (self._flashsize, self._filesize))
-
-
-
-# Generate command line arguments
-parser = argparse.ArgumentParser(description="Update firmware over usb")
-parser.add_argument('-b', '--board', type=str, help="Board configuration json file", default="board.json")
-parser.add_argument('-f', '--file', type=str, help="Complete ec.bin file", default="ec.bin")
-parser.add_argument('-s', '--serial', type=str, help="Serial number", default="")
-parser.add_argument('-l', '--list', action="store_true", help="List regions")
-parser.add_argument('-v', '--verbose', action="store_true", help="Chatty output")
-
-def main():
- global debug
- args = parser.parse_args()
-
-
- brdfile = args.board
- serial = args.serial
- binfile = args.file
- if args.verbose:
- debug = True
-
- with open(brdfile) as data_file:
- names = json.load(data_file)
-
- p = Supdate()
- p.load_board(brdfile)
- p.connect_usb(serialname=serial)
- p.load_file(binfile)
-
- # List solely prints the config.
- if (args.list):
- return
-
- # Start transfer and erase.
- p.start()
- # Upload the bin file
- log("Uploading %s" % binfile)
- p.write_file()
-
- # Finalize
- log("Done. Finalizing.")
- p.stop()
-
-if __name__ == "__main__":
- main()
-
-
diff --git a/extra/usb_updater/sample_descriptor b/extra/usb_updater/sample_descriptor
deleted file mode 100644
index 1566e9e2e1..0000000000
--- a/extra/usb_updater/sample_descriptor
+++ /dev/null
@@ -1,87 +0,0 @@
-# Copyright 2018 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-# Hash descriptor database file consists of sections for various Chrome OS
-# boards. Each board description section starts with a line of 4 characters
-# which is the board ID (the same as the board's RLZ code).
-#
-# Each board description section includes variable number of range
-# descriptor entries, each entry consisting of semicolon separated fields:
-#
-# {a|e|g}:{h|d}:base_addr:size[:value[:value[:value...]]]]
-#
-# Where
-#
-# - the first sindgle character field defines the way the range is accessed:
-# a - AP flash
-# e - EC flash
-# g - EC flash requiring gang programming mode
-# - the second single character field defines the range type
-# h - Cr50 returns the hash of the range
-# d - Cr50 returns actual contents of the range (hex dump)
-# - the third and and forth fields are base address and size of the range
-# - ranges of type 'h' include one or more values for the hash of the range.
-#
-# Descriptor entries can be split along multiple lines. Each entry is
-# terminated by an empty line. Board description section is completed when
-# another board ID or end of file is encountered.
-#
-# All values are expressed in hex. Repeating empty lines and lines starting
-# with '#' are ignored.
-#
-
-QZUX
-
-# 1: Valid hash section.
-a:h:0:10000:
-756c41b90ac9aa23a6c98ce13549dccd72e0a83f8537eb834d9cfc3d12bf3503:
-336c41b90ac9aa23a6c98ce13549dccd72e0a83f8537eb834d9cfc3d12bf3503:
-446c41b90ac9aa23a6c98ce13549dccd72e0a83f8537eb834d9cfc3d12bf3503
-
-# 2: Valid dump section.
-a:d:10:10
-
-# 3: Valid hash section.
-e:h:0:100:
-55d262badc1116520a7ae1d3fda380c0382b4b87f0db10de6495053ba3aadb87:
-444442badc1116520a7ae1d3fda380c0382b4b87f0db10de6495053ba3aadb87:
-443322badc1116520a7ae1d3fda380c0382b4b87f0db10de6495053ba3aadb87
-
-# 4: Invalid dump section (includes hash)
-a:d:20:10:55d262badc1116520a7ae1d3fda380c0382b4b87f0db10de6495053ba3aadb87
-
-# 5: Invalid hash section (does not include hash)
-e:h:0:100:
-
-# 6: Another invalid hash section (does not include hash)
-e:h:0:100:
-
-# extra empty lines
-
-
-# 7: Invalid hash section (hash too short)
-e:h:0:100:
-55d262badc1116520a7ae1d3fda380c0382b4b87f0db10de6495053ba3aadb8
-
-# 8: Invalid hash section (hash too long)
-a:h:0:10000:
-756c41b90ac9aa23a6c98ce13549dccd72e0a83f8537eb834d9cfc3d12bf35034:
-336c41b90ac9aa23a6c98ce13549dccd72e0a83f8537eb834d9cfc3d12bf3503
-
-# 9: Invalid hash section (hash includes non-hex value)
-a:h:0:10000:
-756c41b90ac9aa23a6c98ce13549dccd7xe0a83f8537eb834d9cfc3d12bf3503:
-336c41b90ac9aa23a6c98ce13549dccd72e0a83f8537eb834d9cfc3d12bf3505
-
-# 10: Invalid hash section (hash does not include 3 variants)
-a:h:0:10000:
-756c41b90ac9aa23a6c98ce13549dccd75e0a83f8537eb834d9cfc3d12bf3503:
-336c41b90ac9aa23a6c98ce13549dccd72e0a83f8537eb834d9cfc3d12bf3505
-
-# 11: Invalid dump section (size includes non hex character)
-a:d:10:10x
-
-ABCD
-
-a:d:10:10
diff --git a/extra/usb_updater/servo_micro.json b/extra/usb_updater/servo_micro.json
deleted file mode 100644
index 71b1fd25dc..0000000000
--- a/extra/usb_updater/servo_micro.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "Comment": "This file describes the updateable sections of the flash.",
- "board": "servo_micro",
- "vid": "0x18d1",
- "pid": "0x501a",
- "console": "3",
- "Comment on flash": "This is the base address of writeable flash",
- "flash": "0x8000000",
- "Comment on region format": "name: [baseoffset, length]",
- "regions": {
- "RW": ["0x10000", "0x10000"],
- "PSTATE": ["0xf000", "0x1000"],
- "RO": ["0x0000", "0xf000"]
- }
-}
diff --git a/extra/usb_updater/servo_updater.py b/extra/usb_updater/servo_updater.py
deleted file mode 100755
index 4dff264182..0000000000
--- a/extra/usb_updater/servo_updater.py
+++ /dev/null
@@ -1,456 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2016 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-# Ignore indention messages, since legacy scripts use 2 spaces instead of 4.
-# pylint: disable=bad-indentation,docstring-section-indent
-# pylint: disable=docstring-trailing-quotes
-
-# Note: This is a py2/3 compatible file.
-
-from __future__ import print_function
-
-import argparse
-import errno
-import os
-import re
-import subprocess
-import time
-import tempfile
-
-import json
-
-import fw_update
-import ecusb.tiny_servo_common as c
-from ecusb import tiny_servod
-
-class ServoUpdaterException(Exception):
- """Raised on exceptions generated by servo_updater."""
-
-BOARD_C2D2 = 'c2d2'
-BOARD_SERVO_MICRO = 'servo_micro'
-BOARD_SERVO_V4 = 'servo_v4'
-BOARD_SERVO_V4P1 = 'servo_v4p1'
-BOARD_SWEETBERRY = 'sweetberry'
-
-DEFAULT_BOARD = BOARD_SERVO_V4
-
-# These lists are to facilitate exposing choices in the command-line tool
-# below.
-BOARDS = [BOARD_C2D2, BOARD_SERVO_MICRO, BOARD_SERVO_V4, BOARD_SERVO_V4P1,
- BOARD_SWEETBERRY]
-
-# Servo firmware bundles four channels of firmware. We need to make sure the
-# user does not request a non-existing channel, so keep the lists around to
-# guard on command-line usage.
-
-DEFAULT_CHANNEL = STABLE_CHANNEL = 'stable'
-
-PREV_CHANNEL = 'prev'
-
-# The ordering here matters. From left to right it's the channel that the user
-# is most likely to be running. This is used to inform and warn the user if
-# there are issues. e.g. if the all channels are the same, we want to let the
-# user know they are running the 'stable' version before letting them know they
-# are running 'dev' or even 'alpah' which (while true) might cause confusion.
-
-CHANNELS = [DEFAULT_CHANNEL, PREV_CHANNEL, 'dev', 'alpha']
-
-DEFAULT_BASE_PATH = '/usr/'
-TEST_IMAGE_BASE_PATH = '/usr/local/'
-
-COMMON_PATH = 'share/servo_updater'
-
-FIRMWARE_DIR = "firmware/"
-CONFIGS_DIR = "configs/"
-
-RETRIES_COUNT = 10
-RETRIES_DELAY = 1
-
-def do_with_retries(func, *args):
- """
- Call function passed as argument and check if no error happened.
- If exception was raised by function,
- it will be retried up to RETRIES_COUNT times.
-
- Args:
- func: function that will be called
- args: arguments passed to 'func'
-
- Returns:
- If call to function was successful, its result will be returned.
- If retries count was exceeded, exception will be raised.
- """
-
- retry = 0
- while retry < RETRIES_COUNT:
- try:
- return func(*args)
- except Exception as e:
- print("Retrying function %s: %s" % (func.__name__, e))
- retry = retry + 1
- time.sleep(RETRIES_DELAY)
- continue
-
- raise Exception("'{}' failed after {} retries".format(func.__name__, RETRIES_COUNT))
-
-def flash(brdfile, serialno, binfile):
- """
- Call fw_update to upload to updater USB endpoint.
-
- Args:
- brdfile: path to board configuration file
- serialno: device serial number
- binfile: firmware file
- """
-
- p = fw_update.Supdate()
- p.load_board(brdfile)
- p.connect_usb(serialname=serialno)
- p.load_file(binfile)
-
- # Start transfer and erase.
- p.start()
- # Upload the bin file
- print("Uploading %s" % binfile)
- p.write_file()
-
- # Finalize
- print("Done. Finalizing.")
- p.stop()
-
-def flash2(vidpid, serialno, binfile):
- """
- Call fw update via usb_updater2 commandline.
-
- Args:
- vidpid: vendor id and product id of device
- serialno: device serial number (optional)
- binfile: firmware file
- """
-
- tool = 'usb_updater2'
- cmd = "%s -d %s" % (tool, vidpid)
- if serialno:
- cmd += " -S %s" % serialno
- cmd += " -n"
- cmd += " %s" % binfile
-
- print(cmd)
- help_cmd = '%s --help' % tool
- with open('/dev/null') as devnull:
- valid_check = subprocess.call(help_cmd.split(), stdout=devnull,
- stderr=devnull)
- if valid_check:
- raise ServoUpdaterException('%s exit with res = %d. Make sure the tool '
- 'is available on the device.' % (help_cmd,
- valid_check))
- res = subprocess.call(cmd.split())
-
- if res in (0, 1, 2):
- return res
- else:
- raise ServoUpdaterException("%s exit with res = %d" % (cmd, res))
-
-def select(tinys, region):
- """
- Ensure the servo is in the expected ro/rw region.
- This function jumps to the required region and verify if jump was
- successful by executing 'sysinfo' command and reading current region.
- If response was not received or region is invalid, exception is raised.
-
- Args:
- tinys: TinyServod object
- region: region to jump to, only "rw" and "ro" is allowed
- """
-
- if region not in ["rw", "ro"]:
- raise Exception("Region must be ro or rw")
-
- if region is "ro":
- cmd = "reboot"
- else:
- cmd = "sysjump %s" % region
-
- tinys.pty._issue_cmd(cmd)
-
- tinys.close()
- time.sleep(2)
- tinys.reinitialize()
-
- res = tinys.pty._issue_cmd_get_results("sysinfo", ["Copy:[\s]+(RO|RW)"])
- current_region = res[0][1].lower()
- if current_region != region:
- raise Exception("Invalid region: %s/%s" % (current_region, region))
-
-def do_version(tinys):
- """Check version via ec console 'pty'.
-
- Args:
- tinys: TinyServod object
-
- Returns:
- detected version number
-
- Commands are:
- # > version
- # ...
- # Build: tigertail_v1.1.6749-74d1a312e
- """
- cmd = '\r\nversion\r\n'
- regex = 'Build:\s+(\S+)[\r\n]+'
-
- results = tinys.pty._issue_cmd_get_results(cmd, [regex])[0]
-
- return results[1].strip(' \t\r\n\0')
-
-def do_updater_version(tinys):
- """Check whether this uses python updater or c++ updater
-
- Args:
- tinys: TinyServod object
-
- Returns:
- updater version number. 2 or 6.
- """
- vers = do_version(tinys)
-
- # Servo versions below 58 are from servo-9040.B. Versions starting with _v2
- # are newer than anything _v1, no need to check the exact number. Updater
- # version is not directly queryable.
- if re.search('_v[2-9]\.\d', vers):
- return 6
- m = re.search('_v1\.1\.(\d\d\d\d)', vers)
- if m:
- version_number = int(m.group(1))
- if version_number < 5800:
- return 2
- else:
- return 6
- raise ServoUpdaterException(
- "Can't determine updater target from vers: [%s]" % vers)
-
-def _extract_version(boardname, binfile):
- """Find the version string from |binfile|.
-
- Args:
- boardname: the name of the board, eg. "servo_micro"
- binfile: path to the binary to search
-
- Returns:
- the version string.
- """
- if boardname is None:
- # cannot extract the version if the name is None
- return None
- rawstrings = subprocess.check_output(
- ['cbfstool', binfile, 'read', '-r', 'RO_FRID', '-f', '/dev/stdout'],
- **c.get_subprocess_args())
- m = re.match(r'%s_v\S+' % boardname, rawstrings)
- if m:
- newvers = m.group(0).strip(' \t\r\n\0')
- else:
- raise ServoUpdaterException("Can't find version from file: %s." % binfile)
-
- return newvers
-
-def get_firmware_channel(bname, version):
- """Find out which channel |version| for |bname| came from.
-
- Args:
- bname: board name
- version: current version string
-
- Returns:
- one of the channel names if |version| came from one of those, or None
- """
- for channel in CHANNELS:
- # Pass |bname| as cname to find the board specific file, and pass None as
- # fname to ensure the default directory is searched
- _, _, vers = get_files_and_version(bname, None, channel=channel)
- if version == vers:
- return channel
- # None of the channels matched. This firmware is currently unknown.
- return None
-
-def get_files_and_version(cname, fname=None, channel=DEFAULT_CHANNEL):
- """Select config and firmware binary files.
-
- This checks default file names and paths.
- In: /usr/share/servo_updater/[firmware|configs]
- check for board.json, board.bin
-
- Args:
- cname: board name, or config name. eg. "servo_v4" or "servo_v4.json"
- fname: firmware binary name. Can be None to try default.
- channel: the channel requested for servo firmware. See |CHANNELS| above.
-
- Returns:
- cname, fname, version: validated filenames selected from the path.
- """
- for p in (DEFAULT_BASE_PATH, TEST_IMAGE_BASE_PATH):
- updater_path = os.path.join(p, COMMON_PATH)
- if os.path.exists(updater_path):
- break
- else:
- raise ServoUpdaterException('servo_updater/ dir not found in known spots.')
-
- firmware_path = os.path.join(updater_path, FIRMWARE_DIR)
- configs_path = os.path.join(updater_path, CONFIGS_DIR)
-
- for p in (firmware_path, configs_path):
- if not os.path.exists(p):
- raise ServoUpdaterException('Could not find required path %r' % p)
-
- if not os.path.isfile(cname):
- # If not an existing file, try checking on the default path.
- newname = os.path.join(configs_path, cname)
- if os.path.isfile(newname):
- cname = newname
- else:
- # Try appending ".json" to convert board name to config file.
- cname = newname + ".json"
- if not os.path.isfile(cname):
- raise ServoUpdaterException("Can't find config file: %s." % cname)
-
- # Always retrieve the boardname
- with open(cname) as data_file:
- data = json.load(data_file)
- boardname = data['board']
-
- if not fname:
- # If no |fname| supplied, look for the default locations with the board
- # and channel requested.
- binary_file = '%s.%s.bin' % (boardname, channel)
- newname = os.path.join(firmware_path, binary_file)
- if os.path.isfile(newname):
- fname = newname
- else:
- raise ServoUpdaterException("Can't find firmware binary: %s." %
- binary_file)
- elif not os.path.isfile(fname):
- # If a name is specified but not found, try the default path.
- newname = os.path.join(firmware_path, fname)
- if os.path.isfile(newname):
- fname = newname
- else:
- raise ServoUpdaterException("Can't find file: %s." % fname)
-
- # Lastly, retrieve the version as well for decision making, debug, and
- # informational purposes.
- binvers = _extract_version(boardname, fname)
-
- return cname, fname, binvers
-
-def main():
- parser = argparse.ArgumentParser(description="Image a servo device")
- parser.add_argument('-p', '--print', dest='print_only', action='store_true',
- default=False,
- help='only print available firmware for board/channel')
- parser.add_argument('-s', '--serialno', type=str,
- help="serial number to program", default=None)
- parser.add_argument('-b', '--board', type=str,
- help="Board configuration json file",
- default=DEFAULT_BOARD, choices=BOARDS)
- parser.add_argument('-c', '--channel', type=str,
- help="Firmware channel to use",
- default=DEFAULT_CHANNEL, choices=CHANNELS)
- parser.add_argument('-f', '--file', type=str,
- help="Complete ec.bin file", default=None)
- parser.add_argument('--force', action="store_true",
- help="Update even if version match", default=False)
- parser.add_argument('-v', '--verbose', action="store_true",
- help="Chatty output")
- parser.add_argument('-r', '--reboot', action="store_true",
- help="Always reboot, even after probe.")
-
- args = parser.parse_args()
-
- brdfile, binfile, newvers = get_files_and_version(args.board, args.file,
- args.channel)
-
- # If the user only cares about the information then just print it here,
- # and exit.
- if args.print_only:
- output = ('board: %s\n'
- 'channel: %s\n'
- 'firmware: %s') % (args.board, args.channel, newvers)
- print(output)
- return
-
- serialno = args.serialno
-
- with open(brdfile) as data_file:
- data = json.load(data_file)
- vid, pid = int(data['vid'], 0), int(data['pid'], 0)
- vidpid = "%04x:%04x" % (vid, pid)
- iface = int(data['console'], 0)
- boardname = data['board']
-
- # Make sure device is up.
- print("===== Waiting for USB device =====")
- c.wait_for_usb(vidpid, serialname=serialno)
- # We need a tiny_servod to query some information. Set it up first.
- tinys = tiny_servod.TinyServod(vid, pid, iface, serialno, args.verbose)
-
- if not args.force:
- vers = do_version(tinys)
- print("Current %s version is %s" % (boardname, vers))
- print("Available %s version is %s" % (boardname, newvers))
-
- if newvers == vers:
- print("No version update needed")
- if args.reboot:
- select(tinys, 'ro')
- return
- else:
- print("Updating to recommended version.")
-
- # Make sure the servo MCU is in RO
- print("===== Jumping to RO =====")
- do_with_retries(select, tinys, 'ro')
-
- print("===== Flashing RW =====")
- vers = do_with_retries(do_updater_version, tinys)
- # To make sure that the tiny_servod here does not interfere with other
- # processes, close it out.
- tinys.close()
-
- if vers == 2:
- flash(brdfile, serialno, binfile)
- elif vers == 6:
- flash2(vidpid, serialno, binfile)
- else:
- raise ServoUpdaterException("Can't detect updater version")
-
- # Make sure device is up.
- c.wait_for_usb(vidpid, serialname=serialno)
- # After we have made sure that it's back/available, reconnect the tiny servod.
- tinys.reinitialize()
-
- # Make sure the servo MCU is in RW
- print("===== Jumping to RW =====")
- do_with_retries(select, tinys, 'rw')
-
- print("===== Flashing RO =====")
- vers = do_with_retries(do_updater_version, tinys)
-
- if vers == 2:
- flash(brdfile, serialno, binfile)
- elif vers == 6:
- flash2(vidpid, serialno, binfile)
- else:
- raise ServoUpdaterException("Can't detect updater version")
-
- # Make sure the servo MCU is in RO
- print("===== Rebooting =====")
- do_with_retries(select, tinys, 'ro')
- # Perform additional reboot to free USB/UART resources, taken by tiny servod.
- # See https://issuetracker.google.com/196021317 for background.
- tinys.pty._issue_cmd("reboot")
-
- print("===== Finished =====")
-
-if __name__ == "__main__":
- main()
diff --git a/extra/usb_updater/servo_v4.json b/extra/usb_updater/servo_v4.json
deleted file mode 100644
index e041f56b68..0000000000
--- a/extra/usb_updater/servo_v4.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "Comment": "This file describes the updateable sections of the flash.",
- "board": "servo_v4",
- "vid": "0x18d1",
- "pid": "0x501b",
- "console": "0",
- "Comment on flash": "This is the base address of writeable flash",
- "flash": "0x8000000",
- "Comment on region format": "name: [baseoffset, length]",
- "regions": {
- "RW": ["0x10000", "0x10000"],
- "PSTATE": ["0xf000", "0x1000"],
- "RO": ["0x0000", "0xf000"]
- }
-}
diff --git a/extra/usb_updater/servo_v4p1.json b/extra/usb_updater/servo_v4p1.json
deleted file mode 100644
index 46efbf24ad..0000000000
--- a/extra/usb_updater/servo_v4p1.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "Comment": "This file describes the updateable sections of the flash.",
- "board": "servo_v4p1",
- "vid": "0x18d1",
- "pid": "0x520d",
- "console": "0",
- "Comment on flash": "This is the base address of writeable flash",
- "flash": "0x8000000",
- "Comment on region format": "name: [baseoffset, length]",
- "regions": {
- "RW": ["0x10000", "0x10000"],
- "PSTATE": ["0xf000", "0x1000"],
- "RO": ["0x0000", "0xf000"]
- }
-}
diff --git a/extra/usb_updater/sweetberry.json b/extra/usb_updater/sweetberry.json
deleted file mode 100644
index 6b70d19fad..0000000000
--- a/extra/usb_updater/sweetberry.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "Comment": "This file describes the updateable sections of the flash.",
- "board": "sweetberry",
- "vid": "0x18d1",
- "pid": "0x5020",
- "console": "0",
- "Comment on flash": "This is the base address of writeable flash",
- "flash": "0x8000000",
- "Comment on region format": "name: [baseoffset, length]",
- "regions": {
- "RW": ["0x40000", "0x40000"],
- "RO": ["0x0000", "0x40000"]
- }
-}
diff --git a/extra/usb_updater/usb_updater2.c b/extra/usb_updater/usb_updater2.c
deleted file mode 100644
index 12ee1615fc..0000000000
--- a/extra/usb_updater/usb_updater2.c
+++ /dev/null
@@ -1,1244 +0,0 @@
-/*
- * Copyright 2017 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include <asm/byteorder.h>
-#include <endian.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <libusb.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <fmap.h>
-
-#ifndef __packed
-#define __packed __attribute__((packed))
-#endif
-
-#include "compile_time_macros.h"
-#include "misc_util.h"
-#include "usb_descriptor.h"
-#include "update_fw.h"
-#include "vb21_struct.h"
-
-#ifdef DEBUG
-#define debug printf
-#else
-#define debug(fmt, args...)
-#endif
-
-/*
- * This file contains the source code of a Linux application used to update
- * EC device firmware (common code only, gsctool takes care of cr50).
- */
-
-#define VID USB_VID_GOOGLE
-#define PID 0x5022
-#define SUBCLASS USB_SUBCLASS_GOOGLE_UPDATE
-#define PROTOCOL USB_PROTOCOL_GOOGLE_UPDATE
-
-enum exit_values {
- noop = 0, /* All up to date, no update needed. */
- all_updated = 1, /* Update completed, reboot required. */
- rw_updated = 2, /* RO was not updated, reboot required. */
- update_error = 3 /* Something went wrong. */
-};
-
-struct usb_endpoint {
- struct libusb_device_handle *devh;
- uint8_t ep_num;
- int chunk_len;
-};
-
-struct transfer_descriptor {
- /*
- * offsets of section available for update (not currently active).
- */
- uint32_t offset;
-
- struct usb_endpoint uep;
-};
-
-/* Information about the target */
-static struct first_response_pdu targ;
-
-static uint16_t protocol_version;
-static uint16_t header_type;
-static char *progname;
-static char *short_opts = "bd:efg:hjlnp:rsS:tuw";
-static const struct option long_opts[] = {
- /* name hasarg *flag val */
- {"binvers", 1, NULL, 'b'},
- {"device", 1, NULL, 'd'},
- {"entropy", 0, NULL, 'e'},
- {"fwver", 0, NULL, 'f'},
- {"tp_debug", 1, NULL, 'g'},
- {"help", 0, NULL, 'h'},
- {"jump_to_rw", 0, NULL, 'j'},
- {"follow_log", 0, NULL, 'l'},
- {"no_reset", 0, NULL, 'n'},
- {"tp_update", 1, NULL, 'p'},
- {"reboot", 0, NULL, 'r'},
- {"stay_in_ro", 0, NULL, 's'},
- {"serial", 1, NULL, 'S'},
- {"tp_info", 0, NULL, 't'},
- {"unlock_rollback", 0, NULL, 'u'},
- {"unlock_rw", 0, NULL, 'w'},
- {},
-};
-
-/* Release USB device and return error to the OS. */
-static void shut_down(struct usb_endpoint *uep)
-{
- libusb_close(uep->devh);
- libusb_exit(NULL);
- exit(update_error);
-}
-
-static void usage(int errs)
-{
- printf("\nUsage: %s [options] <binary image>\n"
- "\n"
- "This updates EC firmware over USB (common code EC, no cr50).\n"
- "The required argument is the full RO+RW image.\n"
- "\n"
- "Options:\n"
- "\n"
- " -b,--binvers Report versions of image's "
- "RW and RO, do not update\n"
- " -d,--device VID:PID USB device (default %04x:%04x)\n"
- " -e,--entropy Add entropy to device secret\n"
- " -f,--fwver Report running firmware versions.\n"
- " -g,--tp_debug <hex data> Touchpad debug command\n"
- " -h,--help Show this message\n"
- " -j,--jump_to_rw Tell EC to jump to RW\n"
- " -l,--follow_log Get console log\n"
- " -p,--tp_update file Update touchpad FW\n"
- " -r,--reboot Tell EC to reboot\n"
- " -s,--stay_in_ro Tell EC to stay in RO\n"
- " -S,--serial Device serial number\n"
- " -t,--tp_info Get touchpad information\n"
- " -u,--unlock_rollback Tell EC to unlock the rollback region\n"
- " -w,--unlock_rw Tell EC to unlock the RW region\n"
- "\n", progname, VID, PID);
-
- exit(errs ? update_error : noop);
-}
-
-static void str2hex(const char *str, uint8_t *data, int *len)
-{
- int i;
- int slen = strlen(str);
-
- if (slen/2 > *len) {
- fprintf(stderr, "Hex string too long.\n");
- exit(update_error);
- }
-
- if (slen % 2 != 0) {
- fprintf(stderr, "Hex string length not a multiple of 2.\n");
- exit(update_error);
- }
-
- for (i = 0, *len = 0; i < slen; i += 2, (*len)++) {
- char *end;
- char tmp[3];
-
- tmp[0] = str[i];
- tmp[1] = str[i+1];
- tmp[2] = 0;
-
- data[*len] = strtol(tmp, &end, 16);
-
- if (*end != 0) {
- fprintf(stderr, "Invalid hex string.\n");
- exit(update_error);
- }
- }
-}
-
-static void hexdump(const uint8_t *data, int len)
-{
- int i;
-
- for (i = 0; i < len; i++) {
- printf("%02x", data[i]);
- if ((i % 16) == 15)
- printf("\n");
- }
-
- if ((len % 16) != 0)
- printf("\n");
-}
-
-static void dump_touchpad_info(const uint8_t *data, int len)
-{
- const struct touchpad_info *info = (const struct touchpad_info *)data;
-
- if (len != sizeof(struct touchpad_info)) {
- fprintf(stderr, "Hex string length is not %zu",
- sizeof(struct touchpad_info));
- hexdump(data, len);
- return;
- }
-
- printf("\n");
- printf("status: 0x%02x\n", info->status);
- printf("vendor: 0x%04x\n", info->vendor);
- printf("fw_address: 0x%08x\n", info->fw_address);
- printf("fw_size: 0x%08x\n", info->fw_size);
-
- printf("allowed_fw_hash:\n");
- hexdump(info->allowed_fw_hash, sizeof(info->allowed_fw_hash));
-
- switch (info->vendor) {
- case 0x04f3: /* ELAN */
- case 0x0483: /* ST */
- printf("id: 0x%04x\n", info->elan.id);
- printf("fw_version: 0x%04x\n", info->elan.fw_version);
- printf("fw_fw_checksum: 0x%04x\n", info->elan.fw_checksum);
- break;
- default:
- fprintf(stderr, "Unknown vendor, vendor specific data:\n");
- hexdump((const uint8_t *)&info->elan, sizeof(info->elan));
- break;
- }
-}
-
-/* Read file into buffer */
-static uint8_t *get_file_or_die(const char *filename, size_t *len_ptr)
-{
- FILE *fp;
- struct stat st;
- uint8_t *data;
- size_t len;
-
- fp = fopen(filename, "rb");
- if (!fp) {
- perror(filename);
- exit(update_error);
- }
- if (fstat(fileno(fp), &st)) {
- perror("stat");
- exit(update_error);
- }
-
- len = st.st_size;
-
- data = malloc(len);
- if (!data) {
- perror("malloc");
- exit(update_error);
- }
-
- if (fread(data, st.st_size, 1, fp) != 1) {
- perror("fread");
- exit(update_error);
- }
-
- fclose(fp);
-
- *len_ptr = len;
- return data;
-}
-
-#define USB_ERROR(m, r) \
- fprintf(stderr, "%s:%d, %s returned %d (%s)\n", __FILE__, __LINE__, \
- m, r, libusb_strerror(r))
-
-/*
- * Actual USB transfer function, the 'allow_less' flag indicates that the
- * valid response could be shortef than allotted memory, the 'rxed_count'
- * pointer, if provided along with 'allow_less' lets the caller know how mavy
- * bytes were received.
- */
-static void do_xfer(struct usb_endpoint *uep, void *outbuf, int outlen,
- void *inbuf, int inlen, int allow_less,
- size_t *rxed_count)
-{
-
- int r, actual;
-
- /* Send data out */
- if (outbuf && outlen) {
- actual = 0;
- r = libusb_bulk_transfer(uep->devh, uep->ep_num,
- outbuf, outlen,
- &actual, 2000);
- if (r < 0) {
- USB_ERROR("libusb_bulk_transfer", r);
- exit(update_error);
- }
- if (actual != outlen) {
- fprintf(stderr, "%s:%d, only sent %d/%d bytes\n",
- __FILE__, __LINE__, actual, outlen);
- shut_down(uep);
- }
- }
-
- /* Read reply back */
- if (inbuf && inlen) {
-
- actual = 0;
- r = libusb_bulk_transfer(uep->devh, uep->ep_num | 0x80,
- inbuf, inlen,
- &actual, 5000);
- if (r < 0) {
- USB_ERROR("libusb_bulk_transfer", r);
- exit(update_error);
- }
- if ((actual != inlen) && !allow_less) {
- fprintf(stderr, "%s:%d, only received %d/%d bytes\n",
- __FILE__, __LINE__, actual, inlen);
- hexdump(inbuf, actual);
- shut_down(uep);
- }
-
- if (rxed_count)
- *rxed_count = actual;
- }
-}
-
-static void xfer(struct usb_endpoint *uep, void *outbuf,
- size_t outlen, void *inbuf, size_t inlen, int allow_less)
-{
- do_xfer(uep, outbuf, outlen, inbuf, inlen, allow_less, NULL);
-}
-
-/* Return 0 on error, since it's never gonna be EP 0 */
-static int find_endpoint(const struct libusb_interface_descriptor *iface,
- struct usb_endpoint *uep)
-{
- const struct libusb_endpoint_descriptor *ep;
-
- if (iface->bInterfaceClass == 255 &&
- iface->bInterfaceSubClass == SUBCLASS &&
- iface->bInterfaceProtocol == PROTOCOL &&
- iface->bNumEndpoints) {
- ep = &iface->endpoint[0];
- uep->ep_num = ep->bEndpointAddress & 0x7f;
- uep->chunk_len = ep->wMaxPacketSize;
- return 1;
- }
-
- return 0;
-}
-
-/* Return -1 on error */
-static int find_interface(struct usb_endpoint *uep)
-{
- int iface_num = -1;
- int r, i, j;
- struct libusb_device *dev;
- struct libusb_config_descriptor *conf = 0;
- const struct libusb_interface *iface0;
- const struct libusb_interface_descriptor *iface;
-
- dev = libusb_get_device(uep->devh);
- r = libusb_get_active_config_descriptor(dev, &conf);
- if (r < 0) {
- USB_ERROR("libusb_get_active_config_descriptor", r);
- goto out;
- }
-
- for (i = 0; i < conf->bNumInterfaces; i++) {
- iface0 = &conf->interface[i];
- for (j = 0; j < iface0->num_altsetting; j++) {
- iface = &iface0->altsetting[j];
- if (find_endpoint(iface, uep)) {
- iface_num = i;
- goto out;
- }
- }
- }
-
-out:
- libusb_free_config_descriptor(conf);
- return iface_num;
-}
-
-/* Returns true if parsed. */
-static int parse_vidpid(const char *input, uint16_t *vid_ptr, uint16_t *pid_ptr)
-{
- char *copy, *s, *e = 0;
-
- copy = strdup(input);
-
- s = strchr(copy, ':');
- if (!s)
- return 0;
- *s++ = '\0';
-
- *vid_ptr = (uint16_t) strtoull(copy, &e, 16);
- if (!*optarg || (e && *e))
- return 0;
-
- *pid_ptr = (uint16_t) strtoull(s, &e, 16);
- if (!*optarg || (e && *e))
- return 0;
-
- return 1;
-}
-
-static libusb_device_handle *check_device(libusb_device *dev,
- uint16_t vid, uint16_t pid, char *serialno)
-{
- struct libusb_device_descriptor desc;
- libusb_device_handle *handle = NULL;
- char sn[256];
- int ret;
- int match = 1;
- int snvalid = 0;
-
- ret = libusb_get_device_descriptor(dev, &desc);
- if (ret < 0)
- return NULL;
-
- ret = libusb_open(dev, &handle);
-
- if (ret != LIBUSB_SUCCESS)
- return NULL;
-
- if (desc.iSerialNumber) {
- ret = libusb_get_string_descriptor_ascii(handle,
- desc.iSerialNumber, (unsigned char *)sn, sizeof(sn));
- if (ret > 0)
- snvalid = 1;
- }
-
- if (vid != 0 && vid != desc.idVendor)
- match = 0;
- if (pid != 0 && pid != desc.idProduct)
- match = 0;
- if (serialno != NULL && (!snvalid || strstr(sn, serialno) == NULL))
- match = 0;
-
- if (match)
- return handle;
-
- libusb_close(handle);
- return NULL;
-}
-
-static void usb_findit(uint16_t vid, uint16_t pid,
- char *serialno, struct usb_endpoint *uep)
-{
- int iface_num, r, i;
- libusb_device **devs;
- libusb_device_handle *devh = NULL;
- ssize_t count;
-
- memset(uep, 0, sizeof(*uep));
-
- r = libusb_init(NULL);
- if (r < 0) {
- USB_ERROR("libusb_init", r);
- exit(update_error);
- }
-
- count = libusb_get_device_list(NULL, &devs);
- if (count < 0)
- return;
-
- for (i = 0; devs[i]; i++) {
- devh = check_device(devs[i], vid, pid, serialno);
- if (devh) {
- printf("Found device.\n");
- break;
- }
- }
-
- libusb_free_device_list(devs, 1);
-
- if (!devh) {
- fprintf(stderr, "Can't find device\n");
- exit(update_error);
- }
-
- uep->devh = devh;
-
- iface_num = find_interface(uep);
- if (iface_num < 0) {
- fprintf(stderr, "USB FW update not supported by that device\n");
- shut_down(uep);
- }
- if (!uep->chunk_len) {
- fprintf(stderr, "wMaxPacketSize isn't valid\n");
- shut_down(uep);
- }
-
- printf("found interface %d endpoint %d, chunk_len %d\n",
- iface_num, uep->ep_num, uep->chunk_len);
-
- libusb_set_auto_detach_kernel_driver(uep->devh, 1);
- r = libusb_claim_interface(uep->devh, iface_num);
- if (r < 0) {
- USB_ERROR("libusb_claim_interface", r);
- shut_down(uep);
- }
-
- printf("READY\n-------\n");
-}
-
-static int transfer_block(struct usb_endpoint *uep,
- struct update_frame_header *ufh,
- uint8_t *transfer_data_ptr, size_t payload_size)
-{
- size_t transfer_size;
- uint32_t reply;
- int actual;
- int r;
-
- /* First send the header. */
- xfer(uep, ufh, sizeof(*ufh), NULL, 0, 0);
-
- /* Now send the block, chunk by chunk. */
- for (transfer_size = 0; transfer_size < payload_size;) {
- int chunk_size;
-
- chunk_size = MIN(uep->chunk_len, payload_size - transfer_size);
- xfer(uep, transfer_data_ptr, chunk_size, NULL, 0, 0);
- transfer_data_ptr += chunk_size;
- transfer_size += chunk_size;
- }
-
- /* Now get the reply. */
- r = libusb_bulk_transfer(uep->devh, uep->ep_num | 0x80,
- (void *) &reply, sizeof(reply),
- &actual, 5000);
- if (r) {
- if (r == -7) {
- fprintf(stderr, "Timeout!\n");
- return r;
- }
- USB_ERROR("libusb_bulk_transfer", r);
- shut_down(uep);
- }
-
- reply = *((uint8_t *)&reply);
- if (reply) {
- fprintf(stderr, "Error: status %#x\n", reply);
- exit(update_error);
- }
-
- return 0;
-}
-
-/**
- * Transfer an image section (typically RW or RO).
- *
- * td - transfer descriptor to use to communicate with the target
- * data_ptr - pointer at the section base in the image
- * section_addr - address of the section in the target memory space
- * data_len - section size
- * smart_update - non-zero to enable the smart trailing of 0xff.
- */
-static void transfer_section(struct transfer_descriptor *td,
- uint8_t *data_ptr,
- uint32_t section_addr,
- size_t data_len,
- uint8_t smart_update)
-{
- /*
- * Actually, we can skip trailing chunks of 0xff, as the entire
- * section space must be erased before the update is attempted.
- *
- * FIXME: We can be smarter than this and skip blocks within the image.
- */
- if (smart_update)
- while (data_len && (data_ptr[data_len - 1] == 0xff))
- data_len--;
-
- printf("sending 0x%zx bytes to %#x\n", data_len, section_addr);
- while (data_len) {
- size_t payload_size;
- uint32_t block_base;
- int max_retries;
-
- /* prepare the header to prepend to the block. */
- payload_size = MIN(data_len, targ.common.maximum_pdu_size);
-
- block_base = htobe32(section_addr);
-
- struct update_frame_header ufh;
-
- ufh.block_size = htobe32(payload_size +
- sizeof(struct update_frame_header));
- ufh.cmd.block_base = block_base;
- ufh.cmd.block_digest = 0;
- for (max_retries = 10; max_retries; max_retries--)
- if (!transfer_block(&td->uep, &ufh,
- data_ptr, payload_size))
- break;
-
- if (!max_retries) {
- fprintf(stderr,
- "Failed to transfer block, %zd to go\n",
- data_len);
- exit(update_error);
- }
- data_len -= payload_size;
- data_ptr += payload_size;
- section_addr += payload_size;
- }
-}
-
-/*
- * Each RO or RW section of the new image can be in one of the following
- * states.
- */
-enum upgrade_status {
- not_needed = 0, /* Version below or equal that on the target. */
- not_possible, /*
- * RO is newer, but can't be transferred due to
- * target RW shortcomings.
- */
- needed /*
- * This section needs to be transferred to the
- * target.
- */
-};
-
-/* This array describes all sections of the new image. */
-static struct {
- const char *name;
- uint32_t offset;
- uint32_t size;
- enum upgrade_status ustatus;
- char version[32];
- int32_t rollback;
- uint32_t key_version;
-} sections[] = {
- {"RO"},
- {"RW"}
-};
-
-static const struct fmap_area *fmap_find_area_or_die(const struct fmap *fmap,
- const char *name)
-{
- const struct fmap_area *fmaparea;
-
- fmaparea = fmap_find_area(fmap, name);
- if (!fmaparea) {
- fprintf(stderr, "Cannot find FMAP area %s\n", name);
- exit(update_error);
- }
-
- return fmaparea;
-}
-
-/*
- * Scan the new image and retrieve versions of all sections.
- */
-static void fetch_header_versions(const uint8_t *image, size_t len)
-{
- const struct fmap *fmap;
- const struct fmap_area *fmaparea;
- long int offset;
- size_t i;
-
- offset = fmap_find(image, len);
- if (offset < 0) {
- fprintf(stderr, "Cannot find FMAP in image\n");
- exit(update_error);
- }
- fmap = (const struct fmap *)(image+offset);
-
- /* FIXME: validate fmap struct more than this? */
- if (fmap->size != len) {
- fprintf(stderr, "Mismatch between FMAP size and image size\n");
- exit(update_error);
- }
-
- for (i = 0; i < ARRAY_SIZE(sections); i++) {
- const char *fmap_name;
- const char *fmap_fwid_name;
- const char *fmap_rollback_name = NULL;
- const char *fmap_key_name = NULL;
-
- if (!strcmp(sections[i].name, "RO")) {
- fmap_name = "EC_RO";
- fmap_fwid_name = "RO_FRID";
- } else if (!strcmp(sections[i].name, "RW")) {
- fmap_name = "EC_RW";
- fmap_fwid_name = "RW_FWID";
- fmap_rollback_name = "RW_RBVER";
- /*
- * Key version comes from key RO (RW signature does not
- * contain the key version.
- */
- fmap_key_name = "KEY_RO";
- } else {
- fprintf(stderr, "Invalid section name\n");
- exit(update_error);
- }
-
- fmaparea = fmap_find_area_or_die(fmap, fmap_name);
-
- /* FIXME: endianness? */
- sections[i].offset = fmaparea->offset;
- sections[i].size = fmaparea->size;
-
- fmaparea = fmap_find_area_or_die(fmap, fmap_fwid_name);
-
- if (fmaparea->size != sizeof(sections[i].version)) {
- fprintf(stderr, "Invalid fwid size\n");
- exit(update_error);
- }
- memcpy(sections[i].version, image+fmaparea->offset,
- fmaparea->size);
-
- sections[i].rollback = -1;
- if (fmap_rollback_name) {
- fmaparea = fmap_find_area(fmap, fmap_rollback_name);
- if (fmaparea)
- memcpy(&sections[i].rollback,
- image+fmaparea->offset,
- sizeof(sections[i].rollback));
- }
-
- sections[i].key_version = -1;
- if (fmap_key_name) {
- fmaparea = fmap_find_area(fmap, fmap_key_name);
- if (fmaparea) {
- const struct vb21_packed_key *key =
- (const void *)(image+fmaparea->offset);
- sections[i].key_version = key->key_version;
- }
- }
- }
-}
-
-static int show_headers_versions(const void *image)
-{
- size_t i;
-
- for (i = 0; i < ARRAY_SIZE(sections); i++) {
- printf("%s off=%08x/%08x v=%.32s rb=%d kv=%d\n",
- sections[i].name, sections[i].offset, sections[i].size,
- sections[i].version, sections[i].rollback,
- sections[i].key_version);
- }
- return 0;
-}
-
-/*
- * Pick sections to transfer based on information retrieved from the target,
- * the new image, and the protocol version the target is running.
- */
-static void pick_sections(struct transfer_descriptor *td)
-{
- size_t i;
-
- for (i = 0; i < ARRAY_SIZE(sections); i++) {
- uint32_t offset = sections[i].offset;
-
- /* Skip currently active section. */
- if (offset != td->offset)
- continue;
-
- sections[i].ustatus = needed;
- }
-}
-
-static void setup_connection(struct transfer_descriptor *td)
-{
- size_t rxed_size;
- size_t i;
- uint32_t error_code;
-
- /*
- * Need to be backwards compatible, communicate with targets running
- * different protocol versions.
- */
- union {
- struct first_response_pdu rpdu;
- uint32_t legacy_resp;
- } start_resp;
-
- /* Send start request. */
- printf("start\n");
-
- struct update_frame_header ufh;
- uint8_t inbuf[td->uep.chunk_len];
- int actual = 0;
-
- /* Flush all data from endpoint to recover in case of error. */
- while (!libusb_bulk_transfer(td->uep.devh,
- td->uep.ep_num | 0x80,
- (void *)&inbuf, td->uep.chunk_len,
- &actual, 10)) {
- printf("flush\n");
- }
-
- memset(&ufh, 0, sizeof(ufh));
- ufh.block_size = htobe32(sizeof(ufh));
- do_xfer(&td->uep, &ufh, sizeof(ufh), &start_resp,
- sizeof(start_resp), 1, &rxed_size);
-
- /* We got something. Check for errors in response */
- if (rxed_size < 8) {
- fprintf(stderr, "Unexpected response size %zd: ", rxed_size);
- for (i = 0; i < rxed_size; i++)
- fprintf(stderr, " %02x", ((uint8_t *)&start_resp)[i]);
- fprintf(stderr, "\n");
- exit(update_error);
- }
-
- protocol_version = be16toh(start_resp.rpdu.protocol_version);
- if (protocol_version < 5 || protocol_version > 6) {
- fprintf(stderr, "Unsupported protocol version %d\n",
- protocol_version);
- exit(update_error);
- }
-
- header_type = be16toh(start_resp.rpdu.header_type);
-
- printf("target running protocol version %d (type %d)\n",
- protocol_version, header_type);
- if (header_type != UPDATE_HEADER_TYPE_COMMON) {
- fprintf(stderr, "Unsupported header type %d\n",
- header_type);
- exit(update_error);
- }
-
- error_code = be32toh(start_resp.rpdu.return_value);
-
- if (error_code) {
- fprintf(stderr, "Target reporting error %d\n", error_code);
- shut_down(&td->uep);
- exit(update_error);
- }
-
- td->offset = be32toh(start_resp.rpdu.common.offset);
- memcpy(targ.common.version, start_resp.rpdu.common.version,
- sizeof(start_resp.rpdu.common.version));
- targ.common.maximum_pdu_size =
- be32toh(start_resp.rpdu.common.maximum_pdu_size);
- targ.common.flash_protection =
- be32toh(start_resp.rpdu.common.flash_protection);
- targ.common.min_rollback = be32toh(start_resp.rpdu.common.min_rollback);
- targ.common.key_version = be32toh(start_resp.rpdu.common.key_version);
-
- printf("maximum PDU size: %d\n", targ.common.maximum_pdu_size);
- printf("Flash protection status: %04x\n", targ.common.flash_protection);
- printf("version: %32s\n", targ.common.version);
- printf("key_version: %d\n", targ.common.key_version);
- printf("min_rollback: %d\n", targ.common.min_rollback);
- printf("offset: writable at %#x\n", td->offset);
-
- pick_sections(td);
-}
-
-/*
- * Channel TPM extension/vendor command over USB. The payload of the USB frame
- * in this case consists of the 2 byte subcommand code concatenated with the
- * command body. The caller needs to indicate if a response is expected, and
- * if it is - of what maximum size.
- */
-static int ext_cmd_over_usb(struct usb_endpoint *uep, uint16_t subcommand,
- void *cmd_body, size_t body_size,
- void *resp, size_t *resp_size,
- int allow_less)
-{
- struct update_frame_header *ufh;
- uint16_t *frame_ptr;
- size_t usb_msg_size;
-
- usb_msg_size = sizeof(struct update_frame_header) +
- sizeof(subcommand) + body_size;
-
- ufh = malloc(usb_msg_size);
- if (!ufh) {
- printf("%s: failed to allocate %zd bytes\n",
- __func__, usb_msg_size);
- return -1;
- }
-
- ufh->block_size = htobe32(usb_msg_size);
- ufh->cmd.block_digest = 0;
- ufh->cmd.block_base = htobe32(UPDATE_EXTRA_CMD);
- frame_ptr = (uint16_t *)(ufh + 1);
- *frame_ptr = htobe16(subcommand);
-
- if (body_size)
- memcpy(frame_ptr + 1, cmd_body, body_size);
-
- xfer(uep, ufh, usb_msg_size, resp, resp_size ? *resp_size : 0,
- allow_less);
-
- free(ufh);
- return 0;
-}
-
-/*
- * Indicate to the target that update image transfer has been completed. Upon
- * receiveing of this message the target state machine transitions into the
- * 'rx_idle' state. The host may send an extension command to reset the target
- * after this.
- */
-static void send_done(struct usb_endpoint *uep)
-{
- uint32_t out;
-
- /* Send stop request, ignoring reply. */
- out = htobe32(UPDATE_DONE);
- xfer(uep, &out, sizeof(out), &out, 1, 0);
-}
-
-static void send_subcommand(struct transfer_descriptor *td, uint16_t subcommand,
- void *cmd_body, size_t body_size,
- uint8_t *response, size_t response_size)
-{
- send_done(&td->uep);
-
- ext_cmd_over_usb(&td->uep, subcommand,
- cmd_body, body_size,
- response, &response_size, 0);
- printf("sent command %x, resp %x\n", subcommand, response[0]);
-}
-
-/* Returns number of successfully transmitted image sections. */
-static int transfer_image(struct transfer_descriptor *td,
- uint8_t *data, size_t data_len)
-{
- size_t i;
- int num_txed_sections = 0;
-
- for (i = 0; i < ARRAY_SIZE(sections); i++)
- if (sections[i].ustatus == needed) {
- transfer_section(td,
- data + sections[i].offset,
- sections[i].offset,
- sections[i].size, 1);
- num_txed_sections++;
- }
-
- /*
- * Move USB receiver sate machine to idle state so that vendor
- * commands can be processed later, if any.
- */
- send_done(&td->uep);
-
- if (!num_txed_sections)
- printf("nothing to do\n");
- else
- printf("-------\nupdate complete\n");
- return num_txed_sections;
-}
-
-static void generate_reset_request(struct transfer_descriptor *td)
-{
- size_t response_size;
- uint8_t response;
- uint16_t subcommand;
- uint8_t command_body[2]; /* Max command body size. */
- size_t command_body_size;
-
- if (protocol_version < 6) {
- /*
- * Send a second stop request, which should reboot
- * without replying.
- */
- send_done(&td->uep);
- /* Nothing we can do over /dev/tpm0 running versions below 6. */
- return;
- }
-
- /*
- * If the user explicitly wants it, request post reset instead of
- * immediate reset. In this case next time the target reboots, the h1
- * will reboot as well, and will consider running the uploaded code.
- *
- * In case target RW version is 19 or above, to reset the target the
- * host is supposed to send the command to enable the uploaded image
- * disabled by default.
- *
- * Otherwise the immediate reset command would suffice.
- */
- /* Most common case. */
- command_body_size = 0;
- response_size = 1;
- subcommand = UPDATE_EXTRA_CMD_IMMEDIATE_RESET;
- ext_cmd_over_usb(&td->uep, subcommand,
- command_body, command_body_size,
- &response, &response_size, 0);
-
- printf("reboot not triggered\n");
-}
-
-static void get_random(uint8_t *data, int len)
-{
- FILE *fp;
- int i = 0;
-
- fp = fopen("/dev/random", "rb");
- if (!fp) {
- perror("Can't open /dev/random");
- exit(update_error);
- }
-
- while (i < len) {
- int ret = fread(data+i, len-i, 1, fp);
-
- if (ret < 0) {
- perror("fread");
- exit(update_error);
- }
-
- i += ret;
- }
-
- fclose(fp);
-}
-
-static void read_console(struct transfer_descriptor *td)
-{
- uint8_t payload[] = { 0x1 };
- uint8_t response[64];
- size_t response_size = 64;
- struct timespec sleep_duration = { /* 100 ms */
- .tv_sec = 0,
- .tv_nsec = 100l * 1000l * 1000l,
- };
-
- send_done(&td->uep);
-
- printf("\n");
- while (1) {
- response_size = 1;
- ext_cmd_over_usb(&td->uep,
- UPDATE_EXTRA_CMD_CONSOLE_READ_INIT,
- NULL, 0,
- response, &response_size, 0);
-
- while (1) {
- response_size = 64;
- ext_cmd_over_usb(&td->uep,
- UPDATE_EXTRA_CMD_CONSOLE_READ_NEXT,
- payload, sizeof(payload),
- response, &response_size, 1);
- if (response[0] == 0)
- break;
- /* make sure it's null-terminated. */
- response[response_size - 1] = 0;
- printf("%s", (const char *)response);
- }
- nanosleep(&sleep_duration, NULL);
- }
-}
-
-int main(int argc, char *argv[])
-{
- struct transfer_descriptor td;
- int errorcnt;
- uint8_t *data = 0;
- size_t data_len = 0;
- uint16_t vid = VID, pid = PID;
- char *serialno = NULL;
- int i;
- size_t j;
- int transferred_sections = 0;
- int binary_vers = 0;
- int show_fw_ver = 0;
- int no_reset_request = 0;
- int touchpad_update = 0;
- int extra_command = -1;
- uint8_t extra_command_data[50];
- int extra_command_data_len = 0;
- uint8_t extra_command_answer[64];
- int extra_command_answer_len = 1;
-
- progname = strrchr(argv[0], '/');
- if (progname)
- progname++;
- else
- progname = argv[0];
-
- /* Usb transfer - default mode. */
- memset(&td, 0, sizeof(td));
-
- errorcnt = 0;
- opterr = 0; /* quiet, you */
- while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) {
- switch (i) {
- case 'b':
- binary_vers = 1;
- break;
- case 'd':
- if (!parse_vidpid(optarg, &vid, &pid)) {
- printf("Invalid argument: \"%s\"\n", optarg);
- errorcnt++;
- }
- break;
- case 'e':
- get_random(extra_command_data, 32);
- extra_command_data_len = 32;
- extra_command = UPDATE_EXTRA_CMD_INJECT_ENTROPY;
- break;
- case 'f':
- show_fw_ver = 1;
- break;
- case 'g':
- extra_command = UPDATE_EXTRA_CMD_TOUCHPAD_DEBUG;
- /* Maximum length. */
- extra_command_data_len = 50;
- str2hex(optarg,
- extra_command_data, &extra_command_data_len);
- hexdump(extra_command_data, extra_command_data_len);
- extra_command_answer_len = 64;
- break;
- case 'h':
- usage(errorcnt);
- break;
- case 'j':
- extra_command = UPDATE_EXTRA_CMD_JUMP_TO_RW;
- break;
- case 'l':
- extra_command = UPDATE_EXTRA_CMD_CONSOLE_READ_INIT;
- break;
- case 'n':
- no_reset_request = 1;
- break;
- case 'p':
- touchpad_update = 1;
-
- data = get_file_or_die(optarg, &data_len);
- printf("read %zd(%#zx) bytes from %s\n",
- data_len, data_len, argv[optind - 1]);
-
- break;
- case 'r':
- extra_command = UPDATE_EXTRA_CMD_IMMEDIATE_RESET;
- break;
- case 's':
- extra_command = UPDATE_EXTRA_CMD_STAY_IN_RO;
- break;
- case 'S':
- serialno = optarg;
- break;
- case 't':
- extra_command = UPDATE_EXTRA_CMD_TOUCHPAD_INFO;
- extra_command_answer_len =
- sizeof(struct touchpad_info);
- break;
- case 'u':
- extra_command = UPDATE_EXTRA_CMD_UNLOCK_ROLLBACK;
- break;
- case 'w':
- extra_command = UPDATE_EXTRA_CMD_UNLOCK_RW;
- break;
- case 0: /* auto-handled option */
- break;
- case '?':
- if (optopt)
- printf("Unrecognized option: -%c\n", optopt);
- else
- printf("Unrecognized option: %s\n",
- argv[optind - 1]);
- errorcnt++;
- break;
- case ':':
- printf("Missing argument to %s\n", argv[optind - 1]);
- errorcnt++;
- break;
- default:
- printf("Internal error at %s:%d\n", __FILE__, __LINE__);
- exit(update_error);
- }
- }
-
- if (errorcnt)
- usage(errorcnt);
-
- if (!show_fw_ver && extra_command == -1 && !touchpad_update) {
- if (optind >= argc) {
- fprintf(stderr,
- "\nERROR: Missing required <binary image>\n\n");
- usage(1);
- }
-
- data = get_file_or_die(argv[optind], &data_len);
- printf("read %zd(%#zx) bytes from %s\n",
- data_len, data_len, argv[optind]);
-
- fetch_header_versions(data, data_len);
-
- if (binary_vers)
- exit(show_headers_versions(data));
- } else {
- if (optind < argc)
- printf("Ignoring binary image %s\n", argv[optind]);
- }
-
- usb_findit(vid, pid, serialno, &td.uep);
-
- setup_connection(&td);
-
- if (show_fw_ver) {
- printf("Current versions:\n");
- printf("Writable %32s\n", targ.common.version);
- }
-
- if (data) {
- if (touchpad_update) {
- transfer_section(&td,
- data,
- 0x80000000,
- data_len, 0);
- free(data);
-
- send_done(&td.uep);
- } else {
- transferred_sections = transfer_image(&td,
- data, data_len);
- free(data);
-
- if (transferred_sections && !no_reset_request)
- generate_reset_request(&td);
- }
- } else if (extra_command == UPDATE_EXTRA_CMD_CONSOLE_READ_INIT) {
- read_console(&td);
- } else if (extra_command > -1) {
- send_subcommand(&td, extra_command,
- extra_command_data, extra_command_data_len,
- extra_command_answer, extra_command_answer_len);
-
- switch (extra_command) {
- case UPDATE_EXTRA_CMD_TOUCHPAD_INFO:
- dump_touchpad_info(extra_command_answer,
- extra_command_answer_len);
- break;
- case UPDATE_EXTRA_CMD_TOUCHPAD_DEBUG:
- hexdump(extra_command_answer, extra_command_answer_len);
- break;
- }
- }
-
- libusb_close(td.uep.devh);
- libusb_exit(NULL);
-
- if (!transferred_sections)
- return noop;
- /*
- * We should indicate if RO update was not done because of the
- * insufficient RW version.
- */
- for (j = 0; j < ARRAY_SIZE(sections); j++)
- if (sections[j].ustatus == not_possible) {
- /* This will allow scripting repeat attempts. */
- printf("Failed to update RO, run the command again\n");
- return rw_updated;
- }
-
- printf("image updated\n");
- return all_updated;
-}