‹ troublecurve.me

Tags / nixos


As I have a Quest 3 and think that the excellent colour passthrough is hugely important, I suspect that the upcoming Steam Frame VR headset might feel a slight downgrade. However, I was interested in the wireless dongle that will come with the Frame to allow high-quality streaming from PC to headset.

I’d been using a Ubiquiti U7 Pro as a 6GHz access point for the Quest 3, but because of moving computers around rooms I could no longer have the Windows gaming PC wired to Ethernet, so would have to have the U7 connected to the PC itself for low-latency streaming. This looked like it would also require me to run the Unifi server software on the PC–and when that appeared to be some kind of containerised Linux on Windows pacman kludge I thought there must be a simpler way. Wouldn’t a Wifi 6E/7 USB adaptor just be able to create a wireless access point for direct streaming? Isn’t that just what the Steam Frame dongle will do?

There’s a great site by morrownr on Github about which USB adaptors have good Linux support, so although I planned to use the adaptor on a Windows machine, I went with the Netgear Nighthawk A9000 just in case. Lucky I did, as although it works absolutely fine on Windows to connect to wifi networks, it refused to enter “hotspot” mode (which is what Windows calls bridged wireless access points). Morrownr’s site had said that it could work in AP mode, however, so it was clearly a Windows thing rather than a lack of device functionality.

In came my Raspberry Pi 4 running NixOS. Previously used as a squeezlite music streamer with Hifiberry outputs and simple web server (including this site!) I could relocate it next to the PC, plug in the USB adaptor and then run a short Ethernet cable to the gaming PC. It has ended up working well, but there was somewhat of a learning curve…

Bridges

I’m bridging the Pi’s Ethernet port with the Netgear adaptor so the gaming PC and Quest are on the same subnet. However, don’t add the USB wifi adaptor to the bridge in configuration.nix as it won’t initialise correctly. The hostapd service will do the bridging later on.

networking = {
	bridges = {
      br0 = {
        interfaces = [
          "end0"
        ];
      };
    };
    
    interfaces.br0 = {
      useDHCP = false;
      ipv4 = {
        addresses = [{
          address = "192.168.3.1";
          prefixLength = 24;
        }];
      };
	};
};

You’ll also need the following lines for IP forwarding:

boot.kernel.sysctl = {
    "net.ipv4.conf.all.forwarding" = true;
};

I wasn’t sure if problems I had when troubleshooting were kernel-related, so I also forced an upgrade to Linux Kernel 7.0:

  boot.kernelPackages = pkgs.linuxPackages_7_0;

Apparently you should also add some lines to set region for the adaptor:

  boot.extraModprobeConfig = ''
  options cfg80211 ieee80211_regdom=GB
  '';

Then I needed to use DNSMasq as a simple DHCP server to give addresses to the PC and Quest 3. wlan0 is the Pi’s internal wifi adaptor that I don’t want using DNSMasq (it gets its address from the router):

 services.dnsmasq = {
    enable = true;
    resolveLocalQueries = false;
    settings = {
      server = [ "ADD.YOUR.DNS.SERVER" ];
      interface = [ "br0" ];
      bind-interfaces = true;
      except-interface = [ "wlan0" ];
      dhcp-host = [ "DEVICE:MAC:ADDRESS,STATIC.IP.FOR.THAT.DEVICE" ];
      dhcp-range = [ "IP.RANGE.FROM,IP.RANGE.TO,24h"];
      dhcp-option = [
        "option:router,192.168.3.1"
        "option:dns-server,ADD.YOUR.DNS.SERVER"
      ];
    };
  };

The hardest part was making hostapd’s configuration turn on Wifi 6E (the 6Ghz band). Most of the below was taken from morrownr’s example config on his site and then converted to Nix-friendly formatting. There is also some fussiness about which setting goes in which section, which led to some hair-pulling (and troubleshooting about how NixOS generates config files for services–far above my pay grade).

The Netgear USB wireless adaptor was detected as wlp1s01u1.

  services.hostapd = {
    enable = true;
    radios.wlp1s0u1 = {
      band = "6g";
      channel = 69;
      countryCode = "GB";
      driver = "nl80211";
      settings = {
        op_class = 134;
        country3 = "0x49";
        bridge = "br0";
        logger_syslog = -1;
        logger_syslog_level = 2;
        beacon_int = 100;
        dtim_period = 2;
        skip_inactivity_poll = 1;
        max_num_sta = 12;
        macaddr_acl = 0;
        okc = 1;
        wpa_key_mgmt = "SAE";
        wme_enabled = 1;
        wmm_enabled = 1;
        ieee80211n = true;
        ht_capab= lib.mkForce "[LDPC][HT40+][HT40-][GF][SHORT-GI-40][TX-STBC][RX-STBC1][MAX-AMSDU-7935]";
        he_oper_centr_freq_seg0_idx = 79;
        he_6ghz_max_mpdu = 2;
        he_6ghz_max_ampdu_len_exp = 7;
        he_6ghz_reg_pwr_type = 0;       
        he_bss_color = 37; 
        sae_require_mfp = lib.mkForce false;
      };
      wifi6 = {
        enable = true;
        operatingChannelWidth = "160";
      };
      networks.wlp1s0u1 = {
        ssid = "YOUR_SSID_HERE";
        settings = {
          ieee80211w = 2;
        };
        authentication = {
          mode = "wpa3-sae";
          saePasswords = [
	    { password = "YOUR_SSID_PASSWORD_HERE"; }
            ];
        };
      };
    };
  };  

Anyway, all that gets me a rock-solid 6Ghz connection with my Quest 3 streaming 250mb/s VR flawlessly. Not quite as easy as a dongle, but…

For a long time I was running piCorePlayer on my RaspberryPi 4 Model B with a HiFiBerry DAC+ Light without any problems. The little Pi 4, however, is capable of much more, so I decided to try to put NixOS on it. It wasn’t entirely straightforward.

The guide on the NixOS Wiki is pretty good for the first parts of the installation–flashing a MicroSD card with a recent image (I used 25.05), generating a config, and updating the firmware. Where it got trickier was trying to get the HiFiBerry DAC+ Light to be recognised.

First I had to add the nixos-hardware channel with the following commands:

$ sudo nix-channel --add https://github.com/NixOS/nixos-hardware/archive/master.tar.gz nixos-hardware
$ sudo nix-channel --update

Then add the following a line to the top of /etc/nixos/configuration.nix

  imports =
    [ # Include the results of the hardware scan.
  # The line just below adds the hardware repository so we can add the Hifiberry board
      <nixos-hardware/raspberry-pi/4>
      ./hardware-configuration.nix
    ];

The next step was the hard bit to figure out. A post on the NixOS hardware github was key (thank you, Ramblurr), though I had to change which DTS overlay I copied from as I have a DAC+ Light not the standard DAC+. This is what works for me in my configuration.nix:

  # I'm adding this to enable the HifiBerry DAC+ Light board. Overlays, urgh!

  hardware = {
    raspberry-pi."4".apply-overlays-dtmerge.enable = true;
    deviceTree = {
      enable = true;
      filter = "bcm2711-rpi-4*.dtb";
      overlays = [
        {
          name = "hifiberry-dac";
          dtsText = ''
// Definitions for HiFiBerry DAC
/dts-v1/;
/plugin/;

/ {
	compatible = "brcm,bcm2711";

	fragment@0 {
		target = <&i2s_clk_producer>;
		__overlay__ {
			status = "okay";
		};
	};

	fragment@1 {
		target-path = "/";
		__overlay__ {
			pcm5102a-codec {
				#sound-dai-cells = <0>;
				compatible = "ti,pcm5102a";
				status = "okay";
			};
		};
	};

	fragment@2 {
		target = <&sound>;
		__overlay__ {
			compatible = "hifiberry,hifiberry-dac";
			i2s-controller = <&i2s_clk_producer>;
			status = "okay";
		};
	};
};
'';
   }
 ];
    };
  };

Then I could add the magical NixOS service line for squeezelite in configuration.nix:

  # Squeezelite, and the output sent to the Hifiberry.
  services.squeezelite.enable = true;
  services.squeezelite.extraArguments = "-o sysdefault:CARD=sndrpihifiberry";

And do the magic rebuild:

sudo nixos-rebuild switch

Then restart, and it should all work perfectly.

« Older posts Newer posts »