NixOS / nixos-hardware

A collection of NixOS modules covering hardware quirks.
Creative Commons Zero v1.0 Universal
2.08k stars 641 forks source link

Surface Pro 5: Camera does not work in wireplumber #1208

Open steinuil opened 1 month ago

steinuil commented 1 month ago

I just installed NixOS with the nixos-hardware module on a Surface Pro 5 and neither the camera nor the speaker work out of the box. As discussed in #970 this is caused by an issue with the camera's IPU3 in libcamera, which causes wireplumber to crash and brings audio down with it. There was a workaround proposed in the thread, and it does fix the audio but since it disables the IPU3 kernel module it renders the camera unusable. I need to use the camera on this device so I can't use this workaround.

{
  boot.blacklistedKernelModules = [ 
    "ipu3_imgu"
  ];
}

Here is the error returned by wireplumber (when the IPU3 module is not disabled):

$ journalctl --user -u wireplumber.service
Oct 26 00:40:45 rabarbaro systemd[3587]: Started Multimedia Service Session Manager.
Oct 26 00:40:46 rabarbaro wireplumber[4194]: wp-internal-comp-loader: Loading profile 'main'
Oct 26 00:40:46 rabarbaro wireplumber[4194]: [0:01:37.634143707] [4194]  INFO IPAManager ipa_manager.cpp:143 libcamera is not installed. Adding '/nix/store/src/ipa' to the IPA search path
Oct 26 00:40:46 rabarbaro wireplumber[4194]: [0:01:37.634370145] [4194]  INFO Camera camera_manager.cpp:313 libcamera v0.3.1
Oct 26 00:40:47 rabarbaro wireplumber[4194]: [0:01:37.743229029] [4337]  INFO IPAProxy ipa_proxy.cpp:198 libcamera is not installed. Loading proxy workers from '/nix/store/src/libcamera/proxy/worker'
Oct 26 00:40:47 rabarbaro wireplumber[4194]: [0:01:37.743250952] [4337] ERROR IPAProxy ipu3_ipa_proxy.cpp:52 Failed to get proxy worker path
Oct 26 00:40:47 rabarbaro wireplumber[4194]: [0:01:37.743255210] [4337] ERROR IPAManager ipa_manager.h:43 Failed to load proxy
Oct 26 00:40:47 rabarbaro systemd-coredump[4348]: Process 4194 (wireplumber) of user 1000 dumped core.

The stack trace from GDB says that the segfault is caused by the destructor for IPAProxyIPU3.

#0  0x00007f6df6b0919f in libcamera::ipa::ipu3::IPAProxyIPU3::~IPAProxyIPU3() () from /nix/store/l7s46w5jw2by6xzngxmzxwghwv9n7mpz-libcamera-0.3.2/lib/libcamera.so.0.3
#1  0x00007f6df6b09269 in libcamera::ipa::ipu3::IPAProxyIPU3::~IPAProxyIPU3() () from /nix/store/l7s46w5jw2by6xzngxmzxwghwv9n7mpz-libcamera-0.3.2/lib/libcamera.so.0.3
#2  0x00007f6df6ba1b60 in libcamera::IPU3CameraData::loadIPA() () from /nix/store/l7s46w5jw2by6xzngxmzxwghwv9n7mpz-libcamera-0.3.2/lib/libcamera.so.0.3
#3  0x00007f6df6ba33eb in libcamera::PipelineHandlerIPU3::registerCameras() () from /nix/store/l7s46w5jw2by6xzngxmzxwghwv9n7mpz-libcamera-0.3.2/lib/libcamera.so.0.3

Going from the error messages before the crash, it looks like the IPU3 IPA proxy (whatever that is) cannot be found. This proxy is in ${pkgs.libcamera}/libexec/libcamera/ipu3_ipa_proxy and is supposed to be loaded at runtime by libcamera. The logs come from the constructor for IPAProxyIPU3, which calls IPAProxy::resolvePath() to find out where these this file is located.

resolvePath() first checks if the file exists in LIBCAMERA_IPA_PROXY_PATH, so I tried providing this env variable to wireplumber:

{
  systemd.user.services.wireplumber.environment.LIBCAMERA_IPA_PROXY_PATH = "${pkgs.libcamera}/libexec/libcamera";
}

And this seems to work. But now I'm running into another error:

Oct 26 17:37:22 rabarbaro systemd[1891]: Started Multimedia Service Session Manager.
Oct 26 17:37:23 rabarbaro wireplumber[10417]: wp-internal-comp-loader: Loading profile 'main'
Oct 26 17:37:23 rabarbaro wireplumber[10417]: [1:23:38.245350622] [10417]  INFO IPAManager ipa_manager.cpp:137 libcamera is not installed. Adding '/nix/store/src/ipa' to the IPA search path
Oct 26 17:37:23 rabarbaro wireplumber[10417]: [1:23:38.245590357] [10417]  INFO Camera camera_manager.cpp:325 libcamera v0.3.2
Oct 26 17:37:23 rabarbaro wireplumber[10417]: [1:23:38.338867280] [10436]  WARN IPAProxy ipa_proxy.cpp:160 Configuration file 'ov8865.yaml' not found for IPA module 'ipu3', falling back to 'uncalibrated.yaml'
Oct 26 17:37:23 rabarbaro wireplumber[10417]: [1:23:38.353039847] [10436]  INFO IPU3 ipu3.cpp:1141 Registered Camera[0] "\_SB_.PCI0.I2C3.CAMR" connected to CSI-2 receiver 0
Oct 26 17:37:23 rabarbaro wireplumber[10417]: [1:23:38.356265278] [10436]  WARN IPAProxy ipa_proxy.cpp:160 Configuration file 'ov5693.yaml' not found for IPA module 'ipu3', falling back to 'uncalibrated.yaml'
Oct 26 17:37:23 rabarbaro wireplumber[10417]: [1:23:38.368682037] [10436]  INFO IPU3 ipu3.cpp:1141 Registered Camera[1] "\_SB_.PCI0.I2C2.CAMF" connected to CSI-2 receiver 1
Oct 26 17:37:24 rabarbaro wireplumber[10417]: wplua: wplua_pushobject: assertion 'G_IS_OBJECT (object)' failed
Oct 26 17:37:24 rabarbaro wireplumber[10417]: wplua: [string "name-node.lua"]:25: attempt to index a nil value (local 'properties')
                                              stack traceback:
                                                      [string "name-node.lua"]:25: in function <[string "name-node.lua"]:19>
Oct 26 17:37:24 rabarbaro wireplumber[10417]: wplua: wplua_pushobject: assertion 'G_IS_OBJECT (object)' failed
Oct 26 17:37:24 rabarbaro wireplumber[10417]: wplua: [string "create-node.lua"]:30: bad argument #2 to 'match_rules_update_properties' (table expected, got nil)
                                              stack traceback:
                                                      [C]: in field 'match_rules_update_properties'
                                                      [string "create-node.lua"]:30: in function <[string "create-node.lua"]:24>
Oct 26 17:37:24 rabarbaro wireplumber[10417]: [1:23:39.637440246] [10436] ERROR IPCUnixSocket ipc_unixsocket.cpp:192 Failed to send: Connection refused
Oct 26 17:37:24 rabarbaro wireplumber[10417]: [1:23:39.637460592] [10436] ERROR IPCPipe ipc_pipe_unixsocket.cpp:80 Failed to call async
Oct 26 17:37:24 rabarbaro wireplumber[10417]: [1:23:39.637694314] [10436] ERROR IPCUnixSocket ipc_unixsocket.cpp:192 Failed to send: Connection refused
Oct 26 17:37:24 rabarbaro wireplumber[10417]: [1:23:39.637702908] [10436] ERROR IPCPipe ipc_pipe_unixsocket.cpp:80 Failed to call async
Oct 26 17:37:24 rabarbaro systemd[1891]: Stopping Multimedia Service Session Manager...

The cameras do work with libcamera and I tested this by running qcam directly with the LIBCAMERA_IPA_PROXY_PATH env variable set.

nix shell nixpkgs#libcamera-qcam
LIBCAMERA_IPA_PROXY_PATH=/nix/store/<hash>-libcamera-0.3.1/libexec/libcamera qcam

So I'm not sure what exactly is happening in wireplumber.

steinuil commented 1 month ago

Ok, I looked into wireplumber and activated debug logs for the Lua scripts that are failing with systemd.user.services.wireplumber.environment.WIREPLUMBER_DEBUG = "s-monitors-*:D" and noticed that the scripts have just been rewritten in master to simplify them (commit here). The bug that it closes has similar logs to mine. I guess I'll try compiling the git version and see how it goes...

steinuil commented 4 weeks ago

I have a fix! There were two issues in both wireplumber and libcamera that caused this problem. This overlay will fix both and the other fixes above are not required:

{
  nixpkgs.overlays = [
    (final: prev: {
      wireplumber = prev.wireplumber.overrideAttrs (_: {
        version = "git";
        src = prev.fetchFromGitLab {
          domain = "gitlab.freedesktop.org";
          owner = "pipewire";
          repo = "wireplumber";
          rev = "71f868233792f10848644319dbdc97a4f147d554";
          hash = "sha256-VX3OFsBK9AbISm/XTx8p05ak+z/VcKXfUXhB9aI9ev8=";
        };
      });

      libcamera = prev.libcamera.overrideAttrs (_: {
        postFixup = ''
          ../src/ipa/ipa-sign-install.sh src/ipa-priv-key.pem $out/lib/libcamera/ipa_*.so
        '';
      });
    })
  ];
}

Beware: overriding libcamera will cause a ton of stuff to be rebuilt. I don't recommend building this on the Surface tablet.

It looks like updating wireplumber to the git version fixed only part of the issue, and the other problem was caused by the IPA module signing mechanism in libcamera not liking Nix's fixup phase, which was fixed at some point in nixpkgs but then regressed.

In short, libcamera tries to sign the IPA modules during compilation, which I think involves hashing the .so files using a private key generated at build time, but then Nix comes and strips them and changes all the RPATHs in the fixup phase, which invalidates the signature and causes libcamera to trigger its IPA module isolation feature. This feature spawns another process with the proxy binary I mentioned above, and I think it fails because it calls unshare to run the binary in a separate namespace at some point, which only works as root...

Anyway: as for the issue with libcamera I opened an issue on nixpkgs https://github.com/NixOS/nixpkgs/issues/351842 and hopefully this fix makes its way in nixpkgs directly soon. For the wireplumber issue, there hasn't been a release with this fix yet so I suppose we can wait for 0.5.7 to eventually come out. I'll keep this issue open until both of those have been fixed upstream.

For the moment you can use this overlay :)