NixOS / nixpkgs

Nix Packages collection & NixOS
MIT License
17.17k stars 13.43k forks source link

Raspberry Pi4: GPIO Access and Hardware Revision Missing #122993

Open sheepforce opened 3 years ago

sheepforce commented 3 years ago

Describe the bug Accessing the GPIO functionality on a Raspberry Pi4 is hindered by some special behaviour, that seems to be NixOS specific: /proc/cpuinfo is missing the hardware revision, and some memory protection mechanisms prevent controlling GPIO pins. Therefore, both the piogpio C library and the RPi.GPIO python library do not work on NixOS. While libgpiod from nixpkgs works fine, it lacks lots of functionality (I2C, PWM, event callbacks, ...). Working around the hardware revision checks by patching the pigpio C code, one then hits some memory protection settings, resulting in a permission denied error.

To Reproduce Steps to reproduce the behavior:

  1. Build the current pigpio v79 pigpio.nix (without the patch)
  2. Execute nix-shell -p pigpio --run "pigpiod" on Raspberry Pi4 as root ->
    2021-05-14 15:48:59 initCheckPermitted: 
    +---------------------------------------------------------+
    |Sorry, this system does not appear to be a raspberry pi. |
    |aborting.                                                |
    +---------------------------------------------------------+
  3. Patch pigpio to ignore the hardware revision checks (Pi4Revision.patch.txt), Execute nix-shell -p pigpio --run "pigpiod" on Raspberry Pi4 as root -> initPeripherals: mmap gpio failed (Operation not permitted)
  4. Build RPi.GPIO for Python rpigpio.nix.txt, in a root nix-shell with this python lib the Raspberry wont be recognised as Raspberry Pi4 at all and the library will raise an error on import.

Expected behavior /proc/cpuinfo should contain the Revision field to be compatible with other operating systems and common Raspberry Pi libs. GPIO memory should be accessible.

Additional context

Notify maintainers @thoughtpolice

Metadata

 - system: `"aarch64-linux"`
 - host os: `Linux 5.10.17, NixOS, 21.05pre289039.d1601a40c48 (Okapi)`
 - multi-user?: `yes`
 - sandbox: `yes`
 - version: `nix-env (Nix) 2.3.10`
 - channels(root): `"nixos-21.05pre289039.d1601a40c48"`
 - nixpkgs: `/nix/var/nix/profiles/per-user/root/channels/nixos`

Maintainer information:

# a list of nixpkgs attributes affected by the problem
attribute:
# a list of nixos modules affected by the problem
module:
markuskowa commented 3 years ago

I also ran into that problem. I think the hardware revision problem is connected to the fact that NIxOS runs a vanilla kernel per default and not the (highly patched) kernel from the raspberry foundation. Did you try to run the raspberry pi kernel? I.e.: boot.kernelPackages = pkgs.linuxPackages_rpi4; in the configuration.nix.

CC @samueldr

sheepforce commented 3 years ago

Yep, it is running this kernel version. Here is the relevant part of the configuration.nix:

boot = {
    loader = {
      # Use the extlinux bootloader instead of Grub.
      grub.enable = false;
      generic-extlinux-compatible.enable = true;

      raspberryPi = {
        enable = true;
        version = 4;
        # Stuff that goes into /boot/config.txt
        firmwareConfig = ''
          iomem=relaxed
          strict-devmem=0
        '';
      };
    };

    # Must use a custom raspberry pi 4 kernel for drivers
    kernelPackages = pkgs.linuxPackages_rpi4;

    # Makes the TTYs working
    kernelParams = [
      "8250.nr_uarts=1"
      "console=ttyAMA0,115200"
      "console=tty1"
      "iomem=relaxed"
      "strict-devmem=0"
    ];
  };

The iomem=relaxed and strict-devmem=0 lines are taken from the second bug report against pigpio, but I am not sure if they really make it to the configuration. The Debian wiki also mentions this (section GPIO support), as the default kernel configuration seems to be the cause of at least the mmap error.

joan2937 commented 3 years ago

@sheepforce I have a couple of pure Linux libraries.

They are not as "powerful" as pigpio but may do what you want.

sheepforce commented 3 years ago

I gave the rgpio and lgpio versions a quick try (thank your for the tip @joan2937) and the examples actually work. At least I could turn on my LED on the GPIO pins using the rgpiod daemon and the rgs commands. Without having a look at all the C code; why do they work? What are they doing differently than pigpio?

joan2937 commented 3 years ago

@sheepforce They are pure Linux, they do not use any special features of any particular SBC.

pigpio is tailored to the Raspberry Pi, it will not work an any other SBC. It uses Broadcom features to do things which are not (without a GREAT deal of effort) possible on other SBCs. It needs to identify the Pi model to decide things like how many cores and GPIO. This identification step is often what goes wrong on other OSs running on the PI. They are missing a needed hook. It's never insurmountable but is not something the pigpio maintainers can generally help with as it is OS specific. Our main goal is ensuring pigpio works on PiOS (Raspbian as was).

The motivation behind lgpio/rgpio was to provide general purpose GPIO libraries using the new /dev/gpiochip interface rather than the deprecated sysfs interface. They should work on any Linux SBC out of the box.

nixos-discourse commented 2 years ago

This issue has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/raspberry-pi4-gpio-permissions/12526/3

stale[bot] commented 2 years ago

I marked this as stale due to inactivity. → More info

hexop commented 2 years ago

Still important to me

hexop commented 2 years ago

I'm interested in accesing the hardware PWMs and the I2C of the Raspberry Pi 4 but I wasn't able to get it to work. I tried to load the overlays using the following configuration:

  # A bunch of boot parameters needed for optimal runtime on RPi 4B
  boot.kernelPackages = pkgs.linuxPackages_rpi4;
  boot.kernelParams = [
    "zfs.zfs_arc_max=134217728"
    "console=TTYAMA0,115200"
    "console=tty1"
    "8250.nr_uarts=1"
    "iomem=relaxed"
    "strict-devmem=0"
  ];

  # Enable SATA-HAT GPIO features
  boot.loader.raspberryPi = {
    enable = true;
    version = 4;
    firmwareConfig = ''
      iomem=relaxed
      strict-devmem=0
      dtoverlay=pwm-2chan,pin=12,func=4,pin2=13,func2=4
      dtoverlay=w1-gpio
      dtparam=i2c1=on
    '';
  };

  # Load PWM hardware timers
  hardware.deviceTree = {
    enable = true;
    filter = "*-rpi-*.dtb";
    overlays = [
      {
        name = "pwm-2chan";
        dtboFile = "${pkgs.device-tree_rpi.overlays}/pwm-2chan.dtbo";
      }
      {
        name = "w1-gpio";
        dtboFile = "${pkgs.device-tree_rpi.overlays}/w1-gpio.dtbo";
      }
    ];
  };

  # Enable I2C
  hardware.i2c.enable = true;

Anyone has successfully loaded these device tree overlays? I am not sure I'm doing it the right way

hexop commented 2 years ago

I just realized that the dtoverlay doesn't seem to be executed on boot, if I run the following commands:

modprobe pwm_bcm2835
dtoverlay -d "/boot/nixos/4mamyanz1hlc4wz3c427qjh6rabngwvj-linux-5.10.17-1.20210303-dtbs/overlays/" pwm-2chan,pin=12,func=4,pin2=13,func2=

I can find the pwm timers at /sys/class/pwm

EDIT:

I was able to load the PWM timers by adding the modules to kernelModules:

  boot.kernelModules = [ "pwm_bcm2835" "w1-gpio" ];

And running the dtoverlay commands using a systemd service, if anyone is interested in my config you can find it here

leetNightshade commented 2 years ago

@hexop

And running the dtoverlay commands using a systemd service, if anyone is interested in my config you can find it here

How do you get yours to work? Mine complains about device-tree_rpi in:

    overlays = [
      {
        name = "pwm-2chan";
        dtboFile = "${device-tree_rpi.overlays}/pwm-2chan.dtbo";
      }
      {
        name = "w1-gpio";
        dtboFile = "${device-tree_rpi.overlays}/w1-gpio.dtbo";
      }
    ];

Errors with:

unpacking channels...
error: undefined variable 'device-tree_rpi' at /etc/nixos/configuration.nix:67:25
(use '--show-trace' to show detailed location information)
building Nix...
error: undefined variable 'device-tree_rpi' at /etc/nixos/configuration.nix:67:25
(use '--show-trace' to show detailed location information)
building the system configuration...
error: undefined variable 'device-tree_rpi' at /etc/nixos/configuration.nix:67:25
(use '--show-trace' to show detailed location information)
hexop commented 2 years ago

Errors with:

unpacking channels...
error: undefined variable 'device-tree_rpi' at /etc/nixos/configuration.nix:67:25
(use '--show-trace' to show detailed location information)
building Nix...
error: undefined variable 'device-tree_rpi' at /etc/nixos/configuration.nix:67:25
(use '--show-trace' to show detailed location information)
building the system configuration...
error: undefined variable 'device-tree_rpi' at /etc/nixos/configuration.nix:67:25
(use '--show-trace' to show detailed location information)

I have a with pkgs; in the top of that file so it should be pkgs.device-tree_rpi

newAM commented 2 years ago

Here are all my workarounds for getting basic GPIOs working on the Raspberry Pi 3B+ with NixOS 22.05 and python.

I am using poetry to manage my code, I made a couple hacks to patch in a /proc/cpuinfo from raspbian. This kills auto-detect, but for a personal project it works great.

let
  proc_cpuinfo = ./proc_cpuinfo.txt;
in
poetry2nix.mkPoetryApplication {
  projectDir = ./.;
  # Hacks to fix: https://github.com/NixOS/nixpkgs/issues/122993
  overrides = pkgs.poetry2nix.overrides.withDefaults (self: super: {
    rpi-gpio = super.rpi-gpio.overridePythonAttrs (old: {
      postPatch = ''
        substituteInPlace source/cpuinfo.c \
          --replace "/proc/cpuinfo" "${proc_cpuinfo}"
      '';
    });
    gpiozero = super.gpiozero.overridePythonAttrs (old: {
      postPatch = ''
        substituteInPlace gpiozero/pins/local.py \
          --replace "/proc/cpuinfo" "${proc_cpuinfo}"
      '';
    });
  });
};

Then I got errors because /dev/gpiomem did not exist.

I needed to set boot.kernelPackages to pkgs.linuxKernel.packages.linux_rpi3.

Then it stopped building because of another issue, #154163.

I did the workaround for #154163, an overlay to ignore the problem:

nixpkgs.overlays = [
  (final: super: {
    makeModulesClosure = x:
      super.makeModulesClosure (x // { allowMissing = true; });
  })
];

Then I had kernel panic at boot because of brcmfmac, some closed source binary blob 💩.

Disabling nasty closed source blobs with hardware.enableRedistributableFirmware = false; made it boot.

4 hacks/patches later GPIOs work :tada:

illegalprime commented 1 year ago

@newAM @hexop @sheepforce I didn't see this bug but I merged this in a while ago:

https://github.com/NixOS/nixos-hardware/pull/478

newAM commented 1 year ago

@illegalprime where did that DTS come from? Presumably the same thing could be done for the other PIs?

adminy commented 1 year ago

For GPIO, see if the commands from libgpiod work. Those use the new character device ABI (/dev/gpiochip0), which is the preferred way to do such things these days. If you can find a library for your language that uses that ABI, that would be best.

Not sure what this means but sounds like someone should document this as it seems like libgpiod is the way to go forwards. I was using rpio and ran in all sorts of bugs, started thinking that maybe raspberry Pi 4b is just not suitable for NixOS. Dunno if this still applies to the likes of orange Pi 5 plus though.

nixos-discourse commented 5 months ago

This issue has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/enable-1-wire-on-raspberrpy-4-cm/41886/2

nixos-discourse commented 1 month ago

This issue has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/missing-pwm-on-raspberry-pi-3b/49089/1

nixos-discourse commented 1 month ago

This issue has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/packaging-the-lgpio-python-package/49165/1

doronbehar commented 1 month ago

I don't actually use it yet, so I cannot guarantee it works, but I added all of @joan2937's packages to Nixpkgs in https://github.com/NixOS/nixpkgs/pull/316936 .