Device specific package

From postmarketOS

We aim to have ideally only one device specific package. Other packages should be shared between all devices.

However, in practice, we need an extra package for the kernel (unless the device has been mainlined). And sometimes one for device specific firmware (in theory it is possible to divide these in the firmware files for specific chips, and then share these chip-specific firmware packages across multiple devices, but we're not quite there yet).

Every device specific package contains at least the deviceinfo file, and the APKBUILD. This article describes special characteristics of the device APKBUILD.

Generate a template

The recommended way to start with a new device package is using pmbootstrap init and typing in a new device name. But it is also possible to directly call the device package wizard with pmbootstrap aportgen device-changeme-changeme (adjust changeme accordingly).

devicepkg-dev

In order to share code between the APKBUILDs of these packages, all new device packages depend on a devicepkg-dev package and call its functions inside their APKBUILDs:

makedepends="devicepkg-dev"

build() {
	devicepkg_build $startdir $pkgname
}

package() {
	devicepkg_package $startdir $pkgname
}

The variables $startdir and $pkgname are always available where they are used, and must be passed to these functions. devicepkg_build and devicepkg_package are simple shell scripts, and they get packaged from here.

devicepkg_build

Optionally creates an udev rule for the device's touch screen, based on the data from the deviceinfo. We can extend this to do more when it makes sense.

The source code is in devicepkg_build.sh of the devicepkg-dev pmaport.

devicepkg_package

Installs the deviceinfo file (to /etc/deviceinfo) and all other files, that may have been generated during devicepkg_build.

The source code is in devicepkg_package.sh of the devicepkg-dev pmaport.

Use install_if to pull in packages

If your device requires a specific X11 driver for example, it is possible to say: "Install this driver when this device package and Xorg are installed.".

Here is one example from device-qemu-amd64:

subpackages="$pkgname-x11"

x11() {
	install_if="$pkgname=$pkgver-r$pkgrel xorg-server"
	depends="xf86-video-qxl"
	mkdir "$subpkgdir"
}

We only create the $subpkgdir, because building subpackages fails, when the subpackage folder does not exist. You can add multiple subpackages per package, see APKBUILD_Reference#subpackages.

Add a device specific config file

It might be, that you would like to disable Xweston for your device, because it does not work and then weston does not start. To accomplish this, we will also make use of install_if and a subpackage. But this time we won't pull in another package, but install the config file:

subpackages="$pkgname-weston"
source="deviceinfo weston.ini"

weston() {
	install_if="$pkgname=$pkgver-r$pkgrel weston"
	install -Dm644 "$srcdir"/weston.ini \
		"$subpkgdir"/etc/xdg/weston/weston.ini
}

This will only install the custom Weston config, when weston is installed. The $subpkgdir is implicitly created with install -D.

Camera subpackage

Right now the state in postmarketOS is, that we have multiple camera applications and some only work for some devices. Until we reach the state where one camera application nicely works for every device, or possibly one per UI, we add camera subpackages to devices. As device maintainer, test the various camera applications and add a subpackage like the following for the camera app that works best. It is likely that none of them work out of the box, until you add support for your device upstream.

subpackages="$pkgname-camera"

camera() {
	install_if="$pkgname=$pkgver-r$pkgrel postmarketos-default-camera"
	depends="megapixels"
	mkdir -p "$subpkgdir"
}

See pmaports!3815 for details.

Proprietary firmware

The actual blobs don't belong into the device package, but into their own package:

  • Either a linux-firmware-* package from Alpine
  • Or a firmware- package from postmarketOS' aports
    • check out the existing ones we already have for reference.

Once you have that package, you have to decide which firmware is critical for the device to work and which is optional instead:

  • For firmware that is necessary for the device to boot, it should be included in the depends variable. This makes it impossible for users to disable it. Examples of these situations are firmware that allows the device to be charged, and firmware which is necessary for the display to work.
  • For firmware that provides some functionality but without which the device works <it>just fine</it>. It should be included in the _pmb_recommends variable. This makes it possible for users to remove the firmware if they don't need the provided functionality (for example, when using phones as arm build servers). Examples of these situations are Bluetooth or GPU firmware.

Multiple kernels

Use cases

It is possible to let the user choose between multiple kernels for one device during pmbootstrap init. Some use cases:

  • selection between downstream kernel and mainline kernel (e.g. because mainline isn't as functional as downstream yet)
  • combining similar devices in one device package, with minor differences in:
    • the deviceinfo file (example: two dtbs)
    • dependencies on other packages

How to

  • Remove the current kernel package from depends of your device's APKBUILD
  • Add the kernel subpackages and subpackage functions. One example from device-sony-amami:
...
subpackages="
	$pkgname-kernel-downstream:kernel_downstream
	$pkgname-kernel-mainline:kernel_mainline
	$pkgname-nonfree-firmware:nonfree_firmware
"
...
kernel_downstream() {
	pkgdesc="Display and wifi works (see device table for details)"
	depends="linux-sony-amami"
	devicepkg_subpackage_kernel $startdir $pkgname $subpkgname
}

kernel_mainline() {
	pkgdesc="For kernel development only (most features aren't working)"
	depends="linux-postmarketos-mainline"
	devicepkg_subpackage_kernel $startdir $pkgname $subpkgname
}
...
  • Additionally in your deviceinfo file you can use the suffix from your kernel subpackage (e.g. _mainline or _downstream) to limit the variables to one kernel variant in case you need different values. An example:
...
deviceinfo_dtb_mainline="qcom-msm8974-fairphone-fp2"
deviceinfo_append_dtb_mainline="true"
...
deviceinfo_bootimg_qcdt_downstream="true"
...
  • Also consider adjusting the dependencies of your device package to not include downstream- or mainline-specific helper packages, such as adsp-audio, wcnss-wlan or msm-modem-downstream. You can use device-fairphone-fp2 as an example for that.
  • Run pmbootstrap init afterwards to test the change. It should ask you for the kernel now.
  • See device-qemu-amd64 for another example.
  • Implementation details and discussion: !1363, devicepkg_subpackage_kernel source

Initramfs

Kernel modules

If your device needs to have specific kernel modules in the initramfs, add a modules-initfs file with a list of these kernel modules (and nothing else) next to your APKBUILD. Add it to source= inside the APKBUILD and it will get used.

If your device has #Multiple kernels, then add multiple modules-initfs.NAME files. Replace NAME with the suffix for the kernel you have used in the subpackages. With the examples from above it would be modules-initfs.mainline and modules-initfs.downstream.

Find examples in pmaports.git with find -name 'modules-initfs' (and for multiple kernels find -name 'modules-initfs.*').

Hooks

Some devices need to run special commands at boot time, for example turning on the display or triggering a watchdog. It is possible to do this with a device specific initramfs hook.

Create a new initfs-hook.sh file in your device package's dir and fill it with the command that needs to be executed, along with a comment describing what it does:

#!/bin/sh

# set usb properties
echo 4 > /sys/devices/platform/android_usb/usb_function_switch

Then open the APKBUILD and add initfs-hook.sh to source=.

As usually, update the checksums, build the package with --force and test the hook on your device.

Notes

If you have a modern device-specific APKBUILD (i.e. because you have just generated it), it will use devicepkg_package in package(), which will detect the initfs hook and install it automatically (pmaports!191). This is the preferred way to do things. Otherwise you would have to copy this install command to the end of your package() function.

For reference, you can look at the existing initfs-hooks:

$ cd ~/code/pmbootstrap/aports/device
$ find -name initfs-hook.sh
./device-motorola-osprey/initfs-hook.sh
./device-samsung-i9070/initfs-hook.sh
./device-htc-m8/initfs-hook.sh
./device-htc-a5ul/initfs-hook.sh
...

You can check Troubleshooting:boot:initfshooks to get more initfs hooks examples.

Android dynamic partitions

Some of the newer Android (10+) devices support so-called dynamic partitions. The separate Android dynamic partitions article explains how the deviceinfo package needs to be modified, so postmarketOS can be flashed to them without replacing the super partition. This makes it easier to switch back and forth between other operating systems.

See also