A Linux server for the Rio Receiver

[Change Log] [Theory] [Installation] [Bugs]

Version 0.10 Jeff Mock
Version 0.20 updates Leigh L. Klotz, Jr.

Changelog

v0.20 beta LLK - 3/26/02
v0.10 - 2/20/01

This is a set of Perl CGI scripts for using the Rio Receiver with a Linux server. Download the latest stable release from ftp://ftp.mock.com/pub/rioserve_0_10.tgz. and this release from http://graflex.org/klotz/rio/rioserve_0_20b-llk.tgz.

The Rio Receiver is a piece of home stereo equipment for playing MP3 or WMA files over either ethernet or an HPNA phone line network. The Rio Receiver is also sold as the Dell Digital Audio Receiver. The two products have different front bezels and splash screens, but they are the same product otherwise. Currently the Dell product is sold for $299 including speakers and the Rio product is sold for $349 without speakers. I'll refer to both devices as the Rio Receiver.

The device was developed by Empeg in England and they licensed the design to Rio/S3/Diamond/SonicBlue who later acquired Empeg. Empeg is the company that developed a well known Linux based automotive MP3 player.

The device is designed to work with a Windows host that serves audio files to the device. It's a nice idea, I bought one hoping to use a Linux machine as the server. I read that the Empeg automotive product was Linux based and fairly open, so I purchased a receiver optimistic that the home stereo product is a similar design and that I could coerce it to work with a Linux server.

With the enclosed Perl scripts it's easy to setup a Linux machine running Apache to serve MP3's to any number of these receivers. With a Linux server the device operates nearly identical to a box using a Windows machine as a server although there are still a few features missing from the Linux server. This code currently does not support saved playlists or saving favorites. I have not attempted to support the HPNA networking interface, I also have not attempted to support WMA files. All of my content is in MP3 format, but I suspect that adding WMA support to the server is trivial.

The linux server has a few features not provided in the windows implementation. It's open source and easy to change to suit your needs. The database of songs (stored in DBM files) can be modified while music is playing, the Windows implmentation requires that music play stop during a database update. When an album is selected this implementation plays songs in the original order of the album.

This document is still a bit rough. It is not a clean howto, it's more of a description about how the receiver works, but it should be easy to setup a server. I'm assuming that the reader is pretty technical and comfortable configuring Apache, setting up a DHCP server, and configuring NFS on a linux box.

I have not modified the device software so there's no worry about trashing the firmware stored in flash in the receiver. It's not necessary to open the case of the receiver so there's no worry about voiding the warranty.

Theory of operation

Hardware

The Rio Receiver contains a 74MHz ARM7 Cirrus processor, 512KB of flash memory for booting, 4MB of DRAM, a CS8900 based 10baseT ethernet interface, and a Broadcom HPNA interface. HPNA is a 10mbit/s network that shares the telephone wires in your house. No doubt a remarkable technical achievement, but in a world where 100baseT switched hubs are $99 I don't have any interest in sorting out HPNA.

The receiver also contains a 16-bit DAC, headphone amplifier, 10W power amp for speakers, an LCD, an IR receiver for a consumer remote control, and buttons for a user interface.

The box contains no storage for music, all of the content must come over the network. With 4MB of DRAM it has enough memory for many seconds of music buffering.

The box is currently priced at $299 (Dell) and $349 (SonicBlue). This price is similar to the introductory price for other MP3 devices, but I think the product is terribly over-priced as measured by parts cost. Portable MP3 players typically have 64MB of flash memory that is the main cost of the unit. At today's prices 64MB of flash is about $50 of parts cost. The Rio Receiver only contains 0.5 MB of flash for booting, this is inexpensive and the other components on the board are inexpensive as well. I estimate the parts cost of the receiver to be about $80. Using rule of thumb margins this device could sell online for well under $200. I hope the vendors will price the device more agressively after collecting money from the early adopters like me.

Network Snooping

To develop the Linux code I installed the host software on a Windows machine and used ethereal, a fabulous network snopping tool, to watch packets from my Linux machine. My strategy was to observe the behavior of the device with a windows box and then write code to duplicate the behavior on Linux.

The first thing the box does on power-up is to send out a DHCP request to get an IP address. Rio's windows software has a limited DHCP server that evidently only repsonds to requests from audio receivers. It's easy enough to add a DHCP entry on the Linux box to provide an IP address to the reciever. I don't think there is a method to assign a static IP.

The second thing the box does is broadcast an SSDP request on a specific UDP port (21075) looking for a server. The server responds to the box on the same UDP port with a packet that contains a URL and port number.

The port number contained in the SSDP response is used by the server for all future communication between receiver and server. This is a little problematic in that this port is used for portmapper requests (instead of port 111) for NFS mounts.

My linux solution for the SSDP requests is a very simple UDP server written in Perl that listens on UDP port 21075 for requests from a receiver box and replies with an IP address for the server, but omits the port number. When the port number is ommitted in the SSDP reply the receiver uses standard ports for various requests making linux configuration easier.

I start the SSDP server ssdp.pl from /etc/rc.d/rc.local. It sits quietly and responds to a couple of SSDP requests when the receiver is booted.

NFS

After receiving the SSDP response the box attempts to do a read-only, no_root_squash NFS mount of "/tftpboot" and looks for the path /tftpboot/ipaddr/ on the server. If you have ssdp.pl running you should be able to boot the box and see syslog messages for the NFS mount. Add an entry in /etc/exports to provide an insecure read-only mount for /tftpboot. Please review your network security and make sure you're not doing anything to expose your machine to Internet attack. This warning applies to all these configuration issues, none of this should be considered secure.

After the NFS mount, the box proceeds to access /zImage on the mounted partition. On reset the box first boots Linux from the onboard flash, NFS mounts the root partition, reads /zImage from the mounted partition, and boots linux a second time from the zImage file. The double boot stategy is very clever, the kernel stored in flash does not need to change when software is upgraded, kernel enhancements can be added to /zImage on the NFS partition and take effect on the second boot.

The /tftpboot/ path prefix for the root partition is a bit of a misnomer. TFTP is a different UDP protocol for booting, we are NFS mounting a directory called /tftpboot, there is no TFTP server required. This is a quirk of the default paths for NFS root partitions in the Linux kernel.

Root filesystem

The root filesystem for the receiver is stored in the Windows software distrubution in a file called mercury.arf. This is a standard GNU tar archive file. Untar the file into /tftpboot/ipaddr and you'll find a minimal but familiar looking linux root filesystem.

I have not included mercury.arf in this distribution because I don't believe that I can. In addition to the Linux kernel this tar file contains the closed source player application for the receiver that I do not have permission to distribute.

Once the root filesystem is in place you should be able to reboot the reciever and follow the action in the server's syslog. First DHCP, a SSDP request gets logged, NFS mount of "/", and if all goes well the box will read zImage and boot a second time. On the second boot networking information is passed in the command line to the kernel so it will not make a second DHCP or SSDP request but it will mount the NFS root filesystem a second time. If you snoop the NFS traffic (did I mention how great ethereal is) you'll see the client box read quite a few files including running the application in /empeg/bin/player. When the player starts in the client it will make a different SSDP request you'll see in the syslog. Previous SSDP requrests are labeled "linux" requests, this one will be labeled a "player" request. When you get this far this is quite a milestone. You'll be playing music soon after a little Apache configuration. The second SSDP reply provides the server IP address, but also provides a port number this time. This port is used for HTTP requests to access music content. By default ssdp.pl tells the reciever to use port 81 for HTTP requests but you can easily change this is if you would like to use a different port. I use a non-standard port number and associated Apache virual host configuration to keep my music playing separate from my normal web server activities.

Music Serving

The player application in the receiver uses HTTP for accessing the music database and music content. When I started snooping the network packets and analyzing the requests I figured that I would write a Perl HTTP server to provide all of the receiver's needs. As it turned out, it was much easier to configure Apache and add a few CGI scripts to do the same thing.

An interesting aspect of the receiver's user interface is that most of the onscreen navigation is a front end to web server requests that provide song information. This is an interesting way to build a user interface for a piece of consumer electronics, but sadly the Rio Receiver only takes this concept to the point where the web server provides data. Navigation and UI appearance are hard coded into the client app.

Installation

DHCP Configuration

If you already have a DHCP server, configure it to give the Rio the same IP address every time either by specifying its MAC address, or by giving it infinite timeout. If you cannot give it a static IP address, then you'll have to create symbolic links in the /tftpboot directory for each IP address that it might have. (Don't worry -- they won't interfere with any other non-RIO computers on your network.)

To configure a DHCP server for RedHat Linux 7.2, do the following

# rpm -ivh /mnt/cdrom/RedHat/RPMS/dhcpd-*.rpm

# cat > /etc/dhcpd.conf
## You need a subnet declaration for your external network
## that says not to give out addresses, if you have one.

## subnet 13.242.128.0 netmask 255.255.252.0 { }

subnet 192.168.0.0 netmask 255.255.255.0 {
	option routers			192.168.0.1;	# whatever your router is 
	option subnet-mask		255.255.255.0;	# and your own private network mask

	option domain-name-servers	192.168.0.2;	# whatever your DNS server is

	option time-offset		-8;	# Pacific Standard Time

	range dynamic-bootp 192.168.0.128 192.168.0.255; # rio won't use these options
	default-lease-time 21600;
	max-lease-time 43200;

	# we need the rio to appear at a fixed address
	host rio {
		next-server 192.168.0.2;
		hardware ethernet 00:90:00:11:4a:7e;    # set to your Rio's MAC address
		fixed-address 192.168.0.6;		# Set to the IP address you want for your RIO
	}
}
^D
# /sbin/service dhcpd restart
# tail /var/log/messages
# /sbin/chkconfig --level 345 dhcpd on

Apache Configuration

Use the virtual host facilities of Apache to setup a server specifically for the receiver on a non-standard port. Here are the changes necessary to /etc/httpd/httpd.conf for Apache:

     # This is off by default so remove the uncomment it:
     AddHandler cgi-script .cgi

    # for music server
    Listen 192.168.0.2:81  
    <VirtualHost 192.168.0.2:81>
        ServerAdmin you@example.com
        DocumentRoot /share/rio/mserve
        ServerName 192.168.0.2
        AliasMatch ^/list/   /share/rio/mserve/list.cgi
        AliasMatch ^/tags/   /share/rio/mserve/tags.cgi
        AliasMatch ^/query   /share/rio/mserve/query.cgi
        AliasMatch ^/results /share/rio/mserve/results.cgi
        ErrorLog  logs/mserve-error_log
        CustomLog logs/mserve-access_log common
        <Directory "/share/rio/mserve">
            Options ExecCGI FollowSymLinks
        </Directory>
    </VirtualHost>

If you use a differnt directory other than /share/rio you'll also have to edit /share/rio/mserve/config.pl .

And restart httpd:

# service httpd restart

You can change this to suit your needs. The only funny business is the AliasMatch directive. Some of the HTTP requests come in a form that is a little awkward for running a CGI script so I use the alias feature of Apache to coerce the URL into a suitable form. For example, the box will make a request like GET /tags/4000 to read the mp3 tag information from track 4000. The alias causes the request to run the CGI script /share/rio/mserver/tags.cgi which is passed the orignal URL to sort out and return the correct data. It's not as complicated as I just made it sound.

Untar this distrubution into the newly configured virtual web server, /share/rio/mserver in my case. Change the ownership of all of the files in the directory to apache to avoid any permission problems:

    # chown -R yourusername:apache /share/rio/mserve
    # chmod 775 /share/rio/mserve /share/rio/mserve/content /share/rio/mserve/dbm
    # chmod g+s /share/rio/mserve /share/rio/mserve/content /share/rio/mserve/dbm
    # chown -R apache:apache /share/rio/mserve/content

Restart apache and use a browser to go to http://192.168.0.2:81/ (in my case). You should see a simple page with pointers to this document and some other miscellaneous items.

Logrotate Configuration

Add /var/log/httpd/mserve-error_log and /var/log/httpd/mserve-access_log to /etc/logrotate.d/apache:
#cat >> /etc/logrotate.d/apache
/var/log/httpd/mserve-access_log {
    missingok
    compress 
    delaycompress
    postrotate
	/bin/kill -HUP `cat /var/run/httpd.pid 2>/dev/null` 2> /dev/null || true
    endscript
}

/var/log/httpd/mserve-error_log {
    missingok
    compress 
    delaycompress
    postrotate
	/bin/kill -HUP `cat /var/run/httpd.pid 2>/dev/null` 2> /dev/null || true
    endscript
}
^D

SSDP Configuration

  1. Edit the file /share/rio/mserve/ssdp.pl as follows:

    my $mserve_ip   = "192.168.0.1"; 		# web and NFS server IP address
    my $mserve_port = "81";				# web server port
    
  2. Add the following lines to /etc/rc.local, before the last line that says touch /var/lock/subsys/local:

    #########################################
    ########## Local changes ################
    #########################################
    if [ -x /share/rio/mserve/ssdp.pl ]; then
      /share/rio/mserve/ssdp.pl
    fi
    ##########################################
    
  3. And now execute those in a root shell window to start the SSDP server

NFS Configuration

# rpm -ivh /mnt/cdrom/RedHat/RPMS/nfs-utils-*.rpm
# cat >> /etc/exports
/tftpboot (rw,no_root_squash)
^D
# ln -s /share/rio/tftpboot /tftpboot
# cd /share/rio/tftpboot
# mkdir 192.168.0.6
# cd 192.168.0.6
# tar xf mercury.arf
# /usr/sbin/exportfs
# /usr/sbin/showmount

Building the music database

The next order of business is to build a music database containing information about all of your MP3 files. The scripts are setup assuming that your MP3s are stored in /share/rio/music. If you wish to store them elsewhere then change the .CGI files to reflect the new path.

One of the things that happens when the music database is built is that the content sub-directory is filled with a bunch of symbolic links to the actual MP3 files. I was having trouble getting Apache to follow symbolic links outside of /share/rio and I wound up putting a symbolic link to music files from within the Apache directory tree with
ln -s /share/music /share/rio/music, but that might just be my own ignorance about configuring Apache.

Once you have the paths configured in the scripts and your music files ready. From a web browser click on the "rebuild entire database" link located in the index.html page at the root of the music server. This should spew ugly text output about each MP3 file along with a 4 digit hex identifier used by the receiver. You might look in the content subdirectory and find a large number of symbolic links with names like "4000" that point to actual MP3 files. You should also look in the dbm directory and see a number of DBM files.

Testing the database

Here are a few things you can do to test the database. The "dump" link from the main page prints out the DBM files used for the database in a pretty raw format useful for debugging. The "music" link shows the database in a nicely ordered way. The two main problems you might have building the database are setting the protection bits on the directories such that the CGI scripts cannot write the DBM files, or possibly an Apache configuration problem with symbolic links.

Finally, we can use a browser to make a request the same way the receiver does to verify operation. Try the following cryptic URL from a web browser:

    http://192.168.0.2:81/query?artist=

If the databse has been built should see output something like this although your musical tastes may vary:

    matches=
    0=25,0,0:10,000 Maniacs
    1=2,0,0:AC/DC
    2=65,0,0:Al Green
    3=2,0,0:Alpha Blondie
    4=12,0,0:American Beauty
    5=1,0,0:Archie Bell & The Drells
    6=1,0,0:Arlo Guthrie
    ...
If there are problems the scripts are setup to dump errors to the file error-log in the same directory. If this is working you are pretty much ready to go. Reboot the receiver and you should be rewarded with the MP3 tag information of one of your songs in the display. If we're very lucky you can hit the play button and listen to music.

Once you get this far you should play with the user interface of the box. The only missing features are the "list" button on the remote control and selecting songs by playlist. You can select songs by artist, album, etc. You can use the slightly awkward user interface feature to spell out a portion of a title using the telephone pad on the remote control.

There is another link on the page to incrementally add songs to the database. If the database is completely rebuilt the receiver needs to be rebooted but songs can be added incrementally. The script scans the music directory and only adds songs that aren't already in the database.

Bugs and Complaining

This is an alpha release of 0.20. Please send bugs to klotz@graflex.org first.

I've been using the reveiver to listen to music hosted by my linux machine for several weeks now. I've had a couple of odd occassions where I need to reboot the receiver, but it's been quite reliable for the most part.

I no longer have a CD player in my livingroom. I still buy quite a few CDs, but they arrive from Amazon, get ripped on the CD drive of my PC, and then stored away in a closet and never make it to the livingroom.

The receiver has a generally annoying user interface, the screen is too small, and the UI presents information too small for navigation from across the room. The box has a couple of annoying qualities, the volume control only operates on the headphone and speaker outputs. Based on a suggestion from Hugo Fennes here is a kernel patch for the device to cause the volume control to operate on the line level outputs of the box as well. If you don't fell like getting setup for building an Arm Linux kernel there is a pre-built kernel linked on the page.

It's unfortunate that you can't control the receiver from the network interface. There is a UDP protocol in the receiver for getting status information and doing a few operations like rebooting or reloading database information, but it's not possible to control play from the network interface (as far as I can tell). I keep wanting to use the receiver as an alarm clock controlled by my linux box.

I don't think the HPNA interface was a great idea. I'm probably not a typical consumer, but if a consumer is interested in digital music in their livingroom, they probably use a program like Napster. If they run Napster they probably have a DSL line or a cable modem. If they have a high bandwidth connection then ethernet is already a part of their lives.

Having said that, the device is really nice and I enjoy having it my livingroom quite a lot. I trust the Empeg people are working on a new software release and will work out many of the kinks in the first release.

Console

It is possible to access the linux console of the receiver. If you're just setting up another linux machine as a server it's not necessary and probably voids your warranty to open the box to access the console, but it is kind of fun.

If you open the case there is a 4-pin header labelled JP5. This header provides 3.3v power, ground, Tx, and Rx pins for the serial port built into the Cirrus Arm processor. The pinout of the header is shown below. The Tx and Rx pins are 3.3v logic levels, so you have to build a small piece of hardware to convert the 3.3v logic levels to RS-232 levels. I built a small board with a Linear Tech LTC1348 from Digikey to get serial port out. The console header is show in the image above, the pinout as follows:

  1. Triangle mark on PCB, 3.3v power
  2. Tx data from box (3.3v logic level)
  3. Rx data to box (3.3v logic level)
  4. Ground

If you build a level converter and hook it to the header you'll see familiar Linux boot messages at 115k baud, you'll also see the two stage boot process when it loads the second kernel over NFS.

When the receiver boots it looks for /bin/bash and allows you to drop into a shell from the console by typing "q<cr>" on the console. The mercury.arf file distributed with the recevier does not contain bash, but it's easy enough to build an ARM cross-compiler and then build bash, but there's not much you can do with a shell right now.

Wish list

It's difficult to hack the application running on the receiver. If you look at the filesystem for the receiver you'll find that the player application is a single monolithic statically linked app. The best thing would be to make this app open source. Short of that I would suggest breaking the application into three pieces: the audio player engine, streaming file access, and user interface. This way someone could write a new UI for the player or perhaps a new access mechanism or a new codec without rewriting everything else.

To do

Need to add playlist support and favorites support. I would really like to replace the player app and rewrite the UI, but this is a pretty big project. Hopefully some other people will get involved with hacking the receiver. If you use this package send me some email and let me know how it goes.