New computer setup
Table of Contents
- Setup Git
- Install ZSH
- Install ohmyzsh
- Create symlink for dotzsh file
- Install Alacritty
- Install fzf
- Install npm and yarn
- Install vim
- Install rbenv
- Install Emacs
- Install spacemacs
- Install Mixxx
- Audio on a 2016 MacBook Pro (MacBookPro13,1)
- Bluetooth on a 2016 MacBook Pro (MacBookPro13,1)
- Install optional tools
: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:
- Generate the private/pub key pair
- Start
ssh-agent - Add the private key (ie, not the one with
.pub) tossh-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
http://mirror.us-midwest-1.nexcess.net/gnu/emacs/ unpack it and continue…
# 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 tagto 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 wantsqt6-5compat-dev - main adds
libpipewire-0.3-devandlibspa-0.2-dev(native PipeWire) - main adds
qt6-multimedia-devand several extraqml6-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
OFFbecauselibmp4/libmp4v2headers aren’t in upstream’s dependency list. This does not lose M4A playback:FFMPEG:BOOL=ONand 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: -1andUnexpected 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 pathdev->shutdownis 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 inlinux-firmwarefor this chip and none is needed for basic operation. The genericBCM.hcdname 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:
- The headphones are connected to a second host (multipoint).
- 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. - 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