=== added directory 'lava_dispatcher/actions/lmp'
=== added file 'lava_dispatcher/actions/lmp/__init__.py'
@@ -0,0 +1,1 @@
+__author__ = 'dpigott'
=== added file 'lava_dispatcher/actions/lmp/board.py'
@@ -0,0 +1,132 @@
+# Copyright (C) 2013 Linaro Limited
+#
+# Author: Dave Pigott <dave.pigott@linaro.org>
+#
+# This file is part of LAVA Dispatcher.
+#
+# LAVA Dispatcher is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# LAVA Dispatcher is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along
+# with this program; if not, see <http://www.gnu.org/licenses>.
+
+import serial
+import json
+import logging
+from serial import (
+ serialutil
+)
+from lava_dispatcher.errors import (
+ CriticalError,
+)
+
+class LAVALmpDeviceSerial(object):
+ def __init__(self, serialno, board_type):
+ device_map = {
+ "sdmux": "0a",
+ "sata": "19",
+ "lsgpio": "09",
+ "hdmi": "0c",
+ "usb": "04"
+ }
+ self.serialno = "LL" + device_map[board_type] + serialno.zfill(12)
+ logging.debug("LMP Serial #: %s" % self.serialno)
+ self.lmpType = "org.linaro.lmp." + board_type
+ self.board_type = board_type
+ try:
+ self.port = serial.Serial("/dev/serial/by-id/usb-Linaro_Ltd_LavaLMP_" + self.serialno + "-if00", timeout=1)
+ except serial.serialutil.SerialException as e:
+ logging.error("LMP: Error opening {0:s}: {1:s}".format(self.serialno, e))
+ raise
+ self.START_FRAME = '\x02'
+ self.END_FRAME = '\x04'
+ self.send_frame('{"schema":"org.linaro.lmp.info"}')
+ message = self.get_response("board")
+ if message['serial'] != self.serialno:
+ raise CriticalError("Lmp %s not connected" % serial)
+ # With the sdmux, we must wait until the device has switched to the requested state. Not all Lmp boards provide
+ # the required state information in the report
+ # TODO: Fix firmware so that they all do
+ if board_type == "sdmux":
+ self.wait_for_confirmation = True
+ else:
+ self.wait_for_confirmation = False
+
+ def send_command(self, mode, selection):
+ message = '{"schema":"' + self.lmpType + '",' + \
+ '"serial":"' + self.serialno + '",' + \
+ '"modes":[{"name":"' + mode + '",' + \
+ '"option":"' + selection + '"}]}'
+
+ self.send_frame(message)
+
+ if self.wait_for_confirmation:
+ device_in_mode = False
+
+ while not device_in_mode:
+ try:
+ response = self.get_frame()
+ except ValueError as e:
+ logging.warning("LMP Frame read error: %s" % e)
+ continue
+ else:
+ for i in response["report"]:
+ if i["name"] == "modes":
+ modes = dict(i)
+ for j in modes["modes"]:
+ state = dict(j)
+ if state["name"] == mode and state["mode"] == selection:
+ logging.debug("LMP %s: %s now in mode %s" % (self.board_type, mode, selection))
+ device_in_mode = True
+
+ def send_frame(self, command):
+ logging.debug("LMP: Sending %s" % command)
+ payload = self.START_FRAME + command + self.END_FRAME
+ self.port.write(payload)
+
+ def get_response(self, schema):
+ got_schema = False
+
+ result = self.get_frame()
+
+ while not got_schema:
+ if result['schema'] == "org.linaro.lmp." + schema:
+ got_schema = True
+ else:
+ result = self.get_frame()
+
+ return result
+
+ def get_frame(self):
+ char = self.port.read()
+
+ while char != self.START_FRAME:
+ char = self.port.read()
+
+ response = ""
+
+ while char != self.END_FRAME:
+ char = self.port.read()
+ if char != self.END_FRAME:
+ response += char
+
+ logging.debug("LMP: Got %s" % response)
+
+ return json.loads(response)
+
+ def close(self):
+ self.port.close()
+
+
+def lmp_send_command(serial, lmp_type, mode, state):
+ lmp = LAVALmpDeviceSerial(serial, lmp_type)
+ lmp.send_command(mode, state)
+ lmp.close()
=== added file 'lava_dispatcher/actions/lmp/ethsata.py'
@@ -0,0 +1,29 @@
+# Copyright (C) 2013 Linaro Limited
+#
+# Author: Dave Pigott <dave.pigott@linaro.org>
+#
+# This file is part of LAVA Dispatcher.
+#
+# LAVA Dispatcher is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# LAVA Dispatcher is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along
+# with this program; if not, see <http://www.gnu.org/licenses>.
+
+from lava_dispatcher.actions.lmp.board import lmp_send_command
+
+
+def disconnect(serial):
+ lmp_send_command(serial, "sata", "sata", "disconnect")
+
+
+def passthru(serial):
+ lmp_send_command(serial, "sata", "sata", "passthru")
=== added file 'lava_dispatcher/actions/lmp/hdmi.py'
@@ -0,0 +1,33 @@
+# Copyright (C) 2013 Linaro Limited
+#
+# Author: Dave Pigott <dave.pigott@linaro.org>
+#
+# This file is part of LAVA Dispatcher.
+#
+# LAVA Dispatcher is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# LAVA Dispatcher is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along
+# with this program; if not, see <http://www.gnu.org/licenses>.
+
+from lava_dispatcher.actions.lmp.board import lmp_send_command
+
+
+def disconnect(serial):
+ lmp_send_command(serial, "hdmi", "hdmi", "disconnect")
+
+
+def passthru(serial):
+ lmp_send_command(serial, "hdmi", "hdmi", "passthru")
+
+
+def fake(serial):
+ lmp_send_command(serial, "hdmi", "hdmi", "fake")
=== added file 'lava_dispatcher/actions/lmp/lsgpio.py'
@@ -0,0 +1,45 @@
+# Copyright (C) 2013 Linaro Limited
+#
+# Author: Dave Pigott <dave.pigott@linaro.org>
+#
+# This file is part of LAVA Dispatcher.
+#
+# LAVA Dispatcher is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# LAVA Dispatcher is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along
+# with this program; if not, see <http://www.gnu.org/licenses>.
+
+from lava_dispatcher.actions.lmp.board import lmp_send_command
+
+
+def audio_disconnect(serial):
+ lmp_send_command(serial, "lsgpio", "audio", "disconnect")
+
+
+def audio_passthru(serial):
+ lmp_send_command(serial, "lsgpio", "audio", "passthru")
+
+
+def a_dir_in(serial):
+ lmp_send_command(serial, "lsgpio", "a-dir", "in")
+
+
+def a_dir_out(serial):
+ lmp_send_command(serial, "lsgpio", "a-dir", "out")
+
+
+def b_dir_in(serial):
+ lmp_send_command(serial, "lsgpio", "b-dir", "in")
+
+
+def b_dir_out(serial):
+ lmp_send_command(serial, "lsgpio", "b-dir", "out")
=== added file 'lava_dispatcher/actions/lmp/sdmux.py'
@@ -0,0 +1,53 @@
+# Copyright (C) 2013 Linaro Limited
+#
+# Author: Dave Pigott <dave.pigott@linaro.org>
+#
+# This file is part of LAVA Dispatcher.
+#
+# LAVA Dispatcher is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# LAVA Dispatcher is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along
+# with this program; if not, see <http://www.gnu.org/licenses>.
+
+from lava_dispatcher.actions.lmp.board import lmp_send_command
+
+
+def dut_disconnect(serial):
+ lmp_send_command(serial, "sdmux", "dut", "disconnect")
+
+
+def dut_usda(serial):
+ lmp_send_command(serial, "sdmux", "dut", "uSDA")
+
+
+def dut_usdb(serial):
+ lmp_send_command(serial, "sdmux", "dut", "uSDB")
+
+
+def host_disconnect(serial):
+ lmp_send_command(serial, "sdmux", "host", "disconnect")
+
+
+def host_usda(serial):
+ lmp_send_command(serial, "sdmux", "host", "uSDA")
+
+
+def host_usdb(serial):
+ lmp_send_command(serial, "sdmux", "host", "uSDB")
+
+
+def dut_power_off(serial):
+ lmp_send_command(serial, "sdmux", "dut-power", "short-for-off")
+
+
+def dut_power_on(serial):
+ lmp_send_command(serial, "sdmux", "dut-power", "short-for-on")
=== added file 'lava_dispatcher/actions/lmp/usb.py'
@@ -0,0 +1,33 @@
+# Copyright (C) 2013 Linaro Limited
+#
+# Author: Dave Pigott <dave.pigott@linaro.org>
+#
+# This file is part of LAVA Dispatcher.
+#
+# LAVA Dispatcher is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# LAVA Dispatcher is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along
+# with this program; if not, see <http://www.gnu.org/licenses>.
+
+from lava_dispatcher.actions.lmp.board import lmp_send_command
+
+
+def device(serial):
+ lmp_send_command(serial, "usb", "usb", "device")
+
+
+def host(serial):
+ lmp_send_command(serial, "usb", "usb", "host")
+
+
+def disconnect(serial):
+ lmp_send_command(serial, "usb", "usb", "disconnect")
=== modified file 'lava_dispatcher/config.py'
@@ -81,6 +81,9 @@
testboot_offset = schema.IntOption(fatal=True)
# see doc/sdmux.rst for details
sdmux_id = schema.StringOption()
+ sdmux_usb_id = schema.StringOption()
+ sdmux_mount_retry_seconds = schema.IntOption(default=20)
+ sdmux_mount_wait_seconds = schema.IntOption(default=10)
sdmux_version = schema.StringOption(default="unknown")
simulator_version_command = schema.StringOption()
=== modified file 'lava_dispatcher/default-config/lava-dispatcher/device-defaults.conf'
@@ -133,3 +133,9 @@
# How many times the dispatcher should try to reboot master and test images before failing
boot_retries = 3
+
+# For an sdmux enabled device, maximum amount of time to wait for the /dev/sdX to appear after device has been switched
+# to host mode
+sdmux_mount_retry_seconds = 20
+# How long to wait after the /dev/sdX entry has turned up before trying to unmount anything attached to it
+sdmux_mount_wait_seconds = 10
=== modified file 'lava_dispatcher/device/sdmux.py'
@@ -1,6 +1,7 @@
-# Copyright (C) 2012 Linaro Limited
+# Copyright (C) 2012-2013 Linaro Limited
#
# Author: Andy Doan <andy.doan@linaro.org>
+# Dave Pigott <dave.pigott@linaro.org>
#
# This file is part of LAVA Dispatcher.
#
@@ -21,8 +22,10 @@
import contextlib
import logging
import os
+import glob
import subprocess
import time
+import lava_dispatcher.actions.lmp.sdmux as sdmux
from lava_dispatcher.errors import (
CriticalError,
@@ -64,26 +67,19 @@
This adds support for the "sd mux" device. An SD-MUX device is a piece of
hardware that allows the host and target to both connect to the same SD
card. The control of the SD card can then be toggled between the target
- and host via software. The schematics and pictures of this device can be
- found at:
- http://people.linaro.org/~doanac/sdmux/
-
- Documentation for setting this up is located under doc/sdmux.rst.
-
- NOTE: please read doc/sdmux.rst about kernel versions
- """
+ and host via software.
+"""
def __init__(self, context, config):
super(SDMuxTarget, self).__init__(context, config)
self.proc = None
+ if not config.sdmux_usb_id:
+ raise CriticalError('Device config requires "sdmux_usb_id"')
+
if not config.sdmux_id:
raise CriticalError('Device config requires "sdmux_id"')
- if not config.power_on_cmd:
- raise CriticalError('Device config requires "power_on_cmd"')
- if not config.power_off_cmd:
- raise CriticalError('Device config requires "power_off_cmd"')
if config.pre_connect_command:
self.context.run_command(config.pre_connect_command)
@@ -118,27 +114,19 @@
self._customize_android(img)
self._write_image(img)
- def _as_chunks(self, fname, bsize):
- with open(fname, 'r') as fd:
- while True:
- data = fd.read(bsize)
- if not data:
- break
- yield data
-
def _write_image(self, image):
+ sdmux.dut_disconnect(self.config.sdmux_id)
+ sdmux.host_usda(self.config.sdmux_id)
+
with self.mux_device() as device:
logging.info("dd'ing image to device (%s)", device)
- with open(device, 'w') as of:
- written = 0
- size = os.path.getsize(image)
- # 4M chunks work well for SD cards
- for chunk in self._as_chunks(image, 4 << 20):
- of.write(chunk)
- written += len(chunk)
- if written % (20 * (4 << 20)) == 0: # only log every 80MB
- logging.info("wrote %d of %d bytes", written, size)
- logging.info('closing %s, could take a while...', device)
+ dd_cmd = 'dd if=%s of=%s bs=4096 conv=fsync' % (image, device)
+ dd_proc = subprocess.Popen(dd_cmd, shell=True)
+ dd_proc.wait()
+ if dd_proc.returncode != 0:
+ raise CriticalError("Failed to dd image to device (Error code %d)" % dd_proc.returncode)
+
+ sdmux.host_disconnect(self.config.sdmux_id)
@contextlib.contextmanager
def mux_device(self):
@@ -153,23 +141,36 @@
the USB device connect to the sdmux will be powered off so that the
target will be able to safely access it.
"""
- muxid = self.config.sdmux_id
- source_dir = os.path.abspath(os.path.dirname(__file__))
- muxscript = os.path.join(source_dir, 'sdmux.sh')
- self.power_off(self.proc)
self.proc = None
- try:
- deventry = subprocess.check_output([muxscript, '-d', muxid, 'on'])
- deventry = deventry.strip()
- logging.info('returning sdmux device as: %s', deventry)
+ syspath = "/sys/bus/usb/devices/" + self.config.sdmux_usb_id + \
+ "/" + self.config.sdmux_usb_id + \
+ "*/host*/target*/*:0:0:0/block/*"
+
+ retrycount = 0
+ deventry = ""
+
+ while retrycount < self.config.sdmux_mount_retry_seconds:
+ device_list = glob.glob(syspath)
+ for device in device_list:
+ deventry = os.path.join("/dev/", os.path.basename(device))
+ break
+ if deventry != "":
+ break
+ time.sleep(1)
+ retrycount += 1
+
+ if deventry != "":
+ logging.debug('found sdmux device %s: Waiting %ds for any mounts to complete'
+ % (deventry, self.config.sdmux_mount_wait_seconds))
+ time.sleep(self.config.sdmux_mount_wait_seconds)
+ logging.debug("Unmounting %s*", deventry)
+ os.system("umount %s*" % deventry)
+ logging.debug('returning sdmux device as: %s', deventry)
yield deventry
- except subprocess.CalledProcessError:
+ else:
raise CriticalError('Unable to access sdmux device')
- finally:
- logging.info('powering off sdmux')
- self.context.run_command([muxscript, '-d', muxid, 'off'], failok=False)
@contextlib.contextmanager
def file_system(self, partition, directory):
@@ -219,10 +220,14 @@
def power_off(self, proc):
super(SDMuxTarget, self).power_off(proc)
self.context.run_command(self.config.power_off_cmd)
+ sdmux.dut_disconnect(self.config.sdmux_id)
def power_on(self):
self.proc = connect_to_serial(self.context)
+ sdmux.host_disconnect(self.config.sdmux_id)
+ sdmux.dut_usda(self.config.sdmux_id)
+
logging.info('powering on')
self.context.run_command(self.config.power_on_cmd)
=== removed file 'lava_dispatcher/device/sdmux.sh'
@@ -1,79 +0,0 @@
-#!/bin/bash
-# based on https://github.com/liyan/suspend-usb-device
-
-#set -e
-
-usage()
-{
- cat<<EOF
-This script will turn on/off power to a USB port. Its being
-used in conjunction with the SD Mux device.
-
-Power on/off a device or find its /dev/sdX with:
- $0 -d device_id on|off|deventry
-
-Find the device ID from a /dev/entry with
-$0 -f /dev/sdX
-
-EOF
-}
-
-while getopts "f:d:" opt; do
- case $opt in
- f) DEV=$OPTARG ;;
- d) ID=$OPTARG ;;
- \?) usage ; exit 1 ;;
- esac
-done
-
-if [ -n "$DEV" ] ; then
- echo "Finding id for $DEV"
- DEVICE=$(udevadm info --query=path --name=${DEV} --attribute-walk | \
- egrep "looking at parent device" | head -1 | \
- sed -e "s/.*looking at parent device '\(\/devices\/.*\)\/.*\/host.*/\1/g")
-
- if [ -z $DEVICE ]; then
- 1>&2 echo "cannot find appropriate parent USB device, "
- 1>&2 echo "perhaps ${DEV} is not an USB device?"
- exit 1
- fi
-
- # the trailing basename of ${DEVICE} is DEV_BUS_ID
- DEV_BUS_ID=${DEVICE##*/}
- echo Device: ${DEVICE}
- echo Bus ID: ${DEV_BUS_ID}
-
-elif [ -n "$ID" ] ; then
- ACTION=${!OPTIND:-}
- DIR=/sys/bus/usb/devices/${ID}/${ID}*/host*/target*/*:0:0:0/block
- if [ $ACTION == "on" ] ; then
- if [ -d $DIR ] ; then
- echo "<sdmux script> already on" 1>&2
- else
- echo -n "${ID}" > /sys/bus/usb/drivers/usb/bind
- sleep 4
- fi
- device_path=`ls $DIR 2>/dev/null`
- if [ $? -ne 0 ] ; then
- echo "<sdmux script> No sdmux found at ${DIR}" 1>&2
- exit 1
- fi
- echo /dev/${device_path}
-
- elif [ $ACTION = "off" ] ; then
- echo "<sdmux script> Powering off sdmux: $ID"
- echo -n "${ID}" > /sys/bus/usb/drivers/usb/unbind
- sleep 1
- echo -n '0' > /sys/bus/usb/devices/$ID/power/autosuspend_delay_ms
- sleep 1
- echo -n 'auto' > /sys/bus/usb/devices/$ID/power/control
- sleep 2
- elif [ $ACTION = "deventry" ] ; then
- echo /dev/`ls $DIR`
- else
- echo "ERROR: Action must be on/off"
- usage; exit 1
- fi
-else
- usage
-fi
=== modified file 'setup.py'
@@ -53,6 +53,7 @@
"configglue",
"PyYAML",
'versiontools >= 1.8',
+ "pyserial",
],
setup_requires=[
'versiontools >= 1.8',