Tuesday, 15 October 2013

Connecting a Topfield PVR to a Raspberry Pi

I have a Topfield 5800 PVR (personal video recorder). It has a USB socket that can be used to transfer software and recordings to or from the PVR. Connecting this USB socket to my Raspberry Pi bare bones computer (that's on all the time) allows me to do a few useful things:
  • Download up to 2 weeks of EPG data to supplement the 1 week that's broadcast over the air
  • Update PVR software by FTP from my main computer
  • Copy radio recordings from the PVR and convert them to mp3 format for listening to when out and about
  • Backup various settings from the PVR

Preliminaries

The Raspbian operating system includes the "gphoto2 digital camera library" - a package that interfaces to a wide range of digital cameras. Unfortunately it thinks it can also connect to the Topfield PVR, so it has a "udev" rule to take ownership of it. Disabling this udev rule is quite easy, but will need to be done every time libgphoto2 is updated.

Firstly, connect the PVR to the Pi, turn it on and then use lsusb to check the "idVendor" and "idProduct" of the PVR:
jim@firefly ~ $ lsusb
Bus 001 Device 002: ID 0424:9512 Standard Microsystems Corp. 
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. 
Bus 001 Device 004: ID 1a40:0201 Terminus Technology Inc. FE 2.1 7-port Hub
Bus 001 Device 005: ID 1941:8021 Dream Link WH1080 Weather Station / USB Missile Launcher
Bus 001 Device 007: ID 148f:5370 Ralink Technology, Corp. RT5370 Wireless Adapter
Bus 001 Device 008: ID 059f:0351 LaCie, Ltd 
Bus 001 Device 092: ID 11db:1000 Topfield Co., Ltd. PVR
This shows the idVendor to be 11db and the idProduct to be 1000. Now we can find the udev "rules" file that contains the idVendor value:
jim@firefly ~ $ grep 11db /lib/udev/rules.d/* /etc/udev/rules.d/*
/lib/udev/rules.d/60-libgphoto2-2.rules:ATTRS{idVendor}=="11db", ATTRS{idProduct}=="1000", ENV{ID_GPHOTO2}="1", ENV{GPHOTO2_DRIVER}="proprietary", MODE="0664", GROUP="plugdev"
This shows the rule to be in /lib/udev/rules.d/60-libgphoto2-2.rules, so edit this file (sudo will be needed) and comment out the line by inserting a # character at the beginning.

Now we can create a new group, add our user to that group and then create a new udev rule to assign the PVR to that group:
sudo addgroup --system toppy
sudo adduser jim toppy
sudo echo 'ATTRS{idVendor}=="11db", ATTRS{idProduct}=="1000", GROUP="toppy"' >/etc/udev/rules.d/10-topfield-pvr.rules
sudo udevadm control --reload
sudo udevadm trigger
The USB device corresponding to the PVR (e.g. /dev/bus/usb/001/092) should now belong to the "toppy" group.

Install software

There are two programs used to communicate with the PVR - "puppy" and "ftpd-topfield". These can be downloaded from http://birdman.dynalias.org/cgi-bin/tdl.cgi/armv5tel/puppy-new_dev_scan-static and http://birdman.dynalias.org/cgi-bin/tdl.cgi/armv5tel/ftpd-new_dev_scan-static. I renamed the downloaded files (removing the "-new_dev_scan-static" bit) and put them in /home/jim/bin.

A quick test is to use puppy to get the PVR's disc size and free space:
jim@firefly ~ $ /home/jim/bin/puppy -c size
Total  976749568 kiB  953857 MiB  931 GiB
Free   386535424 kiB  377476 MiB  368 GiB
Testing ftpd-topfield is a little bit more complicated. On the Pi, start the ftpd-topfield daemon in debug mode:
sudo /home/jim/bin/ftpd-topfield -D -P 2021 -d
Then on another machine FTP to port 2021 and get a directory listing:
jim@brains:~$ ftp jim@firefly 2021
Connected to firefly.tracy.island.
220 firefly FTP server (Topfield ftpd 0.7.7p) ready.
331 Guest login ok, type your name as password.
Password:
230 Guest login ok, access restrictions apply.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> dir
229 Entering Extended Passive Mode (|||33252|)
150 Opening ASCII mode data connection for 'file list'.
drwxr-xr-x   1 none     none                0 Jan  1  2003 DataFiles
drwxr-xr-x   1 none     none                0 Jan  1  2003 MP3
drwxr-xr-x   1 none     none                0 Jan  1  2003 ProgramFiles
drwxr-xr-x   1 none     none                0 Jan  1  1970 firmware
drwxr-xr-x   1 none     none                0 Jan  1  1970 turbo
226 Transfer complete.
ftp> bye
221 Goodbye.
You should see some debug output on the Pi when you do this. Finally kill the ftpd-topfield program with <Ctrl>-C.

Using xinetd to start ftpd-topfield

We can't leave ftpd-topfield running all the time, as each time the PVR switches on it gets a new USB device number. The solution is to install the "xinetd" package which can start ftpd-topfield in response to an incoming FTP connection.

Firstly install xinetd:
sudo apt-get install xinetd
Then create a file /etc/xinetd.d/ftp-topfield with the following contents:
service ftpd-topfield
{
        type            = UNLISTED
        socket_type     = stream
        protocol        = tcp
        port            = 2021
        server          = /home/jim/bin/ftpd-topfield
        server_args     = --port=2021 --logging
        wait            = no
        user            = root
        disable         = no
        instances       = 1
}
and restart xinetd:
sudo /etc/init.d/xinetd restart
Now you should be able to FTP from the another machine again. Monitoring processes on the Pi (with ps ax) should show an ftpd-topfield process running when the FTP connection is active.

Running a script when the PVR switches on

The final step in automating transfer of  files to and from the PVR is to run a script each time it turns on. This requires a simple modification to the udev rule created above, adding the name of a script to run:
jim@firefly ~ $ cat /etc/udev/rules.d/10-topfield-pvr.rules 
ATTRS{idVendor}=="11db", ATTRS{idProduct}=="1000", GROUP="toppy", RUN+="/home/jim/scripts/toppy-add.sh"
The script "/home/jim/scripts/toppy-add.sh" is run as root by udev, so if the user isn't my normal user it uses at to restart itself in the background. This allows udev to finish while the script is still running - vital to avoid blocking udev.

The script creates a lock file (to ensure only one instance of it runs at any one time) and calls my Python scripts that use FTP to do all the things I want:
jim@firefly ~ $ cat /home/jim/scripts/toppy-add.sh
#!/bin/sh
#
# toppy-add script
#
# run by hotplug when Toppy switches on

# check lock file
lock_file=/var/lock/toppy-add.lock
if [ -f $lock_file ]; then
  exit
  fi

# udev runs as root, so restart in background as user jim
if [ "$USER" != "jim" ]; then
  echo "sudo -u jim /home/jim/scripts/toppy-add.sh" | at now
  exit 0
  fi

touch $lock_file

# allow Toppy to settle and load TAPs
sleep 60

log=/tmp/log-toppy-add
rm -f $log

python /home/jim/scripts/imagetoppy.py >>$log 2>&1
python /home/jim/scripts/mei2toppy.py >>$log 2>&1
python /home/jim/scripts/toppy2pod.py >>$log 2>&1

# mail the log file
if [ -s $log ]; then
  /home/jim/scripts/email-log.sh $log "toppy-add log"
  fi

rm -f $lock_file

3 comments:

Ville Koskinen said...

Just dropping a comment to let you know this stuff is useful. I'm setting up ftpd-topfield with Raspbmc.

Bash is giving me "no such file or directory" when trying to run the daemon (the binary is at /usr/bin with executable flags set).

After some googling I suspect that it might be caused by a missing library (yes I have the static version of the binary) since the guides seem to assume Raspbian and Raspbmc is quite stripped down package-wise.

Jim said...

I don't know much about Raspbmc, so can't offer much advice. I don't suppose it's something as simple as it not being able to write its pid file? Have you got a /var/run directory that's writeable by everyone?

Ville Koskinen said...

/var/run does exists. Also ldd or strace did not give me any clues as to what's going on.

I might try compiling the binary on the weekend.