Saturday, August 25, 2007

linux@dbox2: implemented AVIA ENX reset

Since this is the first weekend after returning from vacation, i thought i'd turn to the tuxbox project again. This time i experimented with my Sagem dbox2 instead of my dreambox 500.
I was finally fed up with the joy of reverse-engineered hardware: sometimes, if you do strange things to the AVIA ENX chip, it will somehow lock up. Unfortunately, there is no way to reset it, short of rebooting the box. Looking at the code i found out that there actually is a reset function for the chip, but it's only used at module load and unload. Unfortunately you cannot unload the module since lots of other stuff depends on it, so rebooting the box is actually faster than trying to reload the module.

I went for the quick and very dirty way and exported a file in /proc which, if something is echoed into it, performs a chip reset of the AVIA ENX chip.

The diff is quick, dirty, and trivial. But it works for me. The important parts of it are here:
--- a/driver/ext/aviaEXT.c      21 May 2006 23:01:10 -0000
+++ b/driver/ext/aviaEXT.c 25 Aug 2007 12:53:13 -0000
@@ -27,6 +27,7 @@
#include <linux/module.h>
#include <linux/devfs_fs_kernel.h>
#include <asm/uaccess.h>
+#include <linux/proc_fs.h>

#include "avia_av.h"
#include <dbox/aviaEXT.h>
@@ -86,6 +87,14 @@ static struct file_operations aviaEXT_fo
.ioctl = aviaEXT_ioctl
};

+extern void avia_gt_dmx_risc_reset(int);
+static int avia_av_proc_write_avia_reset(struct file *f, const char *b, unsigned long c, void *d)
+{
+ printk("calling avia_gt_dmx_risc_reset(1)\n");
+ avia_gt_dmx_risc_reset(1);
+ return c;
+}
+
static int __init aviaEXT_init(void)
{ if (!(devfs_h = devfs_register(NULL,"dbox/aviaEXT", DEVFS_FL_DEFAULT, 0, 0,
@@ -93,6 +102,18 @@
printk(KERN_ERR "aviaEXT: could not register with devfs.\n");
return -EIO;
}
+
+ struct proc_dir_entry *proc_bus_avia_reset;
+
+ proc_bus_avia_reset = create_proc_entry("avia_reset", 0200, proc_bus);
+ if (!proc_bus_avia_reset) {
+ printk("avia_av_proc: could not create /proc/bus/avia_reset");
+ return -ENOENT;
+ }
+
+ proc_bus_avia_reset->write_proc = avia_av_proc_write_avia_reset;
+ proc_bus_avia_reset->owner = THIS_MODULE;
+
return 0;
}


Of course you need to EXPORT_SYMBOL(avia_gt_dmx_risc_reset); at the appropriate place.

Combine this interface with something like the following:
#!/bin/sh
valold=""
while sleep 1; do
# the "avia" interrupts stop if the chip has crashed
valnew=$(grep avia$ /proc/interrupts)
if [ "$valnew" = "$valold" ]; then
echo "$(date) avia_reset"
echo > /proc/bus/avia_reset
fi
valold="$valnew"
done


It resets the chip automatically, should it crash again while experimenting.