Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add scripts to generate disk images using pi-gen. #14

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[submodule "pi_image/pi-gen"]
path = pi_image/pi-gen
url = https://github.com/RPi-Distro/pi-gen.git
branch = master
shallow = true
[submodule "pi_image/pi-gen-arm64"]
path = pi_image/pi-gen-arm64
url = https://github.com/RPi-Distro/pi-gen.git
branch = arm64
shallow = true
[submodule "pi_image/stage-usb4vc-deps/00-dependencies/files/xpadneo"]
path = pi_image/stage-usb4vc-deps/00-dependencies/files/xpadneo
url = https://github.com/atar-axis/xpadneo.git
branch = release/v0.9
shallow = true
[submodule "pi_image/stage-usb4vc-deps/00-dependencies/files/dkms-hid-nintendo"]
path = pi_image/stage-usb4vc-deps/00-dependencies/files/dkms-hid-nintendo
url = https://github.com/nicman23/dkms-hid-nintendo.git
shallow = true
[submodule "pi_image/stage-usb4vc-deps/00-dependencies/files/joycond"]
path = pi_image/stage-usb4vc-deps/00-dependencies/files/joycond
url = https://github.com/DanielOgorchock/joycond.git
shallow = true
63 changes: 63 additions & 0 deletions pi_image/build-docker.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/bin/bash -eu
# see: https://github.com/RPi-Distro/pi-gen for details on this process

DEPS_STAGE="stage-usb4vc-deps"
APP_STAGE="stage-usb4vc-app"

BUILD_32BIT=${BUILD_32BIT:-1}
BUILD_64BIT=${BUILD_64BIT:-0}
SKIP_PI_STAGES=${SKIP_PI_STAGES:-0}
SKIP_DEPS_STAGE=${SKIP_DEPS_STAGE:-0}

setup_stages() {
# prevent "lite" image output
touch ./$1/stage2/SKIP_IMAGES

# clean up skips
rm -f ./$1/stage0/SKIP ./$1/stage1/SKIP ./$1/stage2/SKIP ./$DEPS_STAGE/SKIP

if [ $SKIP_PI_STAGES = 1 ]; then
touch ./$1/stage0/SKIP ./$1/stage1/SKIP ./$1/stage2/SKIP
fi

if [ $SKIP_DEPS_STAGE = 1 ]; then
touch ./$DEPS_STAGE/SKIP
fi
}

# prevent "lite" image output
setup_stages pi-gen
setup_stages pi-gen-arm64

# setup for the pi-gen build-docker script
CONTAINER_NAME=${CONTAINER_NAME:-pigen_usb4vc_work}

DEPS_STAGE_PATH=$(realpath -s "./${DEPS_STAGE}" || realpath "./${DEPS_STAGE}")
APP_STAGE_PATH=$(realpath -s "./${APP_STAGE}" || realpath "./${APP_STAGE}")
USERPROGRAM_PATH=$(realpath -s "../user_program" || realpath "../user_program")

# paths relative to inside pi-gen
CONFIG_FILE=${CONFIG_FILE:-"../config"}
PIGEN_DOCKER_OPTS=${PIGEN_DOCKER_OPTS:-""}
PIGEN_DOCKER_OPTS="${PIGEN_DOCKER_OPTS} --volume ${DEPS_STAGE_PATH}:/pi-gen/${DEPS_STAGE}"
PIGEN_DOCKER_OPTS="${PIGEN_DOCKER_OPTS} --volume ${APP_STAGE_PATH}:/pi-gen/${APP_STAGE}"
PIGEN_DOCKER_OPTS="${PIGEN_DOCKER_OPTS} --volume ${USERPROGRAM_PATH}:/pi-gen/${APP_STAGE}/00-user-program/files/rpi_app:ro"

export CONTAINER_NAME
export PIGEN_DOCKER_OPTS

if [ $BUILD_32BIT = 1 ]; then
echo "Starting pi-gen..."
pushd ./pi-gen && ./build-docker.sh -c $CONFIG_FILE && popd || popd && exit
echo "pi-gen complete"
fi

if [ $BUILD_64BIT = 1 ]; then
echo "Starting pi-gen for arm64..."
export CONTAINER_NAME="arm64_${CONTAINER_NAME}"
export PIGEN_DOCKER_OPTS=${PIGEN_DOCKER_OPTS:-" -e IS_PIGEN_ARM64=1"}
pushd ./pi-gen-arm64 && ./build-docker.sh -c $CONFIG_FILE && popd || popd && exit
echo "pi-gen arm64 complete"
fi

echo "All images should be created, check the deploy folder of each pi-gen."
15 changes: 15 additions & 0 deletions pi_image/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
IMG_NAME=raspios-bookworm
FIRST_USER_NAME=pi
FIRST_USER_PASS=usb4vc
TARGET_HOSTNAME=usb4vc
KEYBOARD_KEYMAP=us
KEYBOARD_LAYOUT="English (US)"
TIMEZONE_DEFAULT="Etc/UTC"
LOCALE_DEFAULT="en_US.UTF-8"
ENABLE_SSH=1
DISABLE_FIRST_BOOT_USER_RENAME=1
STAGE_LIST="stage0 stage1 stage2 stage-usb4vc-deps stage-usb4vc-app"

# optional
# APT_PROXY=http://172.17.0.1:3142

1 change: 1 addition & 0 deletions pi_image/pi-gen
Submodule pi-gen added at d96689
1 change: 1 addition & 0 deletions pi_image/pi-gen-arm64
Submodule pi-gen-arm64 added at 78444e
142 changes: 142 additions & 0 deletions pi_image/pi_image_creation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# Raspberry Pi Disk Image
#### Building a Raspberry Pi disk image containing usb4vc using pi-gen.

This process currently relies on Docker and pi-gen. Docker is the easiest method for using pi-gen as it requires very specific versions of ubuntu or debian to run standalone.

Please read through the pi-gen documentation to have a good understanding of how to setup the build environment and what stages / config mean:
- https://github.com/RPi-Distro/pi-gen

## Setup
- First the submodules for pi-gen and the usb4vc-deps must be cloned:
```bash
git submodule update --init --recursive
```
These are shallow clones by default, so to switch branch (e.g. to bullseye from bookworm) you will need to perform a fetch.
- Ensure docker is installed and accessible from the command line. Linux hosts / WSL should work fine.
https://docs.docker.com/engine/install/

## Overview
- `pi-gen`: A clone of pi-gen for armhf / armv7 (32bit ARM)
- `pi-gen-arm64`: A clone of pi-gen for arm64 (64bit ARM, newer Pi's)
- `stage-usb4vc-deps`: A pi-gen stage containing all software dependencies for usb4vc.
- `stage-usb4vc-app`: A pi-gen stage containing the scripts to install usb4vc inside the image.
- `config`: The pi-gen config used for the build.
- `build-docker.sh`: Helper script to automatically call pi-gen(-arm64)'s build-docker.sh with the correct arguments.

## Quick Usage
Once docker is installed and the submodules have been updated / cloned, you can run the build script.
- To build just the 32bit image (the default):
```bash
./build-docker.sh
```
- To build 32bit and 64bit images:
```bash
BUILD_32BIT=1 BUILD_64BIT=1 ./build-docker.sh
```
- To build just the 64bit image:
```bash
BUILD_32BIT=0 BUILD_64BIT=1 ./build-docker.sh
```
- To preserve the container to speed up iteration:
```bash
BUILD_32BIT=1 BUILD_64BIT=1 PRESERVE_CONTAINER=1 ./build-docker.sh
```
- To continue and retry a failed build:
```bash
BUILD_32BIT=1 BUILD_64BIT=1 PRESERVE_CONTAINER=1 CONTINUE=1 ./build-docker.sh
```

Outputs can be found in:
- `pi-gen/deploy`
- `pi-gen-arm64/deploy`

### APT Proxy
The APT proxy can be used to cache packages locally to speed up image building from scratch. See `APT_PROXY` under `Config` in pi-gen: https://github.com/RPi-Distro/pi-gen#config

You can then uncomment / modify the line in the `config` file.

## Script Configuration
All configuration for `build-docker.sh` is controlled via environment variables.
- `BUILD_32BIT` (Default: `1`)

Toggle the pi-gen build for a 32bit image.
- `BUILD_64BIT` (Default: `0`)

Toggle the pi-gen-arm64 build for a 64bit image.
- `SKIP_PI_STAGES` (Default: `0`)

Toggle skipping the base pi image stages (stage0, stage1, stage2). It will automatically create the `SKIP` files in each stage directory. This can help speed up image build time when combines with `PRESERVE_CONTAINER=1` and `CONTINUE=1` when something goes wrong in the `stage-usb4vc-deps` or `stage-usb4vc-app` stage.
- `SKIP_DEPS_STAGE` (Default: `0`)

Toggle skipping the `stage-usb4vc-deps` stage. Similar to `SKIP_PI_STAGES` but will skip the dependencies stage specifically (much faster if you already have a preserved base of stage 0-2 with deps you just want to update the app inside).

All environment variables are passed through to the underlying pi-gen build-docker.sh, but some are manipulated by the script here:
- `CONFIG_FILE` (Default `"../config"`)

Path to the config file (relative from inside pi-gen as it mounts it to the container itself).
- `CONTAINER_NAME` (Default `"pigen_usb4vc_work"`)

The name of the container used for the pi-gen work. When `BUILD_64BIT` is `1`, this container name will have `arm64_` prepended to the name as the pi-gen build-docker checks if any container matches the container name (meaning it throws an error if you try to preserve both the 32bit and 64bit containers).
- `PIGEN_DOCKER_OPTS` (Default `""`)

This passes extra options to the `docker run` call for pi-gen. By default this is empty, but the script always adds the volumes for the usb4vc stages and the user_program. You can use this to specify any additonal docker options.

The script mounts some volumes in docker for easy iteration outside the container environment:
- `./stage-usb4vc-deps:/pi-gen/stage-usb4vc-deps`
- `./stage-usb4vc-app:/pi-gen/stage-usb4vc-app`
- `../user_program:/pi-gen/stage-usb4vc-app/00-user-program/files/rpi_app`

## Stages
The build stages run in this order by default (specified in `config`):
- stage0
- stage1
- stage2 (this is a "lite" rpi-image)
- stage-usb4vc-deps
- stage-usb4vc-app (this is where the final image is exported)

### `stage-usb4vc-deps`
This stage installs all software dependencies for usb4vc. See `00-packages` for exact list, but in general:
- python3
- stm32flash
- i2c-tools
- dfutil
- etc..

A virtualenv is created for usb4vc in `/opt/usb4vc/venv`, this is a requirement in newer raspios distributions for installing non api provided python packages.
- Creates python3 venv in `/opt/usb4vc/venv`.
- Installs `evdev`, `spidev`, `serial` and `luma.oled`.
- Purges pip cache to save disk space.

It also compiles and installs (see `00-run.sh`):
- xpadneo
- Only the source code for usb4vc to compile on startup in `/opt/xpadneo`.
- joycond
- dkms-hid-nintendo
- For this module, the script must find all kernels inside the image and install dkms for each (to ensure the module works on every pi the image supports).

After the dependencies are installed, this stage performs some system tweaks specific to usb4vc. See `01-tweaks` and `01-run.sh`:
- Installs uniput and udisks udev rules with a script started by systemd (`usb-automount.service` and `udev_usb_monitor.sh`) to monitor for newly attached USB storage devices and automount them to `/media`. This replaces `usbmount` from old Debian systems which is no longer maintained.
- Attempts to disable boot wait in `config.txt`.
- Disables the interactive serial console and enables hardware serial.
- Enables SPI and I2C.
- Disables the boot splash.
- Sets the boot delay to 0.
- Disables the ctrl-alt-del systemd target.
- Enables the uhid kernel module (for xpadneo).
- Masks the userconf systemd service.
- This may not be necessary but in some cases when connecting a keyboard it was constantly running a background process to configure the key map.
- If the image being built is 32bit:
- Sets arm_64bit to 0 in `config.txt`. This is to prevent the 64bit kernel from loading in the 32bit image, this is the default behaviour with modern rpi images and it breaks compiling xpadneo as there are no cross compilation kernel headers available for the system.

### `stage-usb4vc-app`
This stage copies `user_program` into `/opt/usb4vc/rpi_app` and is also the stage where `EXPORT_IMAGE` is specified.
- Creates the `rpi_app`, `config`, `firmware` and `temp` directories under `/opt/usb4vc`.
- Installs the `usb4vc.service` systemd service to run usb4vc in the background on boot.
- This service configures the environment variables to override the compatability for `/home/pi` in usb4vc, and starts usb4vc using the virtualenv.
- Logs for usb4vc are available via `journalctl -u usb4vc.service`.
- Copies across some helper scripts and a note about the debug log.
- Copies `*.py` and `*.ttf` from `user_program` to `rpi_app`.
- Enables the `usb4vc.service` systemd service.

## Outputs
All outputs can be found in their respective `pi-gen`'s `deploy` folder. Currently 64bit and 32bit images have the same output name, but they output to different folders. The 64bit one can be renamed for upload.
19 changes: 19 additions & 0 deletions pi_image/stage-usb4vc-app/00-user-program/01-run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/bash -e

install -v -o 1000 -g 1000 -d "${ROOTFS_DIR}/opt/usb4vc/rpi_app"
install -v -o 1000 -g 1000 -d "${ROOTFS_DIR}/opt/usb4vc/config"
install -v -o 1000 -g 1000 -d "${ROOTFS_DIR}/opt/usb4vc/firmware"
install -v -o 1000 -g 1000 -d "${ROOTFS_DIR}/opt/usb4vc/temp"

install -v -m 664 "files/usb4vc.service" "${ROOTFS_DIR}/etc/systemd/system/usb4vc.service"
install -v -o 1000 -g 1000 -m 755 "files/start_usb4vc_manual.sh" "${ROOTFS_DIR}/opt/usb4vc/start_usb4vc_manual.sh"
install -v -o 1000 -g 1000 -m 644 "files/usb4vc_debug_log.txt" "${ROOTFS_DIR}/home/${FIRST_USER_NAME}/usb4vc_debug_log.txt"

rsync -r --include="*.py" --include="*.ttf" --exclude="*" \
"files/rpi_app/" "${ROOTFS_DIR}/opt/usb4vc/rpi_app/"

chown -R 1000:1000 "${ROOTFS_DIR}/opt/usb4vc/rpi_app"

on_chroot << EOF
systemctl enable usb4vc.service
EOF
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/sh -e

if [ $(id -u) -ne 0 ]; then
printf "usb4vc must be run as root. Try 'sudo start_usb4vc_manual.sh'\n"
exit 1
fi

pushd /opt/usb4vc/rpi_app
export USB4VC_IS_SYSTEMD=0
export USB4VC_INSTALL_PATH=/opt/usb4vc
export XPADNEO_SOURCE_PATH=/opt/xpadneo

/opt/usb4vc/venv/bin/python3 -u usb4vc_main.py || echo "usb4vc did not exit cleanly: $?"
popd
19 changes: 19 additions & 0 deletions pi_image/stage-usb4vc-app/00-user-program/files/usb4vc.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[Unit]
Description=usb4vc background service
After=multi-user.target

[Service]
User=root
WorkingDirectory=/opt/usb4vc/rpi_app
Environment=USB4VC_IS_SYSTEMD=1
Environment=USB4VC_INSTALL_PATH=/opt/usb4vc
Environment=XPADNEO_SOURCE_PATH=/opt/xpadneo
ExecStart=/opt/usb4vc/venv/bin/python3 -u usb4vc_main.py
Restart=always
RestartSec=1

# 169 << 8 ? (from keep_alive.py)
RestartPreventExitStatus=43264

[Install]
WantedBy=multi-user.target
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Looking for the debug log after a base pi image update?
The usb4vc service has moved to systemd and its install directory to /opt/usb4vc

Use journalctl to get logs:
sudo journalctl -u usb4vc (-f can be added to follow the log)

7 changes: 7 additions & 0 deletions pi_image/stage-usb4vc-app/EXPORT_IMAGE
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
IMG_SUFFIX="-usb4vc"
if [ "${IS_PIGEN_ARM64}" = "1" ]; then
export IMG_SUFFIX="${IMG_SUFFIX}-arm64"
fi
if [ "${USE_QEMU}" = "1" ]; then
export IMG_SUFFIX="${IMG_SUFFIX}-qemu"
fi
5 changes: 5 additions & 0 deletions pi_image/stage-usb4vc-app/prerun.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash -e

if [ ! -d "${ROOTFS_DIR}" ]; then
copy_previous
fi
19 changes: 19 additions & 0 deletions pi_image/stage-usb4vc-deps/00-dependencies/00-packages
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
git
dkms
cmake
i2c-tools
stm32flash
dfu-util
libjpeg-dev
zlib1g-dev
libfreetype6-dev
liblcms2-dev
libopenjp2-7
libtiff-dev
libudev-dev
libevdev-dev
raspberrypi-kernel-headers
udisks2
python3-pip
python3-pil
python3-venv
46 changes: 46 additions & 0 deletions pi_image/stage-usb4vc-deps/00-dependencies/01-run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/bin/bash -e

install -v -o 1000 -g 1000 -d "${ROOTFS_DIR}/opt/usb4vc"
install -v -o 1000 -g 1000 -d "${ROOTFS_DIR}/opt/xpadneo"
install -v -o 1000 -g 1000 -d "${ROOTFS_DIR}/opt/dkms-hid-nintendo"
install -v -o 1000 -g 1000 -d "${ROOTFS_DIR}/opt/joycond"

rsync -lr --exclude=.git --chown=1000:1000 "files/xpadneo/" "${ROOTFS_DIR}/opt/xpadneo/"
rsync -lr --exclude=.git --chown=1000:1000 "files/dkms-hid-nintendo/" "${ROOTFS_DIR}/opt/dkms-hid-nintendo/"
rsync -lr --exclude=.git --chown=1000:1000 "files/joycond/" "${ROOTFS_DIR}/opt/joycond/"

# create venv for usb4vc and install luma.oled
on_chroot << EOF
python3 -m venv --system-site-packages /opt/usb4vc/venv

source /opt/usb4vc/venv/bin/activate
pip install evdev spidev serial luma.oled
pip cache purge || echo "pip cache purge did nothing"
deactivate

chown -R 1000:1000 /opt/usb4vc/venv
EOF

# build and install nintendo hid, joycond
on_chroot << EOF
pushd /opt/dkms-hid-nintendo
dkms remove -m nintendo -v 3.2 --all || echo ""
dkms add .

# find the kernel version in the rootfs for dkms to target
KERNEL_VERSIONS=(\$(ls -1 "/lib/modules/"))

for kernel in "\${KERNEL_VERSIONS[@]}"; do
if [ -d "/usr/src/linux-headers-\$kernel" ]; then
dkms build nintendo -v 3.2 -k \$kernel
dkms install nintendo -v 3.2 -k \$kernel
fi
done
popd

pushd /opt/joycond
cmake .
make install
systemctl enable joycond
popd
EOF
Loading