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>
This commit is contained in:
Bartosz Golaszewski 2024-12-10 13:32:43 +01:00 committed by Khem Raj
parent 06eacb769a
commit ae88d2ff59
No known key found for this signature in database
GPG Key ID: BB053355919D3314
7 changed files with 238 additions and 0 deletions

View File

@ -16,3 +16,7 @@ LAYERVERSION_filesystems-layer = "1"
LAYERDEPENDS_filesystems-layer = "core openembedded-layer networking-layer"
LAYERSERIES_COMPAT_filesystems-layer = "styhead walnascar"
BBFILES_DYNAMIC += " \
meta-python:${LAYERDIR}/dynamic-layers/meta-python/recipes-*/*/*.bb \
"

View File

@ -0,0 +1,84 @@
#! /bin/sh
### BEGIN INIT INFO
# Provides: gpiod-sysfs-proxy
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 1
# Short-Description: User-space, libgpiod-based compatibility layer for linux GPIO sysfs interface.
### END INIT INFO
#
# -*- coding: utf-8 -*-
# Debian init.d script for gpiod-sysfs-proxy
# Copyright (c) 2024 Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
# set -e
# Source function library.
. /etc/init.d/functions
PROG="/usr/bin/gpiod-sysfs-proxy"
NAME="gpiod-sysfs-proxy"
DESC="/sys/class/gpio compatibility layer"
MOUNTPOINT="@mountpoint@"
test -x $PROG || exit 0
do_start()
{
echo -n "Starting $DESC: "
if [ "$MOUNTPOINT" = "/sys/class/gpio" ] && [ ! -e /sys/class/gpio ]; then
mkdir -p /run/gpio/sys /run/gpio/class/gpio /run/gpio/work
mount -t sysfs sysfs /run/gpio/sys -o nosuid,nodev,noexec
# Bail out if overlayfs is not available
set -e
mount -t overlay overlay /sys/class \
-o upperdir=/run/gpio/class,lowerdir=/run/gpio/sys/class,workdir=/run/gpio/work,nosuid,nodev,noexec,relatime,ro
set +e
else
mkdir -p $MOUNTPOINT
fi
$PROG $MOUNTPOINT -o nonempty -o allow_other -o default_permissions -o entry_timeout=0 -f | logger -i $NAME &
echo "done"
}
do_stop()
{
echo -n "Stopping $DESC: "
umount $MOUNTPOINT
mountpoint -q /sys/class
if [ "$?" = "0" ]; then
umount /sys/class
umount /run/gpio/sys
rm -rf /run/gpio
fi
echo "done"
}
case "$1" in
start)
do_start
;;
stop)
do_stop
;;
status)
status $PROG
exit $?
;;
restart)
do_stop
sleep 1
do_start
;;
*)
echo "Usage: /etc/init.d/$NAME {start|stop|status|restart}" >&2
exit 1
;;
esac
exit 0

View File

@ -0,0 +1,15 @@
# SPDX-License-Identifier: CC0-1.0
# SPDX-FileCopyrightText: 2024 Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
[Unit]
Description=User-space, libgpiod-based compatibility layer for linux GPIO sysfs interface
[Service]
RuntimeDirectory=gpio
Type=simple
ExecStart=/usr/bin/gpiod-sysfs-proxy @mountpoint@ -f -o nonempty -o allow_other -o default_permissions -o entry_timeout=0
ExecStop=/bin/umount @mountpoint@
Restart=always
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,13 @@
# SPDX-License-Identifier: CC0-1.0
# SPDX-FileCopyrightText: 2024 Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
[Unit]
Description=Remount of sysfs for gpiod-sysfs-proxy
ConditionPathExists=!/sys/class/gpio
[Mount]
DirectoryMode=0700
What=sysfs
Where=/run/gpio/sys
Type=sysfs
Options=nosuid,nodev,noexec

View File

@ -0,0 +1,21 @@
#!/bin/sh
ptestdir=$(dirname "$(readlink -f "$0")")
testbin="gpio-sysfs-compat-tests"
modprobe gpio-sim
modprobe configfs
mountpoint -q /sys/kernel/config
if [ "$?" -ne "0" ]; then
mount -t configfs configfs /sys/kernel/config
fi
cd $ptestdir/tests
./$testbin -v --gpio-class @mountpoint@ --chown-user gpio-test > ./$testbin.out 2>&1
if [ $? -ne 0 ]; then
echo "FAIL: $testbin"
else
echo "PASS: $testbin"
fi

View File

@ -0,0 +1,16 @@
# SPDX-License-Identifier: CC0-1.0
# SPDX-FileCopyrightText: 2024 Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
[Unit]
Description=Overlay on top of /sys/class adding the gpio class directory
Before=gpiod-sysfs-proxy.service
After=run-gpio-sys.mount
ConditionPathExists=!/sys/class/gpio
[Mount]
RuntimeDirectory=gpio/class/gpio
DirectoryMode=0755
What=overlay
Where=/sys/class
Type=overlay
Options=upperdir=/run/gpio/class,lowerdir=/run/gpio/sys/class,workdir=/run/gpio/work,ro,nosuid,nodev,noexec,relatime

View File

@ -0,0 +1,85 @@
SUMMARY = "User-space, libgpiod-based compatibility layer for linux GPIO sysfs interface."
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://COPYING;md5=0dcf8b702b5c96178978c7223f64a73b"
inherit systemd update-rc.d ptest pypi python_pep517 python_setuptools_build_meta useradd
PYPI_PACKAGE = "gpiod_sysfs_proxy"
SRC_URI += " \
file://gpiod-sysfs-proxy.service.in \
file://run-gpio-sys.mount \
file://sys-class.mount \
file://gpiod-sysfs-proxy.init.in \
file://run-ptest.in \
"
SRC_URI[sha256sum] = "c7830cb6a2c01914df2bc0549aef2dcfcb955520d400f65b3b50fb7a6f77f1b4"
# For full backward compatibility with the kernel sysfs interface, this option
# must be selected. However, we don't make it the default as - with kernel sysfs
# disabled - it plays a silly game with /sys/class, where it mounts a read-only
# overlay containing the missing /sys/class/gpio directory. This is a rather
# non-standard behavior so make sure the user actually wants it.
PACKAGECONFIG[sys-class-mount] = ""
export MOUNTPOINT="${@bb.utils.contains('PACKAGECONFIG', 'sys-class-mount', '\/sys\/class\/gpio', '\/run\/gpio', d)}"
do_install:append() {
if ${@bb.utils.contains('DISTRO_FEATURES','systemd','true','false',d)}; then
install -d ${D}${systemd_system_unitdir}
install -m 0644 ${UNPACKDIR}/gpiod-sysfs-proxy.service.in ${D}${systemd_system_unitdir}/gpiod-sysfs-proxy.service
if ${@bb.utils.contains('PACKAGECONFIG', 'sys-class-mount', 'true', 'false', d)}; then
install -d ${D}${systemd_system_unitdir}/sysinit.target.wants/
install -m 0644 ${UNPACKDIR}/run-gpio-sys.mount ${D}${systemd_system_unitdir}/run-gpio-sys.mount
install -m 0644 ${UNPACKDIR}/sys-class.mount ${D}${systemd_system_unitdir}/sys-class.mount
ln -sf ../run-gpio-sys.mount ${D}${systemd_system_unitdir}/sysinit.target.wants/run-gpio-sys.mount
ln -sf ../sys-class.mount ${D}${systemd_system_unitdir}/sysinit.target.wants/sys-class.mount
fi
sed -i "s/@mountpoint@/$MOUNTPOINT/g" ${D}${systemd_system_unitdir}/gpiod-sysfs-proxy.service
elif ${@bb.utils.contains('DISTRO_FEATURES', 'sysvinit', 'true', 'false', d)}; then
install -d ${D}${sysconfdir}/init.d
install -m 0755 ${UNPACKDIR}/gpiod-sysfs-proxy.init.in ${D}${sysconfdir}/init.d/gpiod-sysfs-proxy
sed -i "s/@mountpoint@/$MOUNTPOINT/g" ${D}${sysconfdir}/init.d/gpiod-sysfs-proxy
fi
}
SYSTEMD_SERVICE:${PN} = "gpiod-sysfs-proxy.service"
SYSTEMD_AUTO_ENABLE = "enable"
INITSCRIPT_NAME = "gpiod-sysfs-proxy"
INITSCRIPT_PARAMS = "start 20 2 3 4 5 . stop 20 0 1 6 ."
FILES:${PN} += "/usr/lib/systemd/system"
RDEPENDS:${PN} += " \
python3-fuse \
python3-gpiod \
python3-pyudev \
"
python __anonymous() {
if d.getVar("PTEST_ENABLED") == "1":
d.appendVar("SRC_URI", "git://github.com/brgl/gpio-sysfs-compat-tests;protocol=https;branch=main;destsuffix=tests;name=tests")
d.setVar("SRCREV_tests", "a3c9daa4650dd1e8d7fd8972db68d9c2c204263d")
}
do_install_ptest() {
install -d ${D}${PTEST_PATH}/tests/
install -m 0755 ${UNPACKDIR}/run-ptest.in ${D}${PTEST_PATH}/run-ptest
sed -i "s/@mountpoint@/$MOUNTPOINT/g" ${D}${PTEST_PATH}/run-ptest
install -m 0755 ${UNPACKDIR}/tests/gpio-sysfs-compat-tests ${D}${PTEST_PATH}/tests/gpio-sysfs-compat-tests
}
# Test user is created for verifying chown() and chmod() operations.
USERADD_PACKAGES = "${PN}-ptest"
GROUPADD_PARAM:${PN}-ptest = "--system gpio-test"
USERADD_PARAM:${PN}-ptest = "--system -M -s /bin/nologin -g gpio-test gpio-test"
RDEPENDS:${PN}-ptest += "kmod"
RRECOMMENDS:${PN}-ptest += "kernel-module-gpio-sim kernel-module-configfs"