/****************************************************************************
 * ioled.c:  Simple device driver to handle the iOpener's front-panel
LED's.
 *
 * Author:  Steven M. Doyle <grep@doyle.net>
 *   Date:  4/1/2000 (but not an april fool's joke)
 *
 * Copyright (c)2000 Steven M. Doyle.
 *
 * Thanks to Robert Rose <rarose@io.com> for discovering how to make the
 * lights work in the first place and posting it for all to play with.
 *
 * This code has been released under GPL.  See http://www.gnu.org for more
 * information.
 *

*----------------------------------------------------------------------------
 * Instructions:
 *
 * To Compile:
 * gcc -O6 -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe \
 *     -fno-strength-reduce -DMODULE -D__KERNEL__ -c ioled.c -o ioled.o
 *
 * To use:
 * - insert as normal
 * - mknod /dev/ioled c 10 240
 * - echo characters at the device.  The following characters have
meanings:
 *    '0' - Turn the Mail LED off
 *    '1' - Turn the Mail LED on
 *    '2' - Turn the Telephone LED off
 *    '3' - Turn the Telephone LED on
 *
 * - example:  echo 13 > /dev/ioled # Turn both LED's on
 *
 * This has not been heavily tested.  Use this module at your own risk.
Let
 * me know if there are any problems.

*****************************************************************************/

#include <linux/module.h>
#include <linux/config.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/miscdevice.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <asm/io.h>

#define IOLED_MINOR 240

MODULE_DESCRIPTION("Driver for IOpener custom LED control");
MODULE_AUTHOR("Steven Doyle <grep@doyle.net>");

static int ioled_open(struct inode *inode, struct file *file) {
  MOD_INC_USE_COUNT;
  return 0;
}

static int ioled_release(struct inode *inode, struct file *file) {
  MOD_DEC_USE_COUNT;
  return 0;
}

static int ioled_write(struct file *file, const char *buf, size_t count,
		       loff_t *ppos) {
  char a;
  int i;

  for (i = 0; i < count; i++) {
    a = inb(0x404C);
    switch(buf[i]) {
    case '1': a &= ~1; break;
    case '0': a |= 1;  break;
    case '3': a &= ~2; break;
    case '2': a |= 2;  break;
    }
    outb(a, 0x404C);
  }
      
  return count;
}

struct file_operations ioled_fops = {
  open:     ioled_open,
  release:  ioled_release,
  write:    ioled_write
};

static struct miscdevice ioled_dev = {
  IOLED_MINOR,
  "ioled",
  &ioled_fops
};

static int __init ioled_init(void) {
  misc_register(&ioled_dev);
  printk(KERN_INFO "Netpliance iOpener LED Driver (grep@doyle.net)\n");
  return 0;
}

static void __exit ioled_cleanup(void) {
  misc_deregister(&ioled_dev);
}

module_init(ioled_init);
module_exit(ioled_cleanup);

