16ca2733471190dd93b627a58148c1aeab5e0d797David Schleef/*
26ca2733471190dd93b627a58148c1aeab5e0d797David Schleef    comedi/drivers/8255.c
36ca2733471190dd93b627a58148c1aeab5e0d797David Schleef    Driver for 8255
46ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
56ca2733471190dd93b627a58148c1aeab5e0d797David Schleef    COMEDI - Linux Control and Measurement Device Interface
66ca2733471190dd93b627a58148c1aeab5e0d797David Schleef    Copyright (C) 1998 David A. Schleef <ds@schleef.org>
76ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
86ca2733471190dd93b627a58148c1aeab5e0d797David Schleef    This program is free software; you can redistribute it and/or modify
96ca2733471190dd93b627a58148c1aeab5e0d797David Schleef    it under the terms of the GNU General Public License as published by
106ca2733471190dd93b627a58148c1aeab5e0d797David Schleef    the Free Software Foundation; either version 2 of the License, or
116ca2733471190dd93b627a58148c1aeab5e0d797David Schleef    (at your option) any later version.
126ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
136ca2733471190dd93b627a58148c1aeab5e0d797David Schleef    This program is distributed in the hope that it will be useful,
146ca2733471190dd93b627a58148c1aeab5e0d797David Schleef    but WITHOUT ANY WARRANTY; without even the implied warranty of
156ca2733471190dd93b627a58148c1aeab5e0d797David Schleef    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
166ca2733471190dd93b627a58148c1aeab5e0d797David Schleef    GNU General Public License for more details.
176ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
186ca2733471190dd93b627a58148c1aeab5e0d797David Schleef    You should have received a copy of the GNU General Public License
196ca2733471190dd93b627a58148c1aeab5e0d797David Schleef    along with this program; if not, write to the Free Software
206ca2733471190dd93b627a58148c1aeab5e0d797David Schleef    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
216ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
226ca2733471190dd93b627a58148c1aeab5e0d797David Schleef*/
236ca2733471190dd93b627a58148c1aeab5e0d797David Schleef/*
246ca2733471190dd93b627a58148c1aeab5e0d797David SchleefDriver: 8255
256ca2733471190dd93b627a58148c1aeab5e0d797David SchleefDescription: generic 8255 support
266ca2733471190dd93b627a58148c1aeab5e0d797David SchleefDevices: [standard] 8255 (8255)
276ca2733471190dd93b627a58148c1aeab5e0d797David SchleefAuthor: ds
286ca2733471190dd93b627a58148c1aeab5e0d797David SchleefStatus: works
296ca2733471190dd93b627a58148c1aeab5e0d797David SchleefUpdated: Fri,  7 Jun 2002 12:56:45 -0700
306ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
316ca2733471190dd93b627a58148c1aeab5e0d797David SchleefThe classic in digital I/O.  The 8255 appears in Comedi as a single
326ca2733471190dd93b627a58148c1aeab5e0d797David Schleefdigital I/O subdevice with 24 channels.  The channel 0 corresponds
336ca2733471190dd93b627a58148c1aeab5e0d797David Schleefto the 8255's port A, bit 0; channel 23 corresponds to port C, bit
346ca2733471190dd93b627a58148c1aeab5e0d797David Schleef7.  Direction configuration is done in blocks, with channels 0-7,
356ca2733471190dd93b627a58148c1aeab5e0d797David Schleef8-15, 16-19, and 20-23 making up the 4 blocks.  The only 8255 mode
366ca2733471190dd93b627a58148c1aeab5e0d797David Schleefsupported is mode 0.
376ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
386ca2733471190dd93b627a58148c1aeab5e0d797David SchleefYou should enable compilation this driver if you plan to use a board
396ca2733471190dd93b627a58148c1aeab5e0d797David Schleefthat has an 8255 chip.  For multifunction boards, the main driver will
406ca2733471190dd93b627a58148c1aeab5e0d797David Schleefconfigure the 8255 subdevice automatically.
416ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
426ca2733471190dd93b627a58148c1aeab5e0d797David SchleefThis driver also works independently with ISA and PCI cards that
436ca2733471190dd93b627a58148c1aeab5e0d797David Schleefdirectly map the 8255 registers to I/O ports, including cards with
446ca2733471190dd93b627a58148c1aeab5e0d797David Schleefmultiple 8255 chips.  To configure the driver for such a card, the
456ca2733471190dd93b627a58148c1aeab5e0d797David Schleefoption list should be a list of the I/O port bases for each of the
466ca2733471190dd93b627a58148c1aeab5e0d797David Schleef8255 chips.  For example,
476ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
486ca2733471190dd93b627a58148c1aeab5e0d797David Schleef  comedi_config /dev/comedi0 8255 0x200,0x204,0x208,0x20c
496ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
506ca2733471190dd93b627a58148c1aeab5e0d797David SchleefNote that most PCI 8255 boards do NOT work with this driver, and
516ca2733471190dd93b627a58148c1aeab5e0d797David Schleefneed a separate driver as a wrapper.  For those that do work, the
526ca2733471190dd93b627a58148c1aeab5e0d797David SchleefI/O port base address can be found in the output of 'lspci -v'.
536ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
546ca2733471190dd93b627a58148c1aeab5e0d797David Schleef*/
556ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
566ca2733471190dd93b627a58148c1aeab5e0d797David Schleef/*
576ca2733471190dd93b627a58148c1aeab5e0d797David Schleef   This file contains an exported subdevice for driving an 8255.
586ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
596ca2733471190dd93b627a58148c1aeab5e0d797David Schleef   To use this subdevice as part of another driver, you need to
606ca2733471190dd93b627a58148c1aeab5e0d797David Schleef   set up the subdevice in the attach function of the driver by
616ca2733471190dd93b627a58148c1aeab5e0d797David Schleef   calling:
626ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
636ca2733471190dd93b627a58148c1aeab5e0d797David Schleef     subdev_8255_init(device, subdevice, callback_function, arg)
646ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
656ca2733471190dd93b627a58148c1aeab5e0d797David Schleef   device and subdevice are pointers to the device and subdevice
666ca2733471190dd93b627a58148c1aeab5e0d797David Schleef   structures.  callback_function will be called to provide the
676ca2733471190dd93b627a58148c1aeab5e0d797David Schleef   low-level input/output to the device, i.e., actual register
686ca2733471190dd93b627a58148c1aeab5e0d797David Schleef   access.  callback_function will be called with the value of arg
696ca2733471190dd93b627a58148c1aeab5e0d797David Schleef   as the last parameter.  If the 8255 device is mapped as 4
706ca2733471190dd93b627a58148c1aeab5e0d797David Schleef   consecutive I/O ports, you can use NULL for callback_function
716ca2733471190dd93b627a58148c1aeab5e0d797David Schleef   and the I/O port base for arg, and an internal function will
726ca2733471190dd93b627a58148c1aeab5e0d797David Schleef   handle the register access.
736ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
746ca2733471190dd93b627a58148c1aeab5e0d797David Schleef   In addition, if the main driver handles interrupts, you can
756ca2733471190dd93b627a58148c1aeab5e0d797David Schleef   enable commands on the subdevice by calling subdev_8255_init_irq()
766ca2733471190dd93b627a58148c1aeab5e0d797David Schleef   instead.  Then, when you get an interrupt that is likely to be
776ca2733471190dd93b627a58148c1aeab5e0d797David Schleef   from the 8255, you should call subdev_8255_interrupt(), which
786ca2733471190dd93b627a58148c1aeab5e0d797David Schleef   will copy the latched value to a Comedi buffer.
796ca2733471190dd93b627a58148c1aeab5e0d797David Schleef */
806ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
816ca2733471190dd93b627a58148c1aeab5e0d797David Schleef#include "../comedidev.h"
826ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
836ca2733471190dd93b627a58148c1aeab5e0d797David Schleef#include <linux/ioport.h>
845a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
85c5efe58b83bc5c1d5811800faf03b1089d1ef054Greg Kroah-Hartman#include "8255.h"
866ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
876ca2733471190dd93b627a58148c1aeab5e0d797David Schleef#define _8255_SIZE 4
886ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
896ca2733471190dd93b627a58148c1aeab5e0d797David Schleef#define _8255_DATA 0
906ca2733471190dd93b627a58148c1aeab5e0d797David Schleef#define _8255_CR 3
916ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
926ca2733471190dd93b627a58148c1aeab5e0d797David Schleef#define CR_C_LO_IO	0x01
936ca2733471190dd93b627a58148c1aeab5e0d797David Schleef#define CR_B_IO		0x02
946ca2733471190dd93b627a58148c1aeab5e0d797David Schleef#define CR_B_MODE	0x04
956ca2733471190dd93b627a58148c1aeab5e0d797David Schleef#define CR_C_HI_IO	0x08
966ca2733471190dd93b627a58148c1aeab5e0d797David Schleef#define CR_A_IO		0x10
976ca2733471190dd93b627a58148c1aeab5e0d797David Schleef#define CR_A_MODE(a)	((a)<<5)
986ca2733471190dd93b627a58148c1aeab5e0d797David Schleef#define CR_CW		0x80
996ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
1006ca2733471190dd93b627a58148c1aeab5e0d797David Schleefstruct subdev_8255_struct {
1016ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	unsigned long cb_arg;
1026ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	int (*cb_func) (int, int, int, unsigned long);
1036ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	int have_irq;
1046ca2733471190dd93b627a58148c1aeab5e0d797David Schleef};
1056ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
1066ca2733471190dd93b627a58148c1aeab5e0d797David Schleef#define CALLBACK_ARG	(((struct subdev_8255_struct *)s->private)->cb_arg)
1076ca2733471190dd93b627a58148c1aeab5e0d797David Schleef#define CALLBACK_FUNC	(((struct subdev_8255_struct *)s->private)->cb_func)
1086ca2733471190dd93b627a58148c1aeab5e0d797David Schleef#define subdevpriv	((struct subdev_8255_struct *)s->private)
1096ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
1100a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukralstatic int dev_8255_attach(struct comedi_device *dev,
1110a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral			   struct comedi_devconfig *it);
11271b5f4f11971dea972832ad63a994c7e5b45db6bBill Pembertonstatic int dev_8255_detach(struct comedi_device *dev);
113139dfbdfacb02e3ef3df936d2fabd1ad5f14ea88Bill Pembertonstatic struct comedi_driver driver_8255 = {
11468c3dbff9fc9f25872408d0e95980d41733d48d0Bill Pemberton	.driver_name = "8255",
11568c3dbff9fc9f25872408d0e95980d41733d48d0Bill Pemberton	.module = THIS_MODULE,
11668c3dbff9fc9f25872408d0e95980d41733d48d0Bill Pemberton	.attach = dev_8255_attach,
11768c3dbff9fc9f25872408d0e95980d41733d48d0Bill Pemberton	.detach = dev_8255_detach,
1186ca2733471190dd93b627a58148c1aeab5e0d797David Schleef};
1196ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
1207114a28011f9d5f3d981731ad341177c21f9d948Arun Thomasstatic int __init driver_8255_init_module(void)
1217114a28011f9d5f3d981731ad341177c21f9d948Arun Thomas{
1227114a28011f9d5f3d981731ad341177c21f9d948Arun Thomas	return comedi_driver_register(&driver_8255);
1237114a28011f9d5f3d981731ad341177c21f9d948Arun Thomas}
1247114a28011f9d5f3d981731ad341177c21f9d948Arun Thomas
1257114a28011f9d5f3d981731ad341177c21f9d948Arun Thomasstatic void __exit driver_8255_cleanup_module(void)
1267114a28011f9d5f3d981731ad341177c21f9d948Arun Thomas{
1277114a28011f9d5f3d981731ad341177c21f9d948Arun Thomas	comedi_driver_unregister(&driver_8255);
1287114a28011f9d5f3d981731ad341177c21f9d948Arun Thomas}
1297114a28011f9d5f3d981731ad341177c21f9d948Arun Thomas
1307114a28011f9d5f3d981731ad341177c21f9d948Arun Thomasmodule_init(driver_8255_init_module);
1317114a28011f9d5f3d981731ad341177c21f9d948Arun Thomasmodule_exit(driver_8255_cleanup_module);
1326ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
1330a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukralstatic void do_config(struct comedi_device *dev, struct comedi_subdevice *s);
1346ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
135c5efe58b83bc5c1d5811800faf03b1089d1ef054Greg Kroah-Hartmanvoid subdev_8255_interrupt(struct comedi_device *dev,
136c5efe58b83bc5c1d5811800faf03b1089d1ef054Greg Kroah-Hartman			   struct comedi_subdevice *s)
1376ca2733471190dd93b627a58148c1aeab5e0d797David Schleef{
138790c55415aa31f4c732729f94d2c3a54f7d3bfc2Bill Pemberton	short d;
1396ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
1406ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	d = CALLBACK_FUNC(0, _8255_DATA, 0, CALLBACK_ARG);
1416ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	d |= (CALLBACK_FUNC(0, _8255_DATA + 1, 0, CALLBACK_ARG) << 8);
1426ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
1436ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	comedi_buf_put(s->async, d);
1446ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	s->async->events |= COMEDI_CB_EOS;
1456ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
1466ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	comedi_event(dev, s);
1476ca2733471190dd93b627a58148c1aeab5e0d797David Schleef}
148228ec3402130b158ccd0e690ac5c4a0b27317786Ben KeroEXPORT_SYMBOL(subdev_8255_interrupt);
1496ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
1506ca2733471190dd93b627a58148c1aeab5e0d797David Schleefstatic int subdev_8255_cb(int dir, int port, int data, unsigned long arg)
1516ca2733471190dd93b627a58148c1aeab5e0d797David Schleef{
1526ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	unsigned long iobase = arg;
1536ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
1546ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	if (dir) {
1556ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		outb(data, iobase + port);
1566ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		return 0;
1576ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	} else {
1586ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		return inb(iobase + port);
1596ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	}
1606ca2733471190dd93b627a58148c1aeab5e0d797David Schleef}
1616ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
1620a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukralstatic int subdev_8255_insn(struct comedi_device *dev,
1630a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral			    struct comedi_subdevice *s,
1640a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral			    struct comedi_insn *insn, unsigned int *data)
1656ca2733471190dd93b627a58148c1aeab5e0d797David Schleef{
1666ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	if (data[0]) {
1676ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		s->state &= ~data[0];
1686ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		s->state |= (data[0] & data[1]);
1696ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
1706ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		if (data[0] & 0xff)
1716ca2733471190dd93b627a58148c1aeab5e0d797David Schleef			CALLBACK_FUNC(1, _8255_DATA, s->state & 0xff,
1720a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral				      CALLBACK_ARG);
1736ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		if (data[0] & 0xff00)
1746ca2733471190dd93b627a58148c1aeab5e0d797David Schleef			CALLBACK_FUNC(1, _8255_DATA + 1, (s->state >> 8) & 0xff,
1750a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral				      CALLBACK_ARG);
1766ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		if (data[0] & 0xff0000)
1776ca2733471190dd93b627a58148c1aeab5e0d797David Schleef			CALLBACK_FUNC(1, _8255_DATA + 2,
1780a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral				      (s->state >> 16) & 0xff, CALLBACK_ARG);
1796ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	}
1806ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
1816ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	data[1] = CALLBACK_FUNC(0, _8255_DATA, 0, CALLBACK_ARG);
1826ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	data[1] |= (CALLBACK_FUNC(0, _8255_DATA + 1, 0, CALLBACK_ARG) << 8);
1836ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	data[1] |= (CALLBACK_FUNC(0, _8255_DATA + 2, 0, CALLBACK_ARG) << 16);
1846ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
1856ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	return 2;
1866ca2733471190dd93b627a58148c1aeab5e0d797David Schleef}
1876ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
1880a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukralstatic int subdev_8255_insn_config(struct comedi_device *dev,
1890a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral				   struct comedi_subdevice *s,
1900a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral				   struct comedi_insn *insn, unsigned int *data)
1916ca2733471190dd93b627a58148c1aeab5e0d797David Schleef{
1926ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	unsigned int mask;
1936ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	unsigned int bits;
1946ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
1956ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	mask = 1 << CR_CHAN(insn->chanspec);
196228ec3402130b158ccd0e690ac5c4a0b27317786Ben Kero	if (mask & 0x0000ff)
1976ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		bits = 0x0000ff;
198228ec3402130b158ccd0e690ac5c4a0b27317786Ben Kero	else if (mask & 0x00ff00)
1996ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		bits = 0x00ff00;
200228ec3402130b158ccd0e690ac5c4a0b27317786Ben Kero	else if (mask & 0x0f0000)
2016ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		bits = 0x0f0000;
202228ec3402130b158ccd0e690ac5c4a0b27317786Ben Kero	else
2036ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		bits = 0xf00000;
2046ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
2056ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	switch (data[0]) {
2066ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	case INSN_CONFIG_DIO_INPUT:
2076ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		s->io_bits &= ~bits;
2086ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		break;
2096ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	case INSN_CONFIG_DIO_OUTPUT:
2106ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		s->io_bits |= bits;
2116ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		break;
2126ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	case INSN_CONFIG_DIO_QUERY:
2136ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT;
2146ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		return insn->n;
2156ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		break;
2166ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	default:
2176ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		return -EINVAL;
2186ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	}
2196ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
2206ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	do_config(dev, s);
2216ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
2226ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	return 1;
2236ca2733471190dd93b627a58148c1aeab5e0d797David Schleef}
2246ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
2250a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukralstatic void do_config(struct comedi_device *dev, struct comedi_subdevice *s)
2266ca2733471190dd93b627a58148c1aeab5e0d797David Schleef{
2276ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	int config;
2286ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
2296ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	config = CR_CW;
2306ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	/* 1 in io_bits indicates output, 1 in config indicates input */
2316ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	if (!(s->io_bits & 0x0000ff))
2326ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		config |= CR_A_IO;
2336ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	if (!(s->io_bits & 0x00ff00))
2346ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		config |= CR_B_IO;
2356ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	if (!(s->io_bits & 0x0f0000))
2366ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		config |= CR_C_LO_IO;
2376ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	if (!(s->io_bits & 0xf00000))
2386ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		config |= CR_C_HI_IO;
2396ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	CALLBACK_FUNC(1, _8255_CR, config, CALLBACK_ARG);
2406ca2733471190dd93b627a58148c1aeab5e0d797David Schleef}
2416ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
2420a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukralstatic int subdev_8255_cmdtest(struct comedi_device *dev,
2430a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral			       struct comedi_subdevice *s,
2440a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral			       struct comedi_cmd *cmd)
2456ca2733471190dd93b627a58148c1aeab5e0d797David Schleef{
2466ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	int err = 0;
2476ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	unsigned int tmp;
2486ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
2496ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	/* step 1 */
2506ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
2516ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	tmp = cmd->start_src;
2526ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	cmd->start_src &= TRIG_NOW;
2536ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	if (!cmd->start_src || tmp != cmd->start_src)
2546ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		err++;
2556ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
2566ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	tmp = cmd->scan_begin_src;
2576ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	cmd->scan_begin_src &= TRIG_EXT;
2586ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
2596ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		err++;
2606ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
2616ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	tmp = cmd->convert_src;
2626ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	cmd->convert_src &= TRIG_FOLLOW;
2636ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	if (!cmd->convert_src || tmp != cmd->convert_src)
2646ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		err++;
2656ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
2666ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	tmp = cmd->scan_end_src;
2676ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	cmd->scan_end_src &= TRIG_COUNT;
2686ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
2696ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		err++;
2706ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
2716ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	tmp = cmd->stop_src;
2726ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	cmd->stop_src &= TRIG_NONE;
2736ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	if (!cmd->stop_src || tmp != cmd->stop_src)
2746ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		err++;
2756ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
2766ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	if (err)
2776ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		return 1;
2786ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
2796ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	/* step 2 */
2806ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
2816ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	if (err)
2826ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		return 2;
2836ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
2846ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	/* step 3 */
2856ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
2866ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	if (cmd->start_arg != 0) {
2876ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		cmd->start_arg = 0;
2886ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		err++;
2896ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	}
2906ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	if (cmd->scan_begin_arg != 0) {
2916ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		cmd->scan_begin_arg = 0;
2926ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		err++;
2936ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	}
2946ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	if (cmd->convert_arg != 0) {
2956ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		cmd->convert_arg = 0;
2966ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		err++;
2976ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	}
2986ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	if (cmd->scan_end_arg != 1) {
2996ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		cmd->scan_end_arg = 1;
3006ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		err++;
3016ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	}
3026ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	if (cmd->stop_arg != 0) {
3036ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		cmd->stop_arg = 0;
3046ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		err++;
3056ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	}
3066ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
3076ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	if (err)
3086ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		return 3;
3096ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
3106ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	/* step 4 */
3116ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
3126ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	if (err)
3136ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		return 4;
3146ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
3156ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	return 0;
3166ca2733471190dd93b627a58148c1aeab5e0d797David Schleef}
3176ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
3180a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukralstatic int subdev_8255_cmd(struct comedi_device *dev,
3190a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral			   struct comedi_subdevice *s)
3206ca2733471190dd93b627a58148c1aeab5e0d797David Schleef{
3216ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	/* FIXME */
3226ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
3236ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	return 0;
3246ca2733471190dd93b627a58148c1aeab5e0d797David Schleef}
3256ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
3260a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukralstatic int subdev_8255_cancel(struct comedi_device *dev,
3270a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral			      struct comedi_subdevice *s)
3286ca2733471190dd93b627a58148c1aeab5e0d797David Schleef{
3296ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	/* FIXME */
3306ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
3316ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	return 0;
3326ca2733471190dd93b627a58148c1aeab5e0d797David Schleef}
3336ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
334c5efe58b83bc5c1d5811800faf03b1089d1ef054Greg Kroah-Hartmanint subdev_8255_init(struct comedi_device *dev, struct comedi_subdevice *s,
335c5efe58b83bc5c1d5811800faf03b1089d1ef054Greg Kroah-Hartman		     int (*cb) (int, int, int, unsigned long),
336c5efe58b83bc5c1d5811800faf03b1089d1ef054Greg Kroah-Hartman		     unsigned long arg)
3376ca2733471190dd93b627a58148c1aeab5e0d797David Schleef{
3386ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	s->type = COMEDI_SUBD_DIO;
3396ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
3406ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	s->n_chan = 24;
3416ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	s->range_table = &range_digital;
3426ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	s->maxdata = 1;
3436ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
3446ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	s->private = kmalloc(sizeof(struct subdev_8255_struct), GFP_KERNEL);
3456ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	if (!s->private)
3466ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		return -ENOMEM;
3476ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
3486ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	CALLBACK_ARG = arg;
349228ec3402130b158ccd0e690ac5c4a0b27317786Ben Kero	if (cb == NULL)
3506ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		CALLBACK_FUNC = subdev_8255_cb;
351228ec3402130b158ccd0e690ac5c4a0b27317786Ben Kero	else
3526ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		CALLBACK_FUNC = cb;
3536ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	s->insn_bits = subdev_8255_insn;
3546ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	s->insn_config = subdev_8255_insn_config;
3556ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
3566ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	s->state = 0;
3576ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	s->io_bits = 0;
3586ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	do_config(dev, s);
3596ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
3606ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	return 0;
3616ca2733471190dd93b627a58148c1aeab5e0d797David Schleef}
362228ec3402130b158ccd0e690ac5c4a0b27317786Ben KeroEXPORT_SYMBOL(subdev_8255_init);
3636ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
364c5efe58b83bc5c1d5811800faf03b1089d1ef054Greg Kroah-Hartmanint subdev_8255_init_irq(struct comedi_device *dev, struct comedi_subdevice *s,
365c5efe58b83bc5c1d5811800faf03b1089d1ef054Greg Kroah-Hartman			 int (*cb) (int, int, int, unsigned long),
366c5efe58b83bc5c1d5811800faf03b1089d1ef054Greg Kroah-Hartman			 unsigned long arg)
3676ca2733471190dd93b627a58148c1aeab5e0d797David Schleef{
3686ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	int ret;
3696ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
3706ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	ret = subdev_8255_init(dev, s, cb, arg);
3716ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	if (ret < 0)
3726ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		return ret;
3736ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
3746ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	s->do_cmdtest = subdev_8255_cmdtest;
3756ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	s->do_cmd = subdev_8255_cmd;
3766ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	s->cancel = subdev_8255_cancel;
3776ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
3786ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	subdevpriv->have_irq = 1;
3796ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
3806ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	return 0;
3816ca2733471190dd93b627a58148c1aeab5e0d797David Schleef}
382228ec3402130b158ccd0e690ac5c4a0b27317786Ben KeroEXPORT_SYMBOL(subdev_8255_init_irq);
3836ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
384c5efe58b83bc5c1d5811800faf03b1089d1ef054Greg Kroah-Hartmanvoid subdev_8255_cleanup(struct comedi_device *dev, struct comedi_subdevice *s)
3856ca2733471190dd93b627a58148c1aeab5e0d797David Schleef{
386e4e1f289be88a75dc8b63d50ade1f9a2e6168021Ilia Mirkin	kfree(s->private);
3876ca2733471190dd93b627a58148c1aeab5e0d797David Schleef}
388228ec3402130b158ccd0e690ac5c4a0b27317786Ben KeroEXPORT_SYMBOL(subdev_8255_cleanup);
3896ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
3906ca2733471190dd93b627a58148c1aeab5e0d797David Schleef/*
3916ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
3926ca2733471190dd93b627a58148c1aeab5e0d797David Schleef   Start of the 8255 standalone device
3936ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
3946ca2733471190dd93b627a58148c1aeab5e0d797David Schleef */
3956ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
3960a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukralstatic int dev_8255_attach(struct comedi_device *dev,
3970a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral			   struct comedi_devconfig *it)
3986ca2733471190dd93b627a58148c1aeab5e0d797David Schleef{
3996ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	int ret;
4006ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	unsigned long iobase;
4016ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	int i;
4026ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
4036ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	dev->board_name = "8255";
4046ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
4056ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	for (i = 0; i < COMEDI_NDEVCONFOPTS; i++) {
4066ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		iobase = it->options[i];
4076ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		if (!iobase)
4086ca2733471190dd93b627a58148c1aeab5e0d797David Schleef			break;
4096ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	}
4106ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	if (i == 0) {
411026386976067996a6fc084b1926558522126cfddMark		printk(KERN_WARNING
412026386976067996a6fc084b1926558522126cfddMark		       "comedi%d: 8255: no devices specified\n", dev->minor);
4136ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		return -EINVAL;
4146ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	}
4156ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
416c3744138715045adb316284ee7a1e608f0278f6cBill Pemberton	ret = alloc_subdevices(dev, i);
417026386976067996a6fc084b1926558522126cfddMark	if (ret < 0) {
418026386976067996a6fc084b1926558522126cfddMark		/* FIXME this printk call should give a proper message, the
419026386976067996a6fc084b1926558522126cfddMark		 * below line just maintains previous functionality */
420026386976067996a6fc084b1926558522126cfddMark		printk("comedi%d: 8255:", dev->minor);
4216ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		return ret;
422026386976067996a6fc084b1926558522126cfddMark	}
423026386976067996a6fc084b1926558522126cfddMark
424026386976067996a6fc084b1926558522126cfddMark	printk(KERN_INFO "comedi%d: 8255:", dev->minor);
4256ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
4266ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	for (i = 0; i < dev->n_subdevices; i++) {
4276ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		iobase = it->options[i];
4286ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
4296ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		printk(" 0x%04lx", iobase);
4306ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		if (!request_region(iobase, _8255_SIZE, "8255")) {
4316ca2733471190dd93b627a58148c1aeab5e0d797David Schleef			printk(" (I/O port conflict)");
4326ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
4336ca2733471190dd93b627a58148c1aeab5e0d797David Schleef			dev->subdevices[i].type = COMEDI_SUBD_UNUSED;
4346ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		} else {
4356ca2733471190dd93b627a58148c1aeab5e0d797David Schleef			subdev_8255_init(dev, dev->subdevices + i, NULL,
4360a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral					 iobase);
4376ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		}
4386ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	}
4396ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
4406ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	printk("\n");
4416ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
4426ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	return 0;
4436ca2733471190dd93b627a58148c1aeab5e0d797David Schleef}
4446ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
44571b5f4f11971dea972832ad63a994c7e5b45db6bBill Pembertonstatic int dev_8255_detach(struct comedi_device *dev)
4466ca2733471190dd93b627a58148c1aeab5e0d797David Schleef{
4476ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	int i;
4486ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	unsigned long iobase;
44934c43922e62708d45e9660eee4b4f1fb7b4bf2c7Bill Pemberton	struct comedi_subdevice *s;
4506ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
451026386976067996a6fc084b1926558522126cfddMark	printk(KERN_INFO "comedi%d: 8255: remove\n", dev->minor);
4526ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
4536ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	for (i = 0; i < dev->n_subdevices; i++) {
4546ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		s = dev->subdevices + i;
4556ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		if (s->type != COMEDI_SUBD_UNUSED) {
4566ca2733471190dd93b627a58148c1aeab5e0d797David Schleef			iobase = CALLBACK_ARG;
4576ca2733471190dd93b627a58148c1aeab5e0d797David Schleef			release_region(iobase, _8255_SIZE);
4586ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		}
4596ca2733471190dd93b627a58148c1aeab5e0d797David Schleef		subdev_8255_cleanup(dev, s);
4606ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	}
4616ca2733471190dd93b627a58148c1aeab5e0d797David Schleef
4626ca2733471190dd93b627a58148c1aeab5e0d797David Schleef	return 0;
4636ca2733471190dd93b627a58148c1aeab5e0d797David Schleef}
46490f703d30dd3e0c16ff80f35e34e511385a05ad5Arun Thomas
46590f703d30dd3e0c16ff80f35e34e511385a05ad5Arun ThomasMODULE_AUTHOR("Comedi http://www.comedi.org");
46690f703d30dd3e0c16ff80f35e34e511385a05ad5Arun ThomasMODULE_DESCRIPTION("Comedi low-level driver");
46790f703d30dd3e0c16ff80f35e34e511385a05ad5Arun ThomasMODULE_LICENSE("GPL");
468