diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2ec5260..574991e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,20 +24,9 @@ jobs: sudo apt-get install -y linux-image-$(uname -r) linux-modules-extra-$(uname -r) - name: Setup user groups run: | - echo KERNEL==\"uinput\", SUBSYSTEM==\"misc\" GROUP=\"docker\", MODE=\"0666\" | sudo tee /etc/udev/rules.d/99-$USER.rules - echo KERNEL==\"event[0-9]*\", SUBSYSTEM==\"input\" GROUP=\"docker\", MODE=\"0666\" | sudo tee -a /etc/udev/rules.d/99-$USER.rules - echo SUBSYSTEM==\"video4linux\" GROUP=\"docker\", MODE=\"0666\" | sudo tee -a /etc/udev/rules.d/99-$USER.rules - echo KERNEL==\"gpiochip[0-9]*\", SUBSYSTEM==\"gpio\", GROUP=\"docker\", MODE=\"0666\" | sudo tee -a /etc/udev/rules.d/99-$USER.rules - sudo udevadm control --reload-rules - sudo udevadm trigger - sudo modprobe -a uinput - sudo modprobe vivid n_devs=1 node_types=0xe1d3d vid_cap_nr=190 vid_out_nr=191 meta_cap_nr=192 meta_out_nr=193 - sudo modprobe gpio-sim - sudo modprobe gpio-aggregator - sudo python scripts/setup-gpio-sim.py + sudo python scripts/test_setup.py ls -lsa /dev/video* /dev/uinput /dev/gpio* /dev/inp* - - name: Set up Python ${{ matrix.python-version }} id: setuppy uses: actions/setup-python@v5 diff --git a/scripts/setup-gpio-sim.py b/scripts/setup-gpio-sim.py deleted file mode 100644 index 64bd47c..0000000 --- a/scripts/setup-gpio-sim.py +++ /dev/null @@ -1,83 +0,0 @@ -import json -import pathlib -import sys - -mounts = pathlib.Path("/proc/mounts") -for dtype, mpath, fstype, *_ in mounts.read_text().splitlines(): - if dtype == "configfs" and fstype == "configfs": - CONFIGFS_PATH = pathlib.Path(mpath) -else: - CONFIGFS_PATH = pathlib.Path("/sys/kernel/config/gpio-sim") - - -def Line(name, direction=None): - result = {"name": name} - if direction: - result["hog"] = {"name": f"{name}-hog", "direction": direction} - return result - - -DEFAULT = { - "name": "chip99", - "banks": [ - { - "lines": [ - Line("L-I0"), - Line("L-I1"), - Line("L-I2", "input"), - Line("L-O0", "output-high"), - Line("L-O1", "output-low"), - Line("L-O2"), - ] - }, - ], -} - - -if len(sys.argv) > 1: - cfg = json.load(sys.argv[-1]) -else: - cfg = DEFAULT - - -def mkdir(path): - print(f"Creating {path}") - path.mkdir() - - -def cleanup(path): - # clean up first - if path.exists(): - live.write_text("0") - for directory, _, _ in path.walk(top_down=False): - directory.rmdir() - - -path = CONFIGFS_PATH / cfg["name"] -live = path / "live" - - -cleanup(path) - -mkdir(path) -for bank_id, bank in enumerate(cfg["banks"]): - lines = bank["lines"] - - bpath = path / f"gpio-bank{bank_id}" - mkdir(bpath) - blabel = bank.get("name", f"gpio-sim-bank{bank_id}") - - (bpath / "num_lines").write_text("16") - (bpath / "label").write_text(blabel) - for line_id, line in enumerate(lines): - lpath = bpath / f"line{line_id}" - mkdir(lpath) - (lpath / "name").write_text(line.get("name", f"L-{line_id}")) - if hog := line.get("hog"): - hpath = lpath / "hog" - mkdir(hpath) - (hpath / "name").write_text(hog["name"]) - (hpath / "direction").write_text(hog["direction"]) - - -live.write_text("1") diff --git a/scripts/test_setup.py b/scripts/test_setup.py new file mode 100644 index 0000000..cfe3a59 --- /dev/null +++ b/scripts/test_setup.py @@ -0,0 +1,133 @@ +from pathlib import Path +from subprocess import run +from sys import argv + +user_group = None if len(argv) < 2 else argv[1] +input_group = user_group or "input" +video_group = user_group or "video" + +RULES = f"""\ +KERNEL=="uinput", SUBSYSTEM=="misc" GROUP="{input_group}", MODE="0666" +KERNEL=="event[0-9]*", SUBSYSTEM=="input" GROUP="{input_group}", MODE="0666" + +KERNEL=="uleds", GROUP="input", MODE="0664" +SUBSYSTEM=="leds", ACTION=="add", RUN+="/bin/chmod -R g=u,o=u /sys%p" +SUBSYSTEM=="leds", ACTION=="change", ENV{{TRIGGER}}!="none", RUN+="/bin/chmod -R g=u,o=u /sys%p" + +SUBSYSTEM=="video4linux" GROUP="{video_group}", MODE="0666" + +KERNEL=="gpiochip[0-9]*", SUBSYSTEM=="gpio", GROUP="{input_group}", MODE="0666" +""" + +MODULES = { + "uinput": "", + "uleds": "", + "gpio-sim": "", + "vivid": "n_devs=1 node_types=0xe1d3d vid_cap_nr=190 vid_out_nr=191 meta_cap_nr=192 meta_out_nr=193", + "snd_seq_midi": "", +} + + +def loaded_modules(): + return {line.split()[0] for line in Path("/proc/modules").read_text().splitlines()} + + +LOADED_MODULES = loaded_modules() + + +def handle_rules(): + print("Checking rules") + rules = Path("/etc/udev/rules.d/99-linuxpy.rules") + if rules.exists() and RULES in rules.read_text(): + print(" Already prepared") + else: + print(f" Writing rules {rules}") + rules.write_text(RULES) + print(" Reloading rules") + run("udevadm control --reload-rules".split()) + run("udevadm trigger".split()) + + +def handle_module(mod_name): + print(f"Handling {mod_name}") + if mod_name in LOADED_MODULES: + print(f" Unloading {mod_name}") + run(f"modprobe -r {mod_name}".split()) + args = MODULES[mod_name] + print(f" Loading {mod_name}") + run(f"modprobe {mod_name} {args}".split()) + + +def handle_gpio_sim(): + print("Preparing GPIO sim") + mounts = Path("/proc/mounts") + for dtype, mpath, fstype, *_ in mounts.read_text().splitlines(): + if dtype == "configfs" and fstype == "configfs": + CONFIGFS_PATH = Path(mpath) + else: + CONFIGFS_PATH = Path("/sys/kernel/config/gpio-sim") + + def Line(name, direction=None): + result = {"name": name} + if direction: + result["hog"] = {"name": f"{name}-hog", "direction": direction} + return result + + cfg = { + "name": "chip99", + "banks": [ + { + "lines": [ + Line("L-I0"), + Line("L-I1"), + Line("L-I2", "input"), + Line("L-O0", "output-high"), + Line("L-O1", "output-low"), + Line("L-O2"), + ] + }, + ], + } + + def mkdir(path): + print(f" Creating {path}") + path.mkdir() + + path = CONFIGFS_PATH / cfg["name"] + live = path / "live" + + # clean up first + if path.exists(): + live.write_text("0") + for directory, _, _ in path.walk(top_down=False): + directory.rmdir() + + mkdir(path) + for bank_id, bank in enumerate(cfg["banks"]): + lines = bank["lines"] + + bpath = path / f"gpio-bank{bank_id}" + mkdir(bpath) + blabel = bank.get("name", f"gpio-sim-bank{bank_id}") + + (bpath / "num_lines").write_text("16") + (bpath / "label").write_text(blabel) + for line_id, line in enumerate(lines): + lpath = bpath / f"line{line_id}" + mkdir(lpath) + (lpath / "name").write_text(line.get("name", f"L-{line_id}")) + if hog := line.get("hog"): + hpath = lpath / "hog" + mkdir(hpath) + (hpath / "name").write_text(hog["name"]) + (hpath / "direction").write_text(hog["direction"]) + + live.write_text("1") + + +handle_rules() + +for mod_name in MODULES: + handle_module(mod_name) + +handle_gpio_sim()