diff -Naur linux-2.4.25.orrig/drivers/char/Config.in linux-2.4.25/drivers/char/Config.in
--- linux-2.4.25.orrig/drivers/char/Config.in	2004-02-18 13:36:31.000000000 +0000
+++ linux-2.4.25/drivers/char/Config.in	2004-03-07 21:02:37.000000000 +0000
@@ -291,6 +291,8 @@
 tristate 'NatSemi SCx200 Support' CONFIG_SCx200
 dep_tristate '  NatSemi SCx200 GPIO Support' CONFIG_SCx200_GPIO $CONFIG_SCx200
 
+dep_tristate 'Soekris net4801 Error LED / GPIO Support' CONFIG_NET4801_GPIO $CONFIG_MGEODE
+
 if [ "$CONFIG_IA64_GENERIC" = "y" -o "$CONFIG_IA64_SGI_SN2" = "y" ] ; then
    bool 'SGI SN2 fetchop support' CONFIG_FETCHOP
 fi
diff -Naur linux-2.4.25.orrig/drivers/char/Makefile linux-2.4.25/drivers/char/Makefile
--- linux-2.4.25.orrig/drivers/char/Makefile	2004-02-18 13:36:31.000000000 +0000
+++ linux-2.4.25/drivers/char/Makefile	2004-03-07 21:03:50.000000000 +0000
@@ -291,6 +291,7 @@
 obj-$(CONFIG_NWFLASH) += nwflash.o
 obj-$(CONFIG_SCx200) += scx200.o
 obj-$(CONFIG_SCx200_GPIO) += scx200_gpio.o
+obj-$(CONFIG_NET4801_GPIO) += net4801gpio.o
 
 # Only one watchdog can succeed. We probe the hardware watchdog
 # drivers first, then the softdog driver.  This means if your hardware
diff -Naur linux-2.4.25.orrig/drivers/char/net4801gpio.c linux-2.4.25/drivers/char/net4801gpio.c
--- linux-2.4.25.orrig/drivers/char/net4801gpio.c	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.4.25/drivers/char/net4801gpio.c	2004-03-07 20:59:06.000000000 +0000
@@ -0,0 +1,939 @@
+/*
+**********************************************************************
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2, or (at your option)
+* any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**********************************************************************
+
+*/
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/fcntl.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/pci.h>
+
+#include <linux/net4801gpio.h>
+
+#define VERSION                 "1.2.0+daj0.1"
+
+/* by default, we use dynamic allocation of major numbers */
+#define GPIO_MAJOR 0
+
+/* minor numbers used*/
+#define MINOR_GPIO 0        /* access to GPIO */
+#define MINOR_LED  1        /* access to the error LED */
+
+static spinlock_t led_lock;
+static spinlock_t gpio_lock;
+
+static int gpio_major =  GPIO_MAJOR;
+
+/*
+  Soekris Net48xx GPIO driver
+  Provides basic control (read and write) over the 12 general purpose
+  IO-pins of the Soekris Net4801 and the error-led (which happens to
+  be connected to another general purpose IO pin)
+
+  Mapping between the
+  Accessing the GPIO pins (assuming all are output):
+  echo 111111111111 > /proc/driver/soekris_gpio --> turns all ports on
+  echo 000000000000 > /proc/driver/soekris_gpio --> turns all ports off
+  echo 000000000100 > /proc/driver/soekris_gpio --> turns GPIO2 on,
+  everything else off
+
+  cat /proc/driver/soekris_gpio   --> prints the current
+  status of the gpio pins
+
+  accessing the error LED:
+  echo 1 > /proc/driver/soekris_error_led --> turns the led on
+
+  cat /proc/driver/soekris_error_led
+  --> prints the current status of the error led
+
+  Setting input or output for a pin
+  echo 000010000001 /proc/driver/soekris_gpio_settings ->
+  turns GPIO7 and GPIO0 to output, all the others to input
+
+  cat /proc/driver/soekris_goio_settings -->prints the current gpio-settings
+
+  Devices:
+  major ? (dynamic) minor 0 : set/read PIO0-11 (Net4801 mapping)
+  major ? (dynamic) minor 1 : set/read error_led (Net4801)
+  Which major number has been assigned can be determined by looking at
+  /proc/devices
+
+
+  Known IOCTL commands:
+
+  GPIORDNUMPINS (int)
+    gets number of pins (12 or 1 depending on device)
+
+  GPIORDDIRECTION,GPIOWRDIRECTION (int)
+    sets/gets direction (bitmask)
+
+  GPIORDDATA,GPIOWRDATA (int)
+    sets/gets data (bitmask)
+
+  Credits/References
+  Linux Device Drivers, 2nd Edition, O'Reilly, ISBN 0-59600-008-1
+  http://www.tldp.org/LDP/lkmpg/
+  soekris-tech mailing list
+  (http://lists.soekris.com/mailman/listinfo/soekris-tech)
+  Linux scx200 device driver
+  Geode SC1100 Specification
+  PC87366  Specification
+
+  (c) Copyright 2003   Martin Hejl <martin.hejl@guh-software.de>
+  G&H Softwareentwicklung GmbH
+
+  Changelog
+  1.2.0 2003-11-09   Added support for net4801
+  1.2.0+daj0.1 2003-12-01   Mostly rewritten by Dave Johnson
+
+Pin assignment of the Net4801 GPIOs (connected to the 87226 GPIO pins
+and _NOT_ to the GPIO pins of the GEODE processor)
+
+                     +----+----+
+               3,3 V |  1 |  2 | 5 V
+                     +----+----+
+GPIO0   Port 2 Bit 0 |  3 |  4 | Port 2 Bit 1   GPIO1
+                     +----+----+
+GPIO2   Port 2 Bit 2 |  5 |  6 | Port 2 Bit 3   GPIO3
+                     +----+----+
+GPIO4   Port 2 Bit 4 |  7 |  8 | Port 2 Bit 5   GPIO5
+                     +----+----+
+GPIO6   Port 2 Bit 6 |  9 | 10 | Port 2 Bit 7   GPIO7
+                     +----+----+
+                 GND | 11 | 12 | Port 0 Bit 4   GPIO8
+                     +----+----+
+GPIO9   Port 0 Bit 5 | 13 | 14 | GND
+                     +----+----+
+GPIO10  Port 1 Bit 3 | 15 | 16 | Port 1 Bit 2   GPIO11
+                     +----+----+
+                 GND | 17 | 18 | unknown (docs say GND, but is 3.3V on my box)
+                     +----+----+
+               3,3 V | 19 | 20 | GND
+                     +----+----+
+
+Error_LED => GEODE GPIO20
+
+*/
+
+/*
+  Soekris Net4801 GPIO driver
+  Provides basic control (read and write) over the 12 general purpose
+  IO-pins of the Soekris Net4801 and the error-led (which happens to
+  be connected to another general purpose IO pin)
+*/
+
+
+
+/* 12 gpio pins  */
+#define NUMBER_OF_PINS 12
+
+#define OUR_NAME "4801gpio"
+
+#define SC1100_VENDOR_ID     0x100B
+#define SC1100_F0_DEVICE_ID  0x0510
+
+/* of set to use if we can't find the SIO at 0x2E */
+#define SIO_BASE_OFFSET 0x20
+
+/* defines for IO-Addresses/offsets */
+#define SIO_GPIO_BANK0 sio_gpio_base
+#define SIO_GPIO_BANK1 sio_gpio_base+0x4
+#define SIO_GPIO_BANK2 sio_gpio_base+0x8
+
+#define SIO_INDEX    (0x2E + offset_87336)
+#define SIO_DATA     (0x2f + offset_87336)
+
+#define SIO_DEV_SEL   0x7
+#define SIO_DEV_ENB   0x30
+#define SIO_GPIO_DEV  0x7
+#define SIO_BASE_LADDR 0x61
+#define SIO_BASE_HADDR 0x60
+#define SIO_GPIO_PIN_SELECT    0xF0
+#define SIO_GPIO_PIN_CONFIGURE 0xF1
+
+#define CONFIG_GPIO_INPUT   0x04
+#define CONFIG_GPIO_OUTPUT  0x03
+#define SIO_SID       0x20    /* SuperI/O ID Register */
+
+
+#define SIO_SID_VALUE 0xe9    /* Expected value in SuperI/O ID Register */
+
+#define GEODE_GPIO_SIZE 0x2c  /* Size of GPIO register block */
+#define GEODE_GPIO_DATA_IN 0x04
+#define GEODE_GPIO_DATA_OUT 0x00
+
+static unsigned short int sio_gpio_base=0;
+static unsigned short int geode_gpio_base=0;
+static unsigned short int offset_87336=0;
+
+
+static char* __our_name=OUR_NAME;
+
+
+static void toString(unsigned long value, char* buffer, int number_of_bits)
+{
+  int i;
+
+  /* convert it into a string */
+  for(i=number_of_bits; i>0; i--)
+    {
+      buffer[number_of_bits-i]=test_bit(i-1,&value)?'1':'0';
+    }
+
+  buffer[number_of_bits] = '\n';
+  buffer[number_of_bits+1] = 0;
+
+}
+
+static unsigned long fromString(char* buffer, int number_of_bits)
+{
+  unsigned long ret_val;
+
+  ret_val = simple_strtoul(buffer, NULL, 2);
+  ret_val &= (1<<number_of_bits)-1;
+  
+  return(ret_val);
+}
+
+
+static void _net4801_write_error_led(unsigned int on)
+{
+  unsigned char value;
+  
+  spin_lock(&led_lock);
+  
+  value = inb(geode_gpio_base+GEODE_GPIO_DATA_OUT+2);
+  if (on)
+    {
+      set_bit(4, &value);
+    }
+  else
+    {
+      clear_bit(4, &value);
+    }
+  
+  outb(value,geode_gpio_base+GEODE_GPIO_DATA_OUT+2);
+  
+  spin_unlock(&led_lock);
+}
+
+static unsigned int _net4801_read_error_led(void)
+{
+  unsigned char value;
+  
+  value = inb(geode_gpio_base+GEODE_GPIO_DATA_OUT+2);
+  
+  return test_bit(4, &value) ? 1 : 0;
+}
+
+static void _net4801_select_pin(unsigned short port, unsigned short pin)
+{
+  /* select port/pin */
+  outb(SIO_GPIO_PIN_SELECT, SIO_INDEX);
+  outb(((port & 0xF)<<4) | (pin & 0xF), SIO_DATA);
+}
+
+static void _net4801_write_config(unsigned short port, unsigned short pin, unsigned char setting)
+{
+  spin_lock(&gpio_lock);
+  
+  _net4801_select_pin(port, pin);
+
+  /* configure selected port/pin */
+  outb(SIO_GPIO_PIN_CONFIGURE, SIO_INDEX);
+  outb(setting, SIO_DATA);
+  
+  spin_unlock(&gpio_lock);
+}
+
+static unsigned char _net4801_read_config(unsigned short port, unsigned short pin)
+{
+  unsigned char ret;
+  
+  spin_lock(&gpio_lock);
+  
+  _net4801_select_pin(port, pin);
+  
+  /* configure selected port/pin */
+  outb(SIO_GPIO_PIN_CONFIGURE, SIO_INDEX);
+  ret = inb(SIO_DATA);
+  
+  spin_unlock(&gpio_lock);
+  return ret;
+}
+
+static void _net4801_writeGPIO(unsigned int new_value)
+{
+  /*
+   * we can safely ignore the direction since the spec explicitly
+   * states that writing to a GPIO set to input will do nothing
+   */
+
+  outb((unsigned char)((new_value & 0x300)>>4), SIO_GPIO_BANK0);
+  outb((unsigned char)((new_value & 0x400)>>7 | ((new_value & 0x800)>>9)), SIO_GPIO_BANK1);
+  outb((unsigned char)(new_value & 0xFF), SIO_GPIO_BANK2);
+}
+
+static unsigned int _net4801_readGPIO(void)
+{
+  unsigned char value;
+  unsigned long ret_val;
+  
+  ret_val = inb(SIO_GPIO_BANK2);
+  
+  value   = inb(SIO_GPIO_BANK1);
+  ret_val |= (value & 0x8)<<7;
+  ret_val |= (value & 0x4)<<9;
+  
+  value = inb(SIO_GPIO_BANK0);
+  ret_val |= (value & 0x30)<<4;
+  
+  return (ret_val);
+}
+
+
+static void _net4801_setGPIODirection(unsigned long new_direction)
+{
+  int pin;
+  
+  for (pin=0;pin<=7;pin++)
+    {
+      _net4801_write_config(2,pin,test_bit(pin, &new_direction)?CONFIG_GPIO_OUTPUT:CONFIG_GPIO_INPUT);
+    }
+  
+  _net4801_write_config(0,4,test_bit(8, &new_direction)?CONFIG_GPIO_OUTPUT:CONFIG_GPIO_INPUT);
+  _net4801_write_config(0,5,test_bit(9, &new_direction)?CONFIG_GPIO_OUTPUT:CONFIG_GPIO_INPUT);
+  
+  _net4801_write_config(1,3,test_bit(10, &new_direction)?CONFIG_GPIO_OUTPUT:CONFIG_GPIO_INPUT);
+  _net4801_write_config(1,2,test_bit(11, &new_direction)?CONFIG_GPIO_OUTPUT:CONFIG_GPIO_INPUT);
+  
+}
+
+
+static unsigned int _net4801_getGPIODirection(void)
+{
+  int pin;
+  unsigned int value=0;
+  unsigned int config;
+
+  for (pin=0;pin<=7;pin++)
+    {
+      config = _net4801_read_config(2,pin);
+      if ((config & 0x01)!=0)
+        {
+          set_bit(pin, &value);
+        }
+    }
+  
+  if ((_net4801_read_config(0,4) & 0x01)!=0)
+    {
+      set_bit(8,&value);
+    }
+  if ((_net4801_read_config(0,5) & 0x01)!=0)
+    {
+      set_bit(9,&value);
+    }
+  if ((_net4801_read_config(1,3) & 0x01)!=0)
+    {
+      set_bit(10,&value);
+    }
+  if ((_net4801_read_config(1,2) & 0x01)!=0)
+    {
+      set_bit(11,&value);
+    }
+
+  return (value&0xFFF);
+}
+
+
+static int net4801_init(void)
+{
+  struct pci_dev *bridge;
+  unsigned base;
+
+  printk(OUR_NAME ": Version " VERSION " (C) 2003 Martin Hejl & Dave Johnson\n");
+
+  if ((bridge = pci_find_device(SC1100_VENDOR_ID,
+                                SC1100_F0_DEVICE_ID ,
+                                NULL)) == NULL)
+    {
+      return -ENODEV;
+    }
+  
+  base = pci_resource_start(bridge, 0);
+  printk(KERN_INFO OUR_NAME ": Geode GPIO base 0x%x\n", base);
+  
+  if (request_region(base, GEODE_GPIO_SIZE, "Soekris net4801 GPIO") == 0)
+    {
+      printk(KERN_ERR OUR_NAME ": can't allocate I/O for Geode GPIOs\n");
+      return -EBUSY;
+    }
+  
+  geode_gpio_base = base;
+  
+  offset_87336 = 0;
+  outb(SIO_SID,SIO_INDEX);
+  if (inb(SIO_DATA) != SIO_SID_VALUE)
+    {
+      offset_87336 = SIO_BASE_OFFSET;
+      outb(SIO_SID,SIO_INDEX);
+      if (inb(SIO_DATA) != SIO_SID_VALUE)
+        {
+          printk(KERN_ERR OUR_NAME ": PC87336 not detected\n" );
+          
+          /* clean up */
+          unregister_chrdev(gpio_major, OUR_NAME);
+          release_region(geode_gpio_base, GEODE_GPIO_SIZE);
+          return -EBUSY;
+        }
+    }
+  
+  outb_p(SIO_DEV_SEL, SIO_INDEX);
+  outb_p(SIO_GPIO_DEV, SIO_DATA);        /* Talk to GPIO regs. */
+  
+  outb_p(SIO_DEV_ENB, SIO_INDEX);
+  if (inb_p(SIO_DATA)==0)
+    {
+      printk(KERN_ERR OUR_NAME ": GPIOs are disabled\n");
+      /* clean up */
+      unregister_chrdev(gpio_major, OUR_NAME);
+      release_region(geode_gpio_base, GEODE_GPIO_SIZE);
+      return -ENODEV;
+    }
+  
+  // read the base address
+  outb(SIO_BASE_HADDR,SIO_INDEX);
+  sio_gpio_base = inb(SIO_DATA) << 8;
+
+  outb(SIO_BASE_LADDR,SIO_INDEX);
+  sio_gpio_base |= inb(SIO_DATA) ;
+
+  /* set gpio to input */
+  _net4801_setGPIODirection(0);
+  
+  return 0;
+}
+
+static void net4801_cleanup(void)
+{
+  release_region(geode_gpio_base, GEODE_GPIO_SIZE);
+}
+
+static int net4xxx_gpio_open(struct inode *inode, struct file *file)
+{
+  switch (MINOR(inode->i_rdev))
+    {
+    case MINOR_GPIO:
+    case MINOR_LED:
+      MOD_INC_USE_COUNT;
+      return 0;
+      
+    default:
+      return -EINVAL;
+    }
+}
+
+static int net4xxx_gpio_release(struct inode *inode, struct file *file)
+{
+  MOD_DEC_USE_COUNT;
+  return 0;
+}
+
+static int net4xxx_gpio_ioctl(struct inode *inode, struct file *filp,
+                              unsigned int cmd, unsigned long arg)
+{
+  int ret;
+  int value=0;
+
+  /*
+   * extract the type and number bitfields, and don't decode
+   * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
+   */
+  if (_IOC_TYPE(cmd) != NET4801_GPIO_IOCTL_BASE)
+    {
+      return -ENOTTY;
+    }
+  
+  /*
+   * the direction is a bitmask, and VERIFY_WRITE catches R/W
+   * transfers. `Type' is user-oriented, while
+   * access_ok is kernel-oriented, so the concept of "read" and
+   * "write" is reversed
+   */
+  if (_IOC_DIR(cmd) & _IOC_READ)
+    {
+      if (!access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd)))
+        {
+          return -EFAULT;
+        }
+    }
+  else if (_IOC_DIR(cmd) & _IOC_WRITE)
+    {
+      if (!access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)))
+        {
+          return -EFAULT;
+        }
+    }
+
+  switch(MINOR(inode->i_rdev))
+    {
+    case MINOR_GPIO:
+      switch(cmd)
+        {
+        case GPIORDNUMPINS:
+          value = NUMBER_OF_PINS;
+          ret = __put_user(value, (unsigned int *)arg);
+          break;
+          
+        case GPIORDDIRECTION:
+          value = _net4801_getGPIODirection();
+          ret = __put_user(value, (unsigned int *)arg);
+          break;
+          
+        case GPIOWRDIRECTION:
+          ret = __get_user(value, (unsigned int *)arg);
+          
+          if (ret==0)
+            {
+              _net4801_setGPIODirection(value);
+            }
+          break;
+          
+        case GPIORDDATA:
+          value = _net4801_readGPIO();
+          
+          ret = __put_user(value, (unsigned int *)arg);
+          break;
+          
+        case GPIOWRDATA:
+          ret = __get_user(value, (unsigned int *)arg);
+          
+          if (ret==0)
+            {
+              _net4801_writeGPIO(value);
+            }
+          break;
+          
+        default:
+          ret = -ENOTTY;
+        }
+      break;
+      
+    case MINOR_LED:
+      switch(cmd)
+        {
+        case GPIORDNUMPINS:
+          value = 1;
+          ret = __put_user(value, (unsigned int *)arg);
+          break;
+          
+        case GPIORDDIRECTION:
+          value = 1;
+          ret = __put_user(value, (unsigned int *)arg);
+          break;
+          
+        case GPIOWRDIRECTION:
+          ret = __get_user(value, (unsigned int *)arg);
+          
+          if (ret==0)
+            {
+              if (value != 1)
+                {
+                  ret = -EINVAL;
+                }
+            }
+          break;
+          
+        case GPIORDDATA:
+          value = _net4801_read_error_led();
+          ret = __put_user(value, (unsigned int *)arg);
+          break;
+          
+        case GPIOWRDATA:
+          ret = __get_user(value, (unsigned int *)arg);
+          
+          if (ret==0)
+            {
+              _net4801_write_error_led(value);
+            }
+          break;
+          
+        default:
+          ret = -ENOTTY;
+        }
+      break;
+      
+    default:
+      ret = -ENOTTY;
+      break;
+    }
+  
+  return ret;
+}
+
+
+static struct file_operations gpio_dispatch_fops = {
+  .owner   = THIS_MODULE,
+  .open    = net4xxx_gpio_open,
+  .release = net4xxx_gpio_release,
+  .ioctl   = net4xxx_gpio_ioctl,
+};
+
+
+//------------------------------------------------
+
+// PROC file
+static int procfile_gpio_read(struct file * file, char * buf,
+                              size_t count, loff_t *ppos)
+{
+  unsigned int port_status;
+  char tmpbuffer[NUMBER_OF_PINS+2];
+  int len;
+  
+  if (*ppos > NUMBER_OF_PINS+1)
+    {
+      return 0;
+    }
+  
+  MOD_INC_USE_COUNT;
+  
+  /* Get the status of the gpio ports */
+  port_status = _net4801_readGPIO();
+  
+  MOD_DEC_USE_COUNT;
+  
+  toString(port_status,tmpbuffer,NUMBER_OF_PINS);
+  
+  if (count > (NUMBER_OF_PINS+1)-(*ppos))
+    {
+      len = (NUMBER_OF_PINS+1)-(*ppos);
+    }
+  else
+    {
+      len = count;
+    }
+  
+  if (copy_to_user(buf, tmpbuffer+(*ppos), len))
+    {
+      return -EFAULT;
+    }
+  
+  *ppos += len;
+  return len;
+}
+
+static int procfile_gpio_write(struct file * file, const char * buf,
+                               size_t count, loff_t *ppos)
+{
+  char new_gpio_state[NUMBER_OF_PINS+1];
+  unsigned long gpio_state;
+  
+  if (count < NUMBER_OF_PINS)
+    {
+      return -EINVAL;
+    }
+  
+  if(copy_from_user(new_gpio_state, buf, NUMBER_OF_PINS))
+    {
+      return -EFAULT;
+    }
+  
+  gpio_state = fromString(new_gpio_state, NUMBER_OF_PINS);
+  
+  MOD_INC_USE_COUNT;
+  
+  _net4801_writeGPIO(gpio_state);
+  
+  MOD_DEC_USE_COUNT;
+  
+  *ppos += count;
+  return NUMBER_OF_PINS;
+}
+
+static struct file_operations procfile_gpio_fops = {
+  .owner   = THIS_MODULE,
+  .read    = procfile_gpio_read,
+  .write   = procfile_gpio_write,
+};
+
+static int procfile_settings_read(struct file * file, char * buf,
+                                  size_t count, loff_t *ppos)
+
+{
+  unsigned int port_status;
+  char tmpbuffer[NUMBER_OF_PINS+2];
+  int len;
+  
+  if (*ppos > NUMBER_OF_PINS+1)
+    {
+      return 0;
+    }
+  
+  MOD_INC_USE_COUNT;
+  
+  /* Get the status of the gpio ports */
+  port_status = _net4801_getGPIODirection();
+  
+  MOD_DEC_USE_COUNT;
+  
+  toString(port_status,tmpbuffer,NUMBER_OF_PINS);
+  
+  if (count > (NUMBER_OF_PINS+1)-(*ppos))
+    {
+      len = (NUMBER_OF_PINS+1)-(*ppos);
+    }
+  else
+    {
+      len = count;
+    }
+  
+  if (copy_to_user(buf, tmpbuffer+(*ppos), len))
+    {
+      return -EFAULT;
+    }
+  
+  *ppos += len;
+  return len;
+}
+
+static int procfile_settings_write(struct file * file, const char * buf,
+                                   size_t count, loff_t *ppos)
+{
+  char new_gpio_state[NUMBER_OF_PINS+1];
+  unsigned long gpio_state;
+  
+  if (count < NUMBER_OF_PINS)
+    {
+      return -EINVAL;
+    }
+  
+  if(copy_from_user(new_gpio_state, buf, NUMBER_OF_PINS))
+    {
+      return -EFAULT;
+    }
+  
+  gpio_state = fromString(new_gpio_state, NUMBER_OF_PINS);
+  
+  MOD_INC_USE_COUNT;
+  
+  _net4801_setGPIODirection(gpio_state);
+  
+  MOD_DEC_USE_COUNT;
+  
+  *ppos += count;
+  return NUMBER_OF_PINS;
+}
+
+static struct file_operations procfile_settings_fops = {
+  .owner   = THIS_MODULE,
+  .read    = procfile_settings_read,
+  .write   = procfile_settings_write,
+};
+
+
+static int procfile_led_read(struct file * file, char * buf,
+                             size_t count, loff_t *ppos)
+
+{
+  unsigned int port_status;
+  char tmpbuffer[1+2];
+  int len;
+  
+  if (*ppos > 1+1)
+    {
+      return 0;
+    }
+  
+  MOD_INC_USE_COUNT;
+  
+  /* Get the status of the gpio ports */
+  port_status = _net4801_read_error_led();
+  
+  MOD_DEC_USE_COUNT;
+  
+  toString(port_status,tmpbuffer,1);
+  
+  if (count > (1+1)-(*ppos))
+    {
+      len = (1+1)-(*ppos);
+    }
+  else
+    {
+      len = count;
+    }
+  
+  if (copy_to_user(buf, tmpbuffer+(*ppos), len))
+    {
+      return -EFAULT;
+    }
+  
+  *ppos += len;
+  return len;
+}
+
+static int procfile_led_write(struct file * file, const char * buf,
+                              size_t count, loff_t *ppos)
+{
+  char new_led_state[1+1];
+  
+  if (count < 1)
+    {
+      return -EINVAL;
+    }
+  
+  if(copy_from_user(new_led_state, buf, 1))
+    {
+      return -EFAULT;
+    }
+  
+  MOD_INC_USE_COUNT;
+  
+  if (new_led_state[0] == '1')
+    {
+      _net4801_write_error_led(1);
+    }
+  else if (new_led_state[0] == '0')
+    {
+      _net4801_write_error_led(0);
+    }
+  
+  MOD_DEC_USE_COUNT;
+  
+  *ppos += count;
+  return 1;
+}
+
+static struct file_operations procfile_led_fops = {
+  .owner   = THIS_MODULE,
+  .read    = procfile_led_read,
+  .write   = procfile_led_write,
+};
+
+//------------------------------------------------
+
+
+static int __init common_init(void)
+{
+  int result;
+  struct proc_dir_entry *proc_file;
+  
+  spin_lock_init(&gpio_lock);
+  spin_lock_init(&led_lock);
+  
+  result = net4801_init();
+  if (result!=0)
+    {
+      return result;
+    }
+  
+  result = register_chrdev(gpio_major, __our_name, &gpio_dispatch_fops);
+  if (result < 0)
+    {
+      printk(KERN_WARNING "%s: can't get major %d\n",__our_name, gpio_major);
+      net4801_cleanup();
+      return result;
+    }
+  
+  if (gpio_major == 0)
+    {
+      gpio_major = result; /* dynamic */
+    }
+  
+  proc_file = create_proc_entry(GPIO_PROC_FILENAME,
+                                S_IFREG | 0644,
+                                NULL);
+
+  if (!proc_file)
+    {
+      printk(KERN_ERR "%s: Could not register " GPIO_PROC_FILENAME  ". Terminating\n", __our_name);
+      unregister_chrdev(gpio_major, __our_name);
+      net4801_cleanup();
+      return -ENOMEM;
+    }
+  
+  proc_file->proc_fops = &procfile_gpio_fops;
+  
+  
+  proc_file = create_proc_entry(LED_PROC_FILENAME,
+                                S_IFREG | 0644,
+                                NULL);
+
+  if (!proc_file)
+    {
+      printk(KERN_ERR "%s:Could not register " LED_PROC_FILENAME ". Terminating\n", __our_name);
+      remove_proc_entry(GPIO_PROC_FILENAME, NULL);
+      unregister_chrdev(gpio_major, __our_name);
+      net4801_cleanup();
+      return -ENOMEM;
+    }
+  
+  proc_file->proc_fops = &procfile_led_fops;
+  
+  
+  proc_file = create_proc_entry(SETTINGS_PROC_FILENAME,
+                                S_IFREG | 0644,
+                                NULL);
+
+  if(!proc_file)
+    {
+      printk(KERN_ERR "%s: Could not register " SETTINGS_PROC_FILENAME ". Terminating\n", __our_name);
+      remove_proc_entry(GPIO_PROC_FILENAME, NULL);
+      remove_proc_entry(LED_PROC_FILENAME, NULL);
+      unregister_chrdev(gpio_major, __our_name);
+      net4801_cleanup();
+      return -ENOMEM;
+    }
+  
+  proc_file->proc_fops = &procfile_settings_fops;
+  
+  SET_MODULE_OWNER(&gpio_dispatch_fops);
+  
+  return(0);
+}
+
+static void __exit common_cleanup(void)
+{
+  unregister_chrdev(gpio_major, __our_name);
+  remove_proc_entry(GPIO_PROC_FILENAME, NULL);
+  remove_proc_entry(LED_PROC_FILENAME, NULL);
+  remove_proc_entry(SETTINGS_PROC_FILENAME, NULL);
+  net4801_cleanup();
+
+}
+
+//------------------------------------------------
+
+module_init(common_init);
+module_exit(common_cleanup);
+
+MODULE_AUTHOR("Martin Hejl & Dave Johnson");
+MODULE_DESCRIPTION("Soekris net4801 GPIO / Error LED driver");
+MODULE_LICENSE("GPL");
+EXPORT_NO_SYMBOLS;
+
+
diff -Naur linux-2.4.25.orrig/include/linux/net4801gpio.h linux-2.4.25/include/linux/net4801gpio.h
--- linux-2.4.25.orrig/include/linux/net4801gpio.h	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.4.25/include/linux/net4801gpio.h	2004-03-07 20:59:06.000000000 +0000
@@ -0,0 +1,40 @@
+/*
+**********************************************************************
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2, or (at your option)
+* any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**********************************************************************
+
+*/
+
+#ifndef __NET4801_GPIO_H
+#define __NET4801_GPIO_H
+
+#define GPIO_PROC_FILENAME      "driver/soekris_gpio"
+#define SETTINGS_PROC_FILENAME  "driver/soekris_gpio_settings"
+#define LED_PROC_FILENAME       "driver/soekris_error_led"
+
+#define NET4801_GPIO_IOCTL_BASE  'Z'
+
+/* Read number of pins */
+#define GPIORDNUMPINS _IOR(NET4801_GPIO_IOCTL_BASE, 0, int)
+
+/* Read/write bitmask that determines input/output pins (1 means output, 0 input) */
+#define GPIORDDIRECTION _IOR(NET4801_GPIO_IOCTL_BASE, 1, int)
+#define GPIOWRDIRECTION _IOW(NET4801_GPIO_IOCTL_BASE, 2, int)
+
+/* Read/write data */
+#define GPIORDDATA _IOR(NET4801_GPIO_IOCTL_BASE, 3, int)
+#define GPIOWRDATA _IOW(NET4801_GPIO_IOCTL_BASE, 4, int)
+
+#endif

