HiFiBerry on Debian ARM64

Long are gone the days of the ten thousands songs Winamp playlist, the modern way of listening to music consists solely of spotify blasting its (not-so-randomly) set of tracks and ads on random variants of laptop/phones/speakers.

But I will forever say no to that.
Spotify is bad for the artists, for you, our planet and it has as much a good impact on musical culture as Facebook has on our social life. Today, the common physical storage available locally (if you don’t depend uniquely on the cloud) has more than enough room to host all the music you’d ever want to listen to. Of course this means that you have to handle your music playlist manually. But that effort will refine your tastes as to what is genuinely good music to your ears, and what is just effective product placement.

To handle my music media center, I always had a RPi stucked to my HiFi system. This RPi would only handle playing music (mpd) and remote control (Cantata, MPDroid, ). This music would be stored remotely on my NAS and accessible in read-only from NFS (yup NFS not SAMBA/CIFS).

The music is played through a RPi HAT, the HiFiBerry DAC+ based on Texas Instruments PCM5102A DAC chip. This was done on a common Raspbian (the only viable solution back then), may be you could do the same easily on the new RaspberryPi OS. I did try to replicate the same setup on FreeBSD but neither the HiFiBerry nor the PCM5102 codec were supported. Attempts to play the music through a USB audio device instead of the HiFiBerry HAT were ultimately unsuccessful probably because of the USB DWC2 host driver. The same attempt on a FreeBSD running RPi4 with PCIe XHCI USB host ran the same USB audio devices perfectly well.

[Debian Diversity Logo -- https://gitlab.com/valessiobrito/artwork (GPLv3 -- https://gitlab.com/valessiobrito/artwork)]

But since I only had a single RPi4 that was already in use, and the fact that RPi4 are currently way overpriced and out-of-stock, I resolved to try a Linux based setup on a RPi3B. But you see, Raspbian is rather restricted by the fact that the RPi platforms ranges from ARMv6 to ARMv8 SoC. That’s 32 to 64-bit, so as a compromise Raspbian runs everything in 32-bit even on ARM64 capable SoC like the RPi3 or better (the new Raspberry Pi OS has some ARM64 images although it’s not widely advertised). The challenge was that it should run Debian ARM64 and play audio through the HiFiBerry DAC+.

The main problem is that the Debian ARM64 image runs a mainline/vanilla Linux kernel. Among other things this version of the kernel does not have support for the HiFiBerry. Instead I resolved to compile my own kernel with the appropriate driver included. Also to avoid the hurdle of porting everything HiFiBerry related and other RPi/BCM goodies to the mainline kernel, I used the Raspberry Pi Foundation kernel source tree.

As a starting point, I used the configuration of the RPi3B kernel from the Debian ARM64 image, which you can find in /boot/config-5.10.0-8-arm64. Crosscompiling the kernel for ARM64 was pretty straight forward:

# Mount the RPi image
mount /dev/sdf1 /mnt/rpi/fat32
mount /dev/sdf2 /mnt/rpi/ext4

# Install and clone the kernel
apt-get install crossbuild-essential-arm64
git clone git://github.com/raspberrypi/linux.git

# Setup some variable for cross-compiling.
cd linux
export KERNEL=kernel8
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-

# Kernel configuration
# (be sure to select the appropriate platform or the Device Tree Blob won't be compiled)
cp /mnt/rpi/ext4/boot/config-5.10.0-8-arm64 .config
make oldconfig
make menuconfig

# Compile!
make Image modules dtbs -j32

# Install
# (you might have to create the 'overlays' directory in the fat32 partition)
VMLINUZ=vmlinuz-5.10.74+
make INSTALL_MOD_PATH=/mnt/rpi/ext4 modules_install
cp arch/arm64/boot/dts/broadcom/*.dtb  /mnt/rpi/fat32
cp arch/arm64/boot/dts/overlays/*.dtb* /mnt/rpi/fat32/overlays/
cp arch/arm64/boot/Image "/mnt/rpi/fat32/${VMLINUZ}"
cp arch/arm64/boot/Image "/mnt/rpi/ext4/boot/${VMLINUZ}"

# Umount the RPi image and boot it still on the old working kernel.
umount /dev/sdf1
umount /dev/sdf2

# Connect to the RPi and create the initrd.
# This should also adapt the boot config. 
update-initramfs -c -k 5.10.74+

If you want to play with Device Tree overlays for example to use a RPi HAT like the HiFiBerry, you will have to compile the dtoverlay manipulation command from the RPi userland repository. Install libfdt-dev, compile and install libdtovl in helpers/dtoverlay, compile dtoverlay in host_applications/linux/apps/dtoverlay.

You need ConfigFS and the following kernel options for the dtoverlay command to work with dynamic DT:

CONFIG_DTC=y
CONFIG_OF=y
CONFIG_OF_UNITTEST=y
CONFIG_OF_DYNAMIC=y
CONFIG_OF_OVERLAY=y
CONFIG_OF_CONFIGFS=y

Also mount ConfigFS:

mkdir /config
mount -t configfs none /config

The command expects to find the overlays in /boot/overlays but on the Debian ARM64 image they will probably be in /boot/firmware/overlays. I fixed this with a symlink.

Next I had to fiddle a bit with the Device Tree sources. If you want some nice documentation about them, here’s a nice pdf from Freescale that explains a lot.

When I first tried to add the HiFiBerry overlay with dtoverlay hifiberry-dacplus, it could not apply because of “incompatible devices”. This was caused by discrepancies between the ARM and ARM64 DT sources for the RPi. This driver was made for the ARM arch than runs Raspbian, not ARM64. And the ARM arch DTS/DTSI exposes devices differently than ARM64. I added the missing devices on ARM64, recompiled and reinstalled the DTB on the RPi image. After that the DT overlay applied like a charm.

The HiFiBerry DAC+ is now happily playing music on Debian ARM64. The RPi3B runs at ~1.14W on idle and ~1.35W when playing music. I don’t how know much the fat-free low-power kernel config contributes to that, but it seems to run at ~1.25W on Debian’s vanilla kernel and I have some ideas to reduce the consumption even further.

Custom user Xsession with SDDM

Debian stretch is out, a lot of obsolete packages, a lot of major upgrades, which all in all resulted in quite a painful transition the last few days. But I’ll tell you more about that in the following posts.

I don’t really spend much time on Linux nowadays so KDE (along with KDM) has always been my goto solution for a jack all trade no-BS works-out-of-the-box desktop environment. And it worked like that just fine, until… well you know how software goes. KDE has been upgraded, KDM has been depreciated and replaced with SDDM.

I also use xsession so that I have a common way of starting session scripts and daemons (such as this one) and configuring stuff across different desktops. I generally selected custom session in the display manager and that was it. But SDDM does not seem to provide a way to do so, or at least that’s not so clear.

By default, it will execute /etc/sddm/xsession which itself sources /etc/X11/Xsession to which it will pass as argument the value of the Exec line in the desktop file (located in /usr/share/xsessions) describing the currently selected session.

If we want to bypass this, we need to scrap the argument passed to /etc/X11/Xsession no matter what SDDM thinks the current session should be. To do so create a wrapper for Xsession in /etc/X11/user-Xsession:

#!/bin/sh

export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin

# Discard argument, we don't care about selecting the desktop environment.
/etc/X11/Xsession

And now configure SDDM to use this instead of its own version of it, in /etc/sddm.conf:

[X11]
SessionCommand=/etc/X11/user-Xsession

SANE USB permissions

Today I had a permission problem with SANE on Linux. SANE stands for “Scanner Access Now Easy”, it provides standardized access to scanner hardware (http://www.sane-project.org) and this is the most commonly used scanning tool on UNIX/Linux.

In my case the USB scanner was not recognized when issuing scanimage -L from my user account although it worked correctly under root and my user is in the scanner group. What more is sane-find-scanner reported permissions errors while running the command as user. The owner and group for the device (in my case it was /dev/bus/usb/002/004) were root:root. At this point we already know that something weird is happening and I expected something like root:scanner instead.

Looking into /lib/udev/rules.d/60-libsane.rules, the line in charge of changing the permissions for each scanner device matched by SANE:

ENV{libsane_matched}=="yes", RUN+="/bin/setfacl -m g:scanner:rw $env{DEVNAME}"

This is nice but I do not use ACL and they are disabled in kernel,  so this command is useless. So I replaced this line with:

ENV{libsane_matched}=="yes", RUN+="/bin/setfacl -m g:scanner:rw $env{DEVNAME}", MODE="0664", GROUP="scanner"

Now the owner and group are correctly set to root:scanner and I can use my scanner as a regular user.

Note that on my system the libsane, sane-utils and xsane are the only packages depending on the acl package. According to what I’ve seen in the ChangeLog they do so in order to cope with MFP which I presume should be accessible as a scanner and printer device at the same time. What I would have done instead would be to create special group for MFP devices and use this instead. IMO still less of a mess than enabling ACL on the whole system for a single package.

GTalk browser plugin on Debian (testing)

So you installed the GTalk browser plugin on Debian testing and it doesn’t work. However GTalk is listed correctly when you list the plugins in your browser. So what now?

Well you can try to remove libudev0. It seems that the plugin has some problems when both libudev1 and libudev0 are present on the system.

Sound problem with zsnes on Debian amd64

You may have some sound problems with zsnes on Debian amd64 (jessie) especially if you want to use pulseaudio with SDL. There are a lot of old posts about these kind of sound problems with zsnes though none of the proposed solutions will work anymore. The last zsnes version (1.510+bz2-5) comes with only the SDL audio output. I configured the SDL audiodriver to pulseaudio but it had to be configured to pulse for zsnes to work (well don’t know why). Anyway all I had to do was this :

export SDL_AUDIODRIVER=pulse
zsnes

And now it works. So you may just add an alias or wrap this into a script to do that automatically.

Start XScreensaver before going to sleep

It seems rational to request XScreensaver to lock your screen when you suspend your machine. This is possible in Debian via the /etc/default/acpi-support file, especially with this line :

# Comment this out to disable screen locking on resume
LOCK_SCREEN=true

However for some obscure reason this will lock the screen (i.e. issue the “xscreensaver-command -lock” command) after suspend, that is on resume. Since the locking process is not immediate your desktop will be available for anyone to watch (and use) for a duration of about one or two second. There is no need to say that this is unacceptable.

It is possible to avoid that by disabling the default screen locking mechanism and hooking it manually to PM. So you should add a script into /etc/pm/sleep.d. The following script is the first version of the script I used (beware it doesn’t work, see below) :

#!/bin/sh
# XScreensaver should be called BEFORE going to sleep to avoid the desktop
# to be shown for a few seconds when the system resumes from sleep.

case "$1" in
  hibernate|suspend)
    xscreensaver-command -lock
    sleep 1 # annoying sleep
    ;;
  *)
    exit 0;;
esac

You may notice that the script issues a sleep just after the xscreensaver-command has returned. It ensures that the screen will be really locked when the system effectively enters into sleep. This is needed because the xscreensaver-command will not lock the screen immediately, that is it is non-blocking in a certain way and you cannot ensure that the screen is effectively locked as soon as the command has returned.

However the script above doesn’t work. As Marcus Moeller commented, the above script won’t work by default on Debian and probably with most other distributions. That is because we don’t issue the xscreensaver lock command as the user owning the xscreensaver daemon. I quote his solution here :

#!/bin/sh
# XScreensaver should be called BEFORE going to sleep to avoid the desktop
# to be shown for a few seconds when the system resumes from sleep.

IS_ACTIVE="$( pidof /usr/bin/xscreensaver )"

case "$1" in 
  hibernate|suspend) 
    # check if xscreensaver is running. if not, just skip on. 
    if [ -z "$IS_ACTIVE" ] 
      then : 
      else 
      # run the lock command as the user who owns xscreensaver process, 
      # and not as root, which won't work.
      su "$( ps aux | grep xscreensaver | grep -v grep | grep $IS_ACTIVE | awk '{print $1}' )" 
             -c "/usr/bin/xscreensaver-command -lock" &
      sleep 1
    fi
    ;;
  *)
    exit 0;;
esac

Digging in xscreensaver’s code shows that what the command actually needs is a connection to the X server. If xscreensaver-command cannot find the display from either command line or environment variables, it will fall back to “:0.0“. But this will fail if root cannot connect to the X server (which is generally the case). That’s how the ‘user approach’ fixes it. However this won’t work anymore if there are multiple instance of xscreensaver running on different displays (only one of them will be locked). Another solution would be to issue the command on each display where root can connect to. However this poses two problems :

  1. It is not as easy as it seems to reliably list all available displays. (see http://stackoverflow.com/questions/11367354/obtaining-list-of-all-xorg-displays).
  2. It requires that each lockable session allows connections from root with “xhost si:localuser:root“.
Here is the modification I posted in response which uses the ‘display approach’ instead:
#!/bin/sh
# XScreensaver should be called BEFORE going to sleep to avoid the desktop to be shown
# for a few seconds when the system resumes from sleep.

case "$1" in
  hibernate|suspend)
  # The X server may not be running
  if [ ! -d /tmp/.X11-unix ]
  then
    exit 0
  fi 

  # Lock each available display
  for socket in $(ls /tmp/.X11-unix)
  do
    display=$(echo "$socket" | tr "X" ":")
    xscreensaver-command -display "$display" -lock
  done

  sleep 1 # annoying sleep
  ;;
  *)
   exit 0;;
esac

However we are not done yet. As you can see we still rely on sleep to ensure that the screen is locked before our script returns control to the suspend procedure. With usage it became clear that one second was not sufficient as the script would return too early from time to time. Incrementing the duration of the sleep would be more than annoying and it doesn’t offer any real guarantee anyway. The only solution would be to find a way to exit the script when we are sure that the display is effectively locked. This is possible by watching at the changes of states of the screensaver while issuing the lock command. There is a slight last problem however. If multiple displays are present we want to issue that “lock ‘n watch” procedure in paralell to avoid accumulating the locking delays. That’s the solution I use in the script below, note that we don’t rely on sleep anymore:

#!/bin/sh
# XScreensaver should be called BEFORE going to sleep to avoid the desktop to be
# shown for a few seconds when the system resumes from sleep.
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin

lock_display() (
  socket="$1"
  display=$(echo "$socket" | tr "X" ":")

  # Temporary pid file for the watching command
  tpid=$(mktemp)

  # Wait until the display is actually locked.
  (timeout 2s xscreensaver-command -display "$display" -watch & echo $! > $tpid) | (
    # Issue the lock command only when we know that
    # the watching pipe is ready.
    xscreensaver-command -display "$display" -lock

    while read line
    do
      line=$(echo $line | cut -d' ' -f 1)

      if [ "$line" = LOCK ]
      then
        # We have to kill the watching command manually before breaking.
        kill -TERM $(cat $tpid)
        break
      fi
    done
  )

  rm $tpid
)

case "$1" in
  hibernate|suspend)
    # The X server may not be running
    if [ ! -d /tmp/.X11-unix ]
    then
      exit 0
    fi

    # Lock each available display
    for socket in $(ls /tmp/.X11-unix)
    do
        # Lock the display
        lock_display $socket &
    done

    # Wait until every displays are locked
    wait
    ;;
  *)
    exit 0;;
esac

As stated above you still need to allow connections from root to your display. You may for example use this command when your session start :

xhost si:localuser:root

Or, as the man page of xhost states, use the file /etc/X*.hosts to do that globally.

Default applications with GTK-3, Chromium and beyond

Today I was surprised to see a GTK-3 application opening an HTTP URL with Opera. I don’t use  Opera and I just installed by curiosity long ago and forgot about it.  I configured the Debian alternatives however GTK-3 seems to use xdg-mime as confirmed with an strace of the concerned application and references to /usr/share/applications/defaults.list. Note that you may have to create a symlink for defaults.list to /usr/share/applications/mimeapps.list.

You can use the xdg-mime command to configure the default application for each protocol:

$ xdg-mime default chromium.desktop x-scheme-handler/http
$ xdg-mime default chromium.desktop x-scheme-handler/https

You can also configure this manually by editing /usr/share/applications/defaults.list.  Just add these two lines:

x-scheme-handler/http=chromium.desktop
x-scheme-handler/https=chromium.desktop

In the [Default Application] section. Thunar and Chromium also use this so you can configure them to open PDF and handle Skype calls properly. See:

[Default Applications]
x-scheme-handler/http=chromium.desktop
x-scheme-handler/https=chromium.desktop
x-scheme-handler/skype=skype.desktop
text/html=chromium.desktop
application/pdf=mupdf.desktop

This should do the trick. You can get the list of available MIME types with this command (note that the path depends on the location of the shared-mime-info database):

find /usr/local/share/mime -name "*.xml" -exec cat {} \; |g -E -o "type=\".*\"" | sort | uniq

Otherwise you may also use the dconf-editor from the dconf-tools package or gconf-editor to configure GNOME-2/3 default applications. With gconf you should search the following keys:

/desktop/gnome/applications
/desktop/gnome/url-handlers

With dconf instead you should search for:

/desktop/gnome/url-handlers
/org/gnome/desktop/applications

Raspberry Pi Ethernet speed

I’ve been a long time user of IPv6 tunnels from SixXS to provide an access to the IPv6 Internet behind my ISP. These tunnels also allow me to use static IP addresses for my home servers along with static AAAA records and this is cool !

Currently I use several Debian GNU/Linux based soft-routers with two (100 and 1000) Ethernet ports. These are often running on old recycled laptops which consumes around 40 Watts of power at peak level. Next to that the ARM Raspberry Pi platform consumes around 3 Watts of power (though I still have to measure it by myself). So I thought about replacing all my home-routers with those.

However the Raspberry Pi model B uses a SMSC LAN951x chip which includes the USB 2.0 Hub and an 10/100 Ethernet controller on top of it (which is known as smsc95xx in the Linux kernel). My  main concern was that it would not be fast enough to support the IPv6 tunnel at its peak bandwidth of 60Mbps (that is 30Mbps downstream/upstream).

I already use one RPi as an experimental home-router here. Our Internet bandwidth is a bit slow (12Mbps) so the USB-Ethernet  shouldn’t be a problem. I’ve conducted quick tests with IPerf and as you can see the results are pretty good as long as it doesn’t involve I/O on the RPi.

------------------------------------------------------------
Server listening on TCP port 5001
TCP window size: 85.3 KByte (default)
------------------------------------------------------------
[ 4] local 10.0.0.1 port 5001 connected with 10.0.0.3 port 37373
[ ID] Interval Transfer Bandwidth
[ 4] 0.0-10.1 sec 114 MBytes 94.4 Mbits/sec
[ 5] local 10.0.0.1 port 5001 connected with 10.0.0.3 port 37376
[ 5] 0.0-10.1 sec 114 MBytes 94.5 Mbits/sec
[ 4] local 10.0.0.1 port 5001 connected with 10.0.0.3 port 37377
[ 4] 0.0-10.1 sec 114 MBytes 94.6 Mbits/sec
[ 5] local 10.0.0.1 port 5001 connected with 10.0.0.3 port 37378
[ 5] 0.0-10.1 sec 114 MBytes 94.5 Mbits/sec

Work around broken dependencies on Debian

If you stumble upon bugs in package dependencies on Debian you may work around these using the equivs package. This package will allow you to create trivial Debian packages.
You need to create the controle file and then build the package :

equivs-control <control-file>
equivs-build <control-file>