Alex's Slip-box

These are my org-mode notes in sort of Zettelkasten style

New computer setup

:ID: 944F1F58-4EC8-444C-B7CA-787ABC19025B

A got a new laptop, a System 76 Gazelle with pop_os. This was the first I time used my dotfiles to setup a Linux box. Previously, I always used MacOS. I want to keep track of the setup procedure, so next time I wont have to figure some of this shit out and maybe automate some of it too.

# Setup Git

I need my dotfiles so first thing is to get git configured.

# Generate SSH key pair

ssh-keygen -t ed25519 -C "Some Comment"
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/myprivatekey

Explaining of the above commands:

  1. Generate the private/pub key pair
  2. Start ssh-agent
  3. Add the private key (ie, not the one with .pub) to ssh-agent

# Add SSH Pub key to GitHub account

Follow their instructions. But the gist of it is go to your account settings, add a new ssh-key and paste the contents of the .pub file.

# Clone dotfiles

# Create symlinks for git dotfiles

With the SSH key stuff out of the way, I cloned the dotfiles repo. After cloning the dotfiles repo create symlinks for:

  • .gitconfig
  • .gitignore_global
ln -s ~/dotfiles/git/gitconfig ~/.gitconfig
ln -s ~/dotfiles/git/gitignore_global ~/.gitignore_global

# Install ZSH

sudo apt-get-update
sudo apt-get install zsh
chsh -s $(which zsh)
sudo reboot

# Install ohmyzsh

# Create symlink for dotzsh file

ln -s ~/dotfiles/shell/zshrc ~/.zshrc

# Install Alacritty

My dotfiles carry shell/alacritty.toml and install.sh symlinks it to ~/.config/alacritty/alacritty.toml, so the config lands even when the terminal itself isn’t installed. Don’t be fooled by that.

That config uses the [general] and [terminal] sections, which need Alacritty 0.14 or newer. Distro packages lag badly here (Ubuntu 24.04 ships 0.13.2), and on an older version those keys are silently misread rather than rejected. So build from source.

# Install a Rust toolchain

Alacritty declares a minimum supported Rust version (0.17.0 wants 1.85). The distro rustc is usually too old and might not even ship cargo (Ubuntu 24.04: rustc 1.75, no cargo). Use rustup instead.

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- \
  -y --no-modify-path --profile minimal

--no-modify-path matters: without it rustup appends to ~/.zshrc, which is a symlink into the dotfiles repo. Cargo ends up at ~/.cargo/bin/cargo and is not on $PATH unless you add it in zshenv.zsh.

# Build dependencies

The package list lives in INSTALL.md in the repo. As of 0.17:

sudo apt install cmake g++ pkg-config libfontconfig1-dev \
  libxcb-xfixes0-dev libxkbcommon-dev python3 scdoc gzip desktop-file-utils

scdoc and gzip are for the man pages, desktop-file-utils for the launcher entry.

# Build it

git clone https://github.com/alacritty/alacritty.git ~/dev/alacritty
cd ~/dev/alacritty
git checkout v0.17.0 # or the latest release tag
~/.cargo/bin/cargo build --release

# Install it

The binary by itself is not enough. Without the desktop entry it never shows up in the app launcher, and without the terminfo TERM=alacritty breaks over SSH.

sudo tic -xe alacritty,alacritty-direct extra/alacritty.info
sudo cp target/release/alacritty /usr/local/bin
sudo cp extra/logo/alacritty-term.svg /usr/share/pixmaps/Alacritty.svg
sudo desktop-file-install extra/linux/Alacritty.desktop
sudo update-desktop-database

Man pages and zsh completions:

sudo mkdir -p /usr/local/share/man/man1 /usr/local/share/man/man5
scdoc < extra/man/alacritty.1.scd | gzip -c | sudo tee /usr/local/share/man/man1/alacritty.1.gz > /dev/null
scdoc < extra/man/alacritty-msg.1.scd | gzip -c | sudo tee /usr/local/share/man/man1/alacritty-msg.1.gz > /dev/null
scdoc < extra/man/alacritty.5.scd | gzip -c | sudo tee /usr/local/share/man/man5/alacritty.5.gz > /dev/null
scdoc < extra/man/alacritty-bindings.5.scd | gzip -c | sudo tee /usr/local/share/man/man5/alacritty-bindings.5.gz > /dev/null
cp extra/completions/_alacritty "${ZSH_CACHE_DIR:-$HOME/.cache/oh-my-zsh}/completions/"

Check the config still parses against the version you built with alacritty migrate --dry-run. It prints the config back unchanged if there’s nothing to migrate.

# Install the font

The config asks for JetBrains Mono. If it’s missing, fontconfig silently substitutes some other monospace and everything just looks wrong.

sudo apt install fonts-jetbrains-mono

Confirm it actually resolves:

fc-match "JetBrains Mono:style=Regular"

# Install fzf

# Install npm and yarn

# Nodesource (Linux)

# yarn

npm install yarn -g

# Install vim

# Create symlink for dotvimrc file

ln -s ~/dotfiles/vim/vimrc ~/.vimrc

# Install rbenv

# Install ruby-build

# Install a version of ruby

rbenv install -l # list most recent stable versions and pick one
rbenv install 3.2.2 # install it
rbenv global 3.2.2 # set it to global

# Install Emacs

# Download source code for the version you want

# Or master branch

Clone the Emacs repo: https://git.savannah.gnu.org/git/emacs.git

Or use the github mirror: git@github.com:emacs-mirror/emacs.git

To install a certain tagged version

  • git tag to list all available tags
  • checkout the version you want: git checkout emacs-29.3

# Dependencies I needed

Run the following to determine if you have the dependencies needed and create the configure file

CC="gcc-10" ./autogen.sh

There will probable be a lot of stuff missing. For example once…

I needed these:

sudo apt install gcc-10 libgccjit0 libgccjit-10-dev autoconf librsvg2-dev libpng-dev \
  libjpeg-dev imagemagick sqlite3 pandoc libcairo2-dev

But another time there was even more…

See this which was helpful. After install those, I was able to move on.

# Configure and install

Create a build directory

mkdir build && cd build

Run ../configure with the desired build flags, whatever they mean

To see all available build flags ./configure --help

This is how I configured and installed it when I installed v28

CC="gcc-10" ./configure --with-modules --with-cairo --with-imagemagick --with-rsvg --with-mailutils --with-native-compilation

And for v29:

../configure --with-native-compilation --with-json --with-xwidgets --with-imagemagick --with-rsvg CC="gcc-10"

Then install it

make -j 2
sudo make install

If using the same emacs repo from previous installs, there might be old .elc files from previous version installs. That will cause a problem. I’m not sure how to recompile them. Maybe the bootstrap command? Anyway deleting and re-cloning the repo works too.

See also https://www.masteringemacs.org/article/speed-up-emacs-libjansson-native-elisp-compilation

# Add chemacs2 and profiles

# Install spacemacs

# Install Mixxx

Mixxx digital DJ software, built from source. CMake + Qt6.

# Clone and pick a version

git clone https://github.com/mixxxdj/mixxx.git ~/dev/mixxx
cd ~/dev/mixxx
git tag --list '[0-9]*' --sort=-v:refname | head   # see what's out
git checkout 2.5.6                                 # a release tag, not main

main is the development branch (2.7-alpha as of this writing). For something I actually DJ on, use the latest release tag.

# Checkout the tag BEFORE installing dependencies

This is the part that will bite me if I forget. The dependency list lives in the repo at tools/debian_buildenv.sh and it differs between versions. Running main’s script against a 2.5.6 checkout installs the wrong packages.

Concretely, 2.5.6 vs main on Ubuntu 24.04:

  • Qt5Compat: 2.5.6 wants libqt6core5compat6-dev, main wants qt6-5compat-dev
  • main adds libpipewire-0.3-dev and libspa-0.2-dev (native PipeWire)
  • main adds qt6-multimedia-dev and several extra qml6-module-* packages
  • main drops qt6-qpa-plugins

So always run the script from the checked-out tag:

tools/debian_buildenv.sh setup

200MB download. It uses ~sudo internally. It also has an interactive prompt if fonts-ubuntu is unavailable, but on Ubuntu that package exists so the prompt never fires.

# Build

mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release \
  -DCMAKE_INSTALL_PREFIX=/usr/local \
  -DCMAKE_C_COMPILER_LAUNCHER=ccache \
  -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
cmake --build . -j"$(nproc)"

30 min on the 2016 MacBook Pro (i7-6660U, 2 cores / 4 threads). The ~ccache launcher args aren’t required but make rebuilds much cheaper.

# Optional features that don’t get found, and why it’s fine

  • FAAD (AAC/M4A) ends up OFF because libmp4 / libmp4v2 headers aren’t in upstream’s dependency list. This does not lose M4A playback: FFMPEG:BOOL=ON and libavcodec handles AAC. FAAD is just the other path.
  • DjInterop is off. That’s Engine Prime / Denon library export.
  • Could NOT find Protobuf (missing: Protobuf_DIR) is a red herring. That’s the CONFIG-mode probe failing; module mode finds it on the next line.
  • Mixxx may say the system libshout “is suffering from issue #10305” and use its bundled copy instead. Broadcasting still works.

# Install

sudo cmake --install .
sudo update-desktop-database
sudo gtk-update-icon-cache -f -t /usr/local/share/icons/hicolor

2800 files. ~/usr/local/share/ is already on XDG_DATA_DIRS on Ubuntu, so the desktop entry is picked up by the app launcher without extra work.

Mixxx can also just be run out of the build dir (./mixxx) without installing, but then there’s no launcher entry and no mixxx on $PATH.

# Upgrading to a new release

There is no make uninstall target. cmake --install happily writes a new version over the old one and leaves orphaned files behind (skins, qml, and translations move around between releases). So uninstall explicitly first, using the manifest from the build that installed it:

cd ~/dev/mixxx/build
sudo xargs rm -f < install_manifest.txt   # remove the OLD install first

Then update and rebuild from scratch. Don’t reuse the old build directory: a new tag changes dependencies and CMake options, and a stale CMakeCache.txt will happily carry the old values forward.

cd ~/dev/mixxx
git fetch --tags
git checkout 2.6.0                 # whatever the new tag is
tools/debian_buildenv.sh setup     # RE-RUN. deps change between versions
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local
cmake --build . -j"$(nproc)"
sudo cmake --install .

Back up settings and the library database before a major version bump. Mixxx migrates its schema on first launch and that is one-way, so going back to the older version afterwards is not reliable:

cp -a ~/.mixxx ~/.mixxx.bak-$(date +%Y%m%d)

Note 2.6 adds native PipeWire support (hence libpipewire-0.3-dev in its dependency list). 2.5.x reaches PipeWire only through PortAudio’s ALSA/JACK compatibility layers.

# Controllers

udev rules are installed to /usr/local/share/mixxx/udev. Installing the files doesn’t activate them. If a USB controller isn’t detected, that’s the first place to look.

# Audio on a 2016 MacBook Pro (MacBookPro13,1)

Only relevant to that machine. Out of the box there is no sound at all, from speakers or headphones.

The cause: it uses a Cirrus CS8409 HDA bridge with a CS42L83 sub-codec. Mainline’s snd-hda-codec-cs8409 only has quirks for Dell’s CS42L42, but it binds anyway on the bridge-only alias, so the sub-codec is never initialized. Symptoms are diagnostic: no Master/Speaker mixers, both jacks report as “Phantom Jack”, and playback exits 0 into silence. Nothing in PipeWire or ALSA config can fix this; it needs an out-of-tree driver.

# The driver

davidjo/snd_hda_macbookpro, installed via DKMS so it rebuilds on kernel updates.

sudo apt install dkms build-essential linux-source-6.8.0 \
     linux-headers-$(uname -r)
git clone https://github.com/davidjo/snd_hda_macbookpro.git ~/dev/snd_hda_macbookpro
cd ~/dev/snd_hda_macbookpro && sudo ./dkms.sh

Confirm it took: cat /proc/asound/card0/codec#0 | head -2 should say CS8409/CS42L83, not just CS8409.

dkms.sh symlinks /usr/src/snd_hda_macbookpro-0.1 to the clone rather than copying it, so don’t move or delete ~/dev/snd_hda_macbookpro — future kernel updates rebuild from it.

# Gotcha: dkms.conf has the wrong module path on pre-6.17 kernels

Upstream ships BUILT_MODULE_LOCATION[0]="build/hda/codecs/cirrus" (the 6.17+ source layout). On 6.8 the module builds at build/hda, so DKMS reports a build failure even though make.log shows a clean compile. The installer tries to sed this itself, but runs as PRE_BUILD — after DKMS has already parsed the file, and against DKMS’s throwaway copy — so it never takes effect. Edit dkms.conf in the repo directly. A git pull will conflict on that line.

This edit is only correct for pre-6.17 kernels. If this box ever lands on 6.17+ (a release upgrade, say), revert the line to upstream’s value or the build will fail the other way around.

# Stay on the 6.8 GA kernel line

The driver needs kernel source to build against, and Ubuntu ships no linux-source package for HWE kernels. So the HWE line (7.0) can’t work with DKMS at all — audio would silently break on every kernel update. Removed HWE and pinned to GA:

sudo apt install linux-generic linux-headers-generic
sudo apt purge 'linux-*-generic-hwe-24.04' 'linux-*-7.0.0-*'

# Wifi needs pcie_port_pm=off on 6.8

Unrelated to audio, but discovered together: on 6.8 the BCM4350 fails to probe (brcmf_chip_recognition: MMIO read failed: 0xffffffff) because the card gets powered down behind its PCIe root port. 7.0 didn’t have this. Add to GRUB_CMDLINE_LINUX_DEFAULT in /etc/default/grub, then sudo update-grub:

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash mem_sleep_default=s2idle pcie_port_pm=off"

This is load bearing. Removing it kills wifi. It is not a missing driver/firmware problem, so no package install will fix it.

# Mixxx travel rig: Xone:K2 as the only interface

The K2’s built-in soundcard is 4 out, 0 in (S16_LE, 44.1/48k). That’s enough for a standalone rig with no external mixer: one stereo pair for master, one for headphone cue.

Mixxx talks to it directly over ALSA (hw:2,0) rather than through PipeWire. Left alone, PipeWire claims the card and forces it into the analog-surround-40 profile — one sink with an FL/FR/FC/LFE map, which is the wrong shape (channel 3 is not a center speaker) — and holds the ALSA device open so Mixxx can’t get it. So tell WirePlumber not to manage it, in ~/.config/wireplumber/main.lua.d/51-xonek2-reserve-for-mixxx.lua:

xonek2_reserve = {
  matches = { { { "device.name", "equals",
                  "alsa_card.usb-ALLEN_HEATH_LTD._XONE_K2-00" }, }, },
  apply_properties = { ["device.disabled"] = true, },
}
table.insert(alsa_monitor.rules, xonek2_reserve)

device.disabled scopes the session manager, it doesn’t disable hardware. Only the audio device is affected — MIDI goes through the ALSA sequencer, so the controller still works. Cost: the K2 no longer shows up in the GNOME volume applet. Delete the file and restart wireplumber to undo.

Since PipeWire is out of the path entirely, none of the pipewire.conf / jack.conf latency tuning in my audio repo applies here. Buffer size is set in Mixxx’s own preferences.

Sound Hardware: API ALSA, 44100 Hz. Channel assignment as it actually landed — headphones on the first pair, master on the second:

<output type="Headphones" channel="0" channel_count="2"/>  <!-- ch 1-2 -->
<output type="Master"     channel="2" channel_count="2"/>  <!-- ch 3-4 -->

# Use the stock Mixxx K2 mapping here, not mine

My K2 mapping is built for an external mixer — faders and EQ knobs are deliberately unmapped so they can drive VST params instead. With no external mixer there’d be nothing to mix with. The stock Allen and Heath Xone K2.midi.xml that ships with Mixxx has the full internal-mixer layout. Both use the same MIDI-channel layout scheme (0xA–0xE), so the hardware channel setting carries over either way.

Note Mixxx only scans files directly in ~/.mixxx/controllers — a cloned repo in a subdirectory there is invisible to it.

# Bluetooth on a 2016 MacBook Pro (MacBookPro13,1)

Same machine as the audio section above. Symptom: the Bluetooth switch in GNOME Settings will not stay in the on position. hciconfig shows hci0 DOWN with BD address all zeros, TX bytes but zero RX, and dmesg has:

Bluetooth: hci0: command 0xfc18 tx timeout
Bluetooth: hci0: BCM: failed to write update baudrate (-110)
Bluetooth: hci0: BCM: Reset failed (-110)

The radio is a BCM4350C0 attached over UART, not USB. It lives at \_SB.PCI0.URT0.BLTH (ACPI HID BCM2E7C) on the Intel LPSS dw-apb-uart.2 and never appears in lsusb, so all the usual USB-dongle advice is irrelevant. It is also not a missing firmware or missing driver problem — no package install will change anything.

# It’s a race, not a permanent failure

This is the whole insight and it took hours to see. The driver loses a race against the controller’s power-up roughly half the time. Check the journal across previous boots:

for b in $(journalctl --list-boots --no-pager -q | awk '{print $1}'); do
  printf "boot %-4s : %s\n" "$b" \
    "$(journalctl -k -b "$b" | grep -oE 'BCM4350C0|Reset failed' | head -1)"
done

6 of the last 12 boots came up fine with no intervention at all. Bluetooth was already working half the time; I just happened to look on a bad boot.

Corollary: never conclude anything here from a single boot. Any change has to be measured over repeated trials or you’re just reading a coin flip.

# Don’t go down the DKMS patch road

Searching these symptoms leads straight to mbp-2016-linux#29, kernel bug 205115, the christophgysin patch (drop the set_device_wakeup() / BTLP call from bcm_gpio_set_power()) and the leifliddy DKMS package that automates it. Everything points there. It’s the wrong tree for this machine.

I built that patch against linux-source-6.8.0 and measured it: it failed all 4 attempts, while stock driver logic passed 4 of 6. With a 50/50 base rate that’s suggestive rather than conclusive, but there is no reason to adopt an out-of-tree module that at best does nothing.

The reason that 2017-2020 thread never converged is that everyone was testing patches one boot at a time against this coin flip, so the same patch “worked” for some people and not others.

# The fix: retry the module load

No patch, no DKMS, no kernel taint, and kernel updates can’t break it. If hci0 didn’t initialize, reloading hci_uart just re-rolls the dice.

/usr/local/sbin/bt-init-retry.sh:

#!/bin/bash
MAX_ATTEMPTS=10

hci_bd() {
    hciconfig hci0 2>/dev/null |
        sed -n 's/.*BD Address: \([0-9A-F:]\{17\}\).*/\1/p'
}
up() {
    local bd; bd=$(hci_bd)
    [ -n "$bd" ] && [ "$bd" != "00:00:00:00:00:00" ]
}

rfkill unblock bluetooth 2>/dev/null

for attempt in $(seq 1 "$MAX_ATTEMPTS"); do
    up && { echo "hci0 up: $(hci_bd) (attempt $attempt)"; exit 0; }
    echo "attempt $attempt: hci0 not initialized, reloading hci_uart"
    modprobe -r hci_uart 2>/dev/null
    sleep 1
    modprobe hci_uart || { echo "modprobe failed"; exit 1; }
    # success shows up in ~1s; the failure path takes ~10s to time out
    for _ in $(seq 1 12); do sleep 1; up && break; done
done

up && { echo "hci0 up: $(hci_bd)"; exit 0; }
echo "hci0 did not initialize after $MAX_ATTEMPTS attempts" >&2
exit 1

/etc/systemd/system/bt-init-retry.service:

[Unit]
Description=Retry Broadcom UART Bluetooth initialization (MacBookPro13,1)
After=bluetooth.service
Wants=bluetooth.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/sbin/bt-init-retry.sh

[Install]
WantedBy=multi-user.target
sudo systemctl enable --now bt-init-retry.service

At ~50% per attempt, 10 attempts leaves about a 1-in-1000 chance of a boot without Bluetooth. A bad boot costs 12s in the background and blocks nothing. ~bluetoothd picks up the controller whenever it appears, so there’s no ordering requirement beyond running after bluetooth.service.

Use modprobe, not insmod: removing hci_uart also unloads btbcm, btqca, btrtl and btintel, and insmod won’t pull them back in. You’ll get a wall of “Unknown symbol” errors if you try.

# Messages that look like problems but aren’t

These appear on successful initialization too. Don’t chase them.

  • Unexpected ACPI gpio_int_idx: -1 and Unexpected number of ACPI GPIOs: 0 — normal on all Apple hardware. The Mac DSDT describes no GPIO resources because power is done through custom ACPI methods (BTPU / BTPD / BTLP) instead.
  • No reset resource, using default baud rate — same reason; on the Apple code path dev->shutdown is always NULL.
  • BCM: failed to write update baudrate (-16) — note -16 (EBUSY), which is the working case. The failing case is -110 (ETIMEDOUT). Easy to confuse at a glance, and it’s the single most useful line for telling a good boot from a bad one.
  • BCM: Patch brcm/BCM.hcd not found — no patchram blob ships in linux-firmware for this chip and none is needed for basic operation. The generic BCM.hcd name means the subver wasn’t in the driver’s UART table, not that a specific file is missing.

A good boot looks like:

Bluetooth: hci0: BCM: chip id 92
Bluetooth: hci0: BCM4350C0 UART 37.4 MHz Gamay USI UHE
Bluetooth: hci0: BCM (003.001.134) build 1532

# A2DP audio

Upstream documents A2DP as “still choppy for unknown reasons” on these models, so I expected trouble.

Worth ruling out before blaming this hardware, in order:

  1. The headphones are connected to a second host (multipoint).
  2. The GNOME Bluetooth settings panel is open — that runs a continuous BR/EDR inquiry which does disrupt an active audio link. Close the window, then confirm with bluetoothctl show | grep Discovering.
  3. Wifi on 2.4GHz. This is a combo chip sharing the radio; 5GHz is much better behaved. Check with nmcli dev wifi list.

Useful counters: hciconfig -a hci0 should show errors:0 — if the UART link were actually corrupting, that’s where it would show.

# Install optional tools

# rclone

# syncthing

# Rustdesk

Download the client from the releases github page

Search Results