labgrid-boardkit 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,141 @@
1
+ import enum
2
+ import subprocess
3
+ import time
4
+ from pathlib import Path
5
+
6
+ import attr
7
+ from labgrid.strategy.common import StrategyError
8
+ from labgrid.util.managedfile import ManagedFile
9
+
10
+ from labgrid_boardkit.strategies.base import BoardStrategy
11
+
12
+
13
+ class FastbootStatus(enum.Enum):
14
+ unknown = 0
15
+ off = 1
16
+ flashed = 2
17
+ shell = 3
18
+
19
+
20
+ @attr.s(eq=False)
21
+ class FastbootFlashStrategy(BoardStrategy):
22
+ """Flash a physical board's storage over USB fastboot, then boot it.
23
+
24
+ Holding the ``recovery`` button while power comes up, then pulsing
25
+ ``reset``, drops the board into USB download mode. Each ``fastboot.stage`` image is
26
+ then staged and continued in order (e.g. SPL, then a fastboot-capable
27
+ U-Boot), and each ``fastboot.flash`` partition is written, except those
28
+ set to null (how a variant drops one of the base board's). File names are
29
+ relative to ``fastboot.image_dir``.
30
+ """
31
+
32
+ bindings = {
33
+ "power": "PowerProtocol",
34
+ "console": "ConsoleProtocol",
35
+ "shell": "ShellDriver",
36
+ "fastboot": "AndroidFastbootDriver",
37
+ "recovery": "DigitalOutputProtocol",
38
+ "reset": "DigitalOutputProtocol",
39
+ }
40
+
41
+ status = attr.ib(default=FastbootStatus.unknown)
42
+
43
+ def __attrs_post_init__(self):
44
+ super().__attrs_post_init__()
45
+ self.flashed = False
46
+
47
+ def _image(self, name: str) -> str:
48
+ return str(Path(self.manifest["fastboot"]["image_dir"]) / name)
49
+
50
+ def _enter_download_mode(self):
51
+ fb = self.manifest["fastboot"]
52
+ press = fb.get("press_time", 1)
53
+ self.target.activate(self.recovery)
54
+ self.target.activate(self.reset)
55
+ self.target.activate(self.power)
56
+ # Hold recovery across power-up (enters download mode if the board
57
+ # starts on power) and a reset pulse (if it was already running).
58
+ self.recovery.set(True)
59
+ time.sleep(press)
60
+ # cycle(), not on(): transition(off) only just cut power, and a relay
61
+ # flipped back within milliseconds never actually drops the board.
62
+ self.power.cycle()
63
+ time.sleep(fb.get("power_on_delay", 2))
64
+ self.reset.set(True)
65
+ time.sleep(press)
66
+ self.reset.set(False)
67
+ # Keep holding recovery until the boot ROM has sampled it.
68
+ time.sleep(press)
69
+ self.recovery.set(False)
70
+
71
+ def _stage(self, remote_path: str):
72
+ fb = self.manifest["fastboot"]
73
+ retries = fb.get("stage_retries", 5)
74
+ # After a continue, the previous stage's USB device lingers until the
75
+ # next one (e.g. FSBL, after DDR training) re-enumerates, and writes to
76
+ # it fail. fastboot waits for a missing device but not a stale one.
77
+ for attempt in range(retries):
78
+ try:
79
+ self.fastboot("stage", remote_path)
80
+ return
81
+ except subprocess.CalledProcessError:
82
+ if attempt == retries - 1:
83
+ raise
84
+ time.sleep(fb.get("continue_delay", 3))
85
+
86
+ def _flash(self):
87
+ fb = self.manifest["fastboot"]
88
+ # Copy staged images to the exporter up front: the boot ROM's download
89
+ # mode doesn't wait around for slow transfers.
90
+ staged = []
91
+ for image in fb.get("stage", []):
92
+ mf = ManagedFile(self._image(image), self.fastboot.fastboot)
93
+ mf.sync_to_resource()
94
+ staged.append(mf.get_remote_path())
95
+ self._enter_download_mode()
96
+ self.target.activate(self.fastboot)
97
+ for remote_path in staged:
98
+ self._stage(remote_path)
99
+ self.fastboot.continue_boot()
100
+ time.sleep(fb.get("continue_delay", 3))
101
+ for partition, image in fb["flash"].items():
102
+ if image is None: # a variant nulls out a base-board partition
103
+ continue
104
+ self.fastboot.flash(partition, self._image(image))
105
+ self.target.deactivate(self.fastboot)
106
+
107
+ def transition(self, status):
108
+ if not isinstance(status, FastbootStatus):
109
+ status = FastbootStatus[status]
110
+ if status == FastbootStatus.unknown:
111
+ raise StrategyError(f"can not transition to {status}")
112
+ elif status == self.status:
113
+ return
114
+ elif status == FastbootStatus.off:
115
+ self.target.deactivate(self.console)
116
+ self.target.activate(self.power)
117
+ self.power.off()
118
+ elif status == FastbootStatus.flashed:
119
+ self.transition(FastbootStatus.off)
120
+ if not self.flashed:
121
+ self._flash()
122
+ self.flashed = True
123
+ elif status == FastbootStatus.shell:
124
+ self.transition(FastbootStatus.flashed)
125
+ self.target.activate(self.console)
126
+ self.power.cycle()
127
+ self.target.activate(self.shell)
128
+ else:
129
+ raise StrategyError(f"no transition found from {self.status} to {status}")
130
+ self.status = status
131
+
132
+ def force(self, status):
133
+ if not isinstance(status, FastbootStatus):
134
+ status = FastbootStatus[status]
135
+ if status == FastbootStatus.off:
136
+ self.target.activate(self.power)
137
+ elif status == FastbootStatus.shell:
138
+ self.target.activate(self.shell)
139
+ else:
140
+ raise StrategyError(f"can not force state {status}")
141
+ self.status = status
@@ -0,0 +1,86 @@
1
+ import enum
2
+
3
+ import attr
4
+ from labgrid.factory import target_factory
5
+ from labgrid.step import step
6
+ from labgrid.strategy.common import StrategyError
7
+
8
+ from labgrid_boardkit.strategies.base import BoardStrategy
9
+
10
+
11
+ class QemuStatus(enum.Enum):
12
+ unknown = 0
13
+ off = 1
14
+ booting = 2
15
+ shell = 3
16
+
17
+
18
+ @attr.s(eq=False)
19
+ class QemuBootStrategy(BoardStrategy):
20
+ """Boot a QEMU-based target."""
21
+
22
+ bindings = {
23
+ "qemu_driver": "QEMUDriver",
24
+ "shell": "ShellDriver",
25
+ }
26
+
27
+ status = attr.ib(default=QemuStatus.unknown)
28
+
29
+ @step(title="transition")
30
+ def transition(self, status):
31
+ if not isinstance(status, QemuStatus):
32
+ status = QemuStatus[status]
33
+
34
+ if status == QemuStatus.off:
35
+ if self.status == QemuStatus.shell:
36
+ self.target.deactivate(self.shell)
37
+ self.qemu_driver.off()
38
+ self.target.deactivate(self.qemu_driver)
39
+ self.status = QemuStatus.off
40
+ elif status == QemuStatus.shell:
41
+ if self.status == QemuStatus.shell:
42
+ return
43
+ if self.status == QemuStatus.booting:
44
+ raise StrategyError("Cannot go to shell directly from booting; turn off first.")
45
+ self.target.activate(self.qemu_driver)
46
+ self.qemu_driver.on()
47
+ self.status = QemuStatus.booting
48
+ self.target.activate(self.shell)
49
+ self.status = QemuStatus.shell
50
+ else:
51
+ raise StrategyError(f"Unknown status {status}")
52
+
53
+
54
+ @target_factory.reg_driver
55
+ @attr.s(eq=False)
56
+ class ExternalQemuStrategy(BoardStrategy):
57
+ """Drive a QEMU guest whose serial console is a subprocess's stdio.
58
+
59
+ Used with ``ExternalConsoleDriver`` when QEMU runs somewhere labgrid's
60
+ ``QEMUDriver`` can't reach, e.g. ``ssh -tt <board> qemu-system-* -nographic``.
61
+ Activating the console starts the subprocess (boots the guest);
62
+ deactivating it kills the subprocess (and, via SIGHUP, the guest).
63
+ """
64
+
65
+ bindings = {
66
+ "console": "ConsoleProtocol",
67
+ "shell": "ShellDriver",
68
+ }
69
+
70
+ status = attr.ib(default=QemuStatus.unknown)
71
+
72
+ @step(title="transition")
73
+ def transition(self, status):
74
+ if not isinstance(status, QemuStatus):
75
+ status = QemuStatus[status]
76
+
77
+ if status == QemuStatus.off:
78
+ self.target.deactivate(self.console)
79
+ elif status == QemuStatus.shell:
80
+ if self.status == QemuStatus.shell:
81
+ return
82
+ self.target.activate(self.console)
83
+ self.target.activate(self.shell)
84
+ else:
85
+ raise StrategyError(f"can not transition to {status}")
86
+ self.status = status
@@ -0,0 +1,81 @@
1
+ import enum
2
+
3
+ import attr
4
+ from labgrid.driver.usbstoragedriver import Mode
5
+ from labgrid.strategy.common import StrategyError
6
+
7
+ from labgrid_boardkit.strategies.base import BoardStrategy
8
+
9
+
10
+ class SdMuxStatus(enum.Enum):
11
+ unknown = 0
12
+ off = 1
13
+ flashed = 2
14
+ sd = 3
15
+
16
+
17
+ @attr.s(eq=False)
18
+ class SdMuxBootStrategy(BoardStrategy):
19
+ """Boot a physical board from an SD card written via USB SD-Mux.
20
+
21
+ ``image_label`` names the image entry in the labgrid client.yaml
22
+ ``images:`` section that holds the wic/gz path.
23
+ """
24
+
25
+ bindings = {
26
+ "power": "PowerProtocol",
27
+ "console": "ConsoleProtocol",
28
+ "shell": "ShellDriver",
29
+ "sdmux": "USBSDMuxDriver",
30
+ "sdcard": "USBStorageDriver",
31
+ }
32
+
33
+ status = attr.ib(default=SdMuxStatus.unknown)
34
+ image_label = attr.ib(default="sdimage")
35
+
36
+ def __attrs_post_init__(self):
37
+ super().__attrs_post_init__()
38
+ self.flashed = False
39
+
40
+ def transition(self, status):
41
+ if not isinstance(status, SdMuxStatus):
42
+ status = SdMuxStatus[status]
43
+ if status == SdMuxStatus.unknown:
44
+ raise StrategyError(f"can not transition to {status}")
45
+ elif status == self.status:
46
+ return
47
+ elif status == SdMuxStatus.off:
48
+ self.target.deactivate(self.console)
49
+ self.target.activate(self.power)
50
+ self.power.off()
51
+ elif status == SdMuxStatus.flashed:
52
+ image = self.target.env.config.get_image_path(self.image_label)
53
+ self.transition(SdMuxStatus.off)
54
+ if not self.flashed:
55
+ self.target.activate(self.sdmux)
56
+ self.sdmux.set_mode("host")
57
+ self.target.activate(self.sdcard)
58
+ self.sdcard.write_image(image, Mode.BMAPTOOL)
59
+ self.target.deactivate(self.sdcard)
60
+ self.sdmux.set_mode("dut")
61
+ self.target.deactivate(self.sdmux)
62
+ self.flashed = True
63
+ elif status == SdMuxStatus.sd:
64
+ self.transition(SdMuxStatus.flashed)
65
+ self.target.activate(self.console)
66
+ self.power.cycle()
67
+ self.target.activate(self.shell)
68
+ else:
69
+ raise StrategyError(f"no transition found from {self.status} to {status}")
70
+ self.status = status
71
+
72
+ def force(self, status):
73
+ if not isinstance(status, SdMuxStatus):
74
+ status = SdMuxStatus[status]
75
+ if status == SdMuxStatus.off:
76
+ self.target.activate(self.power)
77
+ elif status == SdMuxStatus.sd:
78
+ self.target.activate(self.shell)
79
+ else:
80
+ raise StrategyError(f"can not force state {status}")
81
+ self.status = status
@@ -0,0 +1,109 @@
1
+ import enum
2
+
3
+ import attr
4
+ from labgrid.strategy.common import StrategyError
5
+
6
+ from labgrid_boardkit import helpers
7
+ from labgrid_boardkit.strategies.base import BoardStrategy
8
+
9
+
10
+ class TftpStatus(enum.Enum):
11
+ unknown = 0
12
+ off = 1
13
+ uboot = 2
14
+ emmc = 3
15
+ tftp = 4
16
+
17
+
18
+ @attr.s(eq=False)
19
+ class TftpBootStrategy(BoardStrategy):
20
+ """Boot a physical board via U-Boot + TFTP.
21
+
22
+ Reads bootargs, tftp subdir/kernel/dtb, and load addresses from
23
+ ``self.manifest`` (populated by the concrete per-board subclass).
24
+ """
25
+
26
+ bindings = {
27
+ "power": "PowerProtocol",
28
+ "console": "ConsoleProtocol",
29
+ "uboot": "UBootDriver",
30
+ "shell": "ShellDriver",
31
+ "tftp": "TFTPProviderDriver",
32
+ }
33
+
34
+ status = attr.ib(default=TftpStatus.unknown)
35
+
36
+ def __attrs_post_init__(self):
37
+ super().__attrs_post_init__()
38
+ self.staged = False
39
+
40
+ @property
41
+ def bootargs(self) -> str:
42
+ return self.manifest["boot"]["bootargs"].strip()
43
+
44
+ def _stage(self):
45
+ helpers.uboot_stage(self)
46
+
47
+ def _tftp_stage_files(self):
48
+ boot = self.manifest["boot"]
49
+ tftp = self.manifest["tftp"]
50
+ helpers.uboot_set_server_ip(self)
51
+ helpers.uboot_set_bootargs(self, self.bootargs)
52
+ helpers.uboot_tftpboot_file(self, boot["kernel_addr"], tftp["subdir"], tftp["kernel"])
53
+ if "dtb" in tftp:
54
+ # A FIT image carries its own DTB.
55
+ helpers.uboot_tftpboot_file(self, boot["dtb_addr"], tftp["subdir"], tftp["dtb"])
56
+ if "initramfs" in tftp:
57
+ # Staged last so U-Boot's $filesize (set by the most recent tftpboot)
58
+ # reflects the initramfs size for a `$ramdisk_addr_r:$filesize` boot arg.
59
+ helpers.uboot_tftpboot_file(
60
+ self, boot["ramdisk_addr"], tftp["subdir"], tftp["initramfs"]
61
+ )
62
+
63
+ def transition(self, status):
64
+ if not isinstance(status, TftpStatus):
65
+ status = TftpStatus[status]
66
+ if status == TftpStatus.unknown:
67
+ raise StrategyError(f"can not transition to {status}")
68
+ elif status == self.status:
69
+ return
70
+ elif status == TftpStatus.off:
71
+ self.target.deactivate(self.console)
72
+ self.target.activate(self.power)
73
+ self.power.off()
74
+ elif status == TftpStatus.uboot:
75
+ self.transition(TftpStatus.off)
76
+ self.target.activate(self.console)
77
+ self.power.cycle()
78
+ self.target.activate(self.uboot)
79
+ self._stage()
80
+ elif status == TftpStatus.emmc:
81
+ self.transition(TftpStatus.uboot)
82
+ self.uboot.boot("emmc")
83
+ self.uboot.await_boot()
84
+ self.target.activate(self.shell)
85
+ elif status == TftpStatus.tftp:
86
+ self.transition(TftpStatus.uboot)
87
+ self._tftp_stage_files()
88
+ self.uboot.boot("tftp")
89
+ self.uboot.await_boot()
90
+ self.target.activate(self.shell)
91
+ # Keep late kernel messages (e.g. async driver probe errors) off the
92
+ # console, where they interleave with command output; dmesg keeps them.
93
+ self.shell.run("dmesg -n 1")
94
+ else:
95
+ raise StrategyError(f"no transition found from {self.status} to {status}")
96
+ self.status = status
97
+
98
+ def force(self, status):
99
+ if not isinstance(status, TftpStatus):
100
+ status = TftpStatus[status]
101
+ if status == TftpStatus.off:
102
+ self.target.activate(self.power)
103
+ elif status == TftpStatus.uboot:
104
+ self.target.activate(self.uboot)
105
+ elif status in (TftpStatus.emmc, TftpStatus.tftp):
106
+ self.target.activate(self.shell)
107
+ else:
108
+ raise StrategyError(f"can not force state {status}")
109
+ self.status = status
@@ -0,0 +1,55 @@
1
+ {#- Jinja2 template for a fastboot-flash labgrid client.yaml. -#}
2
+ {%- set n = manifest.name -%}
3
+ {%- set fb = manifest.fastboot -%}
4
+ {%- set strategy_class = manifest.strategy_class | default("FastbootFlashStrategy") -%}
5
+ targets:
6
+ main:
7
+ resources:
8
+ RemotePlace:
9
+ name: {{ manifest.remote_place }}
10
+ drivers:
11
+ - SerialDriver:
12
+ name: '{{ n }}-console-1'
13
+ bindings:
14
+ port: 'serial'
15
+ - GpioDigitalOutputDriver:
16
+ name: '{{ n }}-power-gpio-1'
17
+ bindings:
18
+ gpio: 'power-gpio'
19
+ - DigitalOutputPowerDriver:
20
+ name: '{{ n }}-power-1'
21
+ bindings:
22
+ output: '{{ n }}-power-gpio-1'
23
+ {%- for button in manifest.buttons %}
24
+ - GpioDigitalOutputDriver:
25
+ name: '{{ n }}-{{ button }}-gpio-1'
26
+ bindings:
27
+ gpio: '{{ button }}-gpio'
28
+ {%- endfor %}
29
+ - AndroidFastbootDriver:
30
+ name: '{{ n }}-fastboot-1'
31
+ bindings:
32
+ fastboot: 'fastboot'
33
+ - ShellDriver:
34
+ prompt: '{{ manifest.shell.prompt }}'
35
+ login_prompt: '{{ manifest.shell.login_prompt }}'
36
+ login_timeout: 120
37
+ username: '{{ manifest.shell.username }}'
38
+ {%- if manifest.shell.password %}
39
+ password: '{{ manifest.shell.password }}'
40
+ {%- endif %}
41
+ name: '{{ n }}-shell-1'
42
+ bindings:
43
+ console: '{{ n }}-console-1'
44
+ - {{ strategy_class }}:
45
+ name: '{{ n }}-strategy-1'
46
+ bindings:
47
+ power: '{{ n }}-power-1'
48
+ console: '{{ n }}-console-1'
49
+ shell: '{{ n }}-shell-1'
50
+ fastboot: '{{ n }}-fastboot-1'
51
+ recovery: '{{ n }}-{{ fb.recovery_button }}-gpio-1'
52
+ reset: '{{ n }}-{{ fb.reset_button }}-gpio-1'
53
+
54
+ imports:
55
+ - strategy.py
@@ -0,0 +1,44 @@
1
+ {#- Jinja2 template for a QEMU labgrid env.yaml. -#}
2
+ {%- set n = manifest.name -%}
3
+ {%- set strategy_class = manifest.strategy_class | default("QemuBootStrategy") -%}
4
+ {#- virtio-mmio ("-device") for e.g. riscv/arm virt, "-pci" for x86 q35/pc -#}
5
+ {%- set virtio = "pci" if manifest.qemu.virtio_bus | default("mmio") == "pci" else "device" -%}
6
+ imports:
7
+ - strategy.py
8
+
9
+ targets:
10
+ main:
11
+ drivers:
12
+ QEMUDriver:
13
+ qemu_bin: {{ manifest.qemu.binary }}
14
+ {%- if manifest.images.bios is defined %}
15
+ bios: {{ manifest.name }}_bios
16
+ {%- endif %}
17
+ kernel: {{ manifest.name }}_kernel
18
+ cpu: '{{ manifest.qemu.cpu }}'
19
+ memory: "{{ manifest.qemu.memory }}"
20
+ machine: '{{ manifest.qemu.machine }}'
21
+ disk: {{ manifest.name }}_ext4
22
+ boot_args: '{{ manifest.boot.bootargs }}'
23
+ extra_args: >-
24
+ -display none
25
+ -device virtio-net-{{ virtio }},netdev=net0,mac=52:54:00:12:35:02
26
+ -netdev user,id=net0,hostfwd=tcp:127.0.0.1:{{ manifest.qemu.ssh_port }}-:22,hostfwd=tcp:127.0.0.1:{{ manifest.qemu.telnet_port }}-:23
27
+ -object rng-random,filename=/dev/urandom,id=rng0
28
+ -device virtio-rng-{{ virtio }},rng=rng0
29
+ {%- for accel in manifest.qemu.accel | default([]) %}
30
+ -accel {{ accel }}
31
+ {%- endfor %}
32
+ ShellDriver:
33
+ prompt: 'root@[a-zA-Z0-9]+:~#\s'
34
+ login_prompt: ' login: '
35
+ username: '{{ manifest.shell.username }}'
36
+ login_timeout: 300
37
+ {{ strategy_class }}: {}
38
+
39
+ images:
40
+ {%- if manifest.images.bios is defined %}
41
+ {{ manifest.name }}_bios: '{{ manifest.images.bios }}'
42
+ {%- endif %}
43
+ {{ manifest.name }}_ext4: '{{ manifest.images.ext4 }}'
44
+ {{ manifest.name }}_kernel: '{{ manifest.images.kernel }}'
@@ -0,0 +1,58 @@
1
+ {#- Jinja2 template for an SD-Mux labgrid client.yaml. -#}
2
+ {%- set n = manifest.name -%}
3
+ {%- set strategy_class = manifest.strategy_class | default("SdMuxBootStrategy") -%}
4
+ targets:
5
+ main:
6
+ resources:
7
+ RemotePlace:
8
+ name: {{ manifest.remote_place }}
9
+ drivers:
10
+ - SerialDriver:
11
+ name: '{{ n }}-console-1'
12
+ bindings:
13
+ port: 'serial'
14
+ - GpioDigitalOutputDriver:
15
+ name: '{{ n }}-power-gpio-1'
16
+ bindings:
17
+ gpio: 'power-gpio'
18
+ - DigitalOutputPowerDriver:
19
+ name: '{{ n }}-power-1'
20
+ bindings:
21
+ output: '{{ n }}-power-gpio-1'
22
+ - USBStorageDriver:
23
+ image: '{{ manifest.image.label }}'
24
+ bindings:
25
+ storage: 'sdmux'
26
+ - USBSDMuxDriver:
27
+ bindings:
28
+ mux: 'sdmux'
29
+ - ShellDriver:
30
+ prompt: '(\x1b\[\?2004h|\x1b\\)?root@[\w-]+:[^ ]+[#$] '
31
+ login_prompt: 'login: '
32
+ login_timeout: 120
33
+ username: '{{ manifest.shell.username }}'
34
+ {%- if manifest.shell.password %}
35
+ password: '{{ manifest.shell.password }}'
36
+ {%- endif %}
37
+ name: '{{ n }}-shell-1'
38
+ bindings:
39
+ console: '{{ n }}-console-1'
40
+ - {{ strategy_class }}:
41
+ name: '{{ n }}-strategy-1'
42
+ bindings:
43
+ power: '{{ n }}-power-1'
44
+ console: '{{ n }}-console-1'
45
+ shell: '{{ n }}-shell-1'
46
+
47
+ images:
48
+ {{ manifest.image.label }}: "{{ manifest.image.path }}"
49
+
50
+ {%- if manifest.tools %}
51
+ tools:
52
+ {%- for k, v in manifest.tools.items() %}
53
+ {{ k }}: '{{ v }}'
54
+ {%- endfor %}
55
+ {%- endif %}
56
+
57
+ imports:
58
+ - strategy.py
@@ -0,0 +1,59 @@
1
+ {#- Jinja2 template for a TFTP-boot labgrid client.yaml. -#}
2
+ {%- set n = manifest.name -%}
3
+ {%- set strategy_class = manifest.strategy_class | default("TftpBootStrategy") -%}
4
+ targets:
5
+ main:
6
+ resources:
7
+ RemotePlace:
8
+ name: {{ manifest.remote_place }}
9
+ drivers:
10
+ - SerialDriver:
11
+ name: '{{ n }}-console-1'
12
+ bindings:
13
+ port: 'serial'
14
+ - GpioDigitalOutputDriver:
15
+ name: '{{ n }}-power-gpio-1'
16
+ bindings:
17
+ gpio: 'power-gpio'
18
+ - DigitalOutputPowerDriver:
19
+ name: '{{ n }}-power-1'
20
+ bindings:
21
+ output: '{{ n }}-power-gpio-1'
22
+ - TFTPProviderDriver:
23
+ name: '{{ n }}-tftp-1'
24
+ bindings:
25
+ provider: 'tftp'
26
+ - UBootDriver:
27
+ prompt: '{{ manifest.uboot.prompt }}'
28
+ autoboot: '{{ manifest.uboot.autoboot }}'
29
+ interrupt: '{{ manifest.uboot.interrupt }}'
30
+ name: '{{ n }}-uboot-1'
31
+ boot_commands:
32
+ emmc: '{{ manifest.uboot.emmc_cmd }}'
33
+ tftp: '{{ manifest.uboot.tftp_cmd }}'
34
+ bindings:
35
+ console: '{{ n }}-console-1'
36
+ login_timeout: 30
37
+ boot_timeout: 30
38
+ strip_timestamp: True
39
+ - ShellDriver:
40
+ prompt: '{{ manifest.shell.prompt }}'
41
+ login_prompt: '{{ manifest.shell.login_prompt }}'
42
+ username: '{{ manifest.shell.username }}'
43
+ {%- if manifest.shell.password %}
44
+ password: '{{ manifest.shell.password }}'
45
+ {%- endif %}
46
+ name: '{{ n }}-shell-1'
47
+ bindings:
48
+ console: '{{ n }}-console-1'
49
+ - {{ strategy_class }}:
50
+ name: '{{ n }}-strategy-1'
51
+ bindings:
52
+ power: '{{ n }}-power-1'
53
+ console: '{{ n }}-console-1'
54
+ uboot: '{{ n }}-uboot-1'
55
+ shell: '{{ n }}-shell-1'
56
+ tftp: '{{ n }}-tftp-1'
57
+
58
+ imports:
59
+ - strategy.py
@@ -0,0 +1,19 @@
1
+ """Generated by boardkit-render from board.yaml. Do not edit.
2
+
3
+ Regenerate with: uv run boardkit-render
4
+ """
5
+
6
+ from pathlib import Path
7
+
8
+ import attr
9
+ from labgrid_boardkit.manifest import load_board
10
+ from labgrid_boardkit.strategies import {{ family_class }}
11
+ from labgrid.factory import target_factory
12
+
13
+
14
+ @target_factory.reg_driver
15
+ @attr.s(eq=False)
16
+ class {{ class_name }}({{ family_class }}):
17
+ """{{ description }}"""
18
+
19
+ manifest = load_board(Path(__file__).parent)
File without changes
@@ -0,0 +1,23 @@
1
+ """Shared smoke tests for boards flashed over fastboot. Auto-skipped for boards
2
+ whose strategy isn't a FastbootFlashStrategy (see ``fastboot`` fixture in
3
+ ``labgrid_boardkit.fixtures``).
4
+ """
5
+
6
+ import os
7
+
8
+ from labgrid.driver import ExecutionError
9
+
10
+ from labgrid_boardkit import helpers
11
+
12
+
13
+ def test_uname_a(fastboot):
14
+ stdout = fastboot.run_check("/bin/uname -a", timeout=60.0)
15
+ assert helpers.kernel_version_in(os.environ.get("VERSION"), stdout[0]), stdout[0]
16
+
17
+
18
+ def test_buildinfo(fastboot):
19
+ try:
20
+ fastboot.run_check("cat /etc/buildinfo", timeout=60.0)
21
+ except ExecutionError:
22
+ fastboot.run("ls /etc/")
23
+ raise