1
# Copyright (C) 2012 Linaro Limited
3
# Author: Andy Doan <andy.doan@linaro.org>
5
# This file is part of LAVA Dispatcher.
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.
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.
17
# You should have received a copy of the GNU General Public License
19
# with this program; if not, see <http://www.gnu.org/licenses>.
27
from lava_dispatcher.errors import (
30
from lava_dispatcher.device.target import (
33
from lava_dispatcher.client.lmc_utils import (
34
generate_android_image,
36
image_partition_mounted,
38
from lava_dispatcher.downloader import (
41
from lava_dispatcher.utils import (
48
def _flush_files(mntdir):
50
calls to umount can fail because the files haven't completely been written
51
to disk. This helps make sure that happens and eliminates a warning
53
for f in os.listdir('/proc/self/fd'):
54
# check for existances since listdir will include an fd for itself
56
path = os.path.realpath('/proc/self/fd/%s' % f)
57
if path.startswith(mntdir):
62
class SDMuxTarget(Target):
64
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 SD
66
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 be
69
http://people.linaro.org/~doanac/sdmux/
71
Documentation for setting this up is located under doc/sdmux.rst.
73
NOTE: please read doc/sdmux.rst about kernel versions
76
def __init__(self, context, config):
77
super(SDMuxTarget, self).__init__(context, config)
81
if not config.sdmux_id:
82
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"')
88
if config.pre_connect_command:
89
self.context.run_command(config.pre_connect_command)
91
def deploy_linaro(self, hwpack=None, rootfs=None):
92
img = generate_image(self, hwpack, rootfs, self.scratch_dir)
93
self._customize_linux(img)
94
self._write_image(img)
96
def deploy_linaro_prebuilt(self, image):
97
img = download_image(image, self.context)
98
self._customize_linux(img)
99
self._write_image(img)
101
def _customize_android(self, img):
102
sys_part = self.config.sys_part_android_org
103
with image_partition_mounted(img, sys_part) as d:
104
with open('%s/etc/mkshrc' % d, 'a') as f:
105
f.write('\n# LAVA CUSTOMIZATIONS\n')
106
f.write('PS1="%s"\n' % self.ANDROID_TESTER_PS1)
107
self.deployment_data = Target.android_deployment_data
109
def deploy_android(self, boot, system, data):
110
scratch = self.scratch_dir
111
boot = download_image(boot, self.context, scratch, decompress=False)
112
data = download_image(data, self.context, scratch, decompress=False)
113
system = download_image(system, self.context, scratch, decompress=False)
115
img = os.path.join(scratch, 'android.img')
116
device_type = self.config.lmc_dev_arg
117
generate_android_image(self.context, device_type, boot, data, system, img)
118
self._customize_android(img)
119
self._write_image(img)
121
def _as_chunks(self, fname, bsize):
122
with open(fname, 'r') as fd:
124
data = fd.read(bsize)
129
def _write_image(self, image):
130
with self.mux_device() as device:
131
logging.info("dd'ing image to device (%s)", device)
132
with open(device, 'w') as of:
134
size = os.path.getsize(image)
135
# 4M chunks work well for SD cards
136
for chunk in self._as_chunks(image, 4 << 20):
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)
143
@contextlib.contextmanager
144
def mux_device(self):
146
This function gives us a safe context in which to deal with the
147
raw sdmux device. It will ensure that:
148
* the target is powered off
149
* the proper sdmux USB device is powered on
151
It will then yield to the caller a dev entry like /dev/sdb
152
This entry can be used safely during this context. Upon exiting,
153
the USB device connect to the sdmux will be powered off so that the
154
target will be able to safely access it.
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')
160
self.power_off(self.proc)
164
deventry = subprocess.check_output([muxscript, '-d', muxid, 'on'])
165
deventry = deventry.strip()
166
logging.info('returning sdmux device as: %s', deventry)
168
except subprocess.CalledProcessError:
169
raise CriticalError('Unable to access sdmux device')
171
logging.info('powering off sdmux')
172
self.context.run_command([muxscript, '-d', muxid, 'off'], failok=False)
174
@contextlib.contextmanager
175
def file_system(self, partition, directory):
177
This works in cojunction with the "mux_device" function to safely
178
access a partition/directory on the sdmux filesystem
180
mntdir = os.path.join(self.scratch_dir, 'sdmux_mnt')
181
if not os.path.exists(mntdir):
184
with self.mux_device() as device:
185
device = '%s%s' % (device, partition)
187
self.context.run_command(['mount', device, mntdir], failok=False)
188
if directory[0] == '/':
189
directory = directory[1:]
190
path = os.path.join(mntdir, directory)
191
ensure_directory(path)
192
logging.info('sdmux(%s) mounted at: %s', device, path)
194
except CriticalError:
196
except subprocess.CalledProcessError:
197
raise CriticalError('Unable to access sdmux device')
199
logging.exception('Error accessing sdmux filesystem')
200
raise CriticalError('Error accessing sdmux filesystem')
202
logging.info('unmounting sdmux')
205
self.context.run_command(['umount', device], failok=False)
206
except subprocess.CalledProcessError:
207
logging.exception('umount failed, re-try in 10 seconds')
209
if self.context.run_command(['umount', device]) != 0:
211
'Unable to unmount sdmux device %s', device)
213
def extract_tarball(self, tarball_url, partition, directory='/'):
214
logging.info('extracting %s to target', tarball_url)
215
with self.file_system(partition, directory) as mntdir:
216
tb = download_image(tarball_url, self.context, decompress=False)
217
extract_targz(tb, '%s/%s' % (mntdir, directory))
219
def power_off(self, proc):
220
super(SDMuxTarget, self).power_off(proc)
221
self.context.run_command(self.config.power_off_cmd)
224
self.proc = connect_to_serial(self.context)
226
logging.info('powering on')
227
self.context.run_command(self.config.power_on_cmd)
231
def get_device_version(self):
232
return self.config.sdmux_version
234
target_class = SDMuxTarget