In the last couple days I experimented a little with an old crappy laptop I had lying around for a while. I wanted to use it as a media center, but it was too old to support h264 hardware decoding, so playing 1080p content was basically impossible. So I bought a Broadcom BCM970012 Crystal HD hardware decoder for less than 20 euros on Amazon.
This little Mini PCI-E card handles the video decoding in hardware, and does so very efficiently, drawing almost no power. It became famous to enable 1080p on the 1st-gen Apple TV, which funnily enough has the same crappy GPU I have on my old laptop, an Nvidia GeForce Go 7300.
Broadcom is not supporting this card anymore, with the latest Linux driver available released in 2010. Anyway, I managed to get it working on XBMC 13.1 Gotham running on Debian Wheezy (7.0).
Getting the hardware to work
The first thing to note is that you have to run a 32 bit OS to be able to use this card reliably. I tried to use it on 64 bit installs of both Debian and Ubuntu, with no luck.
Second, support for Crystal HD will be dropped by Kodi (née XBMC) version 14.
Given this assumptions, I’ll briefly outline what was needed to get it to work.
First, I installed Debian. I opted for a net-install, in order to carefully choose only the packages I really needed. The laptop was quite old, so I didn’t want to bog it down with stuff I ultimately didn’t need. I went through the usual dance to install Nvidia’s proprietary drivers (version 304.x, since this is a legacy GPU), and installed alsa-utils
in order to enable audio.
Debian doesn’t ship with the required crystalhd kernel module, so I had to compile my own. It was pretty easy (kernel 3.2.0-4-686-pae), I just followed the first part of this guide. Just in case it should disappear from the internet, I took the liberty to upload the source code here, and copy the required steps below:
- Install the required dependencies
sudo apt-get install git automake g++ build-essential linux-headers |
sudo apt-get install git automake g++ build-essential linux-headers
- Compile the driver
cd crystalhd/driver/linux
autoconf
./configure
make
sudo make install |
cd crystalhd/driver/linux
autoconf
./configure
make
sudo make install
- Compile the library
cd ../../linux_lib/libcrystalhd/
make
sudo make install |
cd ../../linux_lib/libcrystalhd/
make
sudo make install
- Load the driver
If all went as it should, you now have a working kernel module for Crystal HD cards.
In Debian Wheezy’s repos, you will find a really old version of XBMC (version 11). It will work, but I always prefer to have the latest stable version of everything. I dug around a bit, and found that in the official wheezy-backports repo there was a fairly recent Gotham release, 13.1RC1. I could have compiled a newer one, but it would have been more trouble than what it’s worth. So I went and added the repo to my sources.list, and installed the version from the repo.
echo "deb http://ftp.debian.org/debian/ wheezy-backports main contrib non-free" | sudo tee -a /etc/apt/sources.list
echo "deb-src http://ftp.debian.org/debian/ wheezy-backports main contrib non-free" | sudo tee -a /etc/apt/sources.list
sudo apt-get update
sudo apt-get -t wheezy-backports install xbmc |
echo "deb http://ftp.debian.org/debian/ wheezy-backports main contrib non-free" | sudo tee -a /etc/apt/sources.list
echo "deb-src http://ftp.debian.org/debian/ wheezy-backports main contrib non-free" | sudo tee -a /etc/apt/sources.list
sudo apt-get update
sudo apt-get -t wheezy-backports install xbmc
That’s it, Crystal HD decoding should now work without further actions.
The only thing was that it stopped working after waking from sleep. The solution was easy: save this script as /etc/pm/sleep.d/80crystalhd
and make it executable with sudo chmod +x /etc/pm/sleep.d/80crystalhd
.
#!/bin/bash
case $1 in
hibernate)
echo "Hey guy, we are going to suspend to disk!"
modprobe -r crystalhd
;;
suspend)
echo "Oh, this time we're doing a suspend to RAM. Cool!"
modprobe -r crystalhd
;;
thaw)
echo "oh, suspend to disk is over, we are resuming..."
modprobe crystalhd
;;
resume)
echo "hey, the suspend to RAM seems to be over..."
modprobe crystalhd
;;
*) echo "somebody is calling me totally wrong."
;;
esac |
#!/bin/bash
case $1 in
hibernate)
echo "Hey guy, we are going to suspend to disk!"
modprobe -r crystalhd
;;
suspend)
echo "Oh, this time we're doing a suspend to RAM. Cool!"
modprobe -r crystalhd
;;
thaw)
echo "oh, suspend to disk is over, we are resuming..."
modprobe crystalhd
;;
resume)
echo "hey, the suspend to RAM seems to be over..."
modprobe crystalhd
;;
*) echo "somebody is calling me totally wrong."
;;
esac
What this does, is it unloads the kernel module before suspending/hibernating and reloads it when the system wakes up. I didn’t think it would work even with XBMC still running, but it did.
Final touches
I then focused on cleaning up the experience of using XBMC:
- It should launch automatically at boot time
- It should be possibile to sleep/shutdown/restart from XBMC without needing the root password
- The system should wake up on any USB activity
Autostart at boot
To launch XBMC at boot time you have to put a .xinitrc
file in the home directory of XBMC’s user (from now on, I’ll assume that it’s xbmc, which makes sense).
Then, save this in /etc/init.d/xbmc
and make it executable (sudo chmod +x /etc/init.d/xbmc
)
#!/bin/sh
### BEGIN INIT INFO
# Provides: xbmc
# Required-Start: $all
# Required-Stop: $all
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts instance of XBMC
# Description: starts instance of XBMC using start-stop-daemon and xinit
### END INIT INFO
############### EDIT ME ##################
# path to xinit exec
DAEMON=/usr/bin/startx
# startup args
#DAEMON_OPTS=" /usr/local/bin/xbmc --standalone -- :0"
# script name
NAME=xbmc
# app name
DESC=XBMC
# user
RUN_AS=xbmc
# Path of the PID file
PID_FILE=/var/run/xbmc.pid
############### END EDIT ME ##################
test -x $DAEMON || exit 0
set -e
case "$1" in
start)
echo "Starting $DESC"
start-stop-daemon --start -c $RUN_AS --background --pidfile $PID_FILE --make-pidfile --exec $DAEMON -- $DAEMON_OPTS
;;
stop)
echo "Stopping $DESC"
start-stop-daemon --stop --pidfile $PID_FILE
;;
restart|force-reload)
echo "Restarting $DESC"
start-stop-daemon --stop --pidfile $PID_FILE
sleep 5
start-stop-daemon --start -c $RUN_AS --background --pidfile $PID_FILE --make-pidfile --exec $DAEMON -- $DAEMON_OPTS
;;
*)
N=/etc/init.d/$NAME
echo "Usage: $N {start|stop|restart|force-reload}" >&2
exit 1
;;
esac
exit 0 |
#!/bin/sh
### BEGIN INIT INFO
# Provides: xbmc
# Required-Start: $all
# Required-Stop: $all
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts instance of XBMC
# Description: starts instance of XBMC using start-stop-daemon and xinit
### END INIT INFO
############### EDIT ME ##################
# path to xinit exec
DAEMON=/usr/bin/startx
# startup args
#DAEMON_OPTS=" /usr/local/bin/xbmc --standalone -- :0"
# script name
NAME=xbmc
# app name
DESC=XBMC
# user
RUN_AS=xbmc
# Path of the PID file
PID_FILE=/var/run/xbmc.pid
############### END EDIT ME ##################
test -x $DAEMON || exit 0
set -e
case "$1" in
start)
echo "Starting $DESC"
start-stop-daemon --start -c $RUN_AS --background --pidfile $PID_FILE --make-pidfile --exec $DAEMON -- $DAEMON_OPTS
;;
stop)
echo "Stopping $DESC"
start-stop-daemon --stop --pidfile $PID_FILE
;;
restart|force-reload)
echo "Restarting $DESC"
start-stop-daemon --stop --pidfile $PID_FILE
sleep 5
start-stop-daemon --start -c $RUN_AS --background --pidfile $PID_FILE --make-pidfile --exec $DAEMON -- $DAEMON_OPTS
;;
*)
N=/etc/init.d/$NAME
echo "Usage: $N {start|stop|restart|force-reload}" >&2
exit 1
;;
esac
exit 0
Then, make it start at boot:
sudo update-rc.d xbmc defaults |
sudo update-rc.d xbmc defaults
Allow shutdown/sleep/reboot from XBMC
To do so, you need a “policy” file. Save this to /etc/polkit-1/localauthority/50-local.d/custom-actions.pkla
[Actions for xbmc user]
Identity=unix-user:xbmc
Action=org.freedesktop.upower.*;org.freedesktop.consolekit.system.*;org.freedesktop.udisks.*
ResultAny=yes
ResultInactive=yes
ResultActive=yes
Allow wake from any USB peripheral
Add this line to /etc/rc.global
, just before exit 0, and make it executable (chmod +x /etc/rc.local
)
echo enabled | tee /sys/bus/usb/devices/*/power/wakeup |
echo enabled | tee /sys/bus/usb/devices/*/power/wakeup
Conclusion
I may have forgotten something while writing this post. What I definitely forgot is when I needed to reboot to apply this changes. I know it is a very Windows-y thing to do, but reboot if things don’t seem to work.