diff options
author | Bartosz Golaszewski <bartosz.golaszewski@linaro.org> | 2024-12-10 13:32:43 +0100 |
---|---|---|
committer | Khem Raj <raj.khem@gmail.com> | 2024-12-10 13:43:54 -0800 |
commit | ae88d2ff59eb851d7badbe5ff8eb11ed7040aeb9 (patch) | |
tree | 7e3729f956c9999b75572b4a5ecb2a74bddaa688 | |
parent | 06eacb769a9915b6eff2d5bf1957ef088ee74c1c (diff) | |
download | meta-openembedded-ae88d2ff59eb851d7badbe5ff8eb11ed7040aeb9.tar.gz |
gpiod-sysfs-proxy: new recipe
Many users are reluctant to use libgpiod instead of the deprecated
/sys/class/gpio interface. The gpiod-sysfs-proxy project aims at making
the transition easier by implementing a compatibility layer in
user-space using FUSE and python3-gpiod. This way we can eat the cookie
by disabling the sysfs ABI and have the users have it too by sticking to
their existing scripts.
The project itself is a very simple setuptools-based python package but
the recipe is quite complex due to comprehensive distro integration.
By default we use /run/gpio as mountpoint. For full backward
compatibility with the kernel interface, the user must explicitly add
the 'sys-class-mount' switch to PACKAGECONFIG. We do this because,
depending on whether CONFIG_GPIO_SYSFS Kconfig option is enabled,
/sys/class/gpio will either be non-empty or not exist at all. In the
latter case, we need to somehow create the /sys/class/gpio and, since
user-space is not allowed to mkdir() inside sysfs, we use overlayfs for
that. As this is rather non-standard, we want the user to be aware of
this.
We support both systemd and sys V init managers.
We also provide a ptest package which uses an external
gpio-sysfs-compat-tests script.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Signed-off-by: Khem Raj <raj.khem@gmail.com>
7 files changed, 238 insertions, 0 deletions
diff --git a/meta-filesystems/conf/layer.conf b/meta-filesystems/conf/layer.conf index 8a0c831e2b..5323913d58 100644 --- a/meta-filesystems/conf/layer.conf +++ b/meta-filesystems/conf/layer.conf | |||
@@ -16,3 +16,7 @@ LAYERVERSION_filesystems-layer = "1" | |||
16 | LAYERDEPENDS_filesystems-layer = "core openembedded-layer networking-layer" | 16 | LAYERDEPENDS_filesystems-layer = "core openembedded-layer networking-layer" |
17 | 17 | ||
18 | LAYERSERIES_COMPAT_filesystems-layer = "styhead walnascar" | 18 | LAYERSERIES_COMPAT_filesystems-layer = "styhead walnascar" |
19 | |||
20 | BBFILES_DYNAMIC += " \ | ||
21 | meta-python:${LAYERDIR}/dynamic-layers/meta-python/recipes-*/*/*.bb \ | ||
22 | " | ||
diff --git a/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/gpiod-sysfs-proxy.init.in b/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/gpiod-sysfs-proxy.init.in new file mode 100644 index 0000000000..a9cf5e4075 --- /dev/null +++ b/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/gpiod-sysfs-proxy.init.in | |||
@@ -0,0 +1,84 @@ | |||
1 | #! /bin/sh | ||
2 | ### BEGIN INIT INFO | ||
3 | # Provides: gpiod-sysfs-proxy | ||
4 | # Required-Start: $remote_fs $syslog | ||
5 | # Required-Stop: $remote_fs $syslog | ||
6 | # Default-Start: 2 3 4 5 | ||
7 | # Default-Stop: 1 | ||
8 | # Short-Description: User-space, libgpiod-based compatibility layer for linux GPIO sysfs interface. | ||
9 | ### END INIT INFO | ||
10 | # | ||
11 | # -*- coding: utf-8 -*- | ||
12 | # Debian init.d script for gpiod-sysfs-proxy | ||
13 | # Copyright (c) 2024 Bartosz Golaszewski <bartosz.golaszewski@linaro.org> | ||
14 | |||
15 | # set -e | ||
16 | |||
17 | # Source function library. | ||
18 | . /etc/init.d/functions | ||
19 | |||
20 | PROG="/usr/bin/gpiod-sysfs-proxy" | ||
21 | NAME="gpiod-sysfs-proxy" | ||
22 | DESC="/sys/class/gpio compatibility layer" | ||
23 | MOUNTPOINT="@mountpoint@" | ||
24 | |||
25 | test -x $PROG || exit 0 | ||
26 | |||
27 | do_start() | ||
28 | { | ||
29 | echo -n "Starting $DESC: " | ||
30 | |||
31 | if [ "$MOUNTPOINT" = "/sys/class/gpio" ] && [ ! -e /sys/class/gpio ]; then | ||
32 | mkdir -p /run/gpio/sys /run/gpio/class/gpio /run/gpio/work | ||
33 | mount -t sysfs sysfs /run/gpio/sys -o nosuid,nodev,noexec | ||
34 | # Bail out if overlayfs is not available | ||
35 | set -e | ||
36 | mount -t overlay overlay /sys/class \ | ||
37 | -o upperdir=/run/gpio/class,lowerdir=/run/gpio/sys/class,workdir=/run/gpio/work,nosuid,nodev,noexec,relatime,ro | ||
38 | set +e | ||
39 | else | ||
40 | mkdir -p $MOUNTPOINT | ||
41 | fi | ||
42 | |||
43 | $PROG $MOUNTPOINT -o nonempty -o allow_other -o default_permissions -o entry_timeout=0 -f | logger -i $NAME & | ||
44 | echo "done" | ||
45 | } | ||
46 | |||
47 | do_stop() | ||
48 | { | ||
49 | echo -n "Stopping $DESC: " | ||
50 | |||
51 | umount $MOUNTPOINT | ||
52 | |||
53 | mountpoint -q /sys/class | ||
54 | if [ "$?" = "0" ]; then | ||
55 | umount /sys/class | ||
56 | umount /run/gpio/sys | ||
57 | rm -rf /run/gpio | ||
58 | fi | ||
59 | echo "done" | ||
60 | } | ||
61 | |||
62 | case "$1" in | ||
63 | start) | ||
64 | do_start | ||
65 | ;; | ||
66 | stop) | ||
67 | do_stop | ||
68 | ;; | ||
69 | status) | ||
70 | status $PROG | ||
71 | exit $? | ||
72 | ;; | ||
73 | restart) | ||
74 | do_stop | ||
75 | sleep 1 | ||
76 | do_start | ||
77 | ;; | ||
78 | *) | ||
79 | echo "Usage: /etc/init.d/$NAME {start|stop|status|restart}" >&2 | ||
80 | exit 1 | ||
81 | ;; | ||
82 | esac | ||
83 | |||
84 | exit 0 | ||
diff --git a/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/gpiod-sysfs-proxy.service.in b/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/gpiod-sysfs-proxy.service.in new file mode 100644 index 0000000000..313523268c --- /dev/null +++ b/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/gpiod-sysfs-proxy.service.in | |||
@@ -0,0 +1,15 @@ | |||
1 | # SPDX-License-Identifier: CC0-1.0 | ||
2 | # SPDX-FileCopyrightText: 2024 Bartosz Golaszewski <bartosz.golaszewski@linaro.org> | ||
3 | |||
4 | [Unit] | ||
5 | Description=User-space, libgpiod-based compatibility layer for linux GPIO sysfs interface | ||
6 | |||
7 | [Service] | ||
8 | RuntimeDirectory=gpio | ||
9 | Type=simple | ||
10 | ExecStart=/usr/bin/gpiod-sysfs-proxy @mountpoint@ -f -o nonempty -o allow_other -o default_permissions -o entry_timeout=0 | ||
11 | ExecStop=/bin/umount @mountpoint@ | ||
12 | Restart=always | ||
13 | |||
14 | [Install] | ||
15 | WantedBy=multi-user.target | ||
diff --git a/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/run-gpio-sys.mount b/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/run-gpio-sys.mount new file mode 100644 index 0000000000..a924cb9b64 --- /dev/null +++ b/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/run-gpio-sys.mount | |||
@@ -0,0 +1,13 @@ | |||
1 | # SPDX-License-Identifier: CC0-1.0 | ||
2 | # SPDX-FileCopyrightText: 2024 Bartosz Golaszewski <bartosz.golaszewski@linaro.org> | ||
3 | |||
4 | [Unit] | ||
5 | Description=Remount of sysfs for gpiod-sysfs-proxy | ||
6 | ConditionPathExists=!/sys/class/gpio | ||
7 | |||
8 | [Mount] | ||
9 | DirectoryMode=0700 | ||
10 | What=sysfs | ||
11 | Where=/run/gpio/sys | ||
12 | Type=sysfs | ||
13 | Options=nosuid,nodev,noexec | ||
diff --git a/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/run-ptest.in b/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/run-ptest.in new file mode 100644 index 0000000000..aab3a5ce3e --- /dev/null +++ b/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/run-ptest.in | |||
@@ -0,0 +1,21 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | ptestdir=$(dirname "$(readlink -f "$0")") | ||
4 | testbin="gpio-sysfs-compat-tests" | ||
5 | |||
6 | modprobe gpio-sim | ||
7 | modprobe configfs | ||
8 | |||
9 | mountpoint -q /sys/kernel/config | ||
10 | if [ "$?" -ne "0" ]; then | ||
11 | mount -t configfs configfs /sys/kernel/config | ||
12 | fi | ||
13 | |||
14 | cd $ptestdir/tests | ||
15 | |||
16 | ./$testbin -v --gpio-class @mountpoint@ --chown-user gpio-test > ./$testbin.out 2>&1 | ||
17 | if [ $? -ne 0 ]; then | ||
18 | echo "FAIL: $testbin" | ||
19 | else | ||
20 | echo "PASS: $testbin" | ||
21 | fi | ||
diff --git a/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/sys-class.mount b/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/sys-class.mount new file mode 100644 index 0000000000..e3e3ce8e74 --- /dev/null +++ b/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/sys-class.mount | |||
@@ -0,0 +1,16 @@ | |||
1 | # SPDX-License-Identifier: CC0-1.0 | ||
2 | # SPDX-FileCopyrightText: 2024 Bartosz Golaszewski <bartosz.golaszewski@linaro.org> | ||
3 | |||
4 | [Unit] | ||
5 | Description=Overlay on top of /sys/class adding the gpio class directory | ||
6 | Before=gpiod-sysfs-proxy.service | ||
7 | After=run-gpio-sys.mount | ||
8 | ConditionPathExists=!/sys/class/gpio | ||
9 | |||
10 | [Mount] | ||
11 | RuntimeDirectory=gpio/class/gpio | ||
12 | DirectoryMode=0755 | ||
13 | What=overlay | ||
14 | Where=/sys/class | ||
15 | Type=overlay | ||
16 | Options=upperdir=/run/gpio/class,lowerdir=/run/gpio/sys/class,workdir=/run/gpio/work,ro,nosuid,nodev,noexec,relatime | ||
diff --git a/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy_0.1.1.bb b/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy_0.1.1.bb new file mode 100644 index 0000000000..4d466d30f0 --- /dev/null +++ b/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy_0.1.1.bb | |||
@@ -0,0 +1,85 @@ | |||
1 | SUMMARY = "User-space, libgpiod-based compatibility layer for linux GPIO sysfs interface." | ||
2 | |||
3 | LICENSE = "MIT" | ||
4 | LIC_FILES_CHKSUM = "file://COPYING;md5=0dcf8b702b5c96178978c7223f64a73b" | ||
5 | |||
6 | inherit systemd update-rc.d ptest pypi python_pep517 python_setuptools_build_meta useradd | ||
7 | |||
8 | PYPI_PACKAGE = "gpiod_sysfs_proxy" | ||
9 | |||
10 | SRC_URI += " \ | ||
11 | file://gpiod-sysfs-proxy.service.in \ | ||
12 | file://run-gpio-sys.mount \ | ||
13 | file://sys-class.mount \ | ||
14 | file://gpiod-sysfs-proxy.init.in \ | ||
15 | file://run-ptest.in \ | ||
16 | " | ||
17 | |||
18 | SRC_URI[sha256sum] = "c7830cb6a2c01914df2bc0549aef2dcfcb955520d400f65b3b50fb7a6f77f1b4" | ||
19 | |||
20 | # For full backward compatibility with the kernel sysfs interface, this option | ||
21 | # must be selected. However, we don't make it the default as - with kernel sysfs | ||
22 | # disabled - it plays a silly game with /sys/class, where it mounts a read-only | ||
23 | # overlay containing the missing /sys/class/gpio directory. This is a rather | ||
24 | # non-standard behavior so make sure the user actually wants it. | ||
25 | PACKAGECONFIG[sys-class-mount] = "" | ||
26 | |||
27 | export MOUNTPOINT="${@bb.utils.contains('PACKAGECONFIG', 'sys-class-mount', '\/sys\/class\/gpio', '\/run\/gpio', d)}" | ||
28 | |||
29 | do_install:append() { | ||
30 | if ${@bb.utils.contains('DISTRO_FEATURES','systemd','true','false',d)}; then | ||
31 | install -d ${D}${systemd_system_unitdir} | ||
32 | install -m 0644 ${UNPACKDIR}/gpiod-sysfs-proxy.service.in ${D}${systemd_system_unitdir}/gpiod-sysfs-proxy.service | ||
33 | |||
34 | if ${@bb.utils.contains('PACKAGECONFIG', 'sys-class-mount', 'true', 'false', d)}; then | ||
35 | install -d ${D}${systemd_system_unitdir}/sysinit.target.wants/ | ||
36 | |||
37 | install -m 0644 ${UNPACKDIR}/run-gpio-sys.mount ${D}${systemd_system_unitdir}/run-gpio-sys.mount | ||
38 | install -m 0644 ${UNPACKDIR}/sys-class.mount ${D}${systemd_system_unitdir}/sys-class.mount | ||
39 | |||
40 | ln -sf ../run-gpio-sys.mount ${D}${systemd_system_unitdir}/sysinit.target.wants/run-gpio-sys.mount | ||
41 | ln -sf ../sys-class.mount ${D}${systemd_system_unitdir}/sysinit.target.wants/sys-class.mount | ||
42 | fi | ||
43 | |||
44 | sed -i "s/@mountpoint@/$MOUNTPOINT/g" ${D}${systemd_system_unitdir}/gpiod-sysfs-proxy.service | ||
45 | elif ${@bb.utils.contains('DISTRO_FEATURES', 'sysvinit', 'true', 'false', d)}; then | ||
46 | install -d ${D}${sysconfdir}/init.d | ||
47 | install -m 0755 ${UNPACKDIR}/gpiod-sysfs-proxy.init.in ${D}${sysconfdir}/init.d/gpiod-sysfs-proxy | ||
48 | sed -i "s/@mountpoint@/$MOUNTPOINT/g" ${D}${sysconfdir}/init.d/gpiod-sysfs-proxy | ||
49 | fi | ||
50 | } | ||
51 | |||
52 | SYSTEMD_SERVICE:${PN} = "gpiod-sysfs-proxy.service" | ||
53 | SYSTEMD_AUTO_ENABLE = "enable" | ||
54 | |||
55 | INITSCRIPT_NAME = "gpiod-sysfs-proxy" | ||
56 | INITSCRIPT_PARAMS = "start 20 2 3 4 5 . stop 20 0 1 6 ." | ||
57 | |||
58 | FILES:${PN} += "/usr/lib/systemd/system" | ||
59 | |||
60 | RDEPENDS:${PN} += " \ | ||
61 | python3-fuse \ | ||
62 | python3-gpiod \ | ||
63 | python3-pyudev \ | ||
64 | " | ||
65 | |||
66 | python __anonymous() { | ||
67 | if d.getVar("PTEST_ENABLED") == "1": | ||
68 | d.appendVar("SRC_URI", "git://github.com/brgl/gpio-sysfs-compat-tests;protocol=https;branch=main;destsuffix=tests;name=tests") | ||
69 | d.setVar("SRCREV_tests", "a3c9daa4650dd1e8d7fd8972db68d9c2c204263d") | ||
70 | } | ||
71 | |||
72 | do_install_ptest() { | ||
73 | install -d ${D}${PTEST_PATH}/tests/ | ||
74 | install -m 0755 ${UNPACKDIR}/run-ptest.in ${D}${PTEST_PATH}/run-ptest | ||
75 | sed -i "s/@mountpoint@/$MOUNTPOINT/g" ${D}${PTEST_PATH}/run-ptest | ||
76 | install -m 0755 ${UNPACKDIR}/tests/gpio-sysfs-compat-tests ${D}${PTEST_PATH}/tests/gpio-sysfs-compat-tests | ||
77 | } | ||
78 | |||
79 | # Test user is created for verifying chown() and chmod() operations. | ||
80 | USERADD_PACKAGES = "${PN}-ptest" | ||
81 | GROUPADD_PARAM:${PN}-ptest = "--system gpio-test" | ||
82 | USERADD_PARAM:${PN}-ptest = "--system -M -s /bin/nologin -g gpio-test gpio-test" | ||
83 | |||
84 | RDEPENDS:${PN}-ptest += "kmod" | ||
85 | RRECOMMENDS:${PN}-ptest += "kernel-module-gpio-sim kernel-module-configfs" | ||