~gmdduf/lava-dispatcher/gauss-support

« back to all changes in this revision

Viewing changes to lava_dispatcher/device/sdmux.py

Update l-m-c cmdline options

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2012 Linaro Limited
2
 
#
3
 
# Author: Andy Doan <andy.doan@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
 
 
21
 
import contextlib
22
 
import logging
23
 
import os
24
 
import subprocess
25
 
import time
26
 
 
27
 
from lava_dispatcher.errors import (
28
 
    CriticalError,
29
 
)
30
 
from lava_dispatcher.device.target import (
31
 
    Target
32
 
)
33
 
from lava_dispatcher.client.lmc_utils import (
34
 
    generate_android_image,
35
 
    generate_image,
36
 
    image_partition_mounted,
37
 
)
38
 
from lava_dispatcher.downloader import (
39
 
    download_image,
40
 
)
41
 
from lava_dispatcher.utils import (
42
 
    connect_to_serial,
43
 
    ensure_directory,
44
 
    extract_targz,
45
 
)
46
 
 
47
 
 
48
 
def _flush_files(mntdir):
49
 
    """
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
52
 
    """
53
 
    for f in os.listdir('/proc/self/fd'):
54
 
        # check for existances since listdir will include an fd for itself
55
 
        if os.path.exists(f):
56
 
            path = os.path.realpath('/proc/self/fd/%s' % f)
57
 
            if path.startswith(mntdir):
58
 
                os.fsync(int(f))
59
 
                os.close(int(f))
60
 
 
61
 
 
62
 
class SDMuxTarget(Target):
63
 
    """
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
68
 
    found at:
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
 
    """
75
 
 
76
 
    def __init__(self, context, config):
77
 
        super(SDMuxTarget, self).__init__(context, config)
78
 
 
79
 
        self.proc = None
80
 
 
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"')
87
 
 
88
 
        if config.pre_connect_command:
89
 
            self.context.run_command(config.pre_connect_command)
90
 
 
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)
95
 
 
96
 
    def deploy_linaro_prebuilt(self, image):
97
 
        img = download_image(image, self.context)
98
 
        self._customize_linux(img)
99
 
        self._write_image(img)
100
 
 
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
108
 
 
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)
114
 
 
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)
120
 
 
121
 
    def _as_chunks(self, fname, bsize):
122
 
        with open(fname, 'r') as fd:
123
 
            while True:
124
 
                data = fd.read(bsize)
125
 
                if not data:
126
 
                    break
127
 
                yield data
128
 
 
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:
133
 
                written = 0
134
 
                size = os.path.getsize(image)
135
 
                # 4M chunks work well for SD cards
136
 
                for chunk in self._as_chunks(image, 4 << 20):
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)
142
 
 
143
 
    @contextlib.contextmanager
144
 
    def mux_device(self):
145
 
        """
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
150
 
 
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.
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')
159
 
 
160
 
        self.power_off(self.proc)
161
 
        self.proc = None
162
 
 
163
 
        try:
164
 
            deventry = subprocess.check_output([muxscript, '-d', muxid, 'on'])
165
 
            deventry = deventry.strip()
166
 
            logging.info('returning sdmux device as: %s', deventry)
167
 
            yield deventry
168
 
        except subprocess.CalledProcessError:
169
 
            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)
173
 
 
174
 
    @contextlib.contextmanager
175
 
    def file_system(self, partition, directory):
176
 
        """
177
 
        This works in cojunction with the "mux_device" function to safely
178
 
        access a partition/directory on the sdmux filesystem
179
 
        """
180
 
        mntdir = os.path.join(self.scratch_dir, 'sdmux_mnt')
181
 
        if not os.path.exists(mntdir):
182
 
            os.mkdir(mntdir)
183
 
 
184
 
        with self.mux_device() as device:
185
 
            device = '%s%s' % (device, partition)
186
 
            try:
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)
193
 
                yield path
194
 
            except CriticalError:
195
 
                raise
196
 
            except subprocess.CalledProcessError:
197
 
                raise CriticalError('Unable to access sdmux device')
198
 
            except:
199
 
                logging.exception('Error accessing sdmux filesystem')
200
 
                raise CriticalError('Error accessing sdmux filesystem')
201
 
            finally:
202
 
                logging.info('unmounting sdmux')
203
 
                try:
204
 
                    _flush_files(mntdir)
205
 
                    self.context.run_command(['umount', device], failok=False)
206
 
                except subprocess.CalledProcessError:
207
 
                    logging.exception('umount failed, re-try in 10 seconds')
208
 
                    time.sleep(10)
209
 
                    if self.context.run_command(['umount', device]) != 0:
210
 
                        logging.error(
211
 
                            'Unable to unmount sdmux device %s', device)
212
 
 
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))
218
 
 
219
 
    def power_off(self, proc):
220
 
        super(SDMuxTarget, self).power_off(proc)
221
 
        self.context.run_command(self.config.power_off_cmd)
222
 
 
223
 
    def power_on(self):
224
 
        self.proc = connect_to_serial(self.context)
225
 
 
226
 
        logging.info('powering on')
227
 
        self.context.run_command(self.config.power_on_cmd)
228
 
 
229
 
        return self.proc
230
 
 
231
 
    def get_device_version(self):
232
 
        return self.config.sdmux_version
233
 
 
234
 
target_class = SDMuxTarget