Merge lp:~dpigott/lava-dispatcher/add-sdmux-support into lp:lava-dispatcher

Proposed by Dave Pigott
Status: Rejected
Rejected by: Neil Williams
Proposed branch: lp:~dpigott/lava-dispatcher/add-sdmux-support
Merge into: lp:lava-dispatcher
Diff against target: 436 lines (+291/-35)
5 files modified
lava_dispatcher/actions/lava_lmp.py (+230/-0)
lava_dispatcher/config.py (+1/-0)
lava_dispatcher/default-config/lava-dispatcher/devices/beaglebone-black-01.conf (+11/-0)
lava_dispatcher/device/sdmux.py (+48/-35)
setup.py (+1/-0)
To merge this branch: bzr merge lp:~dpigott/lava-dispatcher/add-sdmux-support
Reviewer Review Type Date Requested Status
Tyler Baker Approve
Review via email: mp+179195@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Tyler Baker (tyler-baker) wrote :

My only concern is that this will remove bootloader support from beaglebone-black. As all the master image devices are now using client_type = bootloader. This is not a big deal, I'll approve the merge, but lets work together to find a way that master / bootloader/ and sdmux can use the same client_type.

I would recommend rebasing these changes on the tip before the merge, in case their are any conflicts LP is not picking up.

* Do you want the power_on_cmd and power_off_cmd to be defined in that manner?

review: Approve
Revision history for this message
Dave Pigott (dpigott) wrote :

The more I think about it, the more I agree that maybe we should work to integrate this so that if sdmux_id is defined, we use an sdmux, and then we can use client_type = bootloader. I'll look at doing this today.

Revision history for this message
Antonio Terceiro (terceiro) wrote :

I was going to review this merge but seeing the existing comments I think I will wait for the new version.

Unmerged revisions

654. By Dave Pigott

Put board map into init

653. By Dave Pigott

Up mount timeout for safety and do a lookup on the board type id

652. By Dave Pigott

Add mount check code and remove need for sleep(10) calls

651. By Dave Pigott

Handle frame read errors

650. By Dave Pigott

Add changes to sense when board is in correct mode

649. By Dave Pigott

Clean up API and re-structure

648. By Dave Pigott

Remove redundant timeouts

647. By Dave Pigott

Fixes for direct dd call

646. By Dave Pigott

switch to dd

645. By Dave Pigott

Add other lmp stack control

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'lava_dispatcher/actions/lava_lmp.py'
--- lava_dispatcher/actions/lava_lmp.py 1970-01-01 00:00:00 +0000
+++ lava_dispatcher/actions/lava_lmp.py 2013-08-08 13:55:13 +0000
@@ -0,0 +1,230 @@
1# Copyright (C) 2013 Linaro Limited
2#
3# Author: Dave Pigott <dave.pigott@linaro.org>
4#
5# This file is part of LAVA Dispatcher.
6#
7# LAVA Dispatcher is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 2 of the License, or
10# (at your option) any later version.
11#
12# LAVA Dispatcher is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along
19# with this program; if not, see <http://www.gnu.org/licenses>.
20
21import serial
22import json
23import logging
24from serial import (
25 serialutil
26)
27from lava_dispatcher.errors import (
28 CriticalError,
29)
30
31class LAVALmpDeviceSerial(object):
32 def __init__(self, serialno, board_type):
33 device_map = {
34 "sdmux": "0a",
35 "sata": "19",
36 "lsgpio": "09",
37 "hdmi": "0c",
38 "usb": "04"
39 }
40 self.serialno = "LL" + device_map[board_type] + serialno.zfill(12)
41 logging.debug("LMP Serial #: %s" % self.serialno)
42 self.lmpType = "org.linaro.lmp." + board_type
43 self.board_type = board_type
44 try:
45 self.port = serial.Serial("/dev/serial/by-id/usb-Linaro_Ltd_LavaLMP_" + self.serialno + "-if00", timeout=1)
46 except serial.serialutil.SerialException as e:
47 logging.error("LMP: Error opening {0:s}: {1:s}".format(self.serialno, e))
48 raise
49 self.START_FRAME = '\x02'
50 self.END_FRAME = '\x04'
51 self.sendFrame('{"schema":"org.linaro.lmp.info"}')
52 message = self.getResponse("board")
53 if message['serial'] != self.serialno:
54 raise CriticalError("Lmp %s not connected" % serial)
55
56 def sendCommand(self, mode, selection):
57 message = '{"schema":"' + self.lmpType + '",' + \
58 '"serial":"' + self.serialno + '",' + \
59 '"modes":[{"name":"' + mode + '",' + \
60 '"option":"' + selection + '"}]}'
61 self.sendFrame(message)
62 device_in_mode = False
63 while not device_in_mode:
64 try:
65 response = self.getFrame()
66 except ValueError as e:
67 logging.warning("LMP Frame read error: %s" % e)
68 continue
69 else:
70 for i in response["report"]:
71 if i["name"] == "modes":
72 modes = dict(i)
73 for j in modes["modes"]:
74 state = dict(j)
75 if state["name"] == mode and state["mode"] == selection:
76 logging.debug("LMP %s: %s now in mode %s" % (self.board_type, mode, selection))
77 device_in_mode = True
78
79 def sendFrame(self, command):
80 logging.debug("LMP: Sending %s" % command)
81 payload = self.START_FRAME + command + self.END_FRAME
82 self.port.write(payload)
83
84 def getResponse(self, schema):
85 got_schema = False
86
87 result = self.getFrame()
88
89 while not got_schema:
90 if result['schema'] == "org.linaro.lmp." + schema:
91 got_schema = True
92 else:
93 result = self.getFrame()
94
95 return result
96
97 def getFrame(self):
98 char = self.port.read()
99
100 while char != self.START_FRAME:
101 char = self.port.read()
102
103 response = ""
104
105 while char != self.END_FRAME:
106 char = self.port.read()
107 if char != self.END_FRAME:
108 response += char
109
110 logging.debug("LMP: Got %s" % response)
111
112 return json.loads(response)
113
114 def close(self):
115 self.port.close()
116
117
118class LAVALmpSDMux(LAVALmpDeviceSerial):
119 def __init__(self, serialno):
120 super(LAVALmpSDMux, self).__init__(serialno, "sdmux")
121
122 def dutDisconnect(self):
123 self.sendCommand("dut", "disconnect")
124
125 def dutuSDA(self):
126 self.sendCommand("dut", "uSDA")
127
128 def dutuSDB(self):
129 self.sendCommand("dut", "uSDB")
130
131 def hostDisconnect(self):
132 self.sendCommand("host", "disconnect")
133
134 def hostuSDA(self):
135 self.sendCommand("host", "uSDA")
136
137 def hostuSDB(self):
138 self.sendCommand("host", "uSDB")
139
140 def dutPowerShortForOff(self):
141 self.sendCommand("dut-power", "short-for-off")
142
143 def dutPowerShortForOn(self):
144 self.sendCommand("dut-power", "short-for-on")
145
146
147def lmpSdmux_dutDisconnect(serial):
148 sdmux = LAVALmpSDMux(serial)
149 sdmux.dutDisconnect()
150 sdmux.close()
151
152
153def lmpSdmux_dutuSDA(serial):
154 sdmux = LAVALmpSDMux(serial)
155 sdmux.dutuSDA()
156 sdmux.close()
157
158
159def lmpSdmux_hostDisconnect(serial):
160 sdmux = LAVALmpSDMux(serial)
161 sdmux.hostDisconnect()
162 sdmux.close()
163
164
165def lmpSdmux_hostuSDA(serial):
166 sdmux = LAVALmpSDMux(serial)
167 sdmux.hostuSDA()
168 sdmux.close()
169
170
171class LAVALmpEthSata(LAVALmpDeviceSerial):
172 def __init__(self, serialno):
173 super(LAVALmpEthSata, self).__init__(serialno, "sata")
174
175 def dutDisconnect(self):
176 self.sendCommand("sata", "disconnect")
177
178 def dutPassthru(self):
179 self.sendCommand("sata", "passthru")
180
181
182class LAVALmpLsgpio(LAVALmpDeviceSerial):
183 def __init__(self, serialno):
184 super(LAVALmpLsgpio, self).__init__(serialno, "lsgpio")
185
186 def audioDisconnect(self):
187 self.sendCommand("audio", "disconnect")
188
189 def audioPassthru(self):
190 self.sendCommand("audio", "passthru")
191
192 def aDirIn(self):
193 self.sendCommand("a-dir", "in")
194
195 def aDirOut(self):
196 self.sendCommand("a-dir", "out")
197
198 def bDirIn(self):
199 self.sendCommand("b-dir", "in")
200
201 def bDirOut(self):
202 self.sendCommand("b-dir", "out")
203
204
205class LAVALmpHdmi(LAVALmpDeviceSerial):
206 def __init__(self, serialno):
207 super(LAVALmpHdmi, self).__init__(serialno, "hdmi")
208
209 def hdmiDisconnect(self):
210 self.sendCommand("hdmi", "disconnect")
211
212 def hdmiPassthru(self):
213 self.sendCommand("hdmi", "passthru")
214
215 def hdmiFake(self):
216 self.sendCommand("hdmi", "fake")
217
218
219class LAVALmpUsb(LAVALmpDeviceSerial):
220 def __init__(self, serialno):
221 super(LAVALmpUsb, self).__init__(serialno, "usb")
222
223 def usbDevice(self):
224 self.sendCommand("usb", "device")
225
226 def usbHost(self):
227 self.sendCommand("usb", "host")
228
229 def usbDisconnect(self):
230 self.sendCommand("usb", "disconnect")
0231
=== modified file 'lava_dispatcher/config.py'
--- lava_dispatcher/config.py 2013-07-22 21:18:32 +0000
+++ lava_dispatcher/config.py 2013-08-08 13:55:13 +0000
@@ -80,6 +80,7 @@
80 testboot_offset = schema.IntOption(fatal=True)80 testboot_offset = schema.IntOption(fatal=True)
81 # see doc/sdmux.rst for details81 # see doc/sdmux.rst for details
82 sdmux_id = schema.StringOption()82 sdmux_id = schema.StringOption()
83 sdmux_usb_id = schema.StringOption()
83 sdmux_version = schema.StringOption(default="unknown")84 sdmux_version = schema.StringOption(default="unknown")
8485
85 simulator_version_command = schema.StringOption()86 simulator_version_command = schema.StringOption()
8687
=== added file 'lava_dispatcher/default-config/lava-dispatcher/devices/beaglebone-black-01.conf'
--- lava_dispatcher/default-config/lava-dispatcher/devices/beaglebone-black-01.conf 1970-01-01 00:00:00 +0000
+++ lava_dispatcher/default-config/lava-dispatcher/devices/beaglebone-black-01.conf 2013-08-08 13:55:13 +0000
@@ -0,0 +1,11 @@
1device_type = beaglebone-black
2hostname = beaglebone-black-01
3client_type = sdmux
4
5sdmux_id = 13060055
6sdmux_usb_id = 1-1
7sdmux_version = LAVA-Lmp-Rev3a
8
9connection_command = telnet localhost 7001
10power_on_cmd = echo "Please power on the device"
11power_off_cmd = echo "Please power the device down"
012
=== modified file 'lava_dispatcher/device/sdmux.py'
--- lava_dispatcher/device/sdmux.py 2013-07-16 16:04:07 +0000
+++ lava_dispatcher/device/sdmux.py 2013-08-08 13:55:13 +0000
@@ -1,6 +1,7 @@
1# Copyright (C) 2012 Linaro Limited1# Copyright (C) 2012-2013 Linaro Limited
2#2#
3# Author: Andy Doan <andy.doan@linaro.org>3# Author: Andy Doan <andy.doan@linaro.org>
4# Dave Pigott <dave.pigott@linaro.org>
4#5#
5# This file is part of LAVA Dispatcher.6# This file is part of LAVA Dispatcher.
6#7#
@@ -21,6 +22,7 @@
21import contextlib22import contextlib
22import logging23import logging
23import os24import os
25import glob
24import subprocess26import subprocess
25import time27import time
2628
@@ -43,6 +45,12 @@
43 ensure_directory,45 ensure_directory,
44 extract_targz,46 extract_targz,
45)47)
48from lava_dispatcher.actions.lava_lmp import (
49 lmpSdmux_dutDisconnect,
50 lmpSdmux_dutuSDA,
51 lmpSdmux_hostDisconnect,
52 lmpSdmux_hostuSDA
53)
4654
4755
48def _flush_files(mntdir):56def _flush_files(mntdir):
@@ -64,26 +72,19 @@
64 This adds support for the "sd mux" device. An SD-MUX device is a piece of72 This adds support for the "sd mux" device. An SD-MUX device is a piece of
65 hardware that allows the host and target to both connect to the same SD73 hardware that allows the host and target to both connect to the same SD
66 card. The control of the SD card can then be toggled between the target74 card. The control of the SD card can then be toggled between the target
67 and host via software. The schematics and pictures of this device can be75 and host via software.
68 found at:76"""
69 http://people.linaro.org/~doanac/sdmux/
70
71 Documentation for setting this up is located under doc/sdmux.rst.
72
73 NOTE: please read doc/sdmux.rst about kernel versions
74 """
7577
76 def __init__(self, context, config):78 def __init__(self, context, config):
77 super(SDMuxTarget, self).__init__(context, config)79 super(SDMuxTarget, self).__init__(context, config)
7880
79 self.proc = None81 self.proc = None
8082
83 if not config.sdmux_usb_id:
84 raise CriticalError('Device config requires "sdmux_usb_id"')
85
81 if not config.sdmux_id:86 if not config.sdmux_id:
82 raise CriticalError('Device config requires "sdmux_id"')87 raise CriticalError('Device config requires "sdmux_id"')
83 if not config.power_on_cmd:
84 raise CriticalError('Device config requires "power_on_cmd"')
85 if not config.power_off_cmd:
86 raise CriticalError('Device config requires "power_off_cmd"')
8788
88 if config.pre_connect_command:89 if config.pre_connect_command:
89 self.context.run_command(config.pre_connect_command)90 self.context.run_command(config.pre_connect_command)
@@ -119,26 +120,25 @@
119 self._write_image(img)120 self._write_image(img)
120121
121 def _as_chunks(self, fname, bsize):122 def _as_chunks(self, fname, bsize):
122 with open(fname, 'r') as fd:123 with open(fname, 'rb') as fd:
123 while True:124 while True:
124 data = fd.read(bsize)125 data = fd.read(bsize)
125 if not data:126 if not data:
126 break127 break
127 yield data128 yield data
129 fd.close()
128130
129 def _write_image(self, image):131 def _write_image(self, image):
132 lmpSdmux_dutDisconnect(self.config.sdmux_id)
133 lmpSdmux_hostuSDA(self.config.sdmux_id)
134
130 with self.mux_device() as device:135 with self.mux_device() as device:
131 logging.info("dd'ing image to device (%s)", device)136 logging.info("dd'ing image to device (%s)", device)
132 with open(device, 'w') as of:137 dd_cmd = 'dd if=%s of=%s bs=4096 conv=fsync' % (image, device)
133 written = 0138 dd_proc = subprocess.Popen(dd_cmd, shell=True)
134 size = os.path.getsize(image)139 dd_proc.wait()
135 # 4M chunks work well for SD cards140
136 for chunk in self._as_chunks(image, 4 << 20):141 lmpSdmux_hostDisconnect(self.config.sdmux_id)
137 of.write(chunk)
138 written += len(chunk)
139 if written % (20 * (4 << 20)) == 0: # only log every 80MB
140 logging.info("wrote %d of %d bytes", written, size)
141 logging.info('closing %s, could take a while...', device)
142142
143 @contextlib.contextmanager143 @contextlib.contextmanager
144 def mux_device(self):144 def mux_device(self):
@@ -153,23 +153,32 @@
153 the USB device connect to the sdmux will be powered off so that the153 the USB device connect to the sdmux will be powered off so that the
154 target will be able to safely access it.154 target will be able to safely access it.
155 """155 """
156 muxid = self.config.sdmux_id
157 source_dir = os.path.abspath(os.path.dirname(__file__))
158 muxscript = os.path.join(source_dir, 'sdmux.sh')
159156
160 self.power_off(self.proc)
161 self.proc = None157 self.proc = None
162158
163 try:159 syspath = "/sys/bus/usb/devices/" + self.config.sdmux_usb_id + \
164 deventry = subprocess.check_output([muxscript, '-d', muxid, 'on'])160 "/" + self.config.sdmux_usb_id + \
165 deventry = deventry.strip()161 "*/host*/target*/*:0:0:0/block/*"
162
163 retrycount = 0
164
165 while retrycount < 20:
166 device_list = glob.glob(syspath)
167 deventry = ""
168 for device in device_list:
169 deventry = os.path.join("/dev/", os.path.basename(device))
170 if deventry != "":
171 break
172 time.sleep(1)
173 retrycount += 1
174
175 if deventry != "":
166 logging.info('returning sdmux device as: %s', deventry)176 logging.info('returning sdmux device as: %s', deventry)
177 os.system("umount /media/rootfs")
178 os.system("umount /media/boot")
167 yield deventry179 yield deventry
168 except subprocess.CalledProcessError:180 else:
169 raise CriticalError('Unable to access sdmux device')181 raise CriticalError('Unable to access sdmux device')
170 finally:
171 logging.info('powering off sdmux')
172 self.context.run_command([muxscript, '-d', muxid, 'off'], failok=False)
173182
174 @contextlib.contextmanager183 @contextlib.contextmanager
175 def file_system(self, partition, directory):184 def file_system(self, partition, directory):
@@ -219,10 +228,14 @@
219 def power_off(self, proc):228 def power_off(self, proc):
220 super(SDMuxTarget, self).power_off(proc)229 super(SDMuxTarget, self).power_off(proc)
221 self.context.run_command(self.config.power_off_cmd)230 self.context.run_command(self.config.power_off_cmd)
231 lmpSdmux_dutDisconnect(self.config.sdmux_id)
222232
223 def power_on(self):233 def power_on(self):
224 self.proc = connect_to_serial(self.context)234 self.proc = connect_to_serial(self.context)
225235
236 lmpSdmux_hostDisconnect(self.config.sdmux_id)
237 lmpSdmux_dutuSDA(self.config.sdmux_id)
238
226 logging.info('powering on')239 logging.info('powering on')
227 self.context.run_command(self.config.power_on_cmd)240 self.context.run_command(self.config.power_on_cmd)
228241
229242
=== modified file 'setup.py'
--- setup.py 2013-07-17 09:16:24 +0000
+++ setup.py 2013-08-08 13:55:13 +0000
@@ -53,6 +53,7 @@
53 "configglue",53 "configglue",
54 "PyYAML",54 "PyYAML",
55 'versiontools >= 1.8',55 'versiontools >= 1.8',
56 "pyserial",
56 ],57 ],
57 setup_requires=[58 setup_requires=[
58 'versiontools >= 1.8',59 'versiontools >= 1.8',

Subscribers

People subscribed via source and target branches