summaryrefslogtreecommitdiff
path: root/extra
diff options
context:
space:
mode:
authorJack Rosenthal <jrosenth@chromium.org>2021-11-04 12:11:58 -0600
committerCommit Bot <commit-bot@chromium.org>2021-11-05 04:22:34 +0000
commit252457d4b21f46889eebad61d4c0a65331919cec (patch)
tree01856c4d31d710b20e85a74c8d7b5836e35c3b98 /extra
parent08f5a1e6fc2c9467230444ac9b582dcf4d9f0068 (diff)
downloadchrome-ec-stabilize-voshyr-14637.B-ish.tar.gz
In the interest of making long-term branch maintenance incur as little technical debt on us as possible, we should not maintain any files on the branch we are not actually using. This has the added effect of making it extremely clear when merging CLs from the main branch when changes have the possibility to affect us. The follow-on CL adds a convenience script to actually pull updates from the main branch and generate a CL for the update. BUG=b:204206272 BRANCH=ish TEST=make BOARD=arcada_ish && make BOARD=drallion_ish Signed-off-by: Jack Rosenthal <jrosenth@chromium.org> Change-Id: I17e4694c38219b5a0823e0a3e55a28d1348f4b18 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3262038 Reviewed-by: Jett Rink <jettrink@chromium.org> Reviewed-by: Tom Hughes <tomhughes@chromium.org>
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;
-}