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

Proposed by Dave Pigott on 2013-08-08
Status: Rejected
Rejected by: Neil Williams on 2014-07-02
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 2013-08-08 Approve on 2013-08-22
Review via email: mp+179195@code.launchpad.net
To post a comment you must log in.
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
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.

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 on 2013-08-08

Put board map into init

653. By Dave Pigott on 2013-08-08

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

652. By Dave Pigott on 2013-08-08

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

651. By Dave Pigott on 2013-08-07

Handle frame read errors

650. By Dave Pigott on 2013-08-07

Add changes to sense when board is in correct mode

649. By Dave Pigott on 2013-08-07

Clean up API and re-structure

648. By Dave Pigott on 2013-08-07

Remove redundant timeouts

647. By Dave Pigott on 2013-08-05

Fixes for direct dd call

646. By Dave Pigott on 2013-08-05

switch to dd

645. By Dave Pigott on 2013-07-30

Add other lmp stack control

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'lava_dispatcher/actions/lava_lmp.py'
2--- lava_dispatcher/actions/lava_lmp.py 1970-01-01 00:00:00 +0000
3+++ lava_dispatcher/actions/lava_lmp.py 2013-08-08 13:55:13 +0000
4@@ -0,0 +1,230 @@
5+# Copyright (C) 2013 Linaro Limited
6+#
7+# Author: Dave Pigott <dave.pigott@linaro.org>
8+#
9+# This file is part of LAVA Dispatcher.
10+#
11+# LAVA Dispatcher is free software; you can redistribute it and/or modify
12+# it under the terms of the GNU General Public License as published by
13+# the Free Software Foundation; either version 2 of the License, or
14+# (at your option) any later version.
15+#
16+# LAVA Dispatcher is distributed in the hope that it will be useful,
17+# but WITHOUT ANY WARRANTY; without even the implied warranty of
18+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+# GNU General Public License for more details.
20+#
21+# You should have received a copy of the GNU General Public License
22+# along
23+# with this program; if not, see <http://www.gnu.org/licenses>.
24+
25+import serial
26+import json
27+import logging
28+from serial import (
29+ serialutil
30+)
31+from lava_dispatcher.errors import (
32+ CriticalError,
33+)
34+
35+class LAVALmpDeviceSerial(object):
36+ def __init__(self, serialno, board_type):
37+ device_map = {
38+ "sdmux": "0a",
39+ "sata": "19",
40+ "lsgpio": "09",
41+ "hdmi": "0c",
42+ "usb": "04"
43+ }
44+ self.serialno = "LL" + device_map[board_type] + serialno.zfill(12)
45+ logging.debug("LMP Serial #: %s" % self.serialno)
46+ self.lmpType = "org.linaro.lmp." + board_type
47+ self.board_type = board_type
48+ try:
49+ self.port = serial.Serial("/dev/serial/by-id/usb-Linaro_Ltd_LavaLMP_" + self.serialno + "-if00", timeout=1)
50+ except serial.serialutil.SerialException as e:
51+ logging.error("LMP: Error opening {0:s}: {1:s}".format(self.serialno, e))
52+ raise
53+ self.START_FRAME = '\x02'
54+ self.END_FRAME = '\x04'
55+ self.sendFrame('{"schema":"org.linaro.lmp.info"}')
56+ message = self.getResponse("board")
57+ if message['serial'] != self.serialno:
58+ raise CriticalError("Lmp %s not connected" % serial)
59+
60+ def sendCommand(self, mode, selection):
61+ message = '{"schema":"' + self.lmpType + '",' + \
62+ '"serial":"' + self.serialno + '",' + \
63+ '"modes":[{"name":"' + mode + '",' + \
64+ '"option":"' + selection + '"}]}'
65+ self.sendFrame(message)
66+ device_in_mode = False
67+ while not device_in_mode:
68+ try:
69+ response = self.getFrame()
70+ except ValueError as e:
71+ logging.warning("LMP Frame read error: %s" % e)
72+ continue
73+ else:
74+ for i in response["report"]:
75+ if i["name"] == "modes":
76+ modes = dict(i)
77+ for j in modes["modes"]:
78+ state = dict(j)
79+ if state["name"] == mode and state["mode"] == selection:
80+ logging.debug("LMP %s: %s now in mode %s" % (self.board_type, mode, selection))
81+ device_in_mode = True
82+
83+ def sendFrame(self, command):
84+ logging.debug("LMP: Sending %s" % command)
85+ payload = self.START_FRAME + command + self.END_FRAME
86+ self.port.write(payload)
87+
88+ def getResponse(self, schema):
89+ got_schema = False
90+
91+ result = self.getFrame()
92+
93+ while not got_schema:
94+ if result['schema'] == "org.linaro.lmp." + schema:
95+ got_schema = True
96+ else:
97+ result = self.getFrame()
98+
99+ return result
100+
101+ def getFrame(self):
102+ char = self.port.read()
103+
104+ while char != self.START_FRAME:
105+ char = self.port.read()
106+
107+ response = ""
108+
109+ while char != self.END_FRAME:
110+ char = self.port.read()
111+ if char != self.END_FRAME:
112+ response += char
113+
114+ logging.debug("LMP: Got %s" % response)
115+
116+ return json.loads(response)
117+
118+ def close(self):
119+ self.port.close()
120+
121+
122+class LAVALmpSDMux(LAVALmpDeviceSerial):
123+ def __init__(self, serialno):
124+ super(LAVALmpSDMux, self).__init__(serialno, "sdmux")
125+
126+ def dutDisconnect(self):
127+ self.sendCommand("dut", "disconnect")
128+
129+ def dutuSDA(self):
130+ self.sendCommand("dut", "uSDA")
131+
132+ def dutuSDB(self):
133+ self.sendCommand("dut", "uSDB")
134+
135+ def hostDisconnect(self):
136+ self.sendCommand("host", "disconnect")
137+
138+ def hostuSDA(self):
139+ self.sendCommand("host", "uSDA")
140+
141+ def hostuSDB(self):
142+ self.sendCommand("host", "uSDB")
143+
144+ def dutPowerShortForOff(self):
145+ self.sendCommand("dut-power", "short-for-off")
146+
147+ def dutPowerShortForOn(self):
148+ self.sendCommand("dut-power", "short-for-on")
149+
150+
151+def lmpSdmux_dutDisconnect(serial):
152+ sdmux = LAVALmpSDMux(serial)
153+ sdmux.dutDisconnect()
154+ sdmux.close()
155+
156+
157+def lmpSdmux_dutuSDA(serial):
158+ sdmux = LAVALmpSDMux(serial)
159+ sdmux.dutuSDA()
160+ sdmux.close()
161+
162+
163+def lmpSdmux_hostDisconnect(serial):
164+ sdmux = LAVALmpSDMux(serial)
165+ sdmux.hostDisconnect()
166+ sdmux.close()
167+
168+
169+def lmpSdmux_hostuSDA(serial):
170+ sdmux = LAVALmpSDMux(serial)
171+ sdmux.hostuSDA()
172+ sdmux.close()
173+
174+
175+class LAVALmpEthSata(LAVALmpDeviceSerial):
176+ def __init__(self, serialno):
177+ super(LAVALmpEthSata, self).__init__(serialno, "sata")
178+
179+ def dutDisconnect(self):
180+ self.sendCommand("sata", "disconnect")
181+
182+ def dutPassthru(self):
183+ self.sendCommand("sata", "passthru")
184+
185+
186+class LAVALmpLsgpio(LAVALmpDeviceSerial):
187+ def __init__(self, serialno):
188+ super(LAVALmpLsgpio, self).__init__(serialno, "lsgpio")
189+
190+ def audioDisconnect(self):
191+ self.sendCommand("audio", "disconnect")
192+
193+ def audioPassthru(self):
194+ self.sendCommand("audio", "passthru")
195+
196+ def aDirIn(self):
197+ self.sendCommand("a-dir", "in")
198+
199+ def aDirOut(self):
200+ self.sendCommand("a-dir", "out")
201+
202+ def bDirIn(self):
203+ self.sendCommand("b-dir", "in")
204+
205+ def bDirOut(self):
206+ self.sendCommand("b-dir", "out")
207+
208+
209+class LAVALmpHdmi(LAVALmpDeviceSerial):
210+ def __init__(self, serialno):
211+ super(LAVALmpHdmi, self).__init__(serialno, "hdmi")
212+
213+ def hdmiDisconnect(self):
214+ self.sendCommand("hdmi", "disconnect")
215+
216+ def hdmiPassthru(self):
217+ self.sendCommand("hdmi", "passthru")
218+
219+ def hdmiFake(self):
220+ self.sendCommand("hdmi", "fake")
221+
222+
223+class LAVALmpUsb(LAVALmpDeviceSerial):
224+ def __init__(self, serialno):
225+ super(LAVALmpUsb, self).__init__(serialno, "usb")
226+
227+ def usbDevice(self):
228+ self.sendCommand("usb", "device")
229+
230+ def usbHost(self):
231+ self.sendCommand("usb", "host")
232+
233+ def usbDisconnect(self):
234+ self.sendCommand("usb", "disconnect")
235
236=== modified file 'lava_dispatcher/config.py'
237--- lava_dispatcher/config.py 2013-07-22 21:18:32 +0000
238+++ lava_dispatcher/config.py 2013-08-08 13:55:13 +0000
239@@ -80,6 +80,7 @@
240 testboot_offset = schema.IntOption(fatal=True)
241 # see doc/sdmux.rst for details
242 sdmux_id = schema.StringOption()
243+ sdmux_usb_id = schema.StringOption()
244 sdmux_version = schema.StringOption(default="unknown")
245
246 simulator_version_command = schema.StringOption()
247
248=== added file 'lava_dispatcher/default-config/lava-dispatcher/devices/beaglebone-black-01.conf'
249--- lava_dispatcher/default-config/lava-dispatcher/devices/beaglebone-black-01.conf 1970-01-01 00:00:00 +0000
250+++ lava_dispatcher/default-config/lava-dispatcher/devices/beaglebone-black-01.conf 2013-08-08 13:55:13 +0000
251@@ -0,0 +1,11 @@
252+device_type = beaglebone-black
253+hostname = beaglebone-black-01
254+client_type = sdmux
255+
256+sdmux_id = 13060055
257+sdmux_usb_id = 1-1
258+sdmux_version = LAVA-Lmp-Rev3a
259+
260+connection_command = telnet localhost 7001
261+power_on_cmd = echo "Please power on the device"
262+power_off_cmd = echo "Please power the device down"
263
264=== modified file 'lava_dispatcher/device/sdmux.py'
265--- lava_dispatcher/device/sdmux.py 2013-07-16 16:04:07 +0000
266+++ lava_dispatcher/device/sdmux.py 2013-08-08 13:55:13 +0000
267@@ -1,6 +1,7 @@
268-# Copyright (C) 2012 Linaro Limited
269+# Copyright (C) 2012-2013 Linaro Limited
270 #
271 # Author: Andy Doan <andy.doan@linaro.org>
272+# Dave Pigott <dave.pigott@linaro.org>
273 #
274 # This file is part of LAVA Dispatcher.
275 #
276@@ -21,6 +22,7 @@
277 import contextlib
278 import logging
279 import os
280+import glob
281 import subprocess
282 import time
283
284@@ -43,6 +45,12 @@
285 ensure_directory,
286 extract_targz,
287 )
288+from lava_dispatcher.actions.lava_lmp import (
289+ lmpSdmux_dutDisconnect,
290+ lmpSdmux_dutuSDA,
291+ lmpSdmux_hostDisconnect,
292+ lmpSdmux_hostuSDA
293+)
294
295
296 def _flush_files(mntdir):
297@@ -64,26 +72,19 @@
298 This adds support for the "sd mux" device. An SD-MUX device is a piece of
299 hardware that allows the host and target to both connect to the same SD
300 card. The control of the SD card can then be toggled between the target
301- and host via software. The schematics and pictures of this device can be
302- found at:
303- http://people.linaro.org/~doanac/sdmux/
304-
305- Documentation for setting this up is located under doc/sdmux.rst.
306-
307- NOTE: please read doc/sdmux.rst about kernel versions
308- """
309+ and host via software.
310+"""
311
312 def __init__(self, context, config):
313 super(SDMuxTarget, self).__init__(context, config)
314
315 self.proc = None
316
317+ if not config.sdmux_usb_id:
318+ raise CriticalError('Device config requires "sdmux_usb_id"')
319+
320 if not config.sdmux_id:
321 raise CriticalError('Device config requires "sdmux_id"')
322- if not config.power_on_cmd:
323- raise CriticalError('Device config requires "power_on_cmd"')
324- if not config.power_off_cmd:
325- raise CriticalError('Device config requires "power_off_cmd"')
326
327 if config.pre_connect_command:
328 self.context.run_command(config.pre_connect_command)
329@@ -119,26 +120,25 @@
330 self._write_image(img)
331
332 def _as_chunks(self, fname, bsize):
333- with open(fname, 'r') as fd:
334+ with open(fname, 'rb') as fd:
335 while True:
336 data = fd.read(bsize)
337 if not data:
338 break
339 yield data
340+ fd.close()
341
342 def _write_image(self, image):
343+ lmpSdmux_dutDisconnect(self.config.sdmux_id)
344+ lmpSdmux_hostuSDA(self.config.sdmux_id)
345+
346 with self.mux_device() as device:
347 logging.info("dd'ing image to device (%s)", device)
348- with open(device, 'w') as of:
349- written = 0
350- size = os.path.getsize(image)
351- # 4M chunks work well for SD cards
352- for chunk in self._as_chunks(image, 4 << 20):
353- of.write(chunk)
354- written += len(chunk)
355- if written % (20 * (4 << 20)) == 0: # only log every 80MB
356- logging.info("wrote %d of %d bytes", written, size)
357- logging.info('closing %s, could take a while...', device)
358+ dd_cmd = 'dd if=%s of=%s bs=4096 conv=fsync' % (image, device)
359+ dd_proc = subprocess.Popen(dd_cmd, shell=True)
360+ dd_proc.wait()
361+
362+ lmpSdmux_hostDisconnect(self.config.sdmux_id)
363
364 @contextlib.contextmanager
365 def mux_device(self):
366@@ -153,23 +153,32 @@
367 the USB device connect to the sdmux will be powered off so that the
368 target will be able to safely access it.
369 """
370- muxid = self.config.sdmux_id
371- source_dir = os.path.abspath(os.path.dirname(__file__))
372- muxscript = os.path.join(source_dir, 'sdmux.sh')
373
374- self.power_off(self.proc)
375 self.proc = None
376
377- try:
378- deventry = subprocess.check_output([muxscript, '-d', muxid, 'on'])
379- deventry = deventry.strip()
380+ syspath = "/sys/bus/usb/devices/" + self.config.sdmux_usb_id + \
381+ "/" + self.config.sdmux_usb_id + \
382+ "*/host*/target*/*:0:0:0/block/*"
383+
384+ retrycount = 0
385+
386+ while retrycount < 20:
387+ device_list = glob.glob(syspath)
388+ deventry = ""
389+ for device in device_list:
390+ deventry = os.path.join("/dev/", os.path.basename(device))
391+ if deventry != "":
392+ break
393+ time.sleep(1)
394+ retrycount += 1
395+
396+ if deventry != "":
397 logging.info('returning sdmux device as: %s', deventry)
398+ os.system("umount /media/rootfs")
399+ os.system("umount /media/boot")
400 yield deventry
401- except subprocess.CalledProcessError:
402+ else:
403 raise CriticalError('Unable to access sdmux device')
404- finally:
405- logging.info('powering off sdmux')
406- self.context.run_command([muxscript, '-d', muxid, 'off'], failok=False)
407
408 @contextlib.contextmanager
409 def file_system(self, partition, directory):
410@@ -219,10 +228,14 @@
411 def power_off(self, proc):
412 super(SDMuxTarget, self).power_off(proc)
413 self.context.run_command(self.config.power_off_cmd)
414+ lmpSdmux_dutDisconnect(self.config.sdmux_id)
415
416 def power_on(self):
417 self.proc = connect_to_serial(self.context)
418
419+ lmpSdmux_hostDisconnect(self.config.sdmux_id)
420+ lmpSdmux_dutuSDA(self.config.sdmux_id)
421+
422 logging.info('powering on')
423 self.context.run_command(self.config.power_on_cmd)
424
425
426=== modified file 'setup.py'
427--- setup.py 2013-07-17 09:16:24 +0000
428+++ setup.py 2013-08-08 13:55:13 +0000
429@@ -53,6 +53,7 @@
430 "configglue",
431 "PyYAML",
432 'versiontools >= 1.8',
433+ "pyserial",
434 ],
435 setup_requires=[
436 'versiontools >= 1.8',

Subscribers

People subscribed via source and target branches