Since the new DGAP (epca) driver does not support the ISA versions of the
epca serial cards, I have decided to leave in both the pcxx and epca drivers,
just to be safe.

I did have to modify the epca driver to NOT look for or support the PCI epca
cards.  Because of this, the epca and dgap drivers can live together
peacefully.  (The namespaces of both drivers are completely different)

It took me a day here, to make sure both drivers could play together nicely
with each other when loaded as modules and/or static, and the various
combinations of those both.

I also took Alan's advice, and slapped in some "Digi International no longer
supports these drivers" in the c files, along with ORPHANING the driver in
MAINTAINERS.



 25-akpm/Documentation/00-INDEX               |    4 
 25-akpm/Documentation/devices.txt            |    7 
 25-akpm/Documentation/digiboard.txt          |    3 
 25-akpm/Documentation/digidgap.txt           |   18 
 25-akpm/Documentation/digiepca.txt           |   27 
 25-akpm/Documentation/magic-number.txt       |    3 
 25-akpm/MAINTAINERS                          |   17 
 25-akpm/drivers/char/Kconfig                 |   36 
 25-akpm/drivers/char/Makefile                |    1 
 25-akpm/drivers/char/digi/dgap/Makefile      |   19 
 25-akpm/drivers/char/digi/dgap/dgap_conf.h   |  283 +
 25-akpm/drivers/char/digi/dgap/dgap_downld.h |   69 
 25-akpm/drivers/char/digi/dgap/dgap_driver.c | 1561 ++++++++++
 25-akpm/drivers/char/digi/dgap/dgap_driver.h |  828 +++++
 25-akpm/drivers/char/digi/dgap/dgap_fep5.h   |  247 +
 25-akpm/drivers/char/digi/dgap/dgap_mgmt.c   |  719 ++++
 25-akpm/drivers/char/digi/dgap/dgap_mgmt.h   |   33 
 25-akpm/drivers/char/digi/dgap/dgap_parse.c  | 1299 ++++++++
 25-akpm/drivers/char/digi/dgap/dgap_parse.h  |   35 
 25-akpm/drivers/char/digi/dgap/dgap_pci.h    |   83 
 25-akpm/drivers/char/digi/dgap/dgap_proc.c   |  700 ++++
 25-akpm/drivers/char/digi/dgap/dgap_proc.h   |   32 
 25-akpm/drivers/char/digi/dgap/dgap_trace.c  |  182 +
 25-akpm/drivers/char/digi/dgap/dgap_trace.h  |   36 
 25-akpm/drivers/char/digi/dgap/dgap_tty.c    | 3938 +++++++++++++++++++++++++++
 25-akpm/drivers/char/digi/dgap/dgap_tty.h    |   37 
 25-akpm/drivers/char/digi/dgap/dgap_types.h  |   46 
 25-akpm/drivers/char/digi/dgap/digi.h        |  369 ++
 25-akpm/drivers/char/epca.c                  |   15 
 25-akpm/drivers/char/pcxx.c                  |    9 
 25-akpm/include/linux/major.h                |    2 
 31 files changed, 10614 insertions(+), 44 deletions(-)

diff -puN Documentation/00-INDEX~dgap Documentation/00-INDEX
--- 25/Documentation/00-INDEX~dgap	Wed Sep 17 12:34:23 2003
+++ 25-akpm/Documentation/00-INDEX	Wed Sep 17 12:34:23 2003
@@ -64,8 +64,10 @@ devices.txt
 	- plain ASCII listing of all the nodes in /dev/ with major minor #'s
 digiboard.txt
 	- info on the Digiboard PC/X{i,e,eve} multiport boards.
+digidgap.txt
+	- info about the Digiboard EPCA PCI (DGAP) serial boards.
 digiepca.txt
-	- info on Digi Intl. {PC,PCI,EISA}Xx and Xem series cards.
+	- info on Digi Intl. {ISA,EISA}Xx and Xem ISA series cards.
 dnotify.txt
 	- info about directory notification in Linux.
 driver-model.txt
diff -puN Documentation/devices.txt~dgap Documentation/devices.txt
--- 25/Documentation/devices.txt~dgap	Wed Sep 17 12:34:23 2003
+++ 25-akpm/Documentation/devices.txt	Wed Sep 17 12:34:23 2003
@@ -581,10 +581,9 @@ Your cooperation is appreciated.
 		Partitions are handled the same way as for the first
 		interface (see major number 3).
 
- 23 char	Digiboard serial card - alternate devices
-		  0 = /dev/cud0		Callout device for ttyD0
-		  1 = /dev/cud1		Callout device for ttyD1
-		      ...
+ 23 char	Digiboard serial card - EPCA PCI (DGAP) driver.
+		  0-255			DGAP management devices.
+
     block	Mitsumi proprietary CD-ROM
 		  0 = /dev/mcd		Mitsumi CD-ROM
 
diff -puN Documentation/digiboard.txt~dgap Documentation/digiboard.txt
--- 25/Documentation/digiboard.txt~dgap	Wed Sep 17 12:34:23 2003
+++ 25-akpm/Documentation/digiboard.txt	Wed Sep 17 12:34:23 2003
@@ -17,6 +17,9 @@ you need an patch for this driver.
 
 Bernhard Kaindl (bkaindl@netway.at)  6. April 1997.
 
+As of the 2.6 Linux kernel, Digi International no longer supports
+this version of the driver.
+
 Configuring the Driver
 ----------------------
 
diff -puN /dev/null Documentation/digidgap.txt
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/Documentation/digidgap.txt	Wed Sep 17 12:34:23 2003
@@ -0,0 +1,18 @@
+=============================================================================
+
+	Digi Acceleport EPCA PCI (DGAP) Driver Installation Guide
+			for Linux Kernel 2.6
+		Copyright (C) 2003, Digi International
+============================================================================
+
+	This driver supports the Digi Acceleport Xr, Xr920, Xr422, XEM, C/X
+		and EPC/X PCI cards.
+
+	This driver requires the DGAP Tools package to be installed.
+
+	The Tools package can be found at http://www.digi.com
+
+	Once the Tools package is installed, run /usr/sbin/dgap_config to
+		set up the driver.
+
+============================================================================
diff -puN Documentation/digiepca.txt~dgap Documentation/digiepca.txt
--- 25/Documentation/digiepca.txt~dgap	Wed Sep 17 12:34:23 2003
+++ 25-akpm/Documentation/digiepca.txt	Wed Sep 17 12:34:23 2003
@@ -2,19 +2,23 @@ The Digi Intl. epca driver. 
 ----------------------------
 The Digi Intl. epca driver for Linux supports the following boards:
 
-Digi PC/Xem, PC/Xr, PC/Xe, PC/Xi, PC/Xeve 
-Digi EISA/Xem, PCI/Xem, PCI/Xr 
+Digi PC/Xem, PC/Xr, PC/Xe, PC/Xi, PC/Xeve ISA boards.
+Digi EISA/Xem.
 
 Limitations:
 ------------
-Currently the driver only autoprobes for supported PCI boards. 
+This driver no longer supports PCI Digi products.
+Use the new Digi DGAP driver instead.
+For more information read Documentation/digidgap.txt
+
+As of the 2.6 Linux kernel, Digi International no longer supports
+this version of the driver.
 
 The Linux MAKEDEV command does not support generating the Digiboard
 Devices.  Users executing digiConfig to setup EISA and PC series cards
 will have their device nodes automatically constructed (cud?? for ~CLOCAL,
 and ttyD?? for CLOCAL).  Users wishing to boot their board from the LILO
-prompt, or those users booting PCI cards may use buildDIGI to construct 
-the necessary nodes. 
+prompt may use buildDIGI to construct the necessary nodes.
 
 Notes:
 ------
@@ -26,11 +30,6 @@ lines.  For examples see the bottom of t
 Device names start at 0 and continue up.  Beware of this as previous Digi 
 drivers started device names with 1.
 
-PCI boards are auto-detected and configured by the driver.  PCI boards will
-be allocated device numbers (internally) beginning with the lowest PCI slot
-first.  In other words a PCI card in slot 3 will always have higher device
-nodes than a PCI card in slot 1. 
-
 LILO config examples:
 ---------------------
 Using LILO's APPEND command, a string of comma separated identifiers or 
@@ -45,10 +44,8 @@ are:
    I/O Port where card is configured (in HEX if using string identifiers),
    Base of memory window (in HEX if using string identifiers), 
 
-NOTE : PCI boards are auto-detected and configured.  Do not attempt to 
-configure PCI boards with the LILO append command.  If you wish to override
-previous configuration data (As set by digiConfig), but you do not wish to
-configure any specific card (Example if there are PCI cards in the system) 
+NOTE : If you wish to override previous configuration data
+(As set by digiConfig), but you do not wish to configure any specific card
 the following override command will accomplish this:
 -> append="digi=2"
 
@@ -59,7 +56,7 @@ Samples:
 
 Supporting Tools:
 -----------------
-Supporting tools include digiDload, digiConfig, buildPCI, and ditty.  See
+Supporting tools include digiDload, digiConfig, and ditty.  See
 /usr/src/linux/Documentation/README.epca.dir/user.doc for more details.  Note,
 this driver REQUIRES that digiDload be executed prior to it being used. 
 Failure to do this will result in an ENODEV error.
diff -puN Documentation/magic-number.txt~dgap Documentation/magic-number.txt
--- 25/Documentation/magic-number.txt~dgap	Wed Sep 17 12:34:23 2003
+++ 25-akpm/Documentation/magic-number.txt	Wed Sep 17 12:34:23 2003
@@ -134,6 +134,9 @@ EEPROM_MAGIC_VALUE    0X5ab478d2  lanai_
 HDLCDRV_MAGIC         0x5ac6e778  hdlcdrv_state     include/linux/hdlcdrv.h
 EPCA_MAGIC            0x5c6df104  channel           include/linux/epca.h
 PCXX_MAGIC            0x5c6df104  channel           drivers/char/pcxx.h
+DGAP_BOARD_MAGIC      0x5c6df104  board_t           drivers/char/digi/dgap/dgap_driver.h
+DGAP_CHANNEL_MAGIC    0x6c6df104  channel_t         drivers/char/digi/dgap/dgap_driver.h
+DGAP_UNIT_MAGIC       0x7c6df104  un_t              drivers/char/digi/dgap/dgap_driver.h
 KV_MAGIC              0x5f4b565f  kernel_vars_s     include/asm-mips64/sn/klkernvars.h
 I810_STATE_MAGIC      0x63657373  i810_state        sound/oss/i810_audio.c
 TRIDENT_STATE_MAGIC   0x63657373  trient_state      sound/oss/trident.c
diff -puN /dev/null drivers/char/digi/dgap/dgap_conf.h
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/drivers/char/digi/dgap/dgap_conf.h	Wed Sep 17 12:34:23 2003
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ *	Scott H Kilau <Scott_Kilau at digi dot com>
+ *
+ * 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, EXPRESS OR IMPLIED; 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.
+ *
+ *****************************************************************************
+ *
+ *	dgap_conf.h - Header file for installations and parse files.
+ *
+ *	$Id: dgap_conf.h,v 1.8 2003/09/03 18:20:21 scottk Exp $
+ *
+ *	NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!!
+ */
+
+#ifndef _DGAP_CONF_H
+#define _DGAP_CONF_H
+
+#define NULLNODE 0		/* header node, not used */
+#define BNODE 1			/* Board node */
+#define LNODE 2			/* Line node */
+#define CNODE 3			/* Concentrator node */
+#define MNODE 4			/* EBI Module node */
+#define TNODE 5			/* tty name prefix node */
+#define	CUNODE 6		/* cu name prefix (non-SCO) */
+#define PNODE 7			/* trans. print prefix node */
+#define JNODE 8			/* maJor number node */
+#define ANODE 9			/* altpin */
+#define	TSNODE 10		/* tty structure size */
+#define CSNODE 11		/* channel structure size */
+#define BSNODE 12		/* board structure size */
+#define USNODE 13		/* unit schedule structure size */
+#define FSNODE 14		/* f2200 structure size */
+#define VSNODE 15		/* size of VPIX structures */
+#define INTRNODE 16		/* enable interrupt */
+
+/* Enumeration of tokens */
+#define	BEGIN	1
+#define	END	2
+#define	BOARD	10
+
+#define EPCFS	11 /* start of EPC family definitions */
+#define	ICX		11
+#define	MCX		13
+#define PCX	14
+#define	IEPC	15
+#define	EEPC	16
+#define	MEPC	17
+#define	IPCM	18
+#define	EPCM	19
+#define	MPCM	20
+#define PEPC	21
+#define PPCM	22
+#ifdef CP
+#define ICP     23
+#define ECP     24
+#define MCP     25
+#endif
+#define EPCFE	25 /* end of EPC family definitions */
+#define	PC2E	26
+#define	PC4E	27
+#define	PC4E8K	28
+#define	PC8E	29
+#define	PC8E8K	30
+#define	PC16E	31
+#define MC2E8K  34
+#define MC4E8K  35
+#define MC8E8K  36
+
+#define AVANFS	42	/* start of Avanstar family definitions */
+#define A8P 	42
+#define A16P	43
+#define AVANFE	43	/* end of Avanstar family definitions */
+
+#define DA2000FS	44	/* start of AccelePort 2000 family definitions */
+#define DA22 		44 /* AccelePort 2002 */
+#define DA24 		45 /* AccelePort 2004 */
+#define DA28		46 /* AccelePort 2008 */
+#define DA216		47 /* AccelePort 2016 */
+#define DAR4		48 /* AccelePort RAS 4 port */
+#define DAR8		49 /* AccelePort RAS 8 port */
+#define DDR24		50 /* DataFire RAS 24 port */
+#define DDR30		51 /* DataFire RAS 30 port */
+#define DDR48		52 /* DataFire RAS 48 port */
+#define DDR60		53 /* DataFire RAS 60 port */
+#define DA2000FE	53 /* end of AccelePort 2000/RAS family definitions */
+
+#define PCXRFS	106	/* start of PCXR family definitions */
+#define	APORT4	106
+#define	APORT8	107
+#define PAPORT4 108
+#define PAPORT8 109
+#define APORT4_920I	110
+#define APORT8_920I	111
+#define APORT4_920P	112
+#define APORT8_920P	113
+#define APORT2_920P 114
+#define PCXRFE	117	/* end of PCXR family definitions */
+
+#define	LINE	82
+#ifdef T1
+#define T1M	83
+#define E1M	84
+#endif
+#define	CONC	64
+#define	CX	65
+#define	EPC	66
+#define	MOD	67
+#define	PORTS	68
+#define METHOD	69
+#define CUSTOM	70
+#define BASIC	71
+#define STATUS	72
+#define MODEM	73
+/* The following tokens can appear in multiple places */
+#define	SPEED	74
+#define	NPORTS	75
+#define	ID	76
+#define CABLE	77
+#define CONNECT	78
+#define	IO	79
+#define	MEM	80
+#define DPSZ	81
+
+#define	TTYN	90
+#define	CU	91
+#define	PRINT	92
+#define	XPRINT	93
+#define CMAJOR   94
+#define ALTPIN  95
+#define STARTO 96
+#define USEINTR  97
+
+#define	TTSIZ	100
+#define	CHSIZ	101
+#define BSSIZ	102
+#define	UNTSIZ	103
+#define	F2SIZ	104
+#define	VPSIZ	105
+
+#define	TOTAL_BOARD	2
+#define	CURRENT_BRD	4
+#define	BOARD_TYPE	6
+#define	IO_ADDRESS	8
+#define	MEM_ADDRESS	10
+
+#define	FIELDS_PER_PAGE	18
+
+#define TB_FIELD	1
+#define CB_FIELD	3
+#define BT_FIELD	5
+#define IO_FIELD	7
+#define ID_FIELD	8
+#define ME_FIELD	9
+#define TTY_FIELD	11
+#define CU_FIELD	13
+#define PR_FIELD	15
+#define MPR_FIELD	17
+
+#define	MAX_FIELD	512
+
+#define	INIT		0
+#define	NITEMS		128
+#define MAX_ITEM	512
+
+#define	DSCRINST	1
+#define	DSCRNUM		3
+#define	ALTPINQ		5
+#define	SSAVE		7
+
+#define	DSCR		"32"
+#define	ONETONINE	"123456789"
+#define	ALL		"1234567890"
+
+
+struct cnode {
+	struct cnode *next;
+	int type;
+	int numbrd;
+
+	union {
+		struct {
+			char  type;	/* Board Type 		*/
+			short port;	/* I/O Address		*/
+			char  *portstr; /* I/O Address in string */
+			long  addr;	/* Memory Address	*/
+			char  *addrstr; /* Memory Address in string */
+			char  nport;	/* Number of Ports	*/
+			char  *id;	/* tty id		*/
+			int   start;	/* start of tty counting */
+			char  *method;  /* Install method       */
+			char  v_type;
+			char  v_port;
+			char  v_addr;
+			char  v_nport;
+			char  v_id;
+			char  v_start;
+			char  v_method;
+			char  line1;
+			char  line2;
+			char  conc1;   /* total concs in line1 */
+			char  conc2;   /* total concs in line2 */
+			char  module1; /* total modules for line1 */
+			char  module2; /* total modules for line2 */
+			char  *status; /* config status */
+			char  *dimstatus;	 /* Y/N */
+			int   status_index; /* field pointer */
+		} board;
+
+		struct {
+			char  *cable;
+			char  v_cable;
+			char  speed;
+			char  v_speed;
+		} line;
+
+		struct {
+			char  type;
+			char  *connect;
+			char  speed;
+			char  nport;
+			char  *id;
+			char  *idstr;
+			int   start;
+			char  v_type;
+			char  v_connect;
+			char  v_speed;
+			char  v_nport;
+			char  v_id;
+			char  v_start;
+		} conc;
+
+		struct {
+			char type;
+			char nport;
+			char *id;
+			char *idstr;
+			int  start;
+			char v_type;
+			char v_nport;
+			char v_id;
+			char v_start;
+		} module;
+
+		char *ttyname;
+
+		char *cuname;
+
+		char *printname;
+
+		int  majornumber;
+
+		int  altpin;
+
+		int  ttysize;
+
+		int  chsize;
+
+		int  bssize;
+
+		int  unsize;
+
+		int  f2size;
+
+		int  vpixsize;
+
+		int  useintr;
+	} u;
+};
+
+#endif
diff -puN /dev/null drivers/char/digi/dgap/dgap_downld.h
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/drivers/char/digi/dgap/dgap_downld.h	Wed Sep 17 12:34:23 2003
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ *      Scott H Kilau <Scott_Kilau at digi dot com>
+ *
+ * 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, EXPRESS OR IMPLIED; 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.
+ *
+ * $Id: dgap_downld.h,v 1.3 2003/09/10 16:40:23 scottk Exp $
+ *
+ *	NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!!
+ *
+ */
+
+/*
+** downld.h
+**  - describes the interface between the user level download process
+**    and the concentrator download driver.
+*/
+
+#ifndef _DGAP_DOWNLD_H_
+#define _DGAP_DOWNLD_H_
+
+
+struct fepimg {
+    int type;				/* board type */
+    int	len;				/* length of image */
+    char fepimage[1];			/* begining of image */
+};
+
+struct downldio {
+    unsigned int req_type;		/* FEP or concentrator */
+    unsigned int bdid;			/* opaque board identifier */
+    union {
+	struct downld_t dl;		/* download structure */
+	struct fepimg   fi;		/* fep/bios image structure */
+    } image;
+};
+
+#define DIGI_DLREQ_GET	(('d'<<8) | 220)
+#define DIGI_DLREQ_SET	(('d'<<8) | 221)
+
+#define DIGI_DL_NUKE    (('d'<<8) | 222) /* Not really a dl request, but
+					  dangerous enuff to not put in
+					  digi.h */
+/* Packed bits of intarg for DIGI_DL_NUKE */
+#define DIGI_NUKE_RESET_ALL	 (1 << 31)
+#define DIGI_NUKE_INHIBIT_POLLER (1 << 30)
+#define DIGI_NUKE_BRD_NUMB        0x0f
+
+
+
+#define	DLREQ_BIOS	0
+#define	DLREQ_FEP	1
+#define	DLREQ_CONC	2
+#define	DLREQ_CONFIG	3
+#define DLREQ_DEVCREATE 4
+
+#endif
diff -puN /dev/null drivers/char/digi/dgap/dgap_driver.c
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/drivers/char/digi/dgap/dgap_driver.c	Wed Sep 17 12:34:23 2003
@@ -0,0 +1,1561 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ *	Scott H Kilau <Scott_Kilau at digi dot com>
+ *
+ * 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, EXPRESS OR IMPLIED; 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.
+ *
+ *
+ *	NOTE TO LINUX KERNEL HACKERS:  DO NOT REFORMAT THIS CODE!
+ *
+ *	This is shared code between Digi's CVS archive and the
+ *	Linux Kernel sources.
+ *	Changing the source just for reformatting needlessly breaks
+ *	our CVS diff history.
+ *
+ *	Send any bug fixes/changes to:  Eng.Linux at digi dot com.
+ *	Thank you.
+ *
+ */
+
+char *dgap_version = "$Id: dgap_driver.c,v 1.61 2003/09/11 00:51:13 scottk Exp $";
+
+/*
+ * Our driver specific include files.
+ */
+#include "dgap_driver.h"
+#include "dgap_proc.h"
+#include "dgap_pci.h"
+#include "dgap_fep5.h"
+#include "dgap_tty.h"
+#include "dgap_conf.h"
+#include "dgap_parse.h"
+#include "dgap_mgmt.h"
+
+
+/*
+ * Because this driver is supported on older versions of Linux
+ * as well, lets be safe, and just make sure on this one.
+ */
+#if defined(MODULE_LICENSE)
+	MODULE_LICENSE("GPL");
+#endif
+
+MODULE_AUTHOR("Digi International, http://www.digi.com");
+MODULE_DESCRIPTION("Driver for the Digi International EPCA PCI based product line");
+MODULE_SUPPORTED_DEVICE("dgap");
+
+/*
+ * insmod command line overrideable parameters
+ *
+ * NOTE: we use a set of macros to create the variables, which allows
+ * us to specify the variable type, name, initial value, and description.
+ */
+PARM_INT(debug,		0x00,		"Driver debugging level");
+PARM_INT(rawreadok,	1,		"Bypass flip buffers on input");
+PARM_INT(trcbuf_size,	0x100000,	"Debugging trace buffer size. 1M default.");
+
+/*
+ * A generic list of Product names, PCI Vendor ID, and PCI Device ID.
+ */
+struct board_id {
+	uint config_type;
+	uchar *name;
+	u16 vendor;
+	u16 device;
+	uint maxports;
+	u16 dpatype;
+};
+
+static struct board_id Ids[] =
+{
+	{	PPCM,			PCI_DEVICE_XEM_NAME,	DIGI_VID,
+		PCI_DEVICE_XEM_DID,	64,			(T_PCXM | T_PCLITE | T_PCIBUS)	},
+
+	{	PCX,			PCI_DEVICE_CX_NAME,	DIGI_VID,
+		PCI_DEVICE_CX_DID,	128,			(T_CX | T_PCIBUS)		},
+
+	{	PCX,			PCI_DEVICE_CX_IBM_NAME,	DIGI_VID,
+		PCI_DEVICE_CX_IBM_DID,128,			(T_CX | T_PCIBUS)		},
+
+	{	PEPC,			PCI_DEVICE_EPCJ_NAME,	DIGI_VID,
+		PCI_DEVICE_EPCJ_DID,	224,			(T_EPC  | T_PCIBUS)		},
+
+	{	APORT2_920P,		PCI_DEVICE_920_2_NAME,	DIGI_VID,
+		PCI_DEVICE_920_2_DID,	2,			(T_PCXR | T_PCLITE | T_PCIBUS)	},
+
+	{	APORT4_920P,		PCI_DEVICE_920_4_NAME,	DIGI_VID,
+		PCI_DEVICE_920_4_DID,	4,			(T_PCXR | T_PCLITE | T_PCIBUS)	},
+
+	{	APORT8_920P,		PCI_DEVICE_920_8_NAME,	DIGI_VID,
+		PCI_DEVICE_920_8_DID,	8,			(T_PCXR | T_PCLITE | T_PCIBUS)	},
+
+	{	PAPORT8,		PCI_DEVICE_XR_NAME,	DIGI_VID,
+		PCI_DEVICE_XR_DID,	8,			(T_PCXR | T_PCLITE | T_PCIBUS)	},
+
+	{	PAPORT8,		PCI_DEVICE_XRJ_NAME,	DIGI_VID,
+		PCI_DEVICE_XRJ_DID,	8,			(T_PCXR | T_PCLITE | T_PCIBUS)	},
+
+	{	PAPORT8,		PCI_DEVICE_XR_422_NAME,	DIGI_VID,
+		PCI_DEVICE_XR_422_DID,8,			(T_PCXR | T_PCLITE | T_PCIBUS)	},
+
+	{	PAPORT8,		PCI_DEVICE_XR_IBM_NAME,	DIGI_VID,
+		PCI_DEVICE_XR_IBM_DID, 8,			(T_PCXR | T_PCLITE | T_PCIBUS)	},
+
+};
+#define	NIDS	(sizeof(Ids)/sizeof(struct board_id))
+
+
+char *dgap_state_text[] = {
+	"Board Failed",
+	"Configuration for board not found.\n\t\t\t Use /usr/bin/dgap_config to configure board.",
+	"Board Found",
+	"Need Reset",
+	"Finished Reset",
+	"Need Config",
+	"Finished Config",
+	"Need Device Creation",
+	"Requested Device Creation",
+	"Finished Device Creation",
+	"Need BIOS Load",
+	"Requested BIOS",
+	"Doing BIOS Load",
+	"Finished BIOS Load",
+	"Need FEP Load",
+	"Requested FEP",
+	"Doing FEP Load",
+	"Finished FEP Load",
+	"Board READY",
+};
+
+char *dgap_driver_state_text[] = {
+	"Driver Initialized",
+	"Driver needs configuration load.",
+	"Driver requested configuration from download daemon.",
+	"Driver Ready."
+};
+
+
+/**************************************************************************
+ *
+ * protos for this file
+ *
+ */
+
+/* Driver load/unload functions */
+int			dgap_init_module(void);
+void			dgap_cleanup_module(void);
+int			dgap_start(void);
+int			dgap_finalize_board_init(struct board_t *brd);
+
+static void		dgap_init_globals(void);
+static int		dgap_scan(void);
+static int		dgap_found_board(struct pci_dev *pdev, int id);
+static void		dgap_cleanup_board(struct board_t *brd);
+static void		dgap_poll_handler(ulong dummy);
+
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+
+static irqreturn_t	dgap_intr(int irq, void *voidbrd, struct pt_regs *regs);
+module_init(dgap_init_module);
+module_exit(dgap_cleanup_module);
+
+#else
+
+static void		dgap_intr(int irq, void *voidbrd, struct pt_regs *regs);
+/*
+ * Older 2.4 kernels do not support module_init/exit calls.
+ * So we do it this way for now.
+ */
+int			init_module(void);
+void			cleanup_module(void);
+#define dgap_init_module init_module
+#define dgap_cleanup_module cleanup_module
+
+#endif
+
+
+/*
+ * File operations permitted on Control/Management major.
+ */
+static struct file_operations BoardFops =
+{
+	owner:		THIS_MODULE,
+	read:		NULL,
+	write:		NULL,
+	ioctl:		dgap_mgmt_ioctl,
+	mmap:		NULL,
+	open:		dgap_mgmt_open,
+	release:	dgap_mgmt_close
+};
+
+
+/*
+ * Statics/Globals
+ */
+struct board_t		*dgap_Board[MAXBOARDS];
+uchar			dgap_NumBoards;
+spinlock_t		dgap_global_lock = SPIN_LOCK_UNLOCKED;
+ulong			dgap_poll_counter = 0;
+static int		dgap_Major_Control_Registered = FALSE;
+int			dgap_driver_state = DRIVER_INITIALIZED;
+
+spinlock_t		dgap_dl_lock = SPIN_LOCK_UNLOCKED;
+wait_queue_head_t	dgap_dl_wait;
+int			dgap_dl_action;
+
+/* Poller stuff */
+static spinlock_t	dgap_poll_lock = SPIN_LOCK_UNLOCKED;	/* Poll scheduling lock */
+static ulong		dgap_poll_time;				/* Time of next poll */
+static ulong		dgap_poll_tick = 20;			/* Poll interval - 20 ms */
+static struct timer_list dgap_poll_timer = { function: dgap_poll_handler };
+
+static char		*dgap_config_buf;			/* The config file buffer */
+
+
+/************************************************************************
+ *
+ * Driver load/unload functions
+ *
+ ************************************************************************/
+
+/*
+ * init_module()
+ *
+ * Module load.  This is where it all starts.
+ */
+int dgap_init_module(void)
+{
+	APR(("%s, Digi International Part Number %s\n", DG_NAME, DG_PART));
+	return dgap_start();
+}
+
+
+/*
+ * Start of driver.
+ */
+int dgap_start(void)
+{
+	int rc = 0;
+	unsigned long flags;
+
+        /* make sure that the globals are init'd before we do anything else */
+        dgap_init_globals();
+
+	dgap_NumBoards = 0;
+
+	APR(("For the tools package or updated drivers please visit http://www.digi.com\n"));
+
+	/*
+	 * Register our base character device into the kernel.
+	 * This allows the download daemon to connect to the downld device
+	 * before any of the boards are init'ed.
+	 */
+	if (!dgap_Major_Control_Registered) {
+		/*
+		 * Register management/dpa devices
+		 */
+		rc = register_chrdev(DIGI_DGAP_MAJOR, "dgap", &BoardFops);
+		if (rc < 0) {
+			APR(("Can't register dgap driver device (%d)\n", rc));
+			return (rc);
+		}
+		dgap_Major_Control_Registered = TRUE;
+	}
+
+	/*
+	 * Register our basic stuff in /proc/dgap
+	 */
+	dgap_proc_register_basic_prescan();
+
+	/*
+	 * Init any global tty stuff.
+	 */
+	dgap_tty_preinit();
+
+	/*
+	 * Find and configure all the cards
+	 */
+	rc = dgap_scan();
+
+	/*
+	 * If something went wrong in the scan, bail out of driver.
+	 */
+	if (rc) {
+		dgap_cleanup_module();
+	}
+	else {
+		/* Start the poller */
+		if (dgap_NumBoards > 0) {
+			DGAP_LOCK(dgap_poll_lock, flags);
+			dgap_poll_time = jiffies + dgap_jiffies_from_ms(dgap_poll_tick);
+			dgap_poll_timer.expires = dgap_poll_time;
+			DGAP_UNLOCK(dgap_poll_lock, flags);
+
+			add_timer(&dgap_poll_timer);
+
+		}
+
+		dgap_driver_state = DRIVER_NEED_CONFIG_LOAD;
+	}
+
+	return (rc);
+}
+
+
+
+int dgap_after_config_loaded(void)
+{
+	int i = 0;
+	int rc = 0;
+
+	/*
+	 * Register our ttys, now that we have the config loaded.
+	 */
+	for (i = 0; i < dgap_NumBoards; ++i) {
+
+		/*
+		 * Initialize KME waitqueues...
+		 */
+		init_waitqueue_head(&(dgap_Board[i]->kme_wait));
+
+		/*
+		 * allocate flip buffer for board.
+		 */
+		dgap_Board[i]->flipbuf = dgap_driver_kzmalloc(MYFLIPLEN, GFP_ATOMIC);
+	}
+
+	dgap_proc_register_basic_postscan();
+
+	dgap_proc_register_fep();
+
+	return (rc);
+}
+
+
+
+/*
+ * dgap_cleanup_module()
+ *
+ * Module unload.  This is where it all ends.
+ */
+void dgap_cleanup_module(void)
+{
+	int i;
+	ulong lock_flags;
+
+	/* Turn off poller right away. */
+	DGAP_LOCK(dgap_poll_lock, lock_flags );
+	del_timer_sync( &dgap_poll_timer );
+	DGAP_UNLOCK(dgap_poll_lock, lock_flags );
+
+	dgap_proc_unregister_all();
+
+	if (dgap_Major_Control_Registered)
+		unregister_chrdev(DIGI_DGAP_MAJOR, "dgap");
+
+	if (dgap_config_buf)
+		kfree(dgap_config_buf);
+
+	for (i = 0; i < dgap_NumBoards; ++i) {
+		dgap_tty_uninit(dgap_Board[i]);
+		dgap_cleanup_board(dgap_Board[i]);
+	}
+
+	dgap_tty_post_uninit();
+
+#if defined(DGAP_TRACER)
+	/* last thing, make sure we release the tracebuffer */
+	dgap_tracer_free();
+#endif
+}
+
+
+/*
+ * dgap_cleanup_board()
+ *
+ * Free all the memory associated with a board
+ */
+static void dgap_cleanup_board(struct board_t *brd)
+{
+	int i = 0;
+
+        if(!brd || brd->magic != DGAP_BOARD_MAGIC)
+                return;
+
+	if (brd->intr_used && brd->irq)
+		free_irq(brd->irq, brd);
+
+	tasklet_kill(&brd->helper_tasklet);
+
+	if (brd->re_map_port) {
+		DGAP_IOUNMAP(brd->re_map_port);
+		brd->re_map_port = 0;
+	}
+
+	if (brd->re_map_membase) {
+		DGAP_IOUNMAP(brd->re_map_membase);
+		brd->re_map_membase = 0;
+	}
+
+        if (brd->msgbuf_head) {
+                unsigned long flags;
+
+                DGAP_LOCK(dgap_global_lock, flags);
+                brd->msgbuf = NULL;
+                printk(brd->msgbuf_head);
+                DGAP_VFREE(brd->msgbuf_head);
+                brd->msgbuf_head = NULL;
+                DGAP_UNLOCK(dgap_global_lock, flags);
+        }
+
+	/* Free all allocated channels structs */
+	for (i = 0; i < MAXPORTS ; i++) {
+		if (brd->channels[i]) {
+			kfree(brd->channels[i]);
+			brd->channels[i] = NULL;
+		}
+	}
+
+	if (brd->flipbuf)
+		kfree(brd->flipbuf);
+
+	dgap_Board[brd->boardnum] = NULL;
+
+        DGAP_VFREE(brd);
+}
+
+
+/*
+ * dgap_init_globals()
+ *
+ * This is where we initialize the globals from the static insmod
+ * configuration variables.  These are declared near the head of
+ * this file.
+ */
+static void dgap_init_globals(void)
+{
+	int i = 0;
+
+	dgap_rawreadok		= rawreadok;
+        dgap_trcbuf_size	= trcbuf_size;
+	dgap_debug		= debug;
+
+	for (i = 0; i < MAXBOARDS; i++) {
+		dgap_Board[i] = NULL;
+	}
+
+	init_timer( &dgap_poll_timer );
+
+	init_waitqueue_head(&dgap_dl_wait);
+	dgap_dl_action = 0;
+}
+
+
+/*
+ *      dgap_ioctl_name() : Returns a text version of each ioctl value.
+ */
+char *dgap_ioctl_name(int cmd)
+{
+	switch(cmd) {
+
+	case TCGETA:		return("TCGETA");
+	case TCGETS:		return("TCGETS");
+	case TCSETA:		return("TCSETA");
+	case TCSETS:		return("TCSETS");
+	case TCSETAW:		return("TCSETAW");
+	case TCSETSW:		return("TCSETSW");
+	case TCSETAF:		return("TCSETAF");
+	case TCSETSF:		return("TCSETSF");
+	case TCSBRK:		return("TCSBRK");
+	case TCXONC:		return("TCXONC");
+	case TCFLSH:		return("TCFLSH");
+	case TIOCGSID:		return("TIOCGSID");
+
+	case TIOCGETD:		return("TIOCGETD");
+	case TIOCSETD:		return("TIOCSETD");
+	case TIOCGWINSZ:	return("TIOCGWINSZ");
+	case TIOCSWINSZ:	return("TIOCSWINSZ");
+
+	case TIOCMGET:		return("TIOCMGET");
+	case TIOCMSET:		return("TIOCMSET");
+	case TIOCMBIS:		return("TIOCMBIS");
+	case TIOCMBIC:		return("TIOCMBIC");
+
+	/* from digi.h */
+	case DIGI_SETA:		return("DIGI_SETA");
+	case DIGI_SETAW:	return("DIGI_SETAW");
+	case DIGI_SETAF:	return("DIGI_SETAF");
+	case DIGI_SETFLOW:	return("DIGI_SETFLOW");
+	case DIGI_SETAFLOW:	return("DIGI_SETAFLOW");
+	case DIGI_GETFLOW:	return("DIGI_GETFLOW");
+	case DIGI_GETAFLOW:	return("DIGI_GETAFLOW");
+	case DIGI_GETA:		return("DIGI_GETA");
+	case DIGI_GEDELAY:	return("DIGI_GEDELAY");
+	case DIGI_SEDELAY:	return("DIGI_SEDELAY");
+#if 0
+	case DIGI_GETCUSTOMBAUD: return("DIGI_GETCUSTOMBAUD");
+	case DIGI_SETCUSTOMBAUD: return("DIGI_SETCUSTOMBAUD");
+#endif
+	case TIOCMODG:		return("TIOCMODG");
+	case TIOCMODS:		return("TIOCMODS");
+	case TIOCSDTR:		return("TIOCSDTR");
+	case TIOCCDTR:		return("TIOCCDTR");
+
+	default:		return("unknown");
+	}
+}
+
+
+
+void dgap_do_config_load(uchar *uaddr, int len)
+{
+	int orig_len = len;
+	char *to_addr;
+	char *from_addr = uaddr;
+	static char buf[U2BSIZE];
+	int n;
+
+	to_addr = dgap_config_buf = dgap_driver_kzmalloc(len + 1, GFP_ATOMIC);
+	if (!dgap_config_buf) {
+		DPR_INIT(("dgap_do_config_load - unable to allocate memory for file\n"));
+		dgap_driver_state = DRIVER_NEED_CONFIG_LOAD;
+		return;
+	}
+
+	n = U2BSIZE;
+	while (len) {
+
+		if (n > len)
+			n = len;
+
+		if (copy_from_user((char *) &buf, from_addr, n) == -1 ) {
+			/* TODO, SOMETHING SANE */
+			return;
+		}
+
+		/* Copy data from buffer to kernel memory */
+		memcpy(to_addr, buf, n);
+
+		/* increment counts */
+		len -= n;
+		to_addr += n;
+		from_addr += n;
+		n = U2BSIZE;
+        }
+
+	dgap_config_buf[orig_len] = '\0';
+
+	to_addr = dgap_config_buf;
+	dgap_parsefile(&to_addr, TRUE);
+
+	DPR_INIT(("dgap_config_load() finish\n"));
+
+	return;
+}
+
+
+/*
+ * Remap PCI memory.
+ */
+static void dgap_do_remap(struct board_t *brd)
+{
+
+	if (!brd || brd->magic != DGAP_BOARD_MAGIC)
+		return;
+
+	brd->re_map_port = DGAP_IOREMAP((brd->membase + PCI_IO_OFFSET), 0x200000);
+	brd->re_map_membase = DGAP_IOREMAP(brd->membase, 0x200000);
+
+	DPR_INIT(("remapped io: 0x%p  remapped mem: 0x%p\n",
+		brd->re_map_port, brd->re_map_membase));
+}
+
+
+/*=======================================================================
+ *
+ *      usertoboard - copy from user space to board space.
+ *
+ *=======================================================================*/
+
+int dgap_usertoboard(struct board_t *brd, char *to_addr, char *from_addr, int len)
+{
+	static char buf[U2BSIZE];
+	int n = U2BSIZE;
+
+	if (!brd || brd->magic != DGAP_BOARD_MAGIC)
+		return(-EFAULT);
+
+	while (len) {
+		if (n > len)
+			n = len;
+
+		if (copy_from_user((char *) &buf, from_addr, n) == -1 ) {
+			return(-EFAULT);
+		}
+
+		/* Copy data from buffer to card memory */
+		memcpy_toio(to_addr, buf, n);
+
+		/* increment counts */
+		len -= n;
+		to_addr += n;
+		from_addr += n;
+		n = U2BSIZE;
+        }
+	return(0);
+}
+
+
+void dgap_do_bios_load(struct board_t *brd, uchar *ubios, int len)
+{
+	u32 bios;
+	uchar *addr = brd->re_map_membase;
+	int i;
+
+	if (!brd || brd->magic != DGAP_BOARD_MAGIC)
+		return;
+
+	DPR_INIT(("dgap_do_bios_load() start\n"));
+
+	/*
+	 * clear POST area
+	 */
+	for (i = 0; i < 16; i++)
+		writeb(0, addr + POSTAREA + i);
+
+	/*
+	 * Download bios
+	 */
+	bios = 0x1000;
+	if (dgap_usertoboard(brd, addr + bios, ubios, len) == -1 ) {
+		brd->state = BOARD_FAILED;
+		brd->dpastatus = BD_NOFEP;
+		return;
+	}
+
+	writel(0x0bf00401, addr);
+	writel(0, (addr + 4));
+
+	/* Clear the reset, and change states. */
+	writeb(FEPCLR, brd->re_map_port);
+	brd->state = WAIT_BIOS_LOAD;
+}
+
+
+static void dgap_do_wait_for_bios(struct board_t *brd)
+{
+	uchar *addr;
+	u16 word;
+
+	if (!brd || brd->magic != DGAP_BOARD_MAGIC)
+		return;
+
+	addr = brd->re_map_membase;
+	word = readw(addr + POSTAREA);
+
+	/* Check to see if BIOS thinks board is good. (GD). */
+	if (word == *(u16 *) "GD") {
+		DPR_INIT(("GOT GD in memory, moving states.\n"));
+		brd->state = FINISHED_BIOS_LOAD;
+		return;
+	}
+
+	/* Give up on board after too long of time taken */
+	if (brd->wait_for_bios++ > 5000) {
+		u16 err1 = readw(addr + SEQUENCE);
+		u16 err2 = readw(addr + ERROR);
+		APR(("***WARNING*** %s failed diagnostics.  Error #(%x,%x).\n",
+			brd->name, err1, err2));
+		brd->state = BOARD_FAILED;
+		brd->dpastatus = BD_NOFEP;
+	}
+}
+
+void dgap_do_fep_load(struct board_t *brd, uchar *ufep, int len)
+{
+	u32 fepos;
+	uchar *addr;
+
+	if (!brd || brd->magic != DGAP_BOARD_MAGIC)
+		return;
+
+	addr = brd->re_map_membase;
+
+	DPR_INIT(("dgap_do_fep_load() for board %s : start\n", brd->name));
+
+	/*
+	 * Download FEP
+	 */
+
+	/* TODO: don't hard code this stuff */
+	fepos = 0x1000;
+
+	if (dgap_usertoboard(brd, addr + fepos, ufep, len) == -1 ) {
+		brd->state = BOARD_FAILED;
+		brd->dpastatus = BD_NOFEP;
+		return;
+	}
+
+	/*
+	 * If board is a concentrator product, we need to give
+	 * it its config string describing how the concentrators look.
+	 */
+	if ((brd->type == PCX) || (brd->type == PEPC)) {
+		uchar string[100];
+		uchar *config, *xconfig;
+		int i = 0;
+
+		xconfig = dgap_create_config_string(brd, string);
+
+		/* Write string to board memory */
+		config = addr + CONFIG;
+		for (; i < CONFIGSIZE; i++, config++, xconfig++) {
+			writeb(*xconfig, config);
+			if ((*xconfig & 0xff) == 0xff)
+				break;
+		}
+	}
+
+	writel(0xbfc01004, (addr + 0xc34));
+	writel(0x3, (addr + 0xc30));
+
+	/* change states. */
+	brd->state = WAIT_FEP_LOAD;
+
+	DPR_INIT(("dgap_do_fep_load() for board %s : finish\n", brd->name));
+
+}
+
+
+static void dgap_do_wait_for_fep(struct board_t *brd)
+{
+	uchar *addr;
+	u16 word;
+
+	if (!brd || brd->magic != DGAP_BOARD_MAGIC) {
+		return;
+	}
+
+	addr = brd->re_map_membase;
+
+	if (!addr) {
+		APR(("dgap_do_wait_for_fep() addr is NULL\n"));
+		brd->state = BOARD_FAILED;
+		brd->dpastatus = BD_NOFEP;
+		return;
+	}
+
+	DPR_INIT(("dgap_do_wait_for_fep() for board %s : start. addr: %p\n", brd->name, addr));
+
+	word = readw(addr + FEPSTAT);
+
+	/* Check to see if FEP is up and running now. */
+	if (word == *(u16 *) "OS") {
+		DPR_INIT(("GOT OS in memory for board %s, moving states.\n", brd->name));
+		brd->state = FINISHED_FEP_LOAD;
+		return;
+	}
+
+	/* Give up on board after too long of time taken */
+	if (brd->wait_for_fep++ > 5000) {
+		u16 err1 = readw(addr + SEQUENCE);
+		u16 err2 = readw(addr + ERROR);
+		APR(("***WARNING*** FEPOS for %s not functioning.  Error #(%x,%x).\n",
+			brd->name, err1, err2));
+		brd->state = BOARD_FAILED;
+		brd->dpastatus = BD_NOFEP;
+	}
+
+	DPR_INIT(("dgap_do_wait_for_fep() for board %s : finish\n", brd->name));
+}
+
+
+static void dgap_do_reset_board(struct board_t *brd)
+{
+	uchar check;
+	u32 check1;
+	u32 check2;
+	int i = 0;
+
+	if (!brd || brd->magic != DGAP_BOARD_MAGIC)
+		return;
+
+	DPR_INIT(("dgap_do_reset_board() start\n"));
+
+	/* FEPRST does not vary among supported boards */
+	writeb(FEPRST, brd->re_map_port);
+
+	for (i = 0; i <= 1000; i++) {
+		check = readb(brd->re_map_port) & 0xe;
+		if (check == FEPRST)
+			break;
+		udelay(10);
+
+	}
+	if (i > 1000) {
+		APR(("*** WARNING *** Board not resetting...  Failing board.\n"));
+		brd->state = BOARD_FAILED;
+		brd->dpastatus = BD_NOFEP;
+		goto failed;
+	}
+
+	/*
+	 * Make sure there really is memory out there.
+	 */
+	writel(0xa55a3cc3, (brd->re_map_membase + LOWMEM));
+	writel(0x5aa5c33c, (brd->re_map_membase + HIGHMEM));
+	check1 = readl(brd->re_map_membase + LOWMEM);
+	check2 = readl(brd->re_map_membase + HIGHMEM);
+
+	if ((check1 != 0xa55a3cc3) || (check2 != 0x5aa5c33c)) {
+		APR(("*** Warning *** No memory at %p for board.\n", brd->re_map_membase));
+		brd->state = BOARD_FAILED;
+		brd->dpastatus = BD_NOFEP;
+		goto failed;
+	}
+
+	if (brd->state != BOARD_FAILED)
+		brd->state = FINISHED_RESET;
+
+failed:
+	DPR_INIT(("dgap_do_reset_board() finish\n"));
+}
+
+
+void dgap_do_conc_load(struct board_t *brd, uchar *uaddr, int len)
+{
+	char *vaddr;
+	u16 offset = 0;
+	struct downld_t *to_dp;
+
+	if (!brd || brd->magic != DGAP_BOARD_MAGIC)
+		return;
+
+	vaddr = brd->re_map_membase;
+
+	if (!vaddr)
+		return;
+
+	offset = readw((u16 *) (vaddr + DOWNREQ));
+	to_dp = (struct downld_t *) (vaddr + (int) offset);
+
+	memcpy_toio((char *) to_dp, uaddr, sizeof(struct downld_t));
+
+	/* Tell card we have data for it */
+	writew(0, vaddr + (DOWNREQ));
+
+	brd->conc_dl_status = NO_PENDING_CONCENTRATOR_REQUESTS;
+
+}
+
+
+/*
+ * Our board poller function.
+ */
+static void poll_tasklet(unsigned long data)
+{
+        struct board_t *bd = (struct board_t *) data;
+	ulong  lock_flags;
+	ulong  lock_flags2;
+	u32 head, tail;
+	char *vaddr;
+	u16 *chk_addr;
+	u16 check = 0;
+
+	if (!bd || bd->magic != DGAP_BOARD_MAGIC) {
+		APR(("poll_tasklet() - NULL or bad bd.\n"));
+		return;
+	}
+
+	if (bd->inhibit_poller)
+		return;
+
+	DGAP_LOCK(bd->bd_lock, lock_flags);
+
+	vaddr = bd->re_map_membase;
+
+	/*
+	 * If board is ready, parse deeper to see if there is anything to do.
+	 */
+	if (bd->state == BOARD_READY) {
+
+		struct ev_t *eaddr = NULL;
+
+		if (!bd->re_map_membase) {
+			DGAP_UNLOCK(bd->bd_lock, lock_flags);
+			return;
+		}
+		if (!bd->re_map_port) {
+			DGAP_UNLOCK(bd->bd_lock, lock_flags);
+			return;
+		}
+
+		if (!bd->nasync) {
+			goto out;
+		}
+
+		/*
+		 * If this is a CX or EPCX, we need to see if the firmware
+		 * is requesting a concentrator image from us.
+		 */
+		if ((bd->type == PCX) || (bd->type == PEPC)) {
+			chk_addr = (u16 *) (vaddr + DOWNREQ);
+			check = readw(chk_addr);
+			/* Nonzero if FEP is requesting concentrator image. */
+			if (check) {
+				if (bd->conc_dl_status == NO_PENDING_CONCENTRATOR_REQUESTS)
+					bd->conc_dl_status = NEED_CONCENTRATOR;
+				/*
+				 * Signal downloader, its got some work to do.
+				 */
+				DGAP_LOCK(dgap_dl_lock, lock_flags2);
+				if (dgap_dl_action != 1) {
+					dgap_dl_action = 1;
+					wake_up_interruptible(&dgap_dl_wait);
+				}
+				DGAP_UNLOCK(dgap_dl_lock, lock_flags2);
+
+			}
+		}
+
+		eaddr = (struct ev_t *) (vaddr + EVBUF);
+
+		/* Get our head and tail */
+		head = readw(&(eaddr->ev_head));
+		tail = readw(&(eaddr->ev_tail));
+
+		/*
+		 * If there is an event pending. Go service it.
+		 */
+		if (head != tail) {
+			DGAP_UNLOCK(bd->bd_lock, lock_flags);
+			dgap_event(bd);
+			DGAP_LOCK(bd->bd_lock, lock_flags);
+		}
+
+out:
+		/*
+		 * If board is doing interrupts, ACK the interrupt.
+		 */
+		if (bd && bd->intr_running) {
+			readb(bd->re_map_port + 2);
+		}
+
+		DGAP_UNLOCK(bd->bd_lock, lock_flags);
+		return;
+	}
+
+	/* Our state machine to get the board up and running */
+
+	/* Reset board */
+	if (bd->state == NEED_RESET) {
+		dgap_do_reset_board(bd);
+	}
+
+	/* Move to next state */
+	if (bd->state == FINISHED_RESET) {
+		bd->state = NEED_CONFIG;
+	}
+
+	if (bd->state == NEED_CONFIG) {
+		/*
+		 * Match this board to a config the user created for us.
+		 */
+		bd->bd_config = dgap_find_config(bd->type);
+
+		/*
+		 * Register the ttys (if any) into the kernel.
+		 */
+		if (bd->bd_config) {
+			bd->state = FINISHED_CONFIG;
+		}
+		else {
+			bd->state = CONFIG_NOT_FOUND;
+		}
+	}
+
+	/* Move to next state */
+	if (bd->state == FINISHED_CONFIG) {
+		bd->state = NEED_DEVICE_CREATION;
+	}
+
+	/* Move to next state */
+	if (bd->state == NEED_DEVICE_CREATION) {
+		/*
+		 * Signal downloader, its got some work to do.
+		 */
+		DGAP_LOCK(dgap_dl_lock, lock_flags2);
+		if (dgap_dl_action != 1) {
+			dgap_dl_action = 1;
+			wake_up_interruptible(&dgap_dl_wait);
+		}
+		DGAP_UNLOCK(dgap_dl_lock, lock_flags2);
+	}
+
+	/* Move to next state */
+	if (bd->state == FINISHED_DEVICE_CREATION) {
+		bd->state = NEED_BIOS_LOAD;
+	}
+
+	/* Move to next state */
+	if (bd->state == NEED_BIOS_LOAD) {
+		/*
+		 * Signal downloader, its got some work to do.
+		 */
+		DGAP_LOCK(dgap_dl_lock, lock_flags2);
+		if (dgap_dl_action != 1) {
+			dgap_dl_action = 1;
+			wake_up_interruptible(&dgap_dl_wait);
+		}
+		DGAP_UNLOCK(dgap_dl_lock, lock_flags2);
+	}
+
+	/* Wait for BIOS to test board... */
+	if (bd->state == WAIT_BIOS_LOAD) {
+		dgap_do_wait_for_bios(bd);
+	}
+
+	/* Move to next state */
+	if (bd->state == FINISHED_BIOS_LOAD) {
+		bd->state = NEED_FEP_LOAD;
+
+		/*
+		 * Signal downloader, its got some work to do.
+		 */
+		DGAP_LOCK(dgap_dl_lock, lock_flags2);
+		if (dgap_dl_action != 1) {
+			dgap_dl_action = 1;
+			wake_up_interruptible(&dgap_dl_wait);
+		}
+		DGAP_UNLOCK(dgap_dl_lock, lock_flags2);
+	}
+
+	/* Wait for FEP to load on board... */
+	if (bd->state == WAIT_FEP_LOAD) {
+		dgap_do_wait_for_fep(bd);
+	}
+
+	/* Move to next state */
+	if (bd->state == FINISHED_FEP_LOAD) {
+
+		/*
+		 * Do tty device initialization.
+		 */
+		int rc = dgap_tty_init(bd);
+		if (rc < 0) {
+			dgap_tty_uninit(bd);
+			APR(("Can't init tty devices (%d)\n", rc));
+			bd->state = BOARD_FAILED;
+			bd->dpastatus = BD_NOFEP;
+		}
+		else {
+			bd->state = BOARD_READY;
+			bd->dpastatus = BD_RUNNING;
+
+			/*
+			 * If user requested the board to run in interrupt mode,
+			 * go and set it up on the board.
+			 */
+			if (bd->intr_used) {
+				writew(1, (bd->re_map_membase + ENABLE_INTR));
+				/*
+				 * Tell the board to poll the UARTS as fast as possible.
+				 */
+				writew(FEPPOLL_MIN, (bd->re_map_membase + FEPPOLL));
+				bd->intr_running = 1;
+			}
+		}
+	}
+
+	DGAP_UNLOCK(bd->bd_lock, lock_flags);
+}
+
+
+/*****************************************************************************
+*
+* Function:
+*
+*    dgap_poll_handler
+*
+* Author:
+*
+*    Scott H Kilau
+*
+* Parameters:
+*
+*    dummy -- ignored
+*
+* Return Values:
+*
+*    none
+*
+* Description:
+*
+*    As each timer expires, it determines (a) whether the "transmit"
+*    waiter needs to be woken up, and (b) whether the poller needs to
+*    be rescheduled.
+*
+******************************************************************************/
+
+static void dgap_poll_handler(ulong dummy)
+{
+	int i;
+        struct board_t *brd;
+        unsigned long lock_flags;
+        unsigned long lock_flags2;
+
+	dgap_poll_counter++;
+
+
+	/*
+	 * If driver needs the config file still,
+	 * keep trying to wake up the downloader to
+	 * send us the file.
+	 */
+        if (dgap_driver_state == DRIVER_NEED_CONFIG_LOAD) {
+		/*
+		 * Signal downloader, its got some work to do.
+		 */
+		DGAP_LOCK(dgap_dl_lock, lock_flags2);
+		if (dgap_dl_action != 1) {
+			dgap_dl_action = 1;
+			wake_up_interruptible(&dgap_dl_wait);
+		}
+		DGAP_UNLOCK(dgap_dl_lock, lock_flags2);
+		goto schedule_poller;
+        }
+	/*
+	 * Do not start the board state machine until
+	 * driver tells us its up and running, and has
+	 * everything it needs.
+	 */
+	else if (dgap_driver_state != DRIVER_READY) {
+		goto schedule_poller;
+	}
+
+	/*
+	 * If we have just 1 board, or the system is not SMP,
+	 * then use the typical old style poller.
+	 * Otherwise, use our new tasklet based poller, which should
+	 * speed things up for multiple boards.
+	 */
+	if ( (dgap_NumBoards == 1) || (DGAP_NUM_CPUS <= 1) ) {
+		for (i = 0; i < dgap_NumBoards; i++) {
+
+			brd = dgap_Board[i];
+
+			if (brd->state == BOARD_FAILED) {
+				continue;
+			}
+			if (!brd->intr_running) {
+				/* Call the real board poller directly */
+				poll_tasklet((unsigned long) brd);
+			}
+		}
+	}
+	else {
+		/* Go thru each board, kicking off a tasklet for each if needed */
+		for (i = 0; i < dgap_NumBoards; i++) {
+			brd = dgap_Board[i];
+
+			/*
+			 * Attempt to grab the board lock.
+			 *
+			 * If we can't get it, no big deal, the next poll will get it.
+			 * Basically, I just really don't want to spin in here, because I want
+			 * to kick off my tasklets as fast as I can, and then get out the poller.
+			 */
+			if (!spin_trylock(&brd->bd_lock)) {
+				continue;
+			}
+
+			/* If board is in a failed state, don't bother scheduling a tasklet */
+			if (brd->state == BOARD_FAILED) {
+				spin_unlock(&brd->bd_lock);
+				continue;
+			}
+
+			/* Schedule a poll helper task */
+			if (!brd->intr_running) {
+				tasklet_schedule(&brd->helper_tasklet);
+			}
+
+			/*
+			 * Can't do DGAP_UNLOCK here, as we don't have
+			 * lock_flags because we did a trylock above.
+			 */
+			spin_unlock(&brd->bd_lock);
+		}
+	}
+
+schedule_poller:
+
+	/*
+	 * Schedule ourself back at the nominal wakeup interval.
+	 */
+	if (dgap_NumBoards > 0) {
+		ulong time;
+
+		DGAP_LOCK(dgap_poll_lock, lock_flags );
+		dgap_poll_time +=  dgap_jiffies_from_ms(dgap_poll_tick);
+
+		time = dgap_poll_time - jiffies;
+
+		if ((ulong) time >= 2 * dgap_poll_tick) {
+			dgap_poll_time = jiffies +  dgap_jiffies_from_ms(dgap_poll_tick);
+		}
+
+		dgap_poll_timer.expires = dgap_poll_time;
+		DGAP_UNLOCK(dgap_poll_lock, lock_flags );
+
+		add_timer(&dgap_poll_timer);
+	}
+
+}
+
+
+/*
+ * dgap_scan()
+ *
+ * Scan for all boards
+ *
+ * Because of the way the PCI device list is scanned, we will always detect
+ * boards in the order that they are listed in the Ids table defined near the
+ * top of this file.
+ *
+ */
+static int dgap_scan(void)
+{
+	int id;
+	int rc = 0;
+        int i;
+
+	for (id = 0; id < NIDS; id++) {
+		struct pci_dev *pdev;
+
+		pdev = NULL;
+		while ((pdev = pci_find_device(Ids[id].vendor,
+					       Ids[id].device, pdev))) {
+
+			rc = dgap_found_board(pdev, id);
+			if (rc == 0) {
+                                dgap_NumBoards++;
+                        }
+		}
+	}
+
+        for (i = 0; i < dgap_NumBoards ; i++) {
+                unsigned long flags;
+
+                if(!dgap_Board[i])
+                        continue;
+
+                DPR_INIT(("dgap_scan(%d) - printing out the msgbuf\n", i));
+                DGAP_LOCK(dgap_global_lock, flags);
+                dgap_Board[i]->msgbuf = NULL;
+                printk(dgap_Board[i]->msgbuf_head);
+                DGAP_VFREE(dgap_Board[i]->msgbuf_head);
+                dgap_Board[i]->msgbuf_head = NULL;
+                DGAP_UNLOCK(dgap_global_lock, flags);
+                if (rc) {
+                        dgap_cleanup_board(dgap_Board[i]);
+                        APR(("dgap_finalize_board_init failed - %d\n", rc));
+                }
+        }
+
+	if (!dgap_NumBoards) {
+                DPR_INIT(("dgap_scan() - returning ENODEV\n"));
+		return(-ENODEV);
+	} else {
+                DPR_INIT(("dgap_scan() - done\n"));
+		return(0);
+        }
+}
+
+
+/*
+ * dgap_found_board()
+ *
+ * A board has been found, init it.
+ */
+static int dgap_found_board(struct pci_dev *pdev, int id)
+{
+        struct board_t *brd;
+	DGAP_PCI_IRQ_TYPE pci_irq;
+	int i = 0;
+
+        /* get the board structure and prep it */
+        brd = dgap_Board[dgap_NumBoards] =
+        (struct board_t *) DGAP_VMALLOC(sizeof(struct board_t));
+        if (!brd) {
+                return(-ENOMEM);
+        }
+        memset(brd, 0, sizeof(struct board_t));
+
+        /* make a temporary message buffer for the boot messages */
+        brd->msgbuf = brd->msgbuf_head =
+                (char *) DGAP_VMALLOC(sizeof(char) * 8192);
+        if(!brd->msgbuf) {
+                DGAP_VFREE(brd);
+                return(-ENOMEM);
+        }
+        memset(brd->msgbuf_head, 0, sizeof(sizeof(char) * 8192));
+
+	/* store the info for the board we've found */
+	brd->magic = DGAP_BOARD_MAGIC;
+	brd->boardnum = dgap_NumBoards;
+	brd->firstminor = 0;
+	brd->vendor = Ids[id].vendor;
+	brd->device = Ids[id].device;
+	brd->name = Ids[id].name;
+	brd->maxports = Ids[id].maxports;
+	brd->type = Ids[id].config_type;
+	brd->dpatype = Ids[id].dpatype;
+	brd->dpastatus = BD_NOFEP;
+
+	DGAP_SPINLOCK_INIT(brd->bd_lock);
+
+	brd->state		= BOARD_FOUND;
+	brd->runwait		= 0;
+	brd->inhibit_poller	= FALSE;
+	brd->wait_for_bios	= 0;
+	brd->wait_for_fep	= 0;
+
+	for (i = 0; i < MAXPORTS; i++) {
+		brd->channels[i] = NULL;
+	}
+
+	/* store which card & revision we have */
+	pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &brd->subvendor);
+	pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &brd->subdevice);
+	pci_read_config_byte(pdev, PCI_REVISION_ID, &brd->rev);
+
+	pci_irq = pdev->irq;
+	brd->irq = pci_irq;
+
+	/* get the PCI Base Address Registers */
+
+	/* Xr Jupiter and EPC use BAR 2 */
+	if (brd->device == PCI_DEVICE_XRJ_DID || brd->device == PCI_DEVICE_EPCJ_DID) {
+		brd->membase     = pci_resource_start(pdev, 2);
+		brd->membase_end = pci_resource_end(pdev, 2);
+	}
+	/* Everyone else uses BAR 0 */
+	else {
+		brd->membase     = pci_resource_start(pdev, 0);
+		brd->membase_end = pci_resource_end(pdev, 0);
+	}
+
+	if (brd->membase & 1)
+		brd->membase &= ~3;
+	else
+		brd->membase &= ~15;
+
+	/*
+	 * On the PCI boards, there is no IO space allocated
+	 * The I/O registers will be in the first 3 bytes of the
+	 * upper 2MB of the 4MB memory space.  The board memory
+	 * will be mapped into the low 2MB of the 4MB memory space
+	 */
+	brd->port = brd->membase + PCI_IO_OFFSET;
+	brd->port_end = brd->port + PCI_IO_SIZE;
+
+
+	/*
+	 * Special initialization for non-PLX boards
+	 */
+	if (brd->device != PCI_DEVICE_XRJ_DID && brd->device != PCI_DEVICE_EPCJ_DID) {
+
+		pci_write_config_byte(pdev, 0x40, 0);
+		pci_write_config_byte(pdev, 0x46, 0);
+
+		/* Limit burst length to 2 doubleword transactions */
+		pci_write_config_byte(pdev, 0x42, 1);
+	}
+
+	/* init our poll helper tasklet */
+	tasklet_init(&brd->helper_tasklet, poll_tasklet, (unsigned long) brd);
+
+	 /* Log the information about the board */
+	dgap_mbuf(brd, DRVSTR": board %d: %s (rev %d), irq %d\n",
+		dgap_NumBoards, brd->name, brd->rev, brd->irq);
+
+	dgap_do_remap(brd);
+
+	brd->state = NEED_RESET;
+
+        return(0);
+}
+
+
+int dgap_finalize_board_init(struct board_t *brd) {
+
+        int rc;
+
+        DPR_INIT(("dgap_finalize_board_init() - start\n"));
+
+	if (!brd || brd->magic != DGAP_BOARD_MAGIC)
+                return(-ENODEV);
+
+        DPR_INIT(("dgap_finalize_board_init() - start #2\n"));
+
+	brd->use_interrupts = dgap_config_get_useintr(brd);
+
+	/*
+	 * Set up our interrupt handler if we are set to do interrupts.
+	 */
+	if (brd->use_interrupts && brd->irq) {
+
+		rc = request_irq(brd->irq, dgap_intr, SA_SHIRQ, "DGAP", brd);
+
+		if (rc) {
+			dgap_mbuf(brd, DRVSTR": Failed to hook IRQ %d. Board will work in poll mode.\n",
+                                  brd->irq);
+			brd->intr_used = 0;
+		}
+		else
+			brd->intr_used = 1;
+	} else {
+		brd->intr_used = 0;
+	}
+
+	return(0);
+}
+
+
+/*
+ * dgap_intr()
+ *
+ * Driver interrupt handler.
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+static irqreturn_t dgap_intr(int irq, void *voidbrd, struct pt_regs *regs)
+#else
+static void dgap_intr(int irq, void *voidbrd, struct pt_regs *regs)
+#endif
+{
+	struct board_t *brd = (struct board_t *) voidbrd;
+
+	if (!brd) {
+		APR(("Received interrupt (%d) with null board associated\n", irq));
+		DGAP_IRQ_RETURN(IRQ_NONE);
+	}
+
+	/*
+	 * Check to make sure its for us.
+	 */
+	if (brd->magic != DGAP_BOARD_MAGIC) {
+		APR(("Received interrupt (%d) with a board pointer that wasn't ours!\n", irq));
+		DGAP_IRQ_RETURN(IRQ_NONE);
+	}
+
+	brd->intr_count++;
+
+	/*
+	 * Schedule tasklet to run at a better time.
+	 */
+	tasklet_schedule(&brd->helper_tasklet);
+
+	DGAP_IRQ_RETURN(IRQ_HANDLED);
+}
+
+
+/************************************************************************
+ *
+ * Utility functions
+ *
+ ************************************************************************/
+
+
+/*
+ * dgap_driver_kzmalloc()
+ *
+ * Malloc and clear memory,
+ */
+void *dgap_driver_kzmalloc(size_t size, int priority)
+{
+ 	void *p = kmalloc(size, priority);
+	if(p)
+		memset(p, 0, size);
+	return(p);
+}
+
+
+/*
+ * dgap_mbuf()
+ *
+ * Used to print to the message buffer during board init.
+ */
+void dgap_mbuf(struct board_t *brd, const char *fmt, ...) {
+	va_list		ap;
+	char		buf[1024];
+	int		i;
+	unsigned long	flags;
+
+	DGAP_LOCK(dgap_global_lock, flags);
+
+	/* Format buf using fmt and arguments contained in ap. */
+	va_start(ap, fmt);
+	i = vsprintf(buf, fmt,  ap);
+	va_end(ap);
+
+	DPR((buf));
+
+	if (!brd || !brd->msgbuf) {
+		printk(buf);
+		DGAP_UNLOCK(dgap_global_lock, flags);
+		return;
+	}
+
+	memcpy(brd->msgbuf, buf, strlen(buf));
+	brd->msgbuf += strlen(buf);
+	*brd->msgbuf = (char) NULL;
+
+	DGAP_UNLOCK(dgap_global_lock, flags);
+}
+
+
+
+/*
+ * dgap_sleep()
+ *
+ * Put the driver to sleep for "ticks" clock ticks
+ *
+ * Returns 0 if timed out, !0 (showing signal) if interrupted by a signal.
+ */
+int dgap_sleep(int ticks)
+{
+	current->state = TASK_INTERRUPTIBLE;
+	if (HZ == 1000)
+		ticks *= 10;
+	schedule_timeout(ticks);
+	return (signal_pending(current));
+}
+
+
+/*
+ * dgap_ms_sleep()
+ *
+ * Put the driver to sleep for x ms's
+ *
+ * Returns 0 if timed out, !0 (showing signal) if interrupted by a signal.
+ */
+int dgap_ms_sleep(u32 ms)
+{
+	current->state = TASK_INTERRUPTIBLE;
+	schedule_timeout((ms * HZ) / 1000);
+	return (signal_pending(current));
+}
diff -puN /dev/null drivers/char/digi/dgap/dgap_driver.h
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/drivers/char/digi/dgap/dgap_driver.h	Wed Sep 17 12:34:23 2003
@@ -0,0 +1,828 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ *      Scott H Kilau <Scott_Kilau at digi dot com>
+ *
+ * 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, EXPRESS OR IMPLIED; 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.
+ *
+ *	NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!!
+ *
+ *************************************************************************
+ *
+ * Driver includes
+ *
+ *************************************************************************/
+
+#ifndef __DGAP_DRIVER_H
+#define __DGAP_DRIVER_H
+
+#define TRC_TO_CONSOLE 1
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/ioport.h>
+#include <linux/version.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <asm/irq.h>
+
+#include <linux/pci.h>
+
+#include <linux/major.h>
+#include <linux/signal.h>
+
+#include <linux/mm.h>
+
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/stddef.h>
+#include <asm/atomic.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/system.h>
+
+#include <linux/tty.h>
+#include <linux/termios.h>
+#include <linux/poll.h>
+#include <linux/types.h>
+
+#include <linux/serial.h>	/* for struct async_serial */
+
+#include "dgap_types.h"		/* Additional types needed by header files */
+#include "digi.h"
+
+#include "dgap_pci.h"
+#include "dgap_fep5.h"
+#include "dgap_conf.h"
+
+/*************************************************************************
+ *
+ * Driver defines
+ *
+ *************************************************************************/
+
+/*
+ * Driver identification, error and debugging statments
+ *
+ * In theory, you can change all occurances of "digi" in the next
+ * three lines, and the driver printk's will all automagically change.
+ *
+ * APR((fmt, args, ...));	Always prints message
+ * DPR((fmt, args, ...));	Only prints if DGAP_TRACER is defined at
+ *				  compile time and dgap_debug!=0
+ */
+#define	PROCSTR		"dgap"			/* /proc entries	 */
+#define	DEVSTR		"/dev/dg/dgap"		/* /dev entries		 */
+#define	DRVSTR		"dgap"			/* Driver name string
+						 * displayed by APR	 */
+#define	APR(args)	do { PRINTF_TO_KMEM(args); printk(DRVSTR": "); printk args; \
+			   } while (0)
+#define	RAPR(args)	do { PRINTF_TO_KMEM(args); printk args; } while (0)
+
+/*
+ * Debugging levels can be set using debug insmod variable
+ * They can also be compiled out completely.
+ */
+
+#define	DBG_INIT		(dgap_debug & 0x01)
+#define	DBG_BASIC		(dgap_debug & 0x02)
+#define	DBG_CORE		(dgap_debug & 0x04)
+
+#define	DBG_OPEN		(dgap_debug & 0x08)
+#define	DBG_CLOSE		(dgap_debug & 0x10)
+#define	DBG_READ		(dgap_debug & 0x20)
+#define	DBG_WRITE		(dgap_debug & 0x40)
+
+#define	DBG_IOCTL		(dgap_debug & 0x80)
+
+#define	DBG_PROC		(dgap_debug & 0x100)
+#define	DBG_PARAM		(dgap_debug & 0x200)
+#define	DBG_PSCAN		(dgap_debug & 0x400)
+#define	DBG_EVENT		(dgap_debug & 0x800)
+
+#define	DBG_DRAIN		(dgap_debug & 0x1000)
+#define	DBG_CARR		(dgap_debug & 0x2000)
+
+#define	DBG_MGMT		(dgap_debug & 0x4000)
+
+
+#if defined(DGAP_TRACER)
+
+# if defined(TRC_TO_KMEM)
+/* Choose one: */
+#  define TRC_ON_OVERFLOW_WRAP_AROUND
+#  undef  TRC_ON_OVERFLOW_SHIFT_BUFFER
+# endif //TRC_TO_KMEM
+
+# define TRC_MAXMSG		1024
+# define TRC_OVERFLOW		"(OVERFLOW)"
+# define TRC_DTRC		"/usr/bin/dtrc"
+
+#if defined TRC_TO_CONSOLE
+#define PRINTF_TO_CONSOLE(args) { printk(DRVSTR": "); printk args; }
+#else //!defined TRACE_TO_CONSOLE
+#define PRINTF_TO_CONSOLE(args)
+#endif
+
+#if defined TRC_TO_KMEM
+#define PRINTF_TO_KMEM(args) dgap_tracef args
+#else //!defined TRC_TO_KMEM
+#define PRINTF_TO_KMEM(args)
+#endif
+
+#define	TRC(args)	{ PRINTF_TO_KMEM(args); PRINTF_TO_CONSOLE(args) }
+
+# define DPR_INIT(ARGS)		if (DBG_INIT) TRC(ARGS)
+# define DPR_BASIC(ARGS)	if (DBG_BASIC) TRC(ARGS)
+# define DPR_CORE(ARGS)		if (DBG_CORE) TRC(ARGS)
+# define DPR_OPEN(ARGS)		if (DBG_OPEN)  TRC(ARGS)
+# define DPR_CLOSE(ARGS)	if (DBG_CLOSE)  TRC(ARGS)
+# define DPR_READ(ARGS)		if (DBG_READ)  TRC(ARGS)
+# define DPR_WRITE(ARGS)	if (DBG_WRITE) TRC(ARGS)
+# define DPR_IOCTL(ARGS)	if (DBG_IOCTL) TRC(ARGS)
+# define DPR_PROC(ARGS)		if (DBG_PROC)  TRC(ARGS)
+# define DPR_PARAM(ARGS)	if (DBG_PARAM)  TRC(ARGS)
+# define DPR_PSCAN(ARGS)	if (DBG_PSCAN)  TRC(ARGS)
+# define DPR_EVENT(ARGS)	if (DBG_EVENT)  TRC(ARGS)
+# define DPR_DRAIN(ARGS)	if (DBG_DRAIN)  TRC(ARGS)
+# define DPR_CARR(ARGS)		if (DBG_CARR)  TRC(ARGS)
+# define DPR_MGMT(ARGS)		if (DBG_MGMT)  TRC(ARGS)
+
+# define DPR(ARGS)		if (dgap_debug) TRC(ARGS)
+# define P(X)			dgap_tracef(#X "=%p\n", X)
+# define X(X)			dgap_tracef(#X "=%x\n", X)
+
+#else//!defined DGAP_TRACER
+
+#define PRINTF_TO_KMEM(args)
+# define TRC(ARGS)
+# define DPR_INIT(ARGS)
+# define DPR_BASIC(ARGS)
+# define DPR_CORE(ARGS)
+# define DPR_OPEN(ARGS)
+# define DPR_CLOSE(ARGS)
+# define DPR_READ(ARGS)
+# define DPR_WRITE(ARGS)
+# define DPR_IOCTL(ARGS)
+# define DPR_PROC(ARGS)
+# define DPR_PARAM(ARGS)
+# define DPR_PSCAN(ARGS)
+# define DPR_EVENT(ARGS)
+# define DPR_DRAIN(ARGS)
+# define DPR_CARR(ARGS)
+# define DPR_MGMT(ARGS)
+
+# define DPR(args)
+
+#endif//DGAP_TRACER
+
+/* Number of boards we support at once. */
+#define	MAXBOARDS	32
+#define	MAXPORTS	224
+#define MAXTTYNAMELEN	200
+
+/* Our 3 magic numbers for our board, channel and unit structs */
+#define DGAP_BOARD_MAGIC	0x5c6df104
+#define DGAP_CHANNEL_MAGIC	0x6c6df104
+#define DGAP_UNIT_MAGIC		0x7c6df104
+
+/* Serial port types */
+#define DGAP_SERIAL		0
+#define DGAP_PRINT		1
+
+#define	SERIAL_TYPE_NORMAL	1
+
+/* 4 extra for alignment play space */
+#define WRITEBUFLEN		((4096) + 4)
+#define MYFLIPLEN		N_TTY_BUF_SIZE
+
+#define SBREAK_TIME 0x25
+#define USHRT_MAX   65535
+#define U2BSIZE 0x400
+#define dgap_jiffies_from_ms(a) (((a) * HZ) / 1000)
+
+/*
+ * Our major for the mgmt devices.
+ *
+ * We can use 22, because Digi was allocated 22 and 23 for the epca driver.
+ * 22 has now become obsolete now that the "cu" devices have
+ * been removed from 2.6.
+ * Also, this *IS* the epca driver, just PCI only now.
+ */
+#ifndef DIGI_DGAP_MAJOR
+# define DIGI_DGAP_MAJOR         22
+#endif
+
+/* The default location of the config script. */
+#define DGAP_CONFIG_PROGRAM	"/usr/sbin/dgap_config"
+#define DGAP_CONFIG_FILE	"/etc/dgap.conf"
+
+/*
+ * The parameters we use to define the periods of the moving averages.
+ */
+#define		MA_PERIOD	(HZ / 10)
+#define		SMA_DUR		(1 * HZ)
+#define		EMA_DUR		(1 * HZ)
+#define		SMA_NPERIODS	(SMA_DUR / MA_PERIOD)
+#define		EMA_NPERIODS	(EMA_DUR / MA_PERIOD)
+
+/*
+ * Define a local default termios struct. All ports will be created
+ * with this termios initially.  This is the same structure that is defined
+ * as the default in tty_io.c with the same settings overriden as in serial.c
+ *
+ * In short, this should match the internal serial ports' defaults.
+ */
+#define	DEFAULT_IFLAGS	(ICRNL | IXON)
+#define	DEFAULT_OFLAGS	(OPOST | ONLCR)
+#define	DEFAULT_CFLAGS	(B9600 | CS8 | CREAD | HUPCL | CLOCAL)
+#define	DEFAULT_LFLAGS	(ISIG | ICANON | ECHO | ECHOE | ECHOK | \
+			ECHOCTL | ECHOKE | IEXTEN)
+
+
+/*************************************************************************
+ *
+ * Utility defines - should be defined elsewhere, but be paranoid
+ *
+ *************************************************************************/
+
+#if !defined(KERNEL_VERSION) /* defined in version.h in later kernels */
+# define KERNEL_VERSION(a,b,c)	(((a) << 16) + ((b) << 8) + (c))
+#endif
+
+/* The number of elements in an array */
+#if !defined(asizeof)
+# define asizeof(X)	(sizeof(X) / sizeof(X[0]))
+#endif
+
+/*
+ * Only define these in case they are not present in the kernel headers.
+ * A grep of /usr/src/linux-<version>/include/linux
+ * show others worry about this as well.
+ */
+#if !defined(MIN)
+# define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+#if !defined(MAX)
+# define MAX(a,b)	((a) > (b) ? (a) : (b))
+#endif
+
+
+/*
+ * All the possible states the driver can be while being loaded.
+ */
+enum {
+	DRIVER_INITIALIZED = 0,
+	DRIVER_NEED_CONFIG_LOAD,
+	DRIVER_REQUESTED_CONFIG,
+	DRIVER_READY
+};
+
+/*
+ * All the possible states the board can be while booting up.
+ */
+enum {
+	BOARD_FAILED = 0,
+	CONFIG_NOT_FOUND,
+	BOARD_FOUND,
+	NEED_RESET,
+	FINISHED_RESET,
+	NEED_CONFIG,
+	FINISHED_CONFIG,
+	NEED_DEVICE_CREATION,
+	REQUESTED_DEVICE_CREATION,
+	FINISHED_DEVICE_CREATION,
+	NEED_BIOS_LOAD,
+	REQUESTED_BIOS,
+	WAIT_BIOS_LOAD,
+	FINISHED_BIOS_LOAD,
+	NEED_FEP_LOAD,
+	REQUESTED_FEP,
+	WAIT_FEP_LOAD,
+	FINISHED_FEP_LOAD,
+	BOARD_READY
+};
+
+/*
+ * All the possible states that a requested concentrator image can be in.
+ */
+enum {
+	NO_PENDING_CONCENTRATOR_REQUESTS = 0,
+	NEED_CONCENTRATOR,
+	REQUESTED_CONCENTRATOR
+};
+
+extern char *dgap_state_text[];
+extern char *dgap_driver_state_text[];
+
+
+/*************************************************************************
+ *
+ * Some API's changed at linux version 2.1.x.  I'm not sure exactly
+ * when they changed during the 2.1.x development, but it doesn't
+ * matter to us.  Create some macros to preserve compatibility.
+ *
+ * Here you can see what has changed between 2.0 and 2.1+. If you
+ * are reading/writing this file, you should be well aware of these
+ * macros or you won't be able to follow whats going on.  The
+ * alternative was #ifdef spaghetti.
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+# include <asm/irq.h>
+# include <asm/bitops.h>
+# include <asm/io.h>
+# include <asm/uaccess.h>
+# include <linux/gfp.h>
+
+# define DGAP_get_free_page(x)		(get_zeroed_page(GFP_ATOMIC))
+# define DGAP_VMALLOC(x)		(kmalloc((x), GFP_ATOMIC))
+# define DGAP_VFREE(x)			(kfree(x))
+
+# define DGAP_MAJOR(x)			(imajor(x))
+# define DGAP_MINOR(x)			(iminor(x))
+# define DGAP_TTY_MAJOR(x)		(MAJOR(x))
+# define DGAP_TTY_MINOR(x)		(MINOR(x))
+
+# define DGAP_NUM_CPUS			(num_online_cpus())
+
+# define DGAP_MOD_INC_USE_COUNT(rtn)	(rtn = 1)
+# define DGAP_MOD_DEC_USE_COUNT
+
+# define DGAP_IRQ_RETURN(x)		return x;
+
+
+# define DGAP_SPINLOCK_INIT(x)		spin_lock_init(&(x))
+
+# define DGAP_IOREMAP(ADDR, LEN)	ioremap(ADDR, LEN)
+# define DGAP_IOUNMAP(ADDR)		iounmap(ADDR)
+# define COPY_FROM_USER(DST,SRC,LEN)	copy_from_user(DST,SRC,LEN)
+# define COPY_TO_USER(DST,SRC,LEN)	copy_to_user(DST,SRC,LEN)
+# define GET_USER(A1,A2)		get_user(A1,(unsigned int *)A2)
+# define PUT_USER(A1,A2)		put_user(A1,(unsigned long *)A2)
+
+# define READ_PROTO	struct file *file, char *buf, size_t count,loff_t *ppos
+# define READ_ARGS	file, buf, count, ppos
+# define READ_RETURN_T	ssize_t
+
+# define WRITE_PROTO	struct file *file, const char *buf, size_t count, \
+			loff_t *ppos
+# define WRITE_ARGS	file, buf, count, ppos
+# define WRITE_RETURN_T	ssize_t
+
+# define CLOSE_RETURN_T		int
+# define CLOSE_RETURN(X)	return(X)
+
+# define PARM_I_RDEV		file->f_dentry->d_inode->i_rdev
+# define GET_POS()		*ppos
+# define ADD_POS(AMT)		*ppos += (AMT)
+# define SET_POS(AMT)		*ppos = (AMT)
+
+# define DGAP_PCI_IRQ_TYPE	uint
+# define PCI_PRESENT()		pci_present()
+
+# define PARM_STR(VAR, INIT, DESC) \
+		static char *VAR = INIT; \
+		char *dgap_##VAR; \
+		MODULE_PARM(VAR, "s"); \
+		MODULE_PARM_DESC(VAR, DESC);
+
+# define PARM_INT(VAR, INIT, DESC) \
+		static int VAR = INIT; \
+		int dgap_##VAR; \
+		MODULE_PARM(VAR, "i"); \
+		MODULE_PARM_DESC(VAR, DESC);
+
+# define PARM_ULONG(VAR, INIT, DESC) \
+		static ulong VAR = INIT; \
+		ulong dgap_##VAR; \
+		MODULE_PARM(VAR, "l"); \
+		MODULE_PARM_DESC(VAR, DESC);
+
+# define DGAP_LOCK(x,y)		spin_lock_irqsave(&(x), y)
+# define DGAP_UNLOCK(x,y)	spin_unlock_irqrestore(&(x), y)
+//# define DGAP_LOCK(x,y)		spin_lock(&(x))
+//# define DGAP_UNLOCK(x,y)	spin_unlock(&(x))
+# define DGAP_TRYLOCK(x,y)	spin_trylock(&(x))
+
+# define DGAP_PROCID()		smp_processor_id()
+
+/*
+ *  In some revisions within the 2.4 kernel series, there is a patch
+ *  which changes the behavior of a failed open.  In 2.0 and 2.2 and
+ *  most 2.4 revisions, a failed open will be followed _always_ by
+ *  a call to the driver's close routine.  In some 2.4 revisions,
+ *  the close will not be called (and therefore driver use counts
+ *  will be too high, and ports may become unusable).
+ *
+ *  In the case that the ALT_FAIL_OPEN code is allowed to exist,
+ *  some new parameters are required, like ALT_FAIL_OPEN_DEFAULT.
+ */
+# define ALLOW_ALT_FAIL_OPEN 0
+# define ALT_FAIL_OPEN_DEFAULT 0
+
+
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+
+# include <asm/uaccess.h>
+
+# define DGAP_get_free_page(x)		(get_free_page(GFP_KERNEL))
+# define DGAP_VMALLOC(x)		(vmalloc(x))
+# define DGAP_VFREE(x)			(vfree(x))
+
+# define DGAP_MAJOR(x)			(MAJOR((x)->i_rdev))
+# define DGAP_MINOR(x)			(MINOR((x)->i_rdev))
+# define DGAP_TTY_MAJOR(x)		(MAJOR(x))
+# define DGAP_TTY_MINOR(x)		(MINOR(x))
+
+# define DGAP_NUM_CPUS			(smp_num_cpus)
+
+# define DGAP_MOD_INC_USE_COUNT(rtn) \
+	{ \
+		MOD_INC_USE_COUNT; \
+		rtn = 1; \
+	}
+
+# define DGAP_MOD_DEC_USE_COUNT MOD_DEC_USE_COUNT
+
+# define DGAP_IRQ_RETURN(x)	return;
+
+# define DGAP_SPINLOCK_INIT(x)		spin_lock_init(&(x))
+
+# define DGAP_IOREMAP(ADDR, LEN)	ioremap(ADDR, LEN)
+# define DGAP_IOUNMAP(ADDR)		iounmap(ADDR)
+# define COPY_FROM_USER(DST,SRC,LEN)	copy_from_user(DST,SRC,LEN)
+# define COPY_TO_USER(DST,SRC,LEN)	copy_to_user(DST,SRC,LEN)
+# define GET_USER(A1,A2)		get_user(A1,(unsigned int *)A2)
+# define PUT_USER(A1,A2)		put_user(A1,(unsigned long *)A2)
+
+# define READ_PROTO	struct file *file, char *buf, size_t count,loff_t *ppos
+# define READ_ARGS	file, buf, count, ppos
+# define READ_RETURN_T	ssize_t
+
+# define WRITE_PROTO	struct file *file, const char *buf, size_t count, \
+			loff_t *ppos
+# define WRITE_ARGS	file, buf, count, ppos
+# define WRITE_RETURN_T	ssize_t
+
+# define CLOSE_RETURN_T		int
+# define CLOSE_RETURN(X)	return(X)
+
+# define PARM_I_RDEV		file->f_dentry->d_inode->i_rdev
+# define GET_POS()		*ppos
+# define ADD_POS(AMT)		*ppos += (AMT)
+# define SET_POS(AMT)		*ppos = (AMT)
+
+# define DGAP_PCI_IRQ_TYPE	uint
+# define PCI_PRESENT()		pci_present()
+
+# define PARM_STR(VAR, INIT, DESC) \
+		static char *VAR = INIT; \
+		char *dgap_##VAR; \
+		MODULE_PARM(VAR, "s"); \
+		MODULE_PARM_DESC(VAR, DESC);
+
+# define PARM_INT(VAR, INIT, DESC) \
+		static int VAR = INIT; \
+		int dgap_##VAR; \
+		MODULE_PARM(VAR, "i"); \
+		MODULE_PARM_DESC(VAR, DESC);
+
+# define PARM_ULONG(VAR, INIT, DESC) \
+		static ulong VAR = INIT; \
+		ulong dgap_##VAR; \
+		MODULE_PARM(VAR, "l"); \
+		MODULE_PARM_DESC(VAR, DESC);
+
+# define DGAP_LOCK(x,y)		spin_lock_irqsave(&(x), y);
+# define DGAP_UNLOCK(x,y)	spin_unlock_irqrestore(&(x), y);
+
+# define DGAP_PROCID()		smp_processor_id()
+
+/*
+ *  In some revisions within the 2.4 kernel series, there is a patch
+ *  which changes the behavior of a failed open.  In 2.0 and 2.2 and
+ *  most 2.4 revisions, a failed open will be followed _always_ by
+ *  a call to the driver's close routine.  In some 2.4 revisions,
+ *  the close will not be called (and therefore driver use counts
+ *  will be too high, and ports may become unusable).
+ *
+ *  In the case that the ALT_FAIL_OPEN code is allowed to exist,
+ *  some new parameters are required, like ALT_FAIL_OPEN_DEFAULT.
+ */
+# define ALLOW_ALT_FAIL_OPEN 1
+# define ALT_FAIL_OPEN_DEFAULT 0
+
+#else
+
+# error "this driver does not support anything below the 2.4 kernel series."
+
+#endif
+
+
+/*
+ * Modem line constants are defined as macros because DSR and
+ * DCD are swapable using the ditty altpin option.
+ */
+#define D_CD(ch)        ch->ch_cd       /* Carrier detect       */
+#define D_DSR(ch)       ch->ch_dsr      /* Data set ready       */
+#define D_RTS(ch)       DM_RTS          /* Request to send      */
+#define D_CTS(ch)       DM_CTS          /* Clear to send        */
+#define D_RI(ch)        DM_RI           /* Ring indicator       */
+#define D_DTR(ch)       DM_DTR          /* Data terminal ready  */
+
+
+/*************************************************************************
+ *
+ * Structures and closely related defines.
+ *
+ *************************************************************************/
+
+
+/*
+ * A structure to hold a statistics counter.  We also
+ * compute moving averages for this counter.
+ */
+struct macounter
+{
+	u32		cnt;	/* Total count */
+	ulong		accum;	/* Acuumulator per period */
+	ulong		sma;	/* Simple moving average */
+	ulong		ema;	/* Exponential moving average */
+};
+
+
+/*
+ *	Per-board information
+ */
+struct board_t
+{
+	int		magic;		/* Board Magic number.  */
+	int		boardnum;	/* Board number: 0-3 */
+	int		firstminor;	/* First minor, e.g. 0, 30, 60 */
+
+	int		type;		/* Type of board */
+	char		*name;		/* Product Name */
+	u16		vendor;		/* PCI vendor ID */
+	u16		device;		/* PCI device ID */
+	u16		subvendor;	/* PCI subsystem vendor ID */
+	u16		subdevice;	/* PCI subsystem device ID */
+	uchar		rev;		/* PCI revision ID */
+	u16		maxports;	/* MAX ports this board can handle */
+
+	spinlock_t	bd_lock;	/* Used to protect board */
+
+	u32		state;		/* State of card. */
+
+	struct		tasklet_struct helper_tasklet; /* Poll helper tasklet */
+
+	u32		wait_for_bios;
+	u32		wait_for_fep;
+
+	struct cnode *  bd_config;	/* Config of board */
+
+	u16		nasync;		/* Number of ports on card */
+
+	u32		use_interrupts;	/* Should we be interrupt driven? */
+	u32		irq;		/* Interrupt request number */
+	u32		intr_count;	/* Count of interrupts */
+	u32		intr_used;	/* Non-zero if using interrupts */
+	u32		intr_running;	/* Non-zero if FEP knows its doing interrupts */
+
+	ulong		port;		/* Start of base io port of the card */
+	ulong		port_end;	/* End of base io port of the card */
+	ulong		membase;	/* Start of base memory of the card */
+	ulong		membase_end;	/* End of base memory of the card */
+
+	uchar 		*re_map_port;	/* Remapped io port of the card */
+	uchar		*re_map_membase;/* Remapped memory of the card */
+
+	uchar		runwait;	/* # Processes waiting for FEP  */
+	uchar		inhibit_poller; /* Tells  the poller to leave us alone */
+
+	struct channel_t *channels[MAXPORTS]; /* array of pointers to our channels. */
+
+	struct tty_driver	SerialDriver;
+	char		SerialName[200];
+	struct tty_driver	PrintDriver;
+	char		PrintName[200];
+
+	u32		dgap_Major_Serial_Registered;
+	u32		dgap_Major_TransparentPrint_Registered;
+
+	u32		dgap_Serial_Major;
+	u32		dgap_TransparentPrint_Major;
+
+	u32		TtyRefCnt;
+
+	struct bs_t	*bd_bs;			/* Base structure pointer       */
+
+	char	*flipbuf;		/* Our flip buffer, alloced if board is found */
+
+	u16		dpatype;	/* The board "type", as defined by DPA */
+	u16		dpastatus;	/* The board "status", as defined by DPA */
+	wait_queue_head_t kme_wait;	/* Needed for DPA support */
+
+	u32		conc_dl_status;	/* Status of any pending conc download */
+	/*
+	 *	Mgmt data.
+	 */
+        char		*msgbuf_head;
+        char		*msgbuf;
+
+};
+
+
+
+/************************************************************************
+ * Unit flag definitions for un_flags.
+ ************************************************************************/
+#define UN_ISOPEN	0x0001		/* Device is open		*/
+#define UN_CLOSING	0x0002		/* Line is being closed		*/
+#define UN_IMM		0x0004		/* Service immediately		*/
+#define UN_BUSY		0x0008		/* Some work this channel	*/
+#define UN_BREAKI	0x0010		/* Input break received		*/
+#define UN_PWAIT	0x0020		/* Printer waiting for terminal	*/
+#define UN_TIME		0x0040		/* Waiting on time		*/
+#define UN_EMPTY	0x0080		/* Waiting output queue empty	*/
+#define UN_LOW		0x0100		/* Waiting output low water mark*/
+#define UN_EXCL_OPEN	0x0200		/* Open for exclusive use	*/
+#define UN_WOPEN	0x0400		/* Device waiting for open	*/
+#define UN_WIOCTL	0x0800		/* Device waiting for open	*/
+#define UN_HANGUP	0x8000		/* Carrier lost			*/
+
+
+/************************************************************************
+ * Structure for terminal or printer unit.
+ ************************************************************************/
+struct un_t {
+	int	magic;		/* Unit Magic Number.			*/
+	struct	channel_t *un_ch;
+	u32	un_time;
+	u32	un_type;
+	u32	un_open_count;	/* Counter of opens to port		*/
+	struct tty_struct *un_tty;/* Pointer to unit tty structure	*/
+	u32	un_flags;	/* Unit flags				*/
+	wait_queue_head_t un_flags_wait; /* Place to sleep to wait on unit */
+	u32	un_dev;		/* Minor device number			*/
+	tcflag_t un_oflag;	/* oflags being done on board		*/
+	tcflag_t un_lflag;	/* lflags being done on board		*/
+};
+
+
+/************************************************************************
+ * Device flag definitions for ch_flags.
+ ************************************************************************/
+#define CH_PRON         0x0001          /* Printer on string                */
+#define CH_OUT          0x0002          /* Dial-out device open             */
+#define CH_STOP         0x0004          /* Output is stopped                */
+#define CH_STOPI        0x0008          /* Input is stopped                 */
+#define CH_CD           0x0010          /* Carrier is present               */
+#define CH_FCAR         0x0020          /* Carrier forced on                */
+
+#define CH_RXBLOCK      0x0080          /* Enable rx blocked flag           */
+#define CH_WLOW         0x0100          /* Term waiting low event           */
+#define CH_WEMPTY       0x0200          /* Term waiting empty event         */
+#define CH_RENABLE      0x0400          /* Buffer just emptied          */
+#define CH_RACTIVE      0x0800          /* Process active in xxread()   */
+#define CH_RWAIT        0x1000          /* Process waiting in xxread()  */
+#define CH_HANGUP       0x8000		/* Hangup received                  */
+
+
+/************************************************************************
+ * Channel information structure.
+ ************************************************************************/
+struct channel_t {
+	int magic;			/* Channel Magic Number		*/
+	struct bs_t	*ch_bs;		/* Base structure pointer       */
+	struct cm_t	*ch_cm;		/* Command queue pointer        */
+	struct board_t *ch_bd;		/* Board structure pointer      */
+	unsigned char *ch_vaddr;	/* FEP memory origin            */
+	unsigned char *ch_taddr;	/* Write buffer origin          */
+	unsigned char *ch_raddr;	/* Read buffer origin           */
+	struct digi_t  ch_digi;		/* Transparent Print structure  */
+	struct un_t ch_tun;		/* Terminal unit info           */
+	struct un_t ch_pun;		/* Printer unit info            */
+
+	spinlock_t	ch_lock;	/* provide for serialization */
+	wait_queue_head_t ch_flags_wait;
+
+	u32	pscan_state;
+	uchar	pscan_savechar;
+
+	u32 ch_portnum;			/* Port number, 0 offset.	*/
+	u32 ch_open_count;		/* open count			*/
+	u32	ch_flags;		/* Channel flags                */
+
+
+	u32	ch_close_delay;		/* How long we should drop RTS/DTR for */
+
+	u32	ch_cpstime;		/* Time for CPS calculations    */
+
+	tcflag_t ch_c_iflag;		/* channel iflags               */
+	tcflag_t ch_c_cflag;		/* channel cflags               */
+	tcflag_t ch_c_oflag;		/* channel oflags               */
+	tcflag_t ch_c_lflag;		/* channel lflags               */
+
+	u16  ch_fepiflag;            /* FEP tty iflags               */
+	u16  ch_fepcflag;		/* FEP tty cflags               */
+	u16  ch_fepoflag;		/* FEP tty oflags               */
+	u16  ch_wopen;			/* Waiting for open process cnt */
+	u16  ch_tstart;			/* Transmit buffer start        */
+	u16  ch_tsize;			/* Transmit buffer size         */
+	u16  ch_rstart;			/* Receive buffer start         */
+	u16  ch_rsize;			/* Receive buffer size          */
+	u16  ch_rdelay;			/* Receive delay time           */
+
+	u16	ch_tlw;			/* Our currently set low water mark */
+
+	u16  ch_cook;			/* Output character mask        */
+
+	uchar   ch_card;		/* Card channel is on           */
+	uchar   ch_stopc;		/* Stop character               */
+	uchar   ch_startc;		/* Start character              */
+
+	uchar   ch_mostat;		/* FEP output modem status      */
+	uchar   ch_mistat;		/* FEP input modem status       */
+	uchar   ch_mforce;		/* Modem values to be forced    */
+	uchar   ch_mval;		/* Force values                 */
+	uchar   ch_fepstopc;		/* FEP stop character           */
+	uchar   ch_fepstartc;		/* FEP start character          */
+
+	uchar   ch_astopc;		/* Auxiliary Stop character     */
+	uchar   ch_astartc;		/* Auxiliary Start character    */
+	uchar   ch_fepastopc;		/* Auxiliary FEP stop char      */
+	uchar   ch_fepastartc;		/* Auxiliary FEP start char     */
+
+	uchar   ch_hflow;		/* FEP hardware handshake       */
+	uchar   ch_dsr;			/* stores real dsr value        */
+	uchar   ch_cd;			/* stores real cd value         */
+	uchar   ch_tx_win;		/* channel tx buffer window     */
+	uchar   ch_rx_win;		/* channel rx buffer window     */
+	u32  ch_info;			/* Additional channel specific information */
+};
+
+
+/*************************************************************************
+ *
+ * Prototypes for non-static functions used in more than one module
+ *
+ *************************************************************************/
+
+int			dgap_lock_poller(struct board_t *brd);
+void			dgap_unlock_poller(struct board_t *brd);
+void			dgap_mbuf(struct board_t *brd, const char *fmt, ...);
+int			dgap_sleep(int ticks);
+int			dgap_ms_sleep(u32 ms);
+
+int			dgap_tty_brdinit(struct board_t *brd);
+void			dgap_tty_brduninit(struct board_t *brd);
+
+void			*dgap_driver_kzmalloc(size_t size, int priority);
+
+char			*get_event_text(u32);
+char			*get_msignal_text(u32);
+
+char			*dgap_ioctl_name(int cmd);
+
+#if defined(DGAP_TRACER)
+void			dgap_tracef(const char *fmt, ...);
+void			dgap_tracer_free(void);
+#endif
+
+void			dgap_proc_unregister_all(void);
+void			dgap_proc_register_basic(void);
+int                     dgap_finalize_board_init(struct board_t *brd);
+
+void			dgap_do_bios_load(struct board_t *brd, uchar *ubios, int len);
+void			dgap_do_fep_load(struct board_t *brd, uchar *ufep, int len);
+void			dgap_do_conc_load(struct board_t *brd, uchar *uaddr, int len);
+void			dgap_do_config_load(uchar *uaddr, int len);
+
+int			dgap_after_config_loaded(void);
+
+extern spinlock_t		dgap_dl_lock;
+extern wait_queue_head_t	dgap_dl_wait;
+extern int			dgap_dl_action;
+
+extern int			dgap_driver_state;
+
+#endif
diff -puN /dev/null drivers/char/digi/dgap/dgap_fep5.h
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/drivers/char/digi/dgap/dgap_fep5.h	Wed Sep 17 12:34:23 2003
@@ -0,0 +1,247 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ *	Scott H Kilau <Scott_Kilau at digi dot com>
+ *
+ * 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, EXPRESS OR IMPLIED; 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.
+ *
+ *	NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!!
+ *
+ ************************************************************************
+ ***	FEP Version 5 dependent definitions
+ ************************************************************************/
+
+#ifndef __DGAP_FEP5_H
+#define __DGAP_FEP5_H
+
+#include "dgap_types.h"
+
+/************************************************************************
+ *      FEP memory offsets
+ ************************************************************************/
+#define START           0x0004L         /* Execution start address      */
+
+#define CMDBUF          0x0d10L         /* Command (cm_t) structure offset */
+#define CMDSTART        0x0400L         /* Start of command buffer      */
+#define CMDMAX          0x0800L         /* End of command buffer        */
+
+#define EVBUF           0x0d18L         /* Event (ev_t) structure       */
+#define EVSTART         0x0800L         /* Start of event buffer        */
+#define EVMAX           0x0c00L         /* End of event buffer          */
+#define FEP5_PLUS       0x0E40          /* ASCII '5' and ASCII 'A' is here  */
+#define ECS_SEG         0x0E44          /* Segment of the extended channel structure */
+#define LINE_SPEED      0x10            /* Offset into ECS_SEG for line speed   */
+                                        /* if the fep has extended capabilities */
+
+/* BIOS MAGIC SPOTS */
+#define ERROR           0x0C14L		/* BIOS error code              */
+#define SEQUENCE	0x0C12L		/* BIOS sequence indicator      */
+#define POSTAREA	0x0C00L		/* POST complete message area   */
+
+/* FEP MAGIC SPOTS */
+#define FEPSTAT         POSTAREA        /* OS here when FEP comes up    */
+#define NCHAN           0x0C02L         /* number of ports FEP sees     */
+#define PANIC           0x0C10L         /* PANIC area for FEP           */
+#define KMEMEM          0x0C30L         /* Memory for KME use           */
+#define CONFIG          0x0CD0L         /* Concentrator configuration info */
+#define CONFIGSIZE      0x0030          /* configuration info size      */
+#define DOWNREQ         0x0D00          /* Download request buffer pointer */
+
+#define CHANBUF         0x1000L         /* Async channel (bs_t) structs */
+#define FEPOSSIZE       0x1FFF          /* 8K FEPOS                     */
+
+#define XEMPORTS    0xC02	/*
+				 * Offset in board memory where FEP5 stores
+				 * how many ports it has detected.
+				 * NOTE: FEP5 reports 64 ports when the user
+				 * has the cable in EBI OUT instead of EBI IN.
+				 */
+
+#define FEPCLR      0x00
+#define FEPMEM      0x02
+#define FEPRST      0x04
+#define FEPINT      0x08
+#define FEPMASK     0x0e
+#define FEPWIN      0x80
+
+#define LOWMEM      0x0100
+#define HIGHMEM     0x7f00
+
+#define FEPTIMEOUT 200000
+
+#define ENABLE_INTR		0x0e04		/* Enable interrupts flag */
+#define FEPPOLL_MIN		1		/* minimum of 1 millisecond */
+#define FEPPOLL_MAX		20		/* maximum of 20 milliseconds */
+#define FEPPOLL			0x0c26		/* Fep event poll interval */
+
+#define	IALTPIN			0x0080		/* Input flag to swap DSR <-> DCD */
+
+/************************************************************************
+ * Command structure definition.
+ ************************************************************************/
+struct cm_t {
+	volatile unsigned short cm_head;	/* Command buffer head offset	*/
+	volatile unsigned short cm_tail;	/* Command buffer tail offset	*/
+	volatile unsigned short cm_start;	/* start offset of buffer	*/
+	volatile unsigned short cm_max;		/* last offset of buffer	*/
+};
+
+/************************************************************************
+ * Event structure definition.
+ ************************************************************************/
+struct ev_t {
+	volatile unsigned short ev_head;	/* Command buffer head offset	*/
+	volatile unsigned short ev_tail;	/* Command buffer tail offset	*/
+	volatile unsigned short ev_start;	/* start offset of buffer	*/
+	volatile unsigned short ev_max;		/* last offset of buffer	*/
+};
+
+/************************************************************************
+ * Download buffer structure.
+ ************************************************************************/
+struct downld_t {
+	uchar	dl_type;		/* Header                       */
+	uchar	dl_seq;			/* Download sequence            */
+	ushort	dl_srev;		/* Software revision number     */
+	ushort	dl_lrev;		/* Low revision number          */
+	ushort	dl_hrev;		/* High revision number         */
+	ushort	dl_seg;			/* Start segment address        */
+	ushort	dl_size;		/* Number of bytes to download  */
+	uchar	dl_data[1024];		/* Download data                */
+};
+
+/************************************************************************
+ * Per channel buffer structure
+ ************************************************************************
+ *              Base Structure Entries Usage Meanings to Host           *
+ *                                                                      *
+ *        W = read write        R = read only                           *
+ *        C = changed by commands only                                  *
+ *        U = unknown (may be changed w/o notice)                       *
+ ************************************************************************/
+struct bs_t {
+	volatile unsigned short  tp_jmp;	/* Transmit poll jump		 */
+	volatile unsigned short  tc_jmp;	/* Cooked procedure jump	 */
+	volatile unsigned short  ri_jmp;	/* Not currently used		 */
+	volatile unsigned short  rp_jmp;	/* Receive poll jump		 */
+
+	volatile unsigned short  tx_seg;	/* W  Tx segment	 */
+	volatile unsigned short  tx_head;	/* W  Tx buffer head offset	*/
+	volatile unsigned short  tx_tail;	/* R  Tx buffer tail offset	*/
+	volatile unsigned short  tx_max;	/* W  Tx buffer size - 1	 */
+
+	volatile unsigned short  rx_seg;	/* W  Rx segment		*/
+	volatile unsigned short  rx_head;	/* W  Rx buffer head offset	*/
+	volatile unsigned short  rx_tail;	/* R  Rx buffer tail offset	*/
+	volatile unsigned short  rx_max;	/* W  Rx buffer size - 1	 */
+
+	volatile unsigned short  tx_lw;		/* W  Tx buffer low water mark  */
+	volatile unsigned short  rx_lw;		/* W  Rx buffer low water mark  */
+	volatile unsigned short  rx_hw;		/* W  Rx buffer high water mark */
+	volatile unsigned short  incr;		/* W  Increment to next channel */
+
+	volatile unsigned short  fepdev;	/* U  SCC device base address    */
+	volatile unsigned short  edelay;	/* W  Exception delay            */
+	volatile unsigned short  blen;		/* W  Break length              */
+	volatile unsigned short  btime;		/* U  Break complete time       */
+
+	volatile unsigned short  iflag;		/* C  UNIX input flags          */
+	volatile unsigned short  oflag;		/* C  UNIX output flags         */
+	volatile unsigned short  cflag;		/* C  UNIX control flags        */
+	volatile unsigned short  wfill[13];	/* U  Reserved for expansion    */
+
+	volatile unsigned char   num;		/* U  Channel number            */
+	volatile unsigned char   ract;		/* U  Receiver active counter   */
+	volatile unsigned char   bstat;		/* U  Break status bits         */
+	volatile unsigned char   tbusy;		/* W  Transmit busy             */
+	volatile unsigned char   iempty;	/* W  Transmit empty event enable */
+	volatile unsigned char   ilow;		/* W  Transmit low-water event enable */
+	volatile unsigned char   idata;		/* W  Receive data interrupt enable */
+	volatile unsigned char   eflag;		/* U  Host event flags          */
+
+	volatile unsigned char   tflag;		/* U  Transmit flags            */
+	volatile unsigned char   rflag;		/* U  Receive flags             */
+	volatile unsigned char   xmask;		/* U  Transmit ready flags      */
+	volatile unsigned char   xval;		/* U  Transmit ready value      */
+	volatile unsigned char   m_stat;	/* RC Modem status bits          */
+	volatile unsigned char   m_change;	/* U  Modem bits which changed  */
+	volatile unsigned char   m_int;		/* W  Modem interrupt enable bits */
+	volatile unsigned char   m_last;	/* U  Last modem status         */
+
+	volatile unsigned char   mtran;		/* C   Unreported modem trans   */
+	volatile unsigned char   orun;		/* C   Buffer overrun occurred  */
+	volatile unsigned char   astartc;	/* W   Auxiliary Xon char       */
+	volatile unsigned char   astopc;	/* W   Auxiliary Xoff char      */
+	volatile unsigned char   startc;	/* W   Xon character             */
+	volatile unsigned char   stopc;		/* W   Xoff character           */
+	volatile unsigned char   vnextc;	/* W   Vnext character           */
+	volatile unsigned char   hflow;		/* C   Software flow control    */
+
+	volatile unsigned char   fillc;		/* U   Delay Fill character     */
+	volatile unsigned char   ochar;		/* U   Saved output character   */
+	volatile unsigned char   omask;		/* U   Output character mask    */
+
+	volatile unsigned char   bfill[13];	/* U   Reserved for expansion   */
+
+	volatile unsigned char   scc[16];	/* U   SCC registers            */
+};
+
+
+/************************************************************************
+ * FEP supported functions
+ ************************************************************************/
+#define SRLOW           0xe0            /* Set receive low water        */
+#define SRHIGH          0xe1            /* Set receive high water       */
+#define FLUSHTX         0xe2            /* Flush transmit buffer        */
+#define PAUSETX         0xe3            /* Pause data transmission      */
+#define RESUMETX        0xe4            /* Resume data transmission     */
+#define SMINT           0xe5            /* Set Modem Interrupt          */
+#define SAFLOWC         0xe6            /* Set Aux. flow control chars  */
+#define SBREAK          0xe8            /* Send break                   */
+#define SMODEM          0xe9            /* Set 8530 modem control lines */
+#define SIFLAG          0xea            /* Set UNIX iflags              */
+#define SFLOWC          0xeb            /* Set flow control characters  */
+#define STLOW           0xec            /* Set transmit low water mark  */
+#define RPAUSE          0xee            /* Pause recieve                */
+#define RRESUME         0xef            /* Resume receive               */
+#define BUFSETALL       0xf2            /* Set Tx & Rx buffer size avail*/
+#define SOFLAG          0xf3            /* Set UNIX oflags              */
+#define SHFLOW          0xf4            /* Set hardware handshake       */
+#define SCFLAG          0xf5            /* Set UNIX cflags              */
+#define SVNEXT          0xf6            /* Set VNEXT character          */
+#define SPINTFC         0xfc            /* Set VNEXT character          */
+
+
+/************************************************************************
+ *      Event flags.
+ ************************************************************************/
+#define IFBREAK         0x01            /* Break received               */
+#define IFTLW           0x02            /* Transmit low water           */
+#define IFTEM           0x04            /* Transmitter empty            */
+#define IFDATA          0x08            /* Receive data present         */
+#define IFMODEM         0x20            /* Modem status change          */
+
+/************************************************************************
+ *      Modem flags
+ ************************************************************************/
+#       define  DM_RTS          0x02    /* Request to send              */
+#       define  DM_CD           0x80    /* Carrier detect               */
+#       define  DM_DSR          0x20    /* Data set ready               */
+#       define  DM_CTS          0x10    /* Clear to send                */
+#       define  DM_RI           0x40    /* Ring indicator               */
+#       define  DM_DTR          0x01    /* Data terminal ready          */
+
+
+
+#endif
diff -puN /dev/null drivers/char/digi/dgap/dgap_mgmt.c
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/drivers/char/digi/dgap/dgap_mgmt.c	Wed Sep 17 12:34:23 2003
@@ -0,0 +1,719 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ *	Scott H Kilau <Scott_Kilau at digi dot com>
+ *
+ * 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, EXPRESS OR IMPLIED; 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.
+ *
+ *
+ *	NOTE TO LINUX KERNEL HACKERS:  DO NOT REFORMAT THIS CODE!
+ *
+ *	This is shared code between Digi's CVS archive and the
+ *	Linux Kernel sources.
+ *	Changing the source just for reformatting needlessly breaks
+ *	our CVS diff history.
+ *
+ *	Send any bug fixes/changes to:  Eng.Linux at digi dot com.
+ *	Thank you.
+ *
+ */
+
+/************************************************************************
+ *
+ * This file implements the mgmt functionality for the
+ * FEP5 based product lines.
+ *
+ ************************************************************************
+ * $Id: dgap_mgmt.c,v 1.19 2003/09/11 01:27:24 scottk Exp $
+ */
+
+#define __NO_VERSION__
+#include "dgap_driver.h"
+#include <linux/ctype.h>
+#include "dgap_types.h"
+#include "dgap_fep5.h"
+#include "dgap_parse.h"
+#include "dgap_mgmt.h"
+#include "dgap_downld.h"
+#include "dgap_tty.h"
+
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+
+/*
+ * external variables
+ */
+extern int              dgap_debug;
+extern spinlock_t	dgap_global_lock;
+extern int		dgap_NumBoards;
+extern struct board_t	*dgap_Board[];
+
+/* This holds the status of the KME buffer */
+static int 		dgap_kmebusy = 0;
+
+/* Our "in use" variables, to enforce 1 open only */
+static int		dgap_mgmt_in_use = 0;
+static int		dgap_downld_in_use = 0;
+
+
+/*
+ * dgap_mgmt_open()
+ *
+ * Open the mgmt/downld/dpa device
+ */
+int dgap_mgmt_open(struct inode *inode, struct file *file)
+{
+	unsigned long lock_flags;
+	unsigned int minor = DGAP_MINOR(inode);
+
+	DPR_MGMT(("dgap_mgmt_open start.\n"));
+
+	DGAP_LOCK(dgap_global_lock, lock_flags);
+
+	/* mgmt device */
+	if (minor == MGMT_MGMT) {
+		/* Only allow 1 open at a time on mgmt device */
+		if (dgap_mgmt_in_use) {
+			DGAP_UNLOCK(dgap_global_lock, lock_flags);
+			return (-EBUSY);
+		}
+		dgap_mgmt_in_use++;
+	}
+	/* downld device */
+	else if (minor == MGMT_DOWNLD) {
+		/* Only allow 1 open at a time on downld device */
+		if (dgap_downld_in_use) {
+			DGAP_UNLOCK(dgap_global_lock, lock_flags);
+			return (-EBUSY);
+		}
+		dgap_downld_in_use++;
+	}
+	else {
+		DGAP_UNLOCK(dgap_global_lock, lock_flags);
+		return (-ENXIO);
+	}
+
+	DGAP_UNLOCK(dgap_global_lock, lock_flags);
+
+	DPR_MGMT(("dgap_mgmt_open finish.\n"));
+
+	return 0;
+}
+
+/*
+ * dgap_mgmt_close()
+ *
+ * Open the mgmt/dpa device
+ */
+int dgap_mgmt_close(struct inode *inode, struct file *file)
+{
+	unsigned long lock_flags;
+	unsigned int minor = DGAP_MINOR(inode);
+
+	DPR_MGMT(("dgap_mgmt_close start.\n"));
+
+	DGAP_LOCK(dgap_global_lock, lock_flags);
+
+	/* mgmt device */
+	if (minor == MGMT_MGMT) {
+		if (dgap_mgmt_in_use) {
+			dgap_mgmt_in_use = 0;
+		}
+	}
+	/* downld device */
+	else if (minor == MGMT_DOWNLD) {
+		if (dgap_downld_in_use) {
+			dgap_downld_in_use = 0;
+		}
+	}
+
+	DGAP_UNLOCK(dgap_global_lock, lock_flags);
+
+	DPR_MGMT(("dgap_mgmt_close finish.\n"));
+
+	return 0;
+}
+
+
+/*
+ * dgap_mgmt_ioctl()
+ *
+ * ioctl the mgmt/dpa device
+ */
+
+int dgap_mgmt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	unsigned long lock_flags;
+	int error = 0;
+	int i = 0;
+	struct board_t *brd;
+	static struct downldio dlio;
+
+	DPR_MGMT(("dgap_mgmt_ioctl start.\n"));
+
+	switch (cmd) {
+
+	case DIGI_DLREQ_GET:
+	{
+
+get_service:
+
+		DGAP_LOCK(dgap_global_lock, lock_flags);
+
+		if (dgap_driver_state == DRIVER_NEED_CONFIG_LOAD) {
+
+			dgap_driver_state = DRIVER_REQUESTED_CONFIG;
+
+			dlio.req_type = DLREQ_CONFIG;
+			dlio.bdid = 0;
+			dlio.image.fi.type = 0;
+
+			DGAP_UNLOCK(dgap_global_lock, lock_flags);
+
+			if (copy_to_user((void *) arg, &dlio, sizeof(struct downldio))) {
+				DGAP_LOCK(dgap_global_lock, lock_flags);
+				dgap_driver_state = DRIVER_NEED_CONFIG_LOAD;
+				DGAP_UNLOCK(dgap_global_lock, lock_flags);
+				return(-EFAULT);
+			}
+
+			return(0);
+		}
+
+		DGAP_UNLOCK(dgap_global_lock, lock_flags);
+
+		/*
+		 * Loop thru each board.
+		 * Check state, force state machine to start running.
+		 */
+		for (i = 0; i < dgap_NumBoards; i++ ) {
+
+			brd = dgap_Board[i];
+
+			DGAP_LOCK(brd->bd_lock, lock_flags);
+
+			switch (brd->state) {
+
+			case NEED_DEVICE_CREATION:
+
+				/*
+				 * Let go of lock, tty_register() (and us also)
+				 * does a non-atomic malloc, so it would be
+				 * possible to deadlock the system if the
+				 * malloc went to sleep.
+				 */
+				DGAP_UNLOCK(brd->bd_lock, lock_flags);
+				dgap_tty_register(brd);
+				DGAP_LOCK(brd->bd_lock, lock_flags);
+				dgap_finalize_board_init(brd);
+
+				brd->state = REQUESTED_DEVICE_CREATION;
+
+				dlio.req_type = DLREQ_DEVCREATE;
+				dlio.bdid = i;
+				dlio.image.fi.type = brd->dpatype;
+
+				DGAP_UNLOCK(brd->bd_lock, lock_flags);
+
+				if (copy_to_user((void *) arg, &dlio, sizeof(struct downldio))) {
+					DGAP_LOCK(brd->bd_lock, lock_flags);
+					brd->state = NEED_DEVICE_CREATION;
+					DGAP_UNLOCK(brd->bd_lock, lock_flags);
+					return(-EFAULT);
+				}
+
+				return(0);
+
+			case NEED_BIOS_LOAD:
+
+				brd->state = REQUESTED_BIOS;
+
+				dlio.req_type = DLREQ_BIOS;
+				dlio.bdid = i;
+				dlio.image.fi.type = brd->dpatype;
+
+				DGAP_UNLOCK(brd->bd_lock, lock_flags);
+				if (copy_to_user((void *) arg, &dlio, sizeof(struct downldio))) {
+					DGAP_LOCK(brd->bd_lock, lock_flags);
+					brd->state = NEED_BIOS_LOAD;
+					DGAP_UNLOCK(brd->bd_lock, lock_flags);
+					return(-EFAULT);
+				}
+
+				return(0);
+
+			case NEED_FEP_LOAD:
+				brd->state = REQUESTED_FEP;
+
+				dlio.req_type = DLREQ_FEP;
+				dlio.bdid = i;
+				dlio.image.fi.type = brd->dpatype;
+
+				DGAP_UNLOCK(brd->bd_lock, lock_flags);
+
+				if (copy_to_user((void *) arg, &dlio, sizeof(struct downldio))) {
+					DGAP_LOCK(brd->bd_lock, lock_flags);
+					brd->state = NEED_FEP_LOAD;
+					DGAP_UNLOCK(brd->bd_lock, lock_flags);
+					return(-EFAULT);
+				}
+				return(0);
+
+			default:
+				DGAP_UNLOCK(brd->bd_lock, lock_flags);
+				break;
+
+			}
+
+			DGAP_LOCK(brd->bd_lock, lock_flags);
+
+			switch (brd->conc_dl_status) {
+
+			case NEED_CONCENTRATOR:
+			{
+				u16 offset = 0;
+				char *vaddr;
+				struct downld_t *to_dp;
+
+				vaddr = brd->re_map_membase;
+				if (!vaddr) {
+					brd->conc_dl_status = NO_PENDING_CONCENTRATOR_REQUESTS;
+					DGAP_UNLOCK(brd->bd_lock, lock_flags);
+					break;
+				}
+
+				dlio.req_type = DLREQ_CONC;
+				dlio.bdid = i;
+
+				offset = readw((u16 *) (vaddr + DOWNREQ));
+				to_dp = (struct downld_t *) (vaddr + (int) offset);
+
+				if (!to_dp) {
+					brd->conc_dl_status = NO_PENDING_CONCENTRATOR_REQUESTS;
+					DGAP_UNLOCK(brd->bd_lock, lock_flags);
+					break;
+				}
+
+				memcpy(&dlio.image.dl, to_dp, sizeof(struct downld_t));
+
+				brd->conc_dl_status = REQUESTED_CONCENTRATOR;
+
+				DGAP_UNLOCK(brd->bd_lock, lock_flags);
+
+				if (copy_to_user((void *) arg, &dlio, sizeof(struct downldio))) {
+					DGAP_LOCK(brd->bd_lock, lock_flags);
+					brd->conc_dl_status = NEED_CONCENTRATOR;
+					DGAP_UNLOCK(brd->bd_lock, lock_flags);
+					return(-EFAULT);
+				}
+
+				return(0);
+			}
+
+			default:
+				DGAP_UNLOCK(brd->bd_lock, lock_flags);
+				break;
+			}
+		}
+
+		/*
+		 * Go to sleep waiting for the driver to signal an event to us.
+		 */
+		error = wait_event_interruptible(dgap_dl_wait, (dgap_dl_action));
+
+		DGAP_LOCK(dgap_dl_lock, lock_flags);
+		dgap_dl_action = 0;
+		DGAP_UNLOCK(dgap_dl_lock, lock_flags);
+
+		/* Break out of ioctl if user cancelled us */
+		if (error)
+			break;
+
+		goto get_service;
+
+	}
+
+	case DIGI_DLREQ_SET:
+	{
+		uchar *uaddr = NULL;
+
+		if (copy_from_user((char *) &dlio, (char *) arg, sizeof(struct downldio))) {
+			return (-EFAULT);
+		}
+
+		if (dlio.req_type == DLREQ_CONFIG) {
+
+			uaddr = (uchar *) arg +
+				(int) ( ((struct downldio *)0)->image.fi.fepimage);
+
+			dgap_do_config_load(uaddr, dlio.image.fi.len);
+			dgap_after_config_loaded();
+
+			DGAP_LOCK(dgap_global_lock, lock_flags);
+			dgap_driver_state = DRIVER_READY;
+			DGAP_UNLOCK(dgap_global_lock, lock_flags);
+
+			break;
+
+		}
+
+		if (dlio.bdid < 0 || dlio.bdid > dgap_NumBoards) {
+			return(-ENXIO);
+                }
+
+		brd = dgap_Board[dlio.bdid];
+
+		switch(dlio.req_type) {
+
+		case DLREQ_BIOS:
+			if (brd->state == BOARD_FAILED || brd->state == BOARD_READY) {
+				break;
+			}
+
+			if (dlio.image.fi.type == -1) {
+				DGAP_LOCK(brd->bd_lock, lock_flags);
+				brd->state = BOARD_FAILED;
+				brd->dpastatus = BD_NOBIOS;
+				DGAP_UNLOCK(brd->bd_lock, lock_flags);
+				break;
+			}
+
+
+			uaddr = (uchar *) arg +
+				(int) ( ((struct downldio *)0)->image.fi.fepimage);
+
+			dgap_do_bios_load(brd, uaddr, dlio.image.fi.len);
+
+			break;
+		case DLREQ_FEP:
+			if (brd->state == BOARD_FAILED || brd->state == BOARD_READY) {
+				 break;
+			}
+
+			if (dlio.image.fi.type == -1) {
+				DGAP_LOCK(brd->bd_lock, lock_flags);
+				brd->state = BOARD_FAILED;
+				brd->dpastatus = BD_NOBIOS;
+				DGAP_UNLOCK(brd->bd_lock, lock_flags);
+				break;
+			}
+
+			uaddr = (uchar *) arg +
+				(int) ( ((struct downldio *)0)->image.fi.fepimage);
+
+			dgap_do_fep_load(brd, uaddr, dlio.image.fi.len);
+
+			break;
+
+		case DLREQ_CONC:
+
+			if (brd->state == BOARD_FAILED) {
+				 break;
+			}
+
+			if (dlio.image.fi.type == -1) {
+				break;
+			}
+
+			uaddr = (char *) &dlio.image.dl;
+			dgap_do_conc_load(brd, uaddr, sizeof(struct downld_t));
+
+			break;
+
+		case DLREQ_DEVCREATE:
+			if (brd->state == BOARD_FAILED || brd->state == BOARD_READY) {
+				 break;
+			}
+
+			DGAP_LOCK(brd->bd_lock, lock_flags);
+			brd->state = FINISHED_DEVICE_CREATION;
+			DGAP_UNLOCK(brd->bd_lock, lock_flags);
+
+			break;
+
+		}
+
+		break;
+	}
+
+
+	case DIGI_GETDD:
+	{
+		/*
+		 * This returns the total number of boards
+		 * in the system, as well as driver version
+		 * and has space for a reserved entry
+		 */
+		struct digi_dinfo ddi;
+
+		DGAP_LOCK(dgap_global_lock, lock_flags);
+
+		ddi.dinfo_nboards = dgap_NumBoards;
+		sprintf(ddi.dinfo_version, "%s", DG_PART);
+
+		DGAP_UNLOCK(dgap_global_lock, lock_flags);
+
+		DPR_MGMT(("DIGI_GETDD returning numboards: %lu version: %s\n",
+			ddi.dinfo_nboards, ddi.dinfo_version));
+
+		copy_to_user((char *) arg, &ddi, sizeof (ddi));
+		break;
+	}
+
+	case DIGI_GETBD:
+	{
+		int brd;
+		int error = 0;
+
+		struct digi_info di;
+
+		if (copy_from_user(&brd, (unsigned int *) arg, sizeof(int))) {
+			return(-EFAULT);
+		}
+
+		if ((error = verify_area(VERIFY_WRITE, (char*) arg, sizeof(di)))) {
+			return (error);
+		}
+
+		DPR_MGMT(("DIGI_GETBD asking about board: %d\n", brd));
+
+		if ((brd < 0) || (brd > dgap_NumBoards) || (dgap_NumBoards == 0))
+			return (-ENODEV);
+
+		memset(&di, 0, sizeof(di));
+
+		di.info_bdnum = brd;
+
+		DGAP_LOCK(dgap_Board[brd]->bd_lock, lock_flags);
+
+		di.info_bdtype = dgap_Board[brd]->dpatype;
+		di.info_bdstate = dgap_Board[brd]->dpastatus;
+		di.info_ioport = (ulong) dgap_Board[brd]->port;
+		di.info_physaddr = (ulong) dgap_Board[brd]->membase;
+		di.info_physsize = (ulong) dgap_Board[brd]->membase - dgap_Board[brd]->membase_end;
+		if (dgap_Board[brd]->state != BOARD_FAILED)
+			di.info_nports = dgap_Board[brd]->nasync;
+		else
+			di.info_nports = 0;
+
+		DGAP_UNLOCK(dgap_Board[brd]->bd_lock, lock_flags);
+
+		DPR_MGMT(("DIGI_GETBD returning type: %x state: %x ports: %x size: %lx\n",
+			di.info_bdtype, di.info_bdstate, di.info_nports, di.info_physsize));
+
+		copy_to_user((char *) arg, &di, sizeof (di));
+		break;
+	}
+
+	case DIGI_KME:
+	{
+		int itmp, jtmp;
+		unchar *memaddr = NULL;
+		struct rw_t kme;
+		struct rw_t *mp = NULL;
+		int brd = 0;
+		struct board_t *bd;
+
+		/* This ioctl takes an argument of type 'rw_t'
+		 * and uses it to interact with the KME struct
+		 * located on the digiboard itself.
+		 */
+		if ((error = verify_area(VERIFY_READ, (char*)arg, sizeof(kme))))
+			return(error);
+
+		copy_from_user(&kme, (char*)arg, sizeof(kme));
+
+		if (kme.rw_size > 128)
+			kme.rw_size = 128;
+
+		brd = kme.rw_board;
+
+		DPR_MGMT(("dgap_mgmt: DIGI_KME: %s asked for board %d\n", current->comm, brd));
+
+		/* Sanity Checking... */
+		if ((brd < 0) || (brd > dgap_NumBoards) || (dgap_NumBoards == 0))
+			return (-ENODEV);
+
+		bd = dgap_Board[brd];
+
+		DGAP_LOCK(dgap_Board[brd]->bd_lock, lock_flags);
+
+		if (bd->state != BOARD_READY) {
+			DGAP_UNLOCK(dgap_Board[brd]->bd_lock, lock_flags);
+			return(-ENODEV);
+		}
+
+		memaddr = bd->re_map_membase;
+
+		DGAP_UNLOCK(dgap_Board[brd]->bd_lock, lock_flags);
+
+		/* If the concentrator number is 0... */
+		if (kme.rw_conc == 0 && kme.rw_addr < 0x100000) {
+			int page = 0;
+			int addr = kme.rw_addr;
+			int size = kme.rw_size;
+			caddr_t data = (caddr_t) kme.rw_data;
+
+			while ((itmp = size)) {
+
+				switch (kme.rw_req) {
+				case RW_READ:
+				{
+					register caddr_t cp1 = (char *)memaddr + addr;
+					register caddr_t cp2 = kme.rw_data;
+
+					DPR_MGMT(("RW_READ CARDMEM - page=%d rw_addr=0x%lx  rw_size=%x\n",
+						page, kme.rw_addr, kme.rw_size));
+
+					for (jtmp = 0; jtmp < itmp; jtmp++) {
+						*cp2++ = readb(cp1++);
+					}
+				}
+
+				break;
+
+				case RW_WRITE:
+				{
+					register caddr_t cp1 = memaddr + addr;
+					register caddr_t cp2 = data;
+
+					DPR_MGMT(("RW_WRITE CARDMEM - page=%d rw_addr=0x%lx rw_size=%d\n",
+						page, kme.rw_addr, kme.rw_size));
+
+					for (jtmp = 0; jtmp < itmp; jtmp++) {
+						writeb(*cp2++, cp1++);
+					}
+				}
+				break;
+				}
+
+				addr += itmp;
+				data += itmp;
+				size -= itmp;
+
+			}
+
+		}
+		else {
+
+			/*
+			 * Read/Write memory in a REMOTE CONCENTRATOR..
+			 * There is only 1 buffer, so do mutual
+			 * exclusion to make sure only one KME
+			 * request is pending...
+			 */
+
+			mp = (struct rw_t *) (memaddr + KMEMEM);
+
+			while (dgap_kmebusy != 0) {
+				dgap_kmebusy = 2;
+				error = wait_event_interruptible(bd->kme_wait, (!dgap_kmebusy));
+				if (error)
+					goto endkme;
+			}
+
+			dgap_kmebusy = 1;
+
+			/* Copy KME request to the board.. */
+
+			mp->rw_board = kme.rw_board;
+			mp->rw_conc  = kme.rw_conc;
+			mp->rw_reserved = kme.rw_reserved;
+			memcpy(&mp->rw_addr, &kme.rw_addr, sizeof(int));
+			memcpy(&mp->rw_size, &kme.rw_size, sizeof(short));
+
+			if(kme.rw_req == RW_WRITE) {
+				register caddr_t cp1 = (caddr_t) mp->rw_data;
+				register caddr_t cp2 = (caddr_t) kme.rw_data;
+
+				DPR_MGMT(("RW_WRITE CONCMEM - rw_addr=0x%lx  rw_size=%d\n",
+					kme.rw_addr, kme.rw_size));
+
+				for (jtmp = 0; jtmp < (int) kme.rw_size; jtmp++) {
+					writeb(*cp2++, cp1++);
+				}
+			}
+
+			/* EXECUTE REQUEST */
+
+			mp->rw_req = kme.rw_req;
+
+			/*
+			 * Wait for the board to process the
+			 * request, but limit the wait to 2 secs
+			 */
+
+			for (itmp = jiffies + (2 * HZ); mp->rw_req;) {
+				if(jiffies >= itmp) {
+					error = ENXIO;
+					/* Set request back to 0.. */
+					mp->rw_req = 0;
+					goto endkme;
+				}
+
+				schedule_timeout(HZ / 10);
+			}
+
+			/*
+			 * Since this portion of code is looksee
+			 * ported from the HPUX EMUX code, i'm
+			 * leaving OUT a portion of that code where
+			 * the HP/UX code actually puts the process
+			 * to sleep for some reason
+			 */
+			if (mp->rw_size < kme.rw_size)
+				memcpy(&kme.rw_size, &mp->rw_size, sizeof(short));
+
+			/* Copy the READ data back to the source buffer... */
+			if (kme.rw_req == RW_READ) {
+				register caddr_t cp1 = (caddr_t) mp->rw_data;
+				register caddr_t cp2 = (caddr_t) kme.rw_data;
+
+				DPR_MGMT(("RW_READ CONCMEM - rw_addr=0x%lx  rw_size=%d\n",
+					kme.rw_addr, kme.rw_size));
+
+				for (jtmp = 0; jtmp < (int) kme.rw_size; jtmp++) {
+					*cp2++ = readb(cp1++);
+				}
+			}
+
+			/*
+			 * Common exit point for code sharing the
+			 * kme buffer. Before exiting, always wake
+			 * another process waiting for the buffer
+			 */
+
+		endkme:
+
+			if (dgap_kmebusy != 1)
+				wake_up_interruptible(&bd->kme_wait);
+			dgap_kmebusy = 0;
+			if(error == ENXIO)
+				return(-EINVAL);
+		}
+
+		/* Copy the whole (Possibly Modified) mess */
+		/* back out to user space...               */
+
+		if (!error) {
+			copy_to_user((char *) arg, &kme, sizeof(kme));
+			return(0);
+		}
+	}
+	}
+
+	DPR_MGMT(("dgap_mgmt_ioctl finish.\n"));
+
+	return 0;
+}
diff -puN /dev/null drivers/char/digi/dgap/dgap_mgmt.h
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/drivers/char/digi/dgap/dgap_mgmt.h	Wed Sep 17 12:34:23 2003
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ *	Scott H Kilau <Scott_Kilau at digi dot com>
+ *
+ * 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, EXPRESS OR IMPLIED; 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.
+ *
+ *	NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!!
+ */
+
+#ifndef __DGAP_MGMT_H
+#define __DGAP_MGMT_H
+
+#define MGMT_MGMT 0
+#define MGMT_DOWNLD 1
+
+int dgap_mgmt_open(struct inode *inode, struct file *file);
+int dgap_mgmt_close(struct inode *inode, struct file *file);
+int dgap_mgmt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
+
+#endif
+
diff -puN /dev/null drivers/char/digi/dgap/dgap_parse.c
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/drivers/char/digi/dgap/dgap_parse.c	Wed Sep 17 12:34:23 2003
@@ -0,0 +1,1299 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ *	Scott H Kilau <Scott_Kilau at digi dot com>
+ *
+ * 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, EXPRESS OR IMPLIED; 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.
+ *
+ *
+ *	NOTE TO LINUX KERNEL HACKERS:  DO NOT REFORMAT THIS CODE!
+ *
+ *	This is shared code between Digi's CVS archive and the
+ *	Linux Kernel sources.
+ *	Changing the source just for reformatting needlessly breaks
+ *	our CVS diff history.
+ *
+ *	Send any bug fixes/changes to:  Eng.Linux at digi dot com.
+ *	Thank you.
+ *
+ *
+ *****************************************************************************
+ *
+ * dgap_parse.c - Parses the configuration information from the input file.
+ *
+ * $Id: dgap_parse.c,v 1.20 2003/09/08 22:13:53 scottk Exp $
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/ctype.h>
+#include "dgap_types.h"
+#include "dgap_fep5.h"
+#include "dgap_driver.h"
+#include "dgap_conf.h"
+
+/*
+ * Function prototypes.
+ */
+static int dgap_gettok(char **in, struct cnode *p);
+static char *dgap_getword(char **in);
+static char *dgap_savestring(char *s);
+static struct cnode *dgap_newnode(int t);
+static int dgap_checknode(struct cnode *p);
+static void dgap_err(char *s);
+
+/*
+ * Any needed external variables...
+ */
+extern int dgap_debug;
+
+/*
+ * Our needed internal static variables...
+ */
+static struct cnode dgap_head;
+#define MAXCWORD 200
+static char dgap_cword[MAXCWORD];
+
+struct toklist {
+	int	token;
+	char	*string;
+};
+
+static struct toklist tlist[] = {
+	{	BEGIN,		"config_begin"			},
+	{	END,		"config_end"			},
+	{	BOARD,		"board"				},
+	{	PCX,		"Digi_AccelePort_C/X_PCI"	},	/* C/X_PCI */
+	{	PEPC,		"Digi_AccelePort_EPC/X_PCI"	},	/* EPC/X_PCI */
+	{	PPCM,		"Digi_AccelePort_Xem_PCI"	},	/* PCI/Xem */
+	{	APORT2_920P,	"Digi_AccelePort_2r_920_PCI"	},
+	{	APORT4_920P,	"Digi_AccelePort_4r_920_PCI"	},
+	{	APORT8_920P,	"Digi_AccelePort_8r_920_PCI"	},
+	{	PAPORT4,	"Digi_AccelePort_4r_PCI(EIA-232/RS-422)" },
+	{	PAPORT8,	"Digi_AccelePort_8r_PCI(EIA-232/RS-422)" },
+	{	IO,		"io"				},
+	{	LINE,		"line"				},
+	{	CONC,		"conc"				},
+	{	CONC,		"concentrator"			},
+	{	CX,		"cx"				},
+	{	EPC,		"epc"				},
+	{	MOD,		"module"			},
+	{	ID,		"id"				},
+	{	STARTO,		"start"				},
+	{	SPEED,		"speed"				},
+	{	CABLE,		"cable"				},
+	{	CONNECT,	"connect"			},
+	{	METHOD,		"method"			},
+	{	STATUS,		"status"			},
+	{	CUSTOM,		"Custom"			},
+	{	BASIC,		"Basic"				},
+	{	MEM,		"mem"				},
+	{	MEM,		"memory"			},
+	{	PORTS,		"ports"				},
+	{	MODEM,		"modem"				},
+	{	NPORTS,		"nports"			},
+	{	TTYN,		"ttyname"			},
+	{	CU,		"cuname"			},
+	{	PRINT,		"prname"			},
+	{	CMAJOR,		"major"				},
+	{	ALTPIN,		"altpin"			},
+	{	USEINTR,	"useintr"			},
+	{	TTSIZ,		"ttysize"			},
+	{	CHSIZ,		"chsize"			},
+	{	BSSIZ,		"boardsize"			},
+	{	UNTSIZ,		"schedsize"			},
+	{	F2SIZ,		"f2200size"			},
+	{	VPSIZ,		"vpixsize"			},
+	{	0,		NULL				}
+};
+
+
+/*
+ * Parse a configuration file read into memory as a string.
+ */
+int	dgap_parsefile(char **in, int Remove)
+{
+	struct cnode *p, *brd, *line, *conc;
+	int	rc;
+	char	*s = NULL, *s2 = NULL;
+	int	linecnt = 0;
+
+	p = &dgap_head;
+	brd = line = conc = NULL;
+
+	/* perhaps we are adding to an existing list? */
+	while (p->next != NULL) {
+		p = p->next;
+	}
+
+	/* file must start with a BEGIN */
+	while ( (rc = dgap_gettok(in,p)) != BEGIN ) {
+		if (rc == 0) {
+			dgap_err("unexpected EOF");
+			return(-1);
+		}
+	}
+
+	for (; ; ) {
+		rc = dgap_gettok(in,p);
+		if (rc == 0) {
+			dgap_err("unexpected EOF");
+			return(-1);
+		}
+
+		switch (rc) {
+		case 0:
+			dgap_err("unexpected end of file");
+			return(-1);
+
+		case BEGIN:	/* should only be 1 begin */
+			dgap_err("unexpected config_begin\n");
+			return(-1);
+
+		case END:
+			return(0);
+
+		case BOARD:	/* board info */
+			if (dgap_checknode(p))
+				return(-1);
+			if ( (p->next = dgap_newnode(BNODE)) == NULL ) {
+				dgap_err("out of memory");
+				return(-1);
+			}
+			p = p->next;
+
+			p->u.board.status = dgap_savestring("No");
+			line = conc = NULL;
+			brd = p;
+			linecnt = -1;
+			break;
+
+		case APORT2_920P:	/* AccelePort_4 */
+			if (p->type != BNODE) {
+				dgap_err("unexpected Digi_2r_920 string");
+				return(-1);
+			}
+			p->u.board.type = APORT2_920P;
+			p->u.board.v_type = 1;
+			DPR_INIT(("Adding Digi_2r_920 PCI to config...\n"));
+			break;
+
+		case APORT4_920P:	/* AccelePort_4 */
+			if (p->type != BNODE) {
+				dgap_err("unexpected Digi_4r_920 string");
+				return(-1);
+			}
+			p->u.board.type = APORT4_920P;
+			p->u.board.v_type = 1;
+			DPR_INIT(("Adding Digi_4r_920 PCI to config...\n"));
+			break;
+
+		case APORT8_920P:	/* AccelePort_8 */
+			if (p->type != BNODE) {
+				dgap_err("unexpected Digi_8r_920 string");
+				return(-1);
+			}
+			p->u.board.type = APORT8_920P;
+			p->u.board.v_type = 1;
+			DPR_INIT(("Adding Digi_8r_920 PCI to config...\n"));
+			break;
+
+		case PAPORT4:	/* AccelePort_4 PCI */
+			if (p->type != BNODE) {
+				dgap_err("unexpected Digi_4r(PCI) string");
+				return(-1);
+			}
+			p->u.board.type = PAPORT4;
+			p->u.board.v_type = 1;
+			DPR_INIT(("Adding Digi_4r PCI to config...\n"));
+			break;
+
+		case PAPORT8:	/* AccelePort_8 PCI */
+			if (p->type != BNODE) {
+				dgap_err("unexpected Digi_8r string");
+				return(-1);
+			}
+			p->u.board.type = PAPORT8;
+			p->u.board.v_type = 1;
+			DPR_INIT(("Adding Digi_8r PCI to config...\n"));
+			break;
+
+		case PCX:	/* PCI C/X */
+			if (p->type != BNODE) {
+				dgap_err("unexpected Digi_C/X_(PCI) string");
+				return(-1);
+			}
+			p->u.board.type = PCX;
+			p->u.board.v_type = 1;
+			p->u.board.conc1 = 0;
+			p->u.board.conc2 = 0;
+			p->u.board.module1 = 0;
+			p->u.board.module2 = 0;
+			DPR_INIT(("Adding PCI C/X to config...\n"));
+			break;
+
+		case PEPC:	/* PCI EPC/X */
+			if (p->type != BNODE) {
+				dgap_err("unexpected \"Digi_EPC/X_(PCI)\" string");
+				return(-1);
+			}
+			p->u.board.type = PEPC;
+			p->u.board.v_type = 1;
+			p->u.board.conc1 = 0;
+			p->u.board.conc2 = 0;
+			p->u.board.module1 = 0;
+			p->u.board.module2 = 0;
+			DPR_INIT(("Adding PCI EPC/X to config...\n"));
+			break;
+
+		case PPCM:	/* PCI/Xem */
+			if (p->type != BNODE) {
+				dgap_err("unexpected PCI/Xem string");
+				return(-1);
+			}
+			p->u.board.type = PPCM;
+			p->u.board.v_type = 1;
+			p->u.board.conc1 = 0;
+			p->u.board.conc2 = 0;
+			DPR_INIT(("Adding PCI XEM to config...\n"));
+			break;
+
+		case IO:	/* i/o port */
+			if (p->type != BNODE) {
+				dgap_err("IO port only vaild for boards");
+				return(-1);
+			}
+			s = dgap_getword(in);
+			if (s == NULL) {
+				dgap_err("unexpected end of file");
+				return(-1);
+			}
+			p->u.board.portstr = dgap_savestring(s);
+			p->u.board.port = (short)simple_strtol(s, &s2, 0);
+			if ((short)strlen(s) > (short)(s2 - s)) {
+				dgap_err("bad number for IO port");
+				return(-1);
+			}
+			p->u.board.v_port = 1;
+			DPR_INIT(("Adding IO (%s) to config...\n", s));
+			break;
+
+		case MEM:	/* memory address */
+			if (p->type != BNODE) {
+				dgap_err("memory address only vaild for boards");
+				return(-1);
+			}
+			s = dgap_getword(in);
+			if (s == NULL) {
+				dgap_err("unexpected end of file");
+				return(-1);
+			}
+			p->u.board.addrstr = dgap_savestring(s);
+			p->u.board.addr = simple_strtoul(s, &s2, 0);
+			if ((int)strlen(s) > (int)(s2 - s)) {
+				dgap_err("bad number for memory address");
+				return(-1);
+			}
+			p->u.board.v_addr = 1;
+			DPR_INIT(("Adding MEM (%s) to config...\n", s));
+			break;
+
+		case METHOD:
+			if (p->type != BNODE) {
+				dgap_err("install method only vaild for boards");
+				return(-1);
+			}
+			s = dgap_getword(in);
+			if (s == NULL) {
+				dgap_err("unexpected end of file");
+				return(-1);
+			}
+			p->u.board.method = dgap_savestring(s);
+			p->u.board.v_method = 1;
+			DPR_INIT(("Adding METHOD (%s) to config...\n", s));
+			break;
+
+		case STATUS:
+			if (p->type != BNODE) {
+				dgap_err("config status only vaild for boards");
+				return(-1);
+			}
+			s = dgap_getword(in);
+			if (s == NULL) {
+				dgap_err("unexpected end of file");
+				return(-1);
+			}
+			p->u.board.status = dgap_savestring(s);
+			DPR_INIT(("Adding STATUS (%s) to config...\n", s));
+			break;
+
+		case NPORTS:	/* number of ports */
+			if (p->type == BNODE) {
+				s = dgap_getword(in);
+				if (s == NULL) {
+					dgap_err("unexpected end of file");
+					return(-1);
+				}
+				p->u.board.nport = (char)simple_strtol(s, &s2, 0);
+				if ((int)strlen(s) > (int)(s2 - s)) {
+					dgap_err("bad number for number of ports");
+					return(-1);
+				}
+				p->u.board.v_nport = 1;
+			} else if (p->type == CNODE) {
+				s = dgap_getword(in);
+				if (s == NULL) {
+					dgap_err("unexpected end of file");
+					return(-1);
+				}
+				p->u.conc.nport = (char)simple_strtol(s, &s2, 0);
+				if ((int)strlen(s) > (int)(s2 - s)) {
+					dgap_err("bad number for number of ports");
+					return(-1);
+				}
+				p->u.conc.v_nport = 1;
+			} else if (p->type == MNODE) {
+				s = dgap_getword(in);
+				if (s == NULL) {
+					dgap_err("unexpected end of file");
+					return(-1);
+				}
+				p->u.module.nport = (char)simple_strtol(s, &s2, 0);
+				if ((int)strlen(s) > (int)(s2 - s)) {
+					dgap_err("bad number for number of ports");
+					return(-1);
+				}
+				p->u.module.v_nport = 1;
+			} else {
+				dgap_err("nports only valid for concentrators or modules");
+				return(-1);
+			}
+			DPR_INIT(("Adding NPORTS (%s) to config...\n", s));
+			break;
+
+		case ID:	/* letter ID used in tty name */
+			s = dgap_getword(in);
+			if (s == NULL) {
+				dgap_err("unexpected end of file");
+				return(-1);
+			}
+
+			p->u.board.status = dgap_savestring(s);
+
+			if (p->type == CNODE) {
+				p->u.conc.id = dgap_savestring(s);
+				p->u.conc.v_id = 1;
+			} else if (p->type == MNODE) {
+				p->u.module.id = dgap_savestring(s);
+				p->u.module.v_id = 1;
+			} else {
+				dgap_err("id only valid for concentrators or modules");
+				return(-1);
+			}
+			DPR_INIT(("Adding ID (%s) to config...\n", s));
+			break;
+
+		case STARTO:	/* start offset of ID */
+			if (p->type == BNODE) {
+				s = dgap_getword(in);
+				if (s == NULL) {
+					dgap_err("unexpected end of file");
+					return(-1);
+				}
+				p->u.board.start = simple_strtol(s, &s2, 0);
+				if ((int)strlen(s) > (int)(s2 - s)) {
+					dgap_err("bad number for start of tty count");
+					return(-1);
+				}
+				p->u.board.v_start = 1;
+			} else if (p->type == CNODE) {
+				s = dgap_getword(in);
+				if (s == NULL) {
+					dgap_err("unexpected end of file");
+					return(-1);
+				}
+				p->u.conc.start = simple_strtol(s, &s2, 0);
+				if ((int)strlen(s) > (int)(s2 - s)) {
+					dgap_err("bad number for start of tty count");
+					return(-1);
+				}
+				p->u.conc.v_start = 1;
+			} else if (p->type == MNODE) {
+				s = dgap_getword(in);
+				if (s == NULL) {
+					dgap_err("unexpected end of file");
+					return(-1);
+				}
+				p->u.module.start = simple_strtol(s, &s2, 0);
+				if ((int)strlen(s) > (int)(s2 - s)) {
+					dgap_err("bad number for start of tty count");
+					return(-1);
+				}
+				p->u.module.v_start = 1;
+			} else {
+				dgap_err("start only valid for concentrators or modules");
+				return(-1);
+			}
+			DPR_INIT(("Adding START (%s) to config...\n", s));
+			break;
+
+		case TTYN:	/* tty name prefix */
+			if (dgap_checknode(p))
+				return(-1);
+			if ( (p->next = dgap_newnode(TNODE)) == NULL ) {
+				dgap_err("out of memory");
+				return(-1);
+			}
+			p = p->next;
+			if ( (s = dgap_getword(in)) == NULL ) {
+				dgap_err("unexpeced end of file");
+				return(-1);
+			}
+			if ( (p->u.ttyname = dgap_savestring(s)) == NULL ) {
+				dgap_err("out of memory");
+				return(-1);
+			}
+			DPR_INIT(("Adding TTY (%s) to config...\n", s));
+			break;
+
+		case CU:	/* cu name prefix */
+			if (dgap_checknode(p))
+				return(-1);
+			if ( (p->next = dgap_newnode(CUNODE)) == NULL ) {
+				dgap_err("out of memory");
+				return(-1);
+			}
+			p = p->next;
+			if ( (s = dgap_getword(in)) == NULL ) {
+				dgap_err("unexpeced end of file");
+				return(-1);
+			}
+			if ( (p->u.cuname = dgap_savestring(s)) == NULL ) {
+				dgap_err("out of memory");
+				return(-1);
+			}
+			DPR_INIT(("Adding CU (%s) to config...\n", s));
+			break;
+
+		case LINE:	/* line information */
+			if (dgap_checknode(p))
+				return(-1);
+			if (brd == NULL) {
+				dgap_err("must specify board before line info");
+				return(-1);
+			}
+			switch (brd->u.board.type) {
+			case PPCM:
+				dgap_err("line not vaild for PC/em");
+				return(-1);
+			}
+			if ( (p->next = dgap_newnode(LNODE)) == NULL ) {
+				dgap_err("out of memory");
+				return(-1);
+			}
+			p = p->next;
+			conc = NULL;
+			line = p;
+			linecnt++;
+			DPR_INIT(("Adding LINE to config...\n"));
+			break;
+
+		case CONC:	/* concentrator information */
+			if (dgap_checknode(p))
+				return(-1);
+			if (line == NULL) {
+				dgap_err("must specify line info before concentrator");
+				return(-1);
+			}
+			if ( (p->next = dgap_newnode(CNODE)) == NULL ) {
+				dgap_err("out of memory");
+				return(-1);
+			}
+			p = p->next;
+			conc = p;
+			if (linecnt)
+				brd->u.board.conc2++;
+			else
+				brd->u.board.conc1++;
+
+			DPR_INIT(("Adding CONC to config...\n"));
+			break;
+
+		case CX:	/* c/x type concentrator */
+			if (p->type != CNODE) {
+				dgap_err("cx only valid for concentrators");
+				return(-1);
+			}
+			p->u.conc.type = CX;
+			p->u.conc.v_type = 1;
+			DPR_INIT(("Adding CX to config...\n"));
+			break;
+
+		case EPC:	/* epc type concentrator */
+			if (p->type != CNODE) {
+				dgap_err("cx only valid for concentrators");
+				return(-1);
+			}
+			p->u.conc.type = EPC;
+			p->u.conc.v_type = 1;
+			DPR_INIT(("Adding EPC to config...\n"));
+			break;
+
+		case MOD:	/* EBI module */
+			if (dgap_checknode(p))
+				return(-1);
+			if (brd == NULL) {
+				dgap_err("must specify board info before EBI modules");
+				return(-1);
+			}
+			switch (brd->u.board.type) {
+			case PPCM:
+				linecnt = 0;
+				break;
+			default:
+				if (conc == NULL) {
+					dgap_err("must specify concentrator info before EBI module");
+					return(-1);
+				}
+			}
+			if ( (p->next = dgap_newnode(MNODE)) == NULL ) {
+				dgap_err("out of memory");
+				return(-1);
+			}
+			p = p->next;
+			if (linecnt)
+				brd->u.board.module2++;
+			else
+				brd->u.board.module1++;
+
+			DPR_INIT(("Adding MOD to config...\n"));
+			break;
+
+		case PORTS:	/* ports type EBI module */
+			if (p->type != MNODE) {
+				dgap_err("ports only valid for EBI modules");
+				return(-1);
+			}
+			p->u.module.type = PORTS;
+			p->u.module.v_type = 1;
+			DPR_INIT(("Adding PORTS to config...\n"));
+			break;
+
+		case MODEM:	/* ports type EBI module */
+			if (p->type != MNODE) {
+				dgap_err("modem only valid for modem modules");
+				return(-1);
+			}
+			p->u.module.type = MODEM;
+			p->u.module.v_type = 1;
+			DPR_INIT(("Adding MODEM to config...\n"));
+			break;
+
+		case CABLE:
+			if (p->type == LNODE) {
+				if ((s = dgap_getword(in)) == NULL) {
+					dgap_err("unexpected end of file");
+					return(-1);
+				}
+				p->u.line.cable = dgap_savestring(s);
+				p->u.line.v_cable = 1;
+			}
+			DPR_INIT(("Adding CABLE (%s) to config...\n", s));
+			break;
+
+		case SPEED:	/* sync line speed indication */
+			if (p->type == LNODE) {
+				s = dgap_getword(in);
+				if (s == NULL) {
+					dgap_err("unexpected end of file");
+					return(-1);
+				}
+				p->u.line.speed = (char)simple_strtol(s, &s2, 0);
+				if ((short)strlen(s) > (short)(s2 - s)) {
+					dgap_err("bad number for line speed");
+					return(-1);
+				}
+				p->u.line.v_speed = 1;
+			} else if (p->type == CNODE) {
+				s = dgap_getword(in);
+				if (s == NULL) {
+					dgap_err("unexpected end of file");
+					return(-1);
+				}
+				p->u.conc.speed = (char)simple_strtol(s, &s2, 0);
+				if ((short)strlen(s) > (short)(s2 - s)) {
+					dgap_err("bad number for line speed");
+					return(-1);
+				}
+				p->u.conc.v_speed = 1;
+			} else {
+				dgap_err("speed valid only for lines or concentrators.");
+				return(-1);
+			}
+			DPR_INIT(("Adding SPEED (%s) to config...\n", s));
+			break;
+
+		case CONNECT:
+			if (p->type == CNODE) {
+				if ((s = dgap_getword(in)) == NULL) {
+					dgap_err("unexpected end of file");
+					return(-1);
+				}
+				p->u.conc.connect = dgap_savestring(s);
+				p->u.conc.v_connect = 1;
+			}
+			DPR_INIT(("Adding CONNECT (%s) to config...\n", s));
+			break;
+		case PRINT:	/* transparent print name prefix */
+			if (dgap_checknode(p))
+				return(-1);
+			if ( (p->next = dgap_newnode(PNODE)) == NULL ) {
+				dgap_err("out of memory");
+				return(-1);
+			}
+			p = p->next;
+			if ( (s = dgap_getword(in)) == NULL ) {
+				dgap_err("unexpeced end of file");
+				return(-1);
+			}
+			if ( (p->u.printname = dgap_savestring(s)) == NULL ) {
+				dgap_err("out of memory");
+				return(-1);
+			}
+			DPR_INIT(("Adding PRINT (%s) to config...\n", s));
+			break;
+
+		case CMAJOR:	/* major number */
+			if (dgap_checknode(p))
+				return(-1);
+			if ( (p->next = dgap_newnode(JNODE)) == NULL ) {
+				dgap_err("out of memory");
+				return(-1);
+			}
+			p = p->next;
+			s = dgap_getword(in);
+			if (s == NULL) {
+				dgap_err("unexpected end of file");
+				return(-1);
+			}
+			p->u.majornumber = simple_strtol(s, &s2, 0);
+			if ((int)strlen(s) > (int)(s2 - s)) {
+				dgap_err("bad number for major number");
+				return(-1);
+			}
+			DPR_INIT(("Adding CMAJOR (%s) to config...\n", s));
+			break;
+
+		case ALTPIN:	/* altpin setting */
+			if (dgap_checknode(p))
+				return(-1);
+			if ( (p->next = dgap_newnode(ANODE)) == NULL ) {
+				dgap_err("out of memory");
+				return(-1);
+			}
+			p = p->next;
+			s = dgap_getword(in);
+			if (s == NULL) {
+				dgap_err("unexpected end of file");
+				return(-1);
+			}
+			p->u.altpin = simple_strtol(s, &s2, 0);
+			if ((int)strlen(s) > (int)(s2 - s)) {
+				dgap_err("bad number for altpin");
+				return(-1);
+			}
+			DPR_INIT(("Adding ALTPIN (%s) to config...\n", s));
+			break;
+
+		case USEINTR:		/* enable interrupt setting */
+			if (dgap_checknode(p))
+				return(-1);
+			if ( (p->next = dgap_newnode(INTRNODE)) == NULL ) {
+				dgap_err("out of memory");
+				return(-1);
+			}
+			p = p->next;
+			s = dgap_getword(in);
+			if (s == NULL) {
+				dgap_err("unexpected end of file");
+				return(-1);
+			}
+			p->u.useintr = simple_strtol(s, &s2, 0);
+			if ((int)strlen(s) > (int)(s2 - s)) {
+				dgap_err("bad number for useintr");
+				return(-1);
+			}
+			DPR_INIT(("Adding USEINTR (%s) to config...\n", s));
+			break;
+
+		case TTSIZ:	/* size of tty structure */
+			if (dgap_checknode(p))
+				return(-1);
+			if ( (p->next = dgap_newnode(TSNODE)) == NULL ) {
+				dgap_err("out of memory");
+				return(-1);
+			}
+			p = p->next;
+			s = dgap_getword(in);
+			if (s == NULL) {
+				dgap_err("unexpected end of file");
+				return(-1);
+			}
+			p->u.ttysize = simple_strtol(s, &s2, 0);
+			if ((int)strlen(s) > (int)(s2 - s)) {
+				dgap_err("bad number for ttysize");
+				return(-1);
+			}
+			DPR_INIT(("Adding TTSIZ (%s) to config...\n", s));
+			break;
+
+		case CHSIZ:	/* channel structure size */
+			if (dgap_checknode(p))
+				return(-1);
+			if ( (p->next = dgap_newnode(CSNODE)) == NULL ) {
+				dgap_err("out of memory");
+				return(-1);
+			}
+			p = p->next;
+			s = dgap_getword(in);
+			if (s == NULL) {
+				dgap_err("unexpected end of file");
+				return(-1);
+			}
+			p->u.chsize = simple_strtol(s, &s2, 0);
+			if ((int)strlen(s) > (int)(s2 - s)) {
+				dgap_err("bad number for chsize");
+				return(-1);
+			}
+			DPR_INIT(("Adding CHSIZE (%s) to config...\n", s));
+			break;
+
+		case BSSIZ:	/* board structure size */
+			if (dgap_checknode(p))
+				return(-1);
+			if ( (p->next = dgap_newnode(BSNODE)) == NULL ) {
+				dgap_err("out of memory");
+				return(-1);
+			}
+			p = p->next;
+			s = dgap_getword(in);
+			if (s == NULL) {
+				dgap_err("unexpected end of file");
+				return(-1);
+			}
+			p->u.bssize = simple_strtol(s, &s2, 0);
+			if ((int)strlen(s) > (int)(s2 - s)) {
+				dgap_err("bad number for bssize");
+				return(-1);
+			}
+			DPR_INIT(("Adding BSSIZ (%s) to config...\n", s));
+			break;
+
+		case UNTSIZ:	/* sched structure size */
+			if (dgap_checknode(p))
+				return(-1);
+			if ( (p->next = dgap_newnode(USNODE)) == NULL ) {
+				dgap_err("out of memory");
+				return(-1);
+			}
+			p = p->next;
+			s = dgap_getword(in);
+			if (s == NULL) {
+				dgap_err("unexpected end of file");
+				return(-1);
+			}
+			p->u.unsize = simple_strtol(s, &s2, 0);
+			if ((int)strlen(s) > (int)(s2 - s)) {
+				dgap_err("bad number for schedsize");
+				return(-1);
+			}
+			DPR_INIT(("Adding UNTSIZ (%s) to config...\n", s));
+			break;
+
+		case F2SIZ:	/* f2200 structure size */
+			if (dgap_checknode(p))
+				return(-1);
+			if ( (p->next = dgap_newnode(FSNODE)) == NULL ) {
+				dgap_err("out of memory");
+				return(-1);
+			}
+			p = p->next;
+			s = dgap_getword(in);
+			if (s == NULL) {
+				dgap_err("unexpected end of file");
+				return(-1);
+			}
+			p->u.f2size = simple_strtol(s, &s2, 0);
+			if ((int)strlen(s) > (int)(s2 - s)) {
+				dgap_err("bad number for f2200size");
+				return(-1);
+			}
+			DPR_INIT(("Adding F2SIZ (%s) to config...\n", s));
+			break;
+
+		case VPSIZ:	/* vpix structure size */
+			if (dgap_checknode(p))
+				return(-1);
+			if ( (p->next = dgap_newnode(VSNODE)) == NULL ) {
+				dgap_err("out of memory");
+				return(-1);
+			}
+			p = p->next;
+			s = dgap_getword(in);
+			if (s == NULL) {
+				dgap_err("unexpected end of file");
+				return(-1);
+			}
+			p->u.vpixsize = simple_strtol(s, &s2, 0);
+			if ((int)strlen(s) > (int)(s2 - s)) {
+				dgap_err("bad number for vpixsize");
+				return(-1);
+			}
+			DPR_INIT(("Adding VPSIZ (%s) to config...\n", s));
+			break;
+		}
+	}
+}
+
+
+/*
+ * sindex: much like index(), but it looks for a match of any character in
+ * the group, and returns that position.  If the first character is a ^, then
+ * this will match the first occurence not in that group.
+ */
+static char *sindex (char *string, char *group)
+{
+	char    *ptr;
+
+	if (!string || !group)
+		return (char *) NULL;
+
+	if (*group == '^') {
+		group++;
+		for (; *string; string++) {
+			for (ptr = group; *ptr; ptr++) {
+				if (*ptr == *string)
+					break;
+			}
+			if (*ptr == '\0')
+				return string;
+		}
+	}
+	else {
+		for (; *string; string++) {
+			for (ptr = group; *ptr; ptr++) {
+				if (*ptr == *string)
+					return string;
+			}
+		}
+	}
+
+	return (char *) NULL;
+}
+
+
+/*
+ * Get a token from the input file; return 0 if end of file is reached
+ */
+static int dgap_gettok(char **in, struct cnode *p)
+{
+	char	*w;
+	struct toklist *t;
+
+	if (strstr(dgap_cword, "boar")) {
+		w = dgap_getword(in);
+		snprintf(dgap_cword, MAXCWORD, "%s", w);
+		for (t = tlist; t->token != 0; t++) {
+			if ( !strcmp(w, t->string)) {
+				return(t->token);
+			}
+		}
+		dgap_err("board !!type not specified");
+		return(1);
+	}
+	else {
+		while ( (w = dgap_getword(in)) != NULL ) {
+			snprintf(dgap_cword, MAXCWORD, "%s", w);
+			for (t = tlist; t->token != 0; t++) {
+				if ( !strcmp(w, t->string) )
+					return(t->token);
+			}
+		}
+		return(0);
+	}
+}
+
+
+/*
+ * get a word from the input stream, also keep track of current line number.
+ * words are separated by whitespace.
+ */
+static char *dgap_getword(char **in)
+{
+	char *ret_ptr = *in;
+
+        char *ptr = sindex(*in, " \t\n");
+
+	/* If no word found, return null */
+	if (!ptr)
+		return NULL;
+
+	/* Mark new location for our buffer */
+	*ptr = '\0';
+	*in = ptr + 1;
+
+	/* Eat any extra spaces/tabs/newlines that might be present */
+	while (*in && **in && ((**in == ' ') || (**in == '\t') || (**in == '\n'))) {
+		**in = '\0';
+		*in = *in + 1;
+	}
+
+	return ret_ptr;
+}
+
+
+/*
+ * print an error message, giving the line number in the file where
+ * the error occurred.
+ */
+static void dgap_err(char *s)
+{
+	printk("DGAP: parse: %s\n", s);
+}
+
+
+/*
+ * allocate a new configuration node of type t
+ */
+static struct cnode *dgap_newnode(int t)
+{
+	struct cnode *n;
+	if ( (n = (struct cnode *) kmalloc(sizeof(struct cnode ), GFP_ATOMIC) ) != NULL) {
+		memset( (char *)n, 0, sizeof(struct cnode ) );
+		n->type = t;
+	}
+	return(n);
+}
+
+
+/*
+ * dgap_checknode: see if all the necessary info has been supplied for a node
+ * before creating the next node.
+ */
+static int dgap_checknode(struct cnode *p)
+{
+	switch (p->type) {
+	case BNODE:
+		if (p->u.board.v_type == 0) {
+			dgap_err("board type !not specified");
+			return(1);
+		}
+
+		return(0);
+
+	case LNODE:
+		if (p->u.line.v_speed == 0) {
+			dgap_err("line speed not specified");
+			return(1);
+		}
+		return(0);
+
+	case CNODE:
+		if (p->u.conc.v_type == 0) {
+			dgap_err("concentrator type not specified");
+			return(1);
+		}
+		if (p->u.conc.v_speed == 0) {
+			dgap_err("concentrator line speed not specified");
+			return(1);
+		}
+		if (p->u.conc.v_nport == 0) {
+			dgap_err("number of ports on concentrator not specified");
+			return(1);
+		}
+		if (p->u.conc.v_id == 0) {
+			dgap_err("concentrator id letter not specified");
+			return(1);
+		}
+		return(0);
+
+	case MNODE:
+		if (p->u.module.v_type == 0) {
+			dgap_err("EBI module type not specified");
+			return(1);
+		}
+		if (p->u.module.v_nport == 0) {
+			dgap_err("number of ports on EBI module not specified");
+			return(1);
+		}
+		if (p->u.module.v_id == 0) {
+			dgap_err("EBI module id letter not specified");
+			return(1);
+		}
+		return(0);
+	}
+	return(0);
+}
+
+/*
+ * save a string somewhere
+ */
+static char	*dgap_savestring(char *s)
+{
+	char	*p;
+	if ( (p = kmalloc(strlen(s) + 1, GFP_ATOMIC) ) != NULL) {
+		strcpy(p, s);
+	}
+	return(p);
+}
+
+
+/*
+ * Given a board pointer, returns whether we should use interrupts or not.
+ */
+u32 dgap_config_get_useintr(struct board_t *bd)
+{
+	struct cnode *p = NULL;
+
+	if (!bd)
+		return(0);
+
+	for (p = bd->bd_config; p; p = p->next) {
+		switch (p->type) {
+		case INTRNODE:
+			/*
+			 * check for pcxr types.
+			 */
+			return p->u.useintr;
+		default:
+			break;
+		}
+	}
+
+	/* If not found, then don't turn on interrupts. */
+	return 0;
+}
+
+
+/*
+ * Given a board pointer, returns whether we turn on altpin or not.
+ */
+u32 dgap_config_get_altpin(struct board_t *bd)
+{
+	struct cnode *p = NULL;
+
+	if (!bd)
+		return(0);
+
+	for (p = bd->bd_config; p; p = p->next) {
+		switch (p->type) {
+		case ANODE:
+			/*
+			 * check for pcxr types.
+			 */
+			return p->u.altpin;
+		default:
+			break;
+		}
+	}
+
+	/* If not found, then don't turn on interrupts. */
+	return 0;
+}
+
+
+
+/*
+ * Given a specific type of board, if found, detached link and
+ * returns the first occurance in the list.
+ */
+struct cnode *dgap_find_config(int type)
+{
+	struct cnode *p, *prev = NULL, *prev2 = NULL, *found = NULL;
+
+	p = &dgap_head;
+
+	while (p->next != NULL) {
+		prev = p;
+		p = p->next;
+
+		if (p->type == BNODE) {
+			if (p->u.board.type == type) {
+				DPR_INIT(("Matched type in config file\n"));
+
+				found = p;
+				/*
+				 * Keep walking thru the list till we find the next board.
+				 */
+				while (p->next != NULL) {
+					prev2 = p;
+					p = p->next;
+					if (p->type == BNODE) {
+
+						/*
+						 * Mark the end of our 1 board chain of configs.
+						 */
+						prev2->next = NULL;
+
+						/*
+						 * Link the "next" board to the previous board,
+						 * effectively "unlinking" our board from the main config.
+						 */
+						prev->next = p;
+
+						return found;
+					}
+				}
+				/*
+				 * It must be the last board in the list.
+				 */
+				prev->next = NULL;
+				return found;
+			}
+		}
+	}
+	return NULL;
+}
+
+/*
+ * Given a board pointer, walks the config link, counting up
+ * all ports user specified should be on the board.
+ * (This does NOT mean they are all actually present right now tho)
+ */
+u32 dgap_config_get_number_of_ports(struct board_t *bd)
+{
+	int count = 0;
+	struct cnode *p = NULL;
+
+	if (!bd)
+		return(0);
+
+	for (p = bd->bd_config; p; p = p->next) {
+
+		switch (p->type) {
+		case BNODE:
+			/*
+			 * check for pcxr types.
+			 */
+			if (p->u.board.type > EPCFE)
+				count += p->u.board.nport;
+			break;
+		case CNODE:
+			count += p->u.conc.nport;
+			break;
+		case MNODE:
+			count += p->u.module.nport;
+			break;
+		}
+	}
+	return (count);
+}
+
+char *dgap_create_config_string(struct board_t *bd, char *string)
+{
+	char *ptr = string;
+	struct cnode *p = NULL;
+
+	if (!bd) {
+		*ptr = 0xff;
+		return string;
+	}
+
+	for (p = bd->bd_config; p; p = p->next) {
+
+		switch (p->type) {
+		case LNODE:
+			*ptr = '\0';
+			ptr++;
+			*ptr = p->u.line.speed;
+			ptr++;
+			break;
+		case CNODE:
+			*ptr = p->u.conc.nport;
+			ptr++;
+			*ptr = p->u.conc.speed;
+			ptr++;
+			break;
+		}
+	}
+
+	*ptr = 0xff;
+	return string;
+}
+
+
+
+char *dgap_get_config_letters(struct board_t *bd, char *string)
+{
+	int found = FALSE;
+	char *ptr = string;
+	struct cnode *cptr = NULL;
+	int len = 0;
+	int left = MAXTTYNAMELEN;
+
+	if (!bd) {
+		return "<NULL>";
+	}
+
+	for (cptr = bd->bd_config; cptr; cptr = cptr->next) {
+
+		if ((cptr->type == BNODE) &&
+		     ((cptr->u.board.type == APORT2_920P) || (cptr->u.board.type == APORT4_920P) ||
+		     (cptr->u.board.type == APORT8_920P) || (cptr->u.board.type == PAPORT4) ||
+		     (cptr->u.board.type == PAPORT8))) {
+
+			found = TRUE;
+		}
+
+		if (cptr->type == TNODE && found == TRUE) {
+			char *ptr1;
+			if (strstr(cptr->u.ttyname, "tty")) {
+				ptr1 = cptr->u.ttyname;
+				ptr1 += 3;
+			}
+			else {
+				ptr1 = cptr->u.ttyname;
+			}
+			if (ptr1) {
+				len = snprintf(ptr, left, "%s", ptr1);
+				left -= len;
+				ptr  += len;
+				if (left <= 0)
+					break;
+			}
+		}
+
+		if (cptr->type == CNODE) {
+			if (cptr->u.conc.id) {
+				len = snprintf(ptr, left, "%s", cptr->u.conc.id);
+				left -= len;
+				ptr  += len;
+				if (left <= 0)
+					break;
+			}
+                }
+
+		if (cptr->type == MNODE) {
+			if (cptr->u.module.id) {
+				len = snprintf(ptr, left, "%s", cptr->u.module.id);
+				left -= len;
+				ptr  += len;
+				if (left <= 0)
+					break;
+			}
+		}
+	}
+
+	return string;
+}
diff -puN /dev/null drivers/char/digi/dgap/dgap_parse.h
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/drivers/char/digi/dgap/dgap_parse.h	Wed Sep 17 12:34:23 2003
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ *	Scott H Kilau <Scott_Kilau at digi dot com>
+ *
+ * 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, EXPRESS OR IMPLIED; 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.
+ *
+ *	NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!!
+ */
+
+#ifndef _DGAP_PARSE_H
+#define _DGAP_PARSE_H
+
+#include "dgap_driver.h"
+
+int dgap_parsefile(char **in, int Remove);
+struct cnode *dgap_find_config(int type);
+u32 dgap_config_get_number_of_ports(struct board_t *bd);
+char *dgap_create_config_string(struct board_t *bd, char *string);
+char *dgap_get_config_letters(struct board_t *bd, char *string);
+u32 dgap_config_get_useintr(struct board_t *bd);
+u32 dgap_config_get_altpin(struct board_t *bd);
+
+#endif
diff -puN /dev/null drivers/char/digi/dgap/dgap_pci.h
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/drivers/char/digi/dgap/dgap_pci.h	Wed Sep 17 12:34:23 2003
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ *	Scott H Kilau <Scott_Kilau at digi dot com>
+ *
+ * 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, EXPRESS OR IMPLIED; 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.
+ *
+ *	NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!!
+ */
+
+/* $Id: dgap_pci.h,v 1.5 2003/09/03 18:22:19 scottk Exp $ */
+
+#ifndef __DGAP_PCI_H
+#define __DGAP_PCI_H
+
+#define PCIMAX 32			/* maximum number of PCI boards */
+
+#define DIGI_VID		0x114F
+
+#define PCI_DEVICE_EPC_DID	0x0002
+#define PCI_DEVICE_XEM_DID	0x0004
+#define PCI_DEVICE_XR_DID	0x0005
+#define PCI_DEVICE_CX_DID	0x0006
+#define PCI_DEVICE_XRJ_DID	0x0009	/* PLX-based Xr adapter */
+#define PCI_DEVICE_XR_IBM_DID	0x0011	/* IBM 8-port Async Adapter */
+#define PCI_DEVICE_XR_422_DID	0x0012	/* Xr-422 */
+#define PCI_DEVICE_920_2_DID	0x0034	/* XR-Plus 920 K, 2 port */
+#define PCI_DEVICE_920_4_DID	0x0026	/* XR-Plus 920 K, 4 port */
+#define PCI_DEVICE_920_8_DID	0x0027	/* XR-Plus 920 K, 8 port */
+#define PCI_DEVICE_EPCJ_DID	0x000a	/* PLX 9060 chip for PCI  */
+#define PCI_DEVICE_CX_IBM_DID	0x001b	/* IBM 128-port Async Adapter */
+
+#define PCI_DEVICE_XEM_NAME	"AccelePort XEM"
+#define PCI_DEVICE_CX_NAME	"AccelePort CX"
+#define PCI_DEVICE_XR_NAME	"AccelePort Xr"
+#define PCI_DEVICE_XRJ_NAME	"AccelePort Xr (PLX)"
+#define PCI_DEVICE_920_2_NAME	"AccelePort Xr920 2 port"
+#define PCI_DEVICE_920_4_NAME	"AccelePort Xr920 4 port"
+#define PCI_DEVICE_920_8_NAME	"AccelePort Xr920 8 port"
+#define PCI_DEVICE_XR_422_NAME	"AccelePort Xr 422"
+#define PCI_DEVICE_EPCJ_NAME	"AccelePort EPC (PLX)"
+#define PCI_DEVICE_XR_IBM_NAME	"AccelePort Xr (IBM)"
+#define PCI_DEVICE_CX_IBM_NAME	"AccelePort CX (IBM)"
+
+/*
+ * On the PCI boards, there is no IO space allocated
+ * The I/O registers will be in the first 3 bytes of the
+ * upper 2MB of the 4MB memory space.  The board memory
+ * will be mapped into the low 2MB of the 4MB memory space
+ */
+
+/* Potential location of PCI Bios from E0000 to FFFFF*/
+#define PCI_BIOS_SIZE		0x00020000
+
+/* Size of Memory and I/O for PCI (4MB) */
+#define PCI_RAM_SIZE		0x00400000
+
+/* Size of Memory (2MB) */
+#define PCI_MEM_SIZE		0x00200000
+
+/* Max PCI Window Size (2MB) */
+#define PCI_WIN_SIZE		0x00200000
+
+#define PCI_WIN_SHIFT		21 /* 21 bits max */
+
+/* Offset of I/0 in Memory (2MB) */
+#define PCI_IO_OFFSET		0x00200000
+
+/* Size of IO (2MB) */
+#define PCI_IO_SIZE		0x00200000
+
+#endif
diff -puN /dev/null drivers/char/digi/dgap/dgap_proc.c
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/drivers/char/digi/dgap/dgap_proc.c	Wed Sep 17 12:34:23 2003
@@ -0,0 +1,700 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ *	Scott H Kilau <Scott_Kilau at digi dot com>
+ *
+ * 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, EXPRESS OR IMPLIED; 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.
+ *
+ *
+ *	NOTE TO LINUX KERNEL HACKERS:  DO NOT REFORMAT THIS CODE!
+ *
+ *	This is shared code between Digi's CVS archive and the
+ *	Linux Kernel sources.
+ *	Changing the source just for reformatting needlessly breaks
+ *	our CVS diff history.
+ *
+ *	Send any bug fixes/changes to:  Eng.Linux at digi dot com.
+ *	Thank you.
+ *
+ */
+
+/************************************************************************
+ *
+ * /proc/dgap/{*} functions
+ *
+ ************************************************************************/
+#define __NO_VERSION__
+#include "dgap_driver.h"
+#include "dgap_parse.h"
+#include "dgap_mgmt.h"
+#include <linux/ctype.h>
+#include <linux/proc_fs.h>
+
+/* external variables */
+extern int              dgap_debug;
+extern struct board_t	*dgap_Board[MAXBOARDS];
+extern uchar		dgap_NumBoards;
+extern u32		dgap_poll_counter;
+
+
+/*
+ * OK, here's the deal with /proc files.  We want to use the
+ * simple (yet inefficient) interface to /proc where we just get
+ * a buffer and fill it in.  Otherwise, we'd have to provide the
+ * full set of functions for a /proc driver.
+ *
+ * When we get called, "buf" points to a single free page which
+ * we can write into.  If you are careful, you can write up
+ * to PAGE_SIZE (4096) bytes into it.  But to be paranoid,
+ * Linux lies and says the "count" is only PROC_BLOK_SIZE (3072).
+ *
+ * In any case, its easiest to make several /proc/dgap/ entries
+ * if you want to print out more info than can fit in a page.
+ *
+ * Here's some further information.  We are trying to be compatible
+ * with 2.0.xx and 2.1.xx.  There have been improvements in 2.1.x
+ * in this area, but it still isn't perfect.  Anyway, our hands
+ * are tied by the 2.0.x limitations.  For example, you cannot
+ * pass private data to the get_info (2.0.x) routines.  Therefore,
+ * you cannot make a generic function to print the data for one
+ * board.  If you want to do this, you have to make a wrapper
+ * function, one function name per board number.  Yech!
+ */
+
+	#define	PROC_READ_PROTO \
+		char *buf, char **start, off_t fpos, int count, \
+		int *eof, void *private
+
+	#define	PROC_READ_ARGS \
+		buf, start, fpos, count, eof, private
+
+
+/*
+ *	Make wrappers for per-board entries. Gag me.  Sorry,
+ *	2.0.x forced us into this since it has no way to pass
+ *	private data to the read_proc (get_info, actually).
+ */
+/* FIXME - Fix this, we can convert away from this now */
+#define	PROCWRAP_FUNCTION(FUNC, NUM) \
+			static int \
+			FUNC ## NUM (PROC_READ_PROTO) \
+			{ \
+				return FUNC(NUM, PROC_READ_ARGS); \
+			}
+
+#define	PROCWRAP_FUNC(FUNC,NUM) \
+		FUNC ## tbl[NUM]
+
+#define	PROCWRAP_FUNCTIONS(FUNC) \
+		PROCWRAP_FUNCTION(FUNC, 0) \
+		PROCWRAP_FUNCTION(FUNC, 1) \
+		PROCWRAP_FUNCTION(FUNC, 2) \
+		PROCWRAP_FUNCTION(FUNC, 3) \
+		PROCWRAP_FUNCTION(FUNC, 4) \
+		PROCWRAP_FUNCTION(FUNC, 5) \
+		PROCWRAP_FUNCTION(FUNC, 6) \
+		PROCWRAP_FUNCTION(FUNC, 7) \
+		PROCWRAP_FUNCTION(FUNC, 8) \
+		PROCWRAP_FUNCTION(FUNC, 9) \
+		PROCWRAP_FUNCTION(FUNC, 10) \
+		PROCWRAP_FUNCTION(FUNC, 11) \
+		PROCWRAP_FUNCTION(FUNC, 12) \
+		PROCWRAP_FUNCTION(FUNC, 13) \
+		PROCWRAP_FUNCTION(FUNC, 14) \
+		PROCWRAP_FUNCTION(FUNC, 15) \
+		PROCWRAP_FUNCTION(FUNC, 16) \
+		PROCWRAP_FUNCTION(FUNC, 17) \
+		PROCWRAP_FUNCTION(FUNC, 18) \
+		PROCWRAP_FUNCTION(FUNC, 19) \
+		PROCWRAP_FUNCTION(FUNC, 20) \
+		PROCWRAP_FUNCTION(FUNC, 21) \
+		PROCWRAP_FUNCTION(FUNC, 22) \
+		PROCWRAP_FUNCTION(FUNC, 23) \
+		PROCWRAP_FUNCTION(FUNC, 24) \
+		PROCWRAP_FUNCTION(FUNC, 25) \
+		PROCWRAP_FUNCTION(FUNC, 26) \
+		PROCWRAP_FUNCTION(FUNC, 27) \
+		PROCWRAP_FUNCTION(FUNC, 28) \
+		PROCWRAP_FUNCTION(FUNC, 29) \
+		PROCWRAP_FUNCTION(FUNC, 30) \
+		PROCWRAP_FUNCTION(FUNC, 31) \
+
+#define	PROCWRAP_TBL(FUNC) \
+			static read_proc_t * FUNC ## tbl[MAXBOARDS] = \
+		{ \
+			FUNC ## 0, \
+			FUNC ## 1, \
+			FUNC ## 2, \
+			FUNC ## 3, \
+			FUNC ## 4, \
+			FUNC ## 5, \
+			FUNC ## 6, \
+			FUNC ## 7, \
+			FUNC ## 8, \
+			FUNC ## 9, \
+			FUNC ## 10, \
+			FUNC ## 11, \
+			FUNC ## 12, \
+			FUNC ## 13, \
+			FUNC ## 14, \
+			FUNC ## 15, \
+			FUNC ## 16, \
+			FUNC ## 17, \
+			FUNC ## 18, \
+			FUNC ## 19, \
+			FUNC ## 20, \
+			FUNC ## 21, \
+			FUNC ## 22, \
+			FUNC ## 23, \
+			FUNC ## 24, \
+			FUNC ## 25, \
+			FUNC ## 26, \
+			FUNC ## 27, \
+			FUNC ## 28, \
+			FUNC ## 29, \
+			FUNC ## 30, \
+			FUNC ## 31, \
+		};
+
+#define	PROCWRAPPERS(FUNC) \
+		PROCWRAP_FUNCTIONS(FUNC) \
+		PROCWRAP_TBL(FUNC)
+
+
+/*
+ * /proc/dgap/<boardno>/tty
+ */
+static struct proc_dir_entry *ProcDgAPTTY[MAXBOARDS];
+
+static int dgap_proc_tty(int brdno, PROC_READ_PROTO)
+{
+	struct board_t *brd;
+	char	*p = buf;
+
+	DPR_PROC(("dgap_proc_tty count=%d fpos=%ld\n", count, fpos));
+
+        brd = dgap_Board[brdno];
+
+        if(!brd)
+                return(-EINVAL);
+
+	/* Prepare the Header Labels */
+	p += sprintf(p, "         Receive Statistics        "
+				 "Transmit Statistics\n");
+	p += sprintf(p, "%2s %19s %7s %19s %7s %s\n",
+                     "Ch", "Chars Rx", "RxCPS",
+                     "Chars Tx", "TxCPS", " Line Status Flags");
+
+	DPR_PROC(("dgap_proc_tty returns %d\n", p-buf));
+
+	return(p-buf);
+}
+
+PROCWRAPPERS(dgap_proc_tty)
+
+
+/*
+ * /proc/dgap/<boardno>/ttys
+ */
+static struct proc_dir_entry *ProcDgAPTTYs[MAXBOARDS];
+
+static int dgap_proc_ttys(int brdno, PROC_READ_PROTO)
+{
+	struct board_t *brd;
+	char	*p = buf;
+
+	DPR_PROC(("dgap_proc_ttys count=%d fpos=%ld\n", count, fpos));
+
+        brd = dgap_Board[brdno];
+
+        if(!brd)
+                return(-EINVAL);
+
+	p += sprintf(p, "Chan\t&tty\tflags\n");
+
+	DPR_PROC(("dgap_proc_ttys returns %d\n", p-buf));
+
+	return(p-buf);
+}
+
+PROCWRAPPERS(dgap_proc_ttys)
+
+
+/*
+ * /proc/dgap/<boardno>/info
+ *
+ * Variables compatible with the shell
+ */
+static struct proc_dir_entry *ProcDgAPBrdInfo[MAXBOARDS];
+
+static int dgap_proc_brd_info(int brdno, PROC_READ_PROTO)
+{
+	struct board_t	*brd;
+	char		*p = buf;
+	char		*name;
+
+	DPR_PROC(("dgap_proc_brd_info\n"));
+
+        brd = dgap_Board[brdno];
+
+        if(!brd)
+                return(-EINVAL);
+
+	name = brd->name;
+
+	p += sprintf(p, "Board Name = %s\n", name);
+	p += sprintf(p, "Board Type = %d\n", brd->type);
+
+	/*
+	 * report some things about the PCI bus that are important
+	 * to some applications
+	 */
+        p += sprintf(p, "Vendor ID = 0x%x\n", brd->vendor);
+        p += sprintf(p, "Device ID = 0x%x\n", brd->device);
+        p += sprintf(p, "Subvendor ID = 0x%x\n", brd->subvendor);
+        p += sprintf(p, "Subdevice ID = 0x%x\n", brd->subdevice);
+
+	/*
+	 * report the physical addresses assigned to us when we got
+	 * registered
+	 */
+        p += sprintf(p, "IO Port = 0x%lx\n", brd->port);
+        p += sprintf(p, "Memory Base Address = 0x%lx\n", brd->membase);
+        p += sprintf(p, "Remapped IO Port Address = 0x%p\n", brd->re_map_port);
+        p += sprintf(p, "Remapped Memory Base Address = 0x%p\n", brd->re_map_membase);
+
+        p += sprintf(p, "Current state of board = %s\n", dgap_state_text[brd->state]);
+        p += sprintf(p, "Interrupt #: %d. Times interrupted: %d\n",
+		brd->irq, brd->intr_count);
+        p += sprintf(p, "Majors allocated to board = TTY: %d PR: %d\n",
+		brd->SerialDriver.major, brd->PrintDriver.major);
+
+	/*
+	 * report available resources on the card
+	 */
+
+	return(p-buf);
+}
+
+PROCWRAPPERS(dgap_proc_brd_info)
+
+
+/*
+ * /proc/dgap/info
+ *
+ *	Variables compatible with the shell
+ */
+static struct proc_dir_entry *ProcDgAPInfo;
+
+static int dgap_proc_info(PROC_READ_PROTO)
+{
+	char	*p = buf;
+
+	DPR_PROC(("dgap_proc_info\n"));
+	p += sprintf(p, "Max Boards = %d\n", MAXBOARDS);
+	p += sprintf(p, "Current number of boards = %d\n", dgap_NumBoards);
+	p += sprintf(p, "Poll counter = %d\n", dgap_poll_counter);
+	p += sprintf(p, "State of driver: %s\n", dgap_driver_state_text[dgap_driver_state]);
+	return(p-buf);
+}
+
+/*
+ * /proc/dgap/<boardno>/mknod
+ */
+static struct proc_dir_entry *ProcDgAPBrdMknod[MAXBOARDS];
+
+static int mknod_tty_cmd(char *buf, char *nodename,
+                     int major, int firstminor, int count, int start)
+{
+	return sprintf(buf, "dgap_mknod\t%s\t\t%d\t%d\t%d\t%d\n",
+		       nodename, major, firstminor, count, start);
+}
+
+
+static int dgap_proc_brd_mknod(int brdno, PROC_READ_PROTO)
+{
+	struct board_t *brd = dgap_Board[brdno];
+	int	bn;
+	char	*p = buf;
+	struct cnode *cptr = NULL;
+	char str[100];
+	int found = FALSE;
+	int ncount = 0;
+	int starto = 0;
+
+	DPR_PROC(("dgap_proc_brd_mknod(%d) %d\n", brdno, count));
+
+        if(!brd)
+                return(-ENODEV);
+
+        bn = brd->boardnum;
+
+
+	/*
+	 * Output the boilerplate at the top of the shell script
+	 */
+	p += sprintf(p, "#!/bin/sh\n\n");
+
+	p += sprintf(p, "# /proc/dgap/mknod [-d]\n");
+	p += sprintf(p, "#\tMake DGAP device nodes.\n");
+	p += sprintf(p, "#\t-d deletes all existing nodes first\n\n");
+
+	p += sprintf(p, "PATH=" SBINDIR ":$PATH\n");
+
+	p += sprintf(p, "[ -d %s ] || mkdir -p -m 755 %s\n",
+                     DEVSTR, DEVSTR);
+
+	p += sprintf(p, "[ \"$1\" != -d ] || rm -rf %s/* || exit 1\n",
+                     DEVSTR);
+
+	p += sprintf(p, "cd %s || exit 2\n\n", DEVSTR);
+
+	/*
+	 * For each board, output the device information in
+	 * a handy table format...
+	 */
+	p += sprintf(p,	"#\t\tNAME\t\tMAJOR\tMINOR\tCOUNT\tSTART\n");
+
+
+	for (cptr = brd->bd_config; cptr; cptr = cptr->next) {
+
+		if ((cptr->type == BNODE) &&
+		    ((cptr->u.board.type == APORT2_920P) || (cptr->u.board.type == APORT4_920P) ||
+		     (cptr->u.board.type == APORT8_920P) || (cptr->u.board.type == PAPORT4) ||
+		     (cptr->u.board.type == PAPORT8))) {
+
+				found = TRUE;
+				if (cptr->u.board.start)
+					starto = cptr->u.board.start;
+				else
+					starto = 1;
+		}
+
+		if (cptr->type == TNODE && found == TRUE) {
+			char *ptr1;
+			if (strstr(cptr->u.ttyname, "tty")) {
+				ptr1 = cptr->u.ttyname;
+				ptr1 += 3;
+			}
+			else {
+				ptr1 = cptr->u.ttyname;
+			}
+
+			/* TTY devices */
+			sprintf(str, "tty%s%%p", ptr1);
+
+			p += mknod_tty_cmd(p, str,
+				brd->SerialDriver.major,
+				0, dgap_config_get_number_of_ports(brd), starto);
+
+			/* PR devices */
+			sprintf(str, "pr%s%%p", ptr1);
+
+			p += mknod_tty_cmd(p, str,
+				brd->PrintDriver.major,
+				0, dgap_config_get_number_of_ports(brd), starto);
+		}
+
+		if (cptr->type == CNODE) {
+
+			sprintf(str, "tty%s%%p", cptr->u.conc.id);
+
+			/* TTY devices */
+			p += mknod_tty_cmd(p, str,
+				brd->SerialDriver.major,
+				ncount, cptr->u.conc.nport,
+				cptr->u.conc.start ? cptr->u.conc.start : 1);
+
+			sprintf(str, "pr%s%%p", cptr->u.conc.id);
+
+			p += mknod_tty_cmd(p, str,
+				brd->PrintDriver.major,
+				ncount, cptr->u.conc.nport,
+				cptr->u.conc.start ? cptr->u.conc.start : 1);
+
+			ncount += cptr->u.conc.nport;
+		}
+
+		if (cptr->type == MNODE) {
+
+			sprintf(str, "tty%s%%p", cptr->u.module.id);
+
+			/* TTY devices */
+			p += mknod_tty_cmd(p, str,
+				brd->SerialDriver.major,
+				ncount, cptr->u.module.nport,
+				cptr->u.module.start ? cptr->u.module.start : 1);
+
+			sprintf(str, "pr%s%%p", cptr->u.module.id);
+
+			p += mknod_tty_cmd(p, str,
+				brd->PrintDriver.major,
+				ncount, cptr->u.module.nport,
+				cptr->u.module.start ? cptr->u.module.start : 1);
+
+			ncount += cptr->u.module.nport;
+
+		}
+	}
+
+	return(p-buf);
+}
+
+PROCWRAPPERS(dgap_proc_brd_mknod)
+
+
+/*
+ *      /proc/dgap/mknod
+ */
+static struct proc_dir_entry *ProcDgAPMknod;
+
+static int dgap_proc_mknod(PROC_READ_PROTO)
+{
+        char    *p = buf;
+
+        DPR_PROC(("dgap_proc_mknod\n"));
+
+        p += sprintf(p, "#!/bin/sh\n\n");
+
+        p += sprintf(p, "#\t/proc/dgap/mknod [-d]\n");
+        p += sprintf(p, "#\t\tMake DGAP device nodes.\n");
+        p += sprintf(p, "#\t\t-d deletes all existing nodes first\n\n");
+
+	p += sprintf(p, "if [ \"$1\" != -d ]\nthen\n\texit 1\nfi\n");
+
+	p += sprintf(p, "if [ -d %s ]\nthen\n", DEVSTR);
+
+        p += sprintf(p, "\tcd %s\n", DEVSTR);
+
+	/* Delete all our ttys */
+        p += sprintf(p, "\tfor i in tty*\n");
+        p += sprintf(p, "\tdo\n");
+
+        p += sprintf(p, "\t\tif [ \"$i\" != \"tty*\" ]\n");
+        p += sprintf(p, "\t\tthen\n");
+
+        p += sprintf(p, "\t\t\trm -rf $i\n");
+        p += sprintf(p, "\t\t\trm -rf /dev/$i\n");
+
+        p += sprintf(p, "\t\tfi\n");
+
+        p += sprintf(p, "\tdone\n");
+
+	/* Delete all our prs */
+        p += sprintf(p, "\tfor i in pr*\n");
+        p += sprintf(p, "\tdo\n");
+
+        p += sprintf(p, "\t\tif [ \"$i\" != \"pr*\" ]\n");
+        p += sprintf(p, "\t\tthen\n");
+
+        p += sprintf(p, "\t\t\trm -rf $i\n");
+        p += sprintf(p, "\t\t\trm -rf /dev/$i\n");
+
+        p += sprintf(p, "\t\tfi\n");
+
+        p += sprintf(p, "\tdone\n");
+
+        p += sprintf(p, "fi\n");
+
+	/* Delete anything that was left. */
+	p += sprintf(p, "rm -rf %s/*\n", DEVSTR);
+
+        p += sprintf(p, "for i in /proc/dgap/*/mknod\n");
+        p += sprintf(p, "do\n");
+
+        p += sprintf(p, "\tif [ \"$i\" != \"/proc/dgap/*/mknod\" ]\n");
+        p += sprintf(p, "\tthen\n");
+
+        p += sprintf(p, "\t\t$i\n");
+
+        p += sprintf(p, "\tfi\n");
+
+        p += sprintf(p, "done\n");
+
+	p += sprintf(p, "#\tCreate the management device.\n\n");
+	p += mknod_tty_cmd(p, "/dev/dg/dgap/mgmt", DIGI_DGAP_MAJOR, MGMT_MGMT, 1, 0);
+	p += mknod_tty_cmd(p, "/dev/dg/dgap/downld", DIGI_DGAP_MAJOR, MGMT_DOWNLD, 1, 0);
+
+        return (p-buf);
+}
+
+
+/*
+ * The directory entries /proc/dgap/<boardnum>
+ */
+static struct proc_dir_entry *ProcDgAPBrd[MAXBOARDS];
+
+/*
+ * The directory entry /proc/dgap
+ */
+static struct proc_dir_entry *ProcDgAP;
+
+/*
+ * Gee, the proc I/F for drivers is *still* not well done in 2.1.x.
+ *
+ * In any event, we'll *make* a better interface here and then
+ * use it to abstract the differences between 2.0.x and 2.1.x
+ *
+ * Unfortunately, the *best* interface would also allow you
+ * to pass in "private" data.  But that can't be supported
+ * on 2.0.x, so that is why this interface is just "better".
+ */
+
+static struct proc_dir_entry *better_create_proc_entry(
+        const char *name, mode_t mode, unsigned long size,
+        read_proc_t *read_proc,	struct proc_dir_entry *parent)
+{
+        struct proc_dir_entry	*pde;
+
+        pde = create_proc_entry(name, mode, parent);
+        if (!pde) return NULL;
+
+        if (size)
+                pde->size = size;
+        if (read_proc)
+                pde->read_proc = read_proc;
+
+        return pde;
+}
+
+static void better_remove_proc_entry(struct proc_dir_entry *pde)
+{
+        if (!pde) return;
+
+        remove_proc_entry(pde->name, pde->parent);
+}
+
+
+/*
+ * Register the basic /proc/dgap files that appear whenever
+ * the driver is loaded.
+ */
+void dgap_proc_register_basic_prescan(void)
+{
+	char	*buf;
+	int	size;
+
+	/* Register /proc/dgap */
+	ProcDgAP = better_create_proc_entry(PROCSTR, S_IFDIR, 0, NULL, 0);
+
+	/* Register /proc/dgap/info */
+	ProcDgAPInfo = better_create_proc_entry( "info", 0, 0,
+			dgap_proc_info, ProcDgAP);
+
+	/*
+	 * Compute size of /proc/dgap/mknod output, fixup
+	 * proc_dir_entry for it, then register it.
+	 */
+	buf = kmalloc(4096, GFP_KERNEL);
+	if (buf) {
+                size = dgap_proc_mknod(buf, NULL, 0, 4096, 0, 0);
+	} else
+		size = 0;
+	kfree(buf);
+
+	ProcDgAPMknod =
+                better_create_proc_entry("mknod", S_IFREG | S_IRUGO | S_IXUGO,
+                                         size, dgap_proc_mknod, ProcDgAP);
+}
+
+
+
+/*
+ * Register the basic /proc/dgap files that appear whenever
+ * the driver is loaded.
+ */
+void dgap_proc_register_basic_postscan(void)
+{
+	char	*buf;
+	int	size;
+	int	i;
+
+	/*
+	 * Register /proc/dgap/<boardnum> directories
+	 * and the /proc/dgap/<boardnum>/info files
+	 */
+	for (i = 0; i < dgap_NumBoards; ++i) {
+		char	name[2];
+
+		name[0] = i + '0';
+		name[1] = 0;
+		ProcDgAPBrd[i] = better_create_proc_entry(name, S_IFDIR, 0,
+                                                          NULL, ProcDgAP);
+
+		ProcDgAPBrdInfo[i] = better_create_proc_entry("info", 0, 0,
+                        PROCWRAP_FUNC(dgap_proc_brd_info, i),
+                        ProcDgAPBrd[i]);
+
+		/*
+                 * Compute size of /proc/dgap/<boardno>/mknod output, fixup
+                 * proc_dir_entry for it, then register it.
+                 */
+                buf = kmalloc(4096, GFP_ATOMIC);
+                if (buf) {
+                        size = dgap_proc_brd_mknod(i, buf, NULL, 0, 4096, 0, 0);
+                } else
+                        size = 0;
+                kfree(buf);
+
+                ProcDgAPBrdMknod[i] = better_create_proc_entry("mknod",
+                                        S_IFREG | S_IRUGO | S_IXUGO, size,
+                                        PROCWRAP_FUNC(dgap_proc_brd_mknod, i),
+                                        ProcDgAPBrd[i]);
+	}
+}
+
+
+/*
+ * Register the proc devices that appear only if we are
+ * loading up a fep.
+ */
+void dgap_proc_register_fep(void)
+{
+	int	i;
+
+	for (i = 0; i < dgap_NumBoards; ++i) {
+
+		ProcDgAPTTYs[i] = better_create_proc_entry( "ttys", 0, 0,
+			PROCWRAP_FUNC(dgap_proc_ttys, i),
+			ProcDgAPBrd[i]);
+
+		ProcDgAPTTY[i] = better_create_proc_entry( "tty", 0, 0,
+			PROCWRAP_FUNC(dgap_proc_tty, i),
+			ProcDgAPBrd[i]);
+	}
+}
+
+/*
+ * Unregister all of our /proc/dgap/{*} devices
+ */
+void dgap_proc_unregister_all(void)
+{
+	int	i;
+
+#define NUKE_ONE(PDE) \
+	if (PDE) { better_remove_proc_entry(PDE); PDE = NULL; } else
+
+	for (i = 0; i < dgap_NumBoards; ++i) {
+		NUKE_ONE(ProcDgAPTTY[i]);
+		NUKE_ONE(ProcDgAPTTYs[i]);
+		NUKE_ONE(ProcDgAPBrdMknod[i]);
+		NUKE_ONE(ProcDgAPBrdInfo[i]);
+		NUKE_ONE(ProcDgAPBrd[i]);
+	}
+
+	NUKE_ONE(ProcDgAPMknod);
+	NUKE_ONE(ProcDgAPInfo);
+
+	NUKE_ONE(ProcDgAP);
+}
diff -puN /dev/null drivers/char/digi/dgap/dgap_proc.h
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/drivers/char/digi/dgap/dgap_proc.h	Wed Sep 17 12:34:23 2003
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ *	Scott H Kilau <Scott_Kilau at digi dot com>
+ *
+ * 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, EXPRESS OR IMPLIED; 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.
+ *
+ *	NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!!
+ */
+
+#ifndef __DGAP_PROC_H
+#define __DGAP_PROC_H
+
+#include "dgap_driver.h"
+
+void	dgap_proc_unregister_all(void);
+void	dgap_proc_register_basic_prescan(void);
+void	dgap_proc_register_basic_postscan(void);
+void	dgap_proc_register_fep(void);
+
+#endif
diff -puN /dev/null drivers/char/digi/dgap/dgap_trace.c
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/drivers/char/digi/dgap/dgap_trace.c	Wed Sep 17 12:34:23 2003
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ *	Scott H Kilau <Scott_Kilau at digi dot com>
+ *
+ * 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, EXPRESS OR IMPLIED; 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.
+ *
+ *
+ *	NOTE TO LINUX KERNEL HACKERS:  DO NOT REFORMAT THIS CODE!
+ *
+ *	This is shared code between Digi's CVS archive and the
+ *	Linux Kernel sources.
+ *	Changing the source just for reformatting needlessly breaks
+ *	our CVS diff history.
+ *
+ *	Send any bug fixes/changes to:  Eng.Linux at digi dot com.
+ *	Thank you.
+ *
+ */
+
+/* $Id: dgap_trace.c,v 1.5 2003/09/08 22:13:53 scottk Exp $ */
+
+#define __NO_VERSION__
+#include "dgap_driver.h"
+#define TRC_TO_CONSOLE 1
+
+
+
+#include <linux/vmalloc.h>
+
+/* file level globals */
+static char *dgap_trcbuf;		/* the ringbuffer */
+
+#if defined(TRC_TO_KMEM)
+static int dgap_trcbufi = 0;		/* index of the tilde at the end of */
+#endif
+
+extern int dgap_trcbuf_size;		/* size of the ringbuffer */
+
+#if defined(TRC_TO_KMEM)
+static spinlock_t dgap_tracef_lock = SPIN_LOCK_UNLOCKED;
+#endif
+
+
+#if !defined(TRC_TO_KMEM) && !defined(TRC_TO_CONSOLE)
+void dgap_tracef(const char *fmt, ...)
+{
+	return;
+}
+
+#else /* !defined(TRC_TO_KMEM) && !defined(TRC_TO_CONSOLE) */
+
+void dgap_tracef(const char *fmt, ...)
+{
+	va_list	         ap;
+	char  	         buf[TRC_MAXMSG+1];
+	size_t		 lenbuf;
+	int		 i;
+	static int	 failed = FALSE;
+# if defined(TRC_TO_KMEM)
+	unsigned long	 flags;
+#endif
+
+	if(failed)
+		return;
+# if defined(TRC_TO_KMEM)
+	DGAP_LOCK(dgap_tracef_lock, flags);
+#endif
+
+	/* Format buf using fmt and arguments contained in ap. */
+	va_start(ap, fmt);
+	i = vsprintf(buf, fmt,  ap);
+	va_end(ap);
+	lenbuf = strlen(buf);
+
+# if defined(TRC_TO_KMEM)
+	{
+		static int	 initd=0;
+
+		/*
+		 * Now, in addition to (or instead of) printing this stuff out
+		 * (which is a buffered operation), also tuck it away into a
+		 * corner of memory which can be examined post-crash in kdb.
+		 */
+		if (!initd) {
+			dgap_trcbuf = (char *) vmalloc(dgap_trcbuf_size);
+			if(!dgap_trcbuf) {
+				failed = TRUE;
+				printk("dgap: tracing init failed!\n");
+				return;
+			}
+
+			memset(dgap_trcbuf, '\0',  dgap_trcbuf_size);
+			dgap_trcbufi = 0;
+			initd++;
+
+			printk("dgap: tracing enabled - " TRC_DTRC
+				" 0x%lx 0x%x\n",
+				(unsigned long)dgap_trcbuf,
+				dgap_trcbuf_size);
+		}
+
+#  if defined(TRC_ON_OVERFLOW_WRAP_AROUND)
+		/*
+		 * This is the less CPU-intensive way to do things.  We simply
+		 * wrap around before we fall off the end of the buffer.  A
+		 * tilde (~) demarcates the current end of the trace.
+		 *
+		 * This method should be used if you are concerned about race
+		 * conditions as it is less likely to affect the timing of
+		 * things.
+		 */
+
+		if (dgap_trcbufi + lenbuf >= dgap_trcbuf_size) {
+			/* We are wrapping, so wipe out the last tilde. */
+			dgap_trcbuf[dgap_trcbufi] = '\0';
+			/* put the new string at the beginning of the buffer */
+			dgap_trcbufi = 0;
+		}
+
+		strcpy(&dgap_trcbuf[dgap_trcbufi], buf);
+		dgap_trcbufi += lenbuf;
+		dgap_trcbuf[dgap_trcbufi] = '~';
+
+#  elif defined(TRC_ON_OVERFLOW_SHIFT_BUFFER)
+		/*
+		 * This is the more CPU-intensive way to do things.  If we
+		 * venture into the last 1/8 of the buffer, we shift the
+		 * last 7/8 of the buffer forward, wiping out the first 1/8.
+		 * Advantage: No wrap-around, only truncation from the
+		 * beginning.
+		 *
+		 * This method should not be used if you are concerned about
+		 * timing changes affecting the behaviour of the driver (ie,
+		 * race conditions).
+		 */
+		strcpy(&dgap_trcbuf[dgap_trcbufi], buf);
+		dgap_trcbufi += lenbuf;
+		dgap_trcbuf[dgap_trcbufi] = '~';
+		dgap_trcbuf[dgap_trcbufi+1] = '\0';
+
+		/* If we're near the end of the trace buffer... */
+		if (dgap_trcbufi > (dgap_trcbuf_size/8)*7) {
+			/* Wipe out the first eighth to make some more room. */
+			strcpy(dgap_trcbuf, &dgap_trcbuf[dgap_trcbuf_size/8]);
+			dgap_trcbufi = strlen(dgap_trcbuf)-1;
+			/* Plop overflow message at the top of the buffer. */
+			bcopy(TRC_OVERFLOW, dgap_trcbuf, strlen(TRC_OVERFLOW));
+		}
+#  else
+#   error "TRC_ON_OVERFLOW_WRAP_AROUND or TRC_ON_OVERFLOW_SHIFT_BUFFER?"
+#  endif
+	}
+	DGAP_UNLOCK(dgap_tracef_lock, flags);
+
+# endif /* defined(TRC_TO_KMEM) */
+}
+
+#endif /* !defined(TRC_TO_KMEM) && !defined(TRC_TO_CONSOLE) */
+
+
+/*
+ * dgap_tracer_free()
+ *
+ *
+ */
+void dgap_tracer_free(void)
+{
+	if(dgap_trcbuf)
+		vfree(dgap_trcbuf);
+}
diff -puN /dev/null drivers/char/digi/dgap/dgap_trace.h
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/drivers/char/digi/dgap/dgap_trace.h	Wed Sep 17 12:34:23 2003
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ *	Scott H Kilau <Scott_Kilau at digi dot com>
+ *
+ * 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, EXPRESS OR IMPLIED; 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.
+ *
+ *	NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!!
+ *
+ *****************************************************************************
+ * Header file for dgap_trace.c
+ *
+ * $Id: dgap_trace.h,v 1.3 2003/09/03 18:22:32 scottk Exp $
+ */
+
+#ifndef __DGAP_TRACE_H
+#define __DGAP_TRACE_H
+
+#include "dgap_driver.h"
+
+void dgap_tracef(const char *fmt, ...);
+void dgap_tracer_free(void);
+
+#endif
+
diff -puN /dev/null drivers/char/digi/dgap/dgap_tty.c
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/drivers/char/digi/dgap/dgap_tty.c	Wed Sep 17 12:34:23 2003
@@ -0,0 +1,3938 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ *	Scott H Kilau <Scott_Kilau at digi dot com>
+ *
+ * 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, EXPRESS OR IMPLIED; 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.
+ *
+ *
+ *	NOTE TO LINUX KERNEL HACKERS:  DO NOT REFORMAT THIS CODE!
+ *
+ *	This is shared code between Digi's CVS archive and the
+ *	Linux Kernel sources.
+ *	Changing the source just for reformatting needlessly breaks
+ *	our CVS diff history.
+ *
+ *	Send any bug fixes/changes to:  Eng.Linux at digi dot com.
+ *	Thank you.
+ */
+
+/************************************************************************
+ *
+ * This file implements the tty driver functionality for the
+ * FEP5 based product lines.
+ *
+ ************************************************************************
+ *
+ * $Id: dgap_tty.c,v 1.61 2003/09/09 15:19:47 scottk Exp $
+ */
+
+#include "dgap_driver.h"
+#include <linux/ctype.h>
+#include "dgap_types.h"
+#include "dgap_fep5.h"
+#include "dgap_parse.h"
+
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+
+#ifndef _POSIX_VDISABLE
+#define   _POSIX_VDISABLE '\0'
+#endif
+
+/*
+ * external variables
+ */
+extern int              dgap_debug;
+extern int              dgap_rawreadok;
+extern spinlock_t	dgap_global_lock;
+
+/*
+ * internal variables
+ */
+static struct board_t	*dgap_BoardsByMajor[256];
+static u32		dgap_count = 500;
+static uchar		*dgap_TmpWriteBuf = NULL;
+static DECLARE_MUTEX(dgap_TmpWriteSem);
+
+/*
+ * Default transparent print information.
+ */
+static struct digi_t digi_init = {
+	DIGI_COOK,		/* Flags			*/
+	100,			/* Max CPS			*/
+	50,			/* Max chars in print queue	*/
+	100,			/* Printer buffer size		*/
+	4,			/* size of printer on string	*/
+	4,			/* size of printer off string	*/
+	"\033[5i",		/* ANSI printer on string ]	*/
+	"\033[4i",		/* ANSI printer off string ]	*/
+	"ansi"			/* default terminal type	*/
+};
+
+
+/*
+ * Define a local default termios struct. All ports will be created
+ * with this termios initially.
+ *
+ * This defines a raw port at 9600 baud, 8 data bits, no parity,
+ * 1 stop bit.
+ */
+static struct termios DefaultTermios =
+{
+	c_iflag: (DEFAULT_IFLAGS),	/* iflags */
+	c_oflag: (DEFAULT_OFLAGS),	/* oflags */
+	c_cflag: (DEFAULT_CFLAGS),	/* cflags */
+	c_lflag: (DEFAULT_LFLAGS),	/* lflags */
+	c_cc:    INIT_C_CC,
+	c_line:  0,
+};
+
+/* Our function prototypes */
+static void dgap_cmdb(struct channel_t *ch, uchar cmd, uchar byte1, uchar byte2, u32 ncmds);
+static void dgap_cmdw(struct channel_t *ch, uchar cmd, u16 word, u32 ncmds);
+static int dgap_tty_open(struct tty_struct *tty, struct file *file);
+static void dgap_tty_close(struct tty_struct *tty, struct file *file);
+static int block_til_ready(struct tty_struct *tty, struct file *file, struct channel_t *ch);
+static void dgap_carrier(struct channel_t *ch);
+static int dgap_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg);
+static int dgap_tty_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count);
+static int dgap_tty_digigeta(struct tty_struct *tty, struct digi_t *retinfo);
+static int dgap_tty_digiseta(struct tty_struct *tty, struct digi_t *new_info);
+static int dgap_tty_write_room(struct tty_struct* tty);
+static void dgap_tty_put_char(struct tty_struct *tty, unsigned char c);
+static void dgap_tty_set_termios(struct tty_struct *tty, struct termios *old_termios);
+static int dgap_tty_chars_in_buffer(struct tty_struct* tty);
+static void dgap_tty_start(struct tty_struct *tty);
+static void dgap_tty_stop(struct tty_struct *tty);
+static void dgap_tty_throttle(struct tty_struct *tty);
+static void dgap_tty_unthrottle(struct tty_struct *tty);
+static void dgap_tty_flush_chars(struct tty_struct *tty);
+static void dgap_tty_flush_buffer(struct tty_struct *tty);
+static void dgap_tty_hangup(struct tty_struct *tty);
+static int dgap_wait_for_drain(struct tty_struct *tty);
+static int dgap_set_modem_info(struct tty_struct *tty, unsigned int command, unsigned int *value);
+static int dgap_get_modem_info(struct channel_t *ch, unsigned int *value);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ static int dgap_tty_tiocmget(struct tty_struct *tty, struct file *file);
+ static int dgap_tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear);
+#endif
+
+
+/************************************************************************
+ *
+ * TTY Initialization/Cleanup Functions
+ *
+ ************************************************************************/
+
+/*
+ * dgap_tty_preinit()
+ *
+ * Initialize any global tty related data before we download any boards.
+ */
+int dgap_tty_preinit(void)
+{
+	unsigned long flags;
+
+	DGAP_LOCK(dgap_global_lock, flags);
+
+	/*
+	 * Allocate a buffer for doing the copy from user space to
+	 * kernel space in dgap_input().  We only use one buffer and
+	 * control access to it with a semaphore.  If we are paging, we
+	 * are already in trouble so one buffer won't hurt much anyway.
+	 */
+	dgap_TmpWriteBuf = kmalloc(WRITEBUFLEN, GFP_ATOMIC);
+
+	if (!dgap_TmpWriteBuf) {
+		DGAP_UNLOCK(dgap_global_lock, flags);
+		DPR_INIT(("unable to allocate tmp write buf"));
+		return (-ENOMEM);
+	}
+
+        DGAP_UNLOCK(dgap_global_lock, flags);
+        return(0);
+}
+
+
+/*
+ * dgap_tty_register()
+ *
+ * Init the tty subsystem for this board.
+ */
+int dgap_tty_register(struct board_t *brd)
+{
+	int rc = 0;
+	char buf[MAXTTYNAMELEN];
+	char *ptr;
+
+	DPR_INIT(("tty_register start"));
+
+	buf[0] = '\0';
+
+	ptr = dgap_get_config_letters(brd, buf);
+
+        memset(&brd->SerialDriver, 0, sizeof(struct tty_driver));
+	memset(&brd->PrintDriver, 0, sizeof(struct tty_driver));
+
+	brd->SerialDriver.magic = TTY_DRIVER_MAGIC;
+
+	snprintf(brd->SerialName, MAXTTYNAMELEN, "tty[%s]", buf[0] != '\0' ? buf : "");
+	brd->SerialDriver.name = brd->SerialName;
+	brd->SerialDriver.name_base = 0;
+	brd->SerialDriver.major = 0;
+	brd->SerialDriver.minor_start = 0;
+	brd->SerialDriver.num = MAXPORTS;
+	brd->SerialDriver.type = TTY_DRIVER_TYPE_SERIAL;
+	brd->SerialDriver.subtype = SERIAL_TYPE_NORMAL;
+	brd->SerialDriver.init_termios = DefaultTermios;
+	brd->SerialDriver.flags = (TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS);
+	brd->SerialDriver.driver_name = DRVSTR;
+
+	/*
+	 * The kernel wants space to store pointers to
+	 * tty_struct's and termios's.
+	 */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+	brd->SerialDriver.ttys = dgap_driver_kzmalloc(MAXPORTS * sizeof(struct tty_struct *), GFP_KERNEL);
+	if (!brd->SerialDriver.ttys)
+		return(-ENOMEM);
+
+	brd->SerialDriver.refcount = brd->TtyRefCnt;
+#else
+	brd->SerialDriver.table = dgap_driver_kzmalloc(MAXPORTS * sizeof(struct tty_struct *), GFP_KERNEL);
+	if (!brd->SerialDriver.table)
+		return(-ENOMEM);
+
+	brd->SerialDriver.refcount = &brd->TtyRefCnt;
+
+#endif
+
+	brd->SerialDriver.termios = dgap_driver_kzmalloc(MAXPORTS * sizeof(struct termios *), GFP_KERNEL);
+
+	if (!brd->SerialDriver.termios)
+		return(-ENOMEM);
+
+	brd->SerialDriver.termios_locked = dgap_driver_kzmalloc(MAXPORTS * sizeof(struct termios *), GFP_KERNEL);
+
+	if (!brd->SerialDriver.termios_locked)
+		return(-ENOMEM);
+
+	/*
+	 * Entry points for driver.  Called by the kernel from
+	 * tty_io.c and n_tty.c.
+	 */
+	brd->SerialDriver.open = dgap_tty_open;
+	brd->SerialDriver.close = dgap_tty_close;
+	brd->SerialDriver.ioctl = dgap_tty_ioctl;
+	brd->SerialDriver.write = dgap_tty_write;
+	brd->SerialDriver.write_room = dgap_tty_write_room;
+	brd->SerialDriver.put_char = dgap_tty_put_char;
+	brd->SerialDriver.set_termios = dgap_tty_set_termios;
+	brd->SerialDriver.chars_in_buffer = dgap_tty_chars_in_buffer;
+	brd->SerialDriver.stop = dgap_tty_stop;
+	brd->SerialDriver.start = dgap_tty_start;
+	brd->SerialDriver.throttle = dgap_tty_throttle;
+	brd->SerialDriver.unthrottle = dgap_tty_unthrottle;
+	brd->SerialDriver.flush_chars = dgap_tty_flush_chars;
+	brd->SerialDriver.flush_buffer = dgap_tty_flush_buffer;
+	brd->SerialDriver.hangup = dgap_tty_hangup;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+	brd->SerialDriver.owner = THIS_MODULE;
+	brd->SerialDriver.tiocmget = dgap_tty_tiocmget;
+	brd->SerialDriver.tiocmset = dgap_tty_tiocmset;
+#endif
+
+	/*
+	 * If we're doing transparent print, we have to do all of the above
+	 * again, seperately so we don't get the LD confused about what major
+	 * we are when we get into the dgap_tty_open() routine.
+	 */
+	brd->PrintDriver.magic = TTY_DRIVER_MAGIC;
+	snprintf(brd->PrintName, MAXTTYNAMELEN, "pr[%s]", buf[0] != '\0' ? buf : "");
+	brd->PrintDriver.name = brd->PrintName;
+	brd->PrintDriver.name_base = 0;
+	brd->PrintDriver.major = 0;
+	brd->PrintDriver.minor_start = 0;
+	brd->PrintDriver.num = MAXPORTS;
+	brd->PrintDriver.type = TTY_DRIVER_TYPE_SERIAL;
+	brd->PrintDriver.subtype = SERIAL_TYPE_NORMAL;
+	brd->PrintDriver.init_termios = DefaultTermios;
+	brd->PrintDriver.flags = (TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS);
+	brd->PrintDriver.driver_name = DRVSTR;
+
+	/*
+	 * The kernel wants space to store pointers to
+	 * tty_struct's and termios's.  Must be seperate from
+	 * the Serial Driver so we don't get confused
+	 */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+	brd->PrintDriver.ttys = dgap_driver_kzmalloc(MAXPORTS * sizeof(struct tty_struct *), GFP_KERNEL);
+	if (!brd->PrintDriver.ttys)
+		return(-ENOMEM);
+
+	brd->PrintDriver.refcount = brd->TtyRefCnt;
+#else
+	brd->PrintDriver.table = dgap_driver_kzmalloc(MAXPORTS * sizeof(struct tty_struct *), GFP_KERNEL);
+	if (!brd->PrintDriver.table)
+		return(-ENOMEM);
+
+	brd->PrintDriver.refcount = &brd->TtyRefCnt;
+
+#endif
+
+	brd->PrintDriver.termios = dgap_driver_kzmalloc(MAXPORTS * sizeof(struct termios *), GFP_KERNEL);
+
+	if (!brd->PrintDriver.termios)
+		return(-ENOMEM);
+
+	brd->PrintDriver.termios_locked = dgap_driver_kzmalloc(MAXPORTS * sizeof(struct termios *), GFP_KERNEL);
+
+	if (!brd->PrintDriver.termios_locked)
+		return(-ENOMEM);
+
+	/*
+	 * Entry points for driver.  Called by the kernel from
+	 * tty_io.c and n_tty.c.
+	 */
+	brd->PrintDriver.open = dgap_tty_open;
+	brd->PrintDriver.close = dgap_tty_close;
+	brd->PrintDriver.ioctl = dgap_tty_ioctl;
+	brd->PrintDriver.write = dgap_tty_write;
+	brd->PrintDriver.write_room = dgap_tty_write_room;
+	brd->PrintDriver.put_char = dgap_tty_put_char;
+	brd->PrintDriver.set_termios = dgap_tty_set_termios;
+	brd->PrintDriver.chars_in_buffer = dgap_tty_chars_in_buffer;
+	brd->PrintDriver.stop = dgap_tty_stop;
+	brd->PrintDriver.start = dgap_tty_start;
+	brd->PrintDriver.throttle = dgap_tty_throttle;
+	brd->PrintDriver.unthrottle = dgap_tty_unthrottle;
+	brd->PrintDriver.flush_chars = dgap_tty_flush_chars;
+	brd->PrintDriver.flush_buffer = dgap_tty_flush_buffer;
+	brd->PrintDriver.hangup = dgap_tty_hangup;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+	brd->SerialDriver.owner = THIS_MODULE;
+	brd->PrintDriver.tiocmget = dgap_tty_tiocmget;
+	brd->PrintDriver.tiocmset = dgap_tty_tiocmset;
+#endif
+
+	if (!brd->dgap_Major_Serial_Registered) {
+		/* Register tty devices */
+		rc = tty_register_driver(&brd->SerialDriver);
+		if (rc < 0) {
+			APR(("Can't register tty device (%d)\n", rc));
+			return(rc);
+		}
+		brd->dgap_Major_Serial_Registered = TRUE;
+		dgap_BoardsByMajor[brd->SerialDriver.major] = brd;
+		brd->dgap_Serial_Major = brd->SerialDriver.major;
+	}
+
+	if (!brd->dgap_Major_TransparentPrint_Registered) {
+		/* Register Transparent Print devices */
+ 		rc = tty_register_driver(&brd->PrintDriver);
+		if (rc < 0) {
+			APR(("Can't register Transparent Print device (%d)\n", rc));
+			return(rc);
+		}
+		brd->dgap_Major_TransparentPrint_Registered = TRUE;
+		dgap_BoardsByMajor[brd->PrintDriver.major] = brd;
+		brd->dgap_TransparentPrint_Major = brd->PrintDriver.major;
+	}
+
+	DPR_INIT(("DGAP REGISTER TTY: MAJORS: %d %d\n", brd->SerialDriver.major,
+		brd->PrintDriver.major));
+
+	return (rc);
+}
+
+
+/*
+ * dgap_tty_init()
+ *
+ * Init the tty subsystem.  Called once per board after board has been
+ * downloaded and init'ed.
+ */
+int dgap_tty_init(struct board_t *brd)
+{
+	int i;
+	int tlw;
+	uint true_count = 0;
+	uchar *vaddr;
+	uchar modem = 0;
+	struct channel_t *ch;
+	struct bs_t *bs;
+	struct cm_t *cm;
+
+	if (!brd)
+		return (-ENXIO);
+
+	DPR_INIT(("dgap_tty_init start\n"));
+
+	/*
+	 * Initialize board structure elements.
+	 */
+
+	vaddr = brd->re_map_membase;
+	true_count = readw((vaddr + NCHAN));
+
+	brd->nasync = dgap_config_get_number_of_ports(brd);
+
+	if (!brd->nasync) {
+		brd->nasync = brd->maxports;
+	}
+
+	if (brd->nasync > brd->maxports) {
+		brd->nasync = brd->maxports;
+	}
+
+	if (true_count != brd->nasync) {
+		if ((brd->type == PPCM) && (true_count == 64)) {
+			APR(("***WARNING**** %s configured for %d ports, has %d ports.\nPlease make SURE the EBI cable running from the card\nto each EM module is plugged into EBI IN!\n",
+				brd->name, brd->nasync, true_count));
+		}
+		else if ((brd->type == PPCM) && (true_count == 0)) {
+			APR(("***WARNING**** %s configured for %d ports, has %d ports.\nPlease make SURE the EBI cable running from the card\nto each EM module is plugged into EBI IN!\n",
+				brd->name, brd->nasync, true_count));
+		}
+		else {
+			APR(("***WARNING**** %s configured for %d ports, has %d ports.\n",
+				brd->name, brd->nasync, true_count));
+		}
+
+		brd->nasync = true_count;
+
+		/* If no ports, don't bother going any further */
+		if (!brd->nasync) {
+			brd->state = BOARD_FAILED;
+			brd->dpastatus = BD_NOFEP;
+			return(-ENXIO);
+		}
+	}
+
+	/*
+	 * Allocate channel memory that might not have been allocated
+	 * when the driver was first loaded.
+	 */
+	for (i = 0; i < brd->nasync; i++) {
+		if (!brd->channels[i]) {
+			brd->channels[i] = dgap_driver_kzmalloc(sizeof(struct channel_t), GFP_ATOMIC);
+			if (!brd->channels[i]) {
+				DPR_CORE(("%s:%d Unable to allocate memory for channel struct\n",
+				    __FILE__, __LINE__));
+			}
+		}
+	}
+
+	ch = brd->channels[0];
+	vaddr = brd->re_map_membase;
+
+	bs = (struct bs_t *) ((ulong) vaddr + CHANBUF);
+	cm = (struct cm_t *) ((ulong) vaddr + CMDBUF);
+
+	brd->bd_bs = bs;
+
+	/* Set up channel variables */
+	for (i = 0; i < brd->nasync; i++, ch = brd->channels[i], bs++) {
+
+		if (!brd->channels[i])
+			continue;
+
+		DGAP_SPINLOCK_INIT(ch->ch_lock);
+
+		/* Store all our magic numbers */
+		ch->magic = DGAP_CHANNEL_MAGIC;
+		ch->ch_tun.magic = DGAP_UNIT_MAGIC;
+		ch->ch_pun.magic = DGAP_UNIT_MAGIC;
+
+		ch->ch_vaddr = vaddr;
+		ch->ch_bs = bs;
+		ch->ch_cm = cm;
+		ch->ch_bd = brd;
+		ch->ch_portnum = i;
+		ch->ch_digi = digi_init;
+
+		/*
+		 * Set up digi dsr and dcd bits based on altpin flag.
+		 */
+		if (dgap_config_get_altpin(brd)) {
+			ch->ch_dsr	= DM_CD;
+			ch->ch_cd	= DM_DSR;
+			ch->ch_digi.digi_flags |= DIGI_ALTPIN;
+		}
+		else {
+			ch->ch_cd	= DM_CD;
+			ch->ch_dsr	= DM_DSR;
+		}
+
+		ch->ch_taddr = vaddr + ((ch->ch_bs->tx_seg) << 4);
+		ch->ch_raddr = vaddr + ((ch->ch_bs->rx_seg) << 4);
+		ch->ch_tx_win = 0;
+		ch->ch_rx_win = 0;
+		ch->ch_tsize = readw(&(ch->ch_bs->tx_max)) + 1;
+		ch->ch_rsize = readw(&(ch->ch_bs->rx_max)) + 1;
+		ch->ch_tstart = 0;
+		ch->ch_rstart = 0;
+
+		/* .25 second delay */
+		ch->ch_close_delay = 250;
+
+		/*
+		 * Set queue water marks, interrupt mask,
+		 * and general tty parameters.
+		 */
+		ch->ch_tlw = tlw = ch->ch_tsize >= 2000 ? ((ch->ch_tsize * 5) / 8) : ch->ch_tsize / 2;
+
+		dgap_cmdw(ch, STLOW, tlw, 0);
+
+		dgap_cmdw(ch, SRLOW, ch->ch_rsize / 2, 0);
+
+		dgap_cmdw(ch, SRHIGH, 7 * ch->ch_rsize / 8, 0);
+
+		ch->ch_mistat = readb(&(ch->ch_bs->m_stat));
+
+		init_waitqueue_head(&ch->ch_flags_wait);
+		init_waitqueue_head(&ch->ch_tun.un_flags_wait);
+		init_waitqueue_head(&ch->ch_pun.un_flags_wait);
+
+		/* Turn on all modem interrupts for now */
+		modem = (DM_CD | DM_DSR | DM_CTS | DM_RI);
+		writeb(modem, &(ch->ch_bs->m_int));
+	}
+
+	/*
+	 * Set edelay to 0 if interrupts are turned on,
+	 * otherwise set edelay to the usual 100.
+	 */
+	if (brd->intr_used)
+		writew(0, &(brd->bd_bs->edelay));
+	else
+		writew(100, &(brd->bd_bs->edelay));
+
+	writeb(1, &(brd->bd_bs->idata));
+
+	DPR_INIT(("dgap_tty_init finish\n"));
+
+	return (0);
+}
+
+
+/*
+ * dgap_tty_post_uninit()
+ *
+ * UnInitialize any global tty related data.
+ */
+void dgap_tty_post_uninit(void)
+{
+	if (dgap_TmpWriteBuf) {
+		kfree(dgap_TmpWriteBuf);
+		dgap_TmpWriteBuf = NULL;
+	}
+}
+
+
+/*
+ * dgap_tty_uninit()
+ *
+ * Uninitialize the TTY portion of this driver.  Free all memory and
+ * resources.
+ */
+void dgap_tty_uninit(struct board_t *brd)
+{
+	if (brd->dgap_Major_Serial_Registered) {
+		dgap_BoardsByMajor[brd->SerialDriver.major] = NULL;
+		brd->dgap_Serial_Major = 0;
+		tty_unregister_driver(&brd->SerialDriver);
+		brd->dgap_Major_Serial_Registered = FALSE;
+	}
+
+	if (brd->dgap_Major_TransparentPrint_Registered) {
+		dgap_BoardsByMajor[brd->PrintDriver.major] = 0;
+		brd->dgap_TransparentPrint_Major = 0;
+		tty_unregister_driver(&brd->PrintDriver);
+		brd->dgap_Major_TransparentPrint_Registered = FALSE;
+	}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+	if (brd->SerialDriver.ttys) {
+		kfree(brd->SerialDriver.ttys);
+		brd->SerialDriver.ttys = NULL;
+	}
+	if (brd->PrintDriver.ttys) {
+		kfree(brd->PrintDriver.ttys);
+		brd->PrintDriver.ttys = NULL;
+        }
+#else
+	if (brd->SerialDriver.table) {
+		kfree(brd->SerialDriver.table);
+		brd->SerialDriver.table = NULL;
+	}
+	if (brd->PrintDriver.table) {
+		kfree(brd->PrintDriver.table);
+		brd->PrintDriver.table = NULL;
+        }
+#endif
+}
+
+
+/*=======================================================================
+ *
+ *      dgap_cmdb - Sends a 2 byte command to the FEP.
+ *
+ *              ch      - Pointer to channel structure.
+ *              cmd     - Command to be sent.
+ *              byte1   - Integer containing first byte to be sent.
+ *              byte2   - Integer containing second byte to be sent.
+ *              ncmds   - Wait until ncmds or fewer cmds are left
+ *                        in the cmd buffer before returning.
+ *
+ *=======================================================================*/
+void dgap_cmdb(struct channel_t *ch, uchar cmd, uchar byte1, uchar byte2, u32 ncmds)
+{
+	char		*vaddr = NULL;
+	struct cm_t	*cm_addr = NULL;
+	u16		head;
+        u16		tail;
+	u32		count;
+	u32		n;
+
+	if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+		return;
+
+	/*
+	 * Check if board is still alive.
+	 */
+	if (ch->ch_bd->state == BOARD_FAILED) {
+		DPR_CORE(("%s:%d board is in failed state.\n", __FILE__, __LINE__));
+		return;
+        }
+
+	/*
+	 * Make sure the pointers are in range before
+	 * writing to the FEP memory.
+	 */
+	vaddr = ch->ch_bd->re_map_membase;
+
+	if (!vaddr)
+		return;
+
+	cm_addr = (struct cm_t *) (vaddr + CMDBUF);
+	head = readw(&(cm_addr->cm_head));
+
+	/*
+	 * Forget it if pointers out of range.
+	 */
+	if (head >= (CMDMAX - CMDSTART) || (head & 03)) {
+		DPR_CORE(("%s:%d pointers out of range, failing board!\n", __FILE__, __LINE__));
+		ch->ch_bd->state = BOARD_FAILED;
+		return;
+	}
+
+	/*
+	 * Put the data in the circular command buffer.
+	 */
+	writeb(cmd, (char *) (vaddr + head + CMDSTART + 0));
+	writeb((uchar) ch->ch_portnum, (char *) (vaddr + head + CMDSTART + 1));
+	writeb(byte1, (char *) (vaddr + head + CMDSTART + 2));
+	writeb(byte2, (char *) (vaddr + head + CMDSTART + 3));
+
+	head = (head + 4) & (CMDMAX - CMDSTART - 4);
+
+	writew(head, &(cm_addr->cm_head));
+
+	/*
+	 * Wait if necessary before updating the head
+	 * pointer to limit the number of outstanding
+	 * commands to the FEP.   If the time spent waiting
+	 * is outlandish, declare the FEP dead.
+	 */
+	for (count = dgap_count ;;) {
+
+		head = readw(&(cm_addr->cm_head));
+		tail = readw(&(cm_addr->cm_tail));
+
+		n = (head - tail) & (CMDMAX - CMDSTART - 4);
+
+		if (n <= ncmds * sizeof(struct cm_t))
+			break;
+
+		if (--count == 0) {
+			DPR_CORE(("%s:%d failing board.\n",__FILE__, __LINE__));
+			ch->ch_bd->state = BOARD_FAILED;
+			return;
+		}
+		udelay(10);
+	}
+}
+
+
+/*=======================================================================
+ *
+ *      dgap_cmdw - Sends a 1 word command to the FEP.
+ *
+ *              ch      - Pointer to channel structure.
+ *              cmd     - Command to be sent.
+ *              word    - Integer containing word to be sent.
+ *              ncmds   - Wait until ncmds or fewer cmds are left
+ *                        in the cmd buffer before returning.
+ *
+ *=======================================================================*/
+void dgap_cmdw(struct channel_t *ch, uchar cmd, u16 word, u32 ncmds)
+{
+	char		*vaddr = NULL;
+	struct cm_t	*cm_addr = NULL;
+	u16		head;
+	u16		tail;
+	u32		count;
+	u32		n;
+
+	if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+		return;
+
+	/*
+	 * Check if board is still alive.
+	 */
+	if (ch->ch_bd->state == BOARD_FAILED) {
+		DPR_CORE(("%s:%d board is failed!\n", __FILE__, __LINE__));
+		return;
+	}
+
+	/*
+	 * Make sure the pointers are in range before
+	 * writing to the FEP memory.
+	 */
+	vaddr = ch->ch_bd->re_map_membase;
+	if (!vaddr)
+		return;
+
+	cm_addr = (struct cm_t *) (vaddr + CMDBUF);
+	head = readw(&(cm_addr->cm_head));
+
+	/*
+	 * Forget it if pointers out of range.
+	 */
+	if (head >= (CMDMAX - CMDSTART) || (head & 03)) {
+		DPR_CORE(("%s:%d Pointers out of range.  Failing board.\n",__FILE__, __LINE__));
+		ch->ch_bd->state = BOARD_FAILED;
+		return;
+	}
+
+	/*
+	 * Put the data in the circular command buffer.
+	 */
+	writeb(cmd, (char *) (vaddr + head + CMDSTART + 0));
+	writeb((uchar) ch->ch_portnum, (char *) (vaddr + head + CMDSTART + 1));
+	writew((u16) word, (char *) (vaddr + head + CMDSTART + 2));
+
+	head = (head + 4) & (CMDMAX - CMDSTART - 4);
+
+	writew(head, &(cm_addr->cm_head));
+
+	/*
+	 * Wait if necessary before updating the head
+	 * pointer to limit the number of outstanding
+	 * commands to the FEP.   If the time spent waiting
+	 * is outlandish, declare the FEP dead.
+	 */
+	for (count = dgap_count ;;) {
+
+		head = readw(&(cm_addr->cm_head));
+		tail = readw(&(cm_addr->cm_tail));
+
+		n = (head - tail) & (CMDMAX - CMDSTART - 4);
+
+		if (n <= ncmds * sizeof(struct cm_t))
+			break;
+
+		if (--count == 0) {
+			DPR_CORE(("%s:%d Failing board.\n",__FILE__, __LINE__));
+			ch->ch_bd->state = BOARD_FAILED;
+			return;
+		}
+		udelay(10);
+	}
+}
+
+
+/*=======================================================================
+ *
+ *      dgap_wmove - Write data to FEP buffer.
+ *
+ *              ch      - Pointer to channel structure.
+ *              buf     - Poiter to characters to be moved.
+ *              cnt     - Number of characters to move.
+ *
+ *=======================================================================*/
+void dgap_wmove(struct channel_t *ch, char *buf, u32 cnt)
+{
+	int    n;
+	char   *taddr;
+	struct bs_t    *bs;
+	u32    head;
+
+	if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+		return;
+
+	/*
+	 * Check parameters.
+	 */
+	bs   = ch->ch_bs;
+	head = readw(&(bs->tx_head));
+
+	/*
+	 * If pointers are out of range, just return.
+	 */
+	if ((cnt > ch->ch_tsize) || (unsigned)(head - ch->ch_tstart) >= ch->ch_tsize) {
+		DPR_CORE(("%s:%d pointer out of range", __FILE__, __LINE__));
+		return;
+	}
+
+	/*
+	 * If the write wraps over the top of the circular buffer,
+	 * move the portion up to the wrap point, and reset the
+	 * pointers to the bottom.
+	 */
+	n = ch->ch_tstart + ch->ch_tsize - head;
+
+	if (cnt >= n) {
+		cnt -= n;
+		taddr = ch->ch_taddr + head;
+		memcpy_toio(taddr, buf, n);
+		head = ch->ch_tstart;
+		buf += n;
+	}
+
+	/*
+	 * Move rest of data.
+	 */
+	taddr = ch->ch_taddr + head;
+	n = cnt;
+	memcpy_toio(taddr, buf, n);
+	head += cnt;
+
+	writew(head, &(bs->tx_head));
+}
+
+
+
+/*=======================================================================
+ *
+ *      dgap_param - Set Digi parameters.
+ *
+ *              struct tty_struct *     - TTY for port.
+ *
+ *=======================================================================*/
+int dgap_param(struct tty_struct *tty)
+{
+	struct termios *ts;
+	struct board_t *bd;
+	struct channel_t *ch;
+	struct bs_t   *bs;
+	struct un_t   *un;
+	u32	head;
+	u32	cflag;
+	u32	iflag;
+	uchar	mval;
+	uchar	hflow;
+
+	if (!tty || tty->magic != TTY_MAGIC) {
+		return (-ENXIO);
+	}
+
+	un = (struct un_t *) tty->driver_data;
+	if (!un || un->magic != DGAP_UNIT_MAGIC) {
+		return (-ENXIO);
+	}
+
+	ch = un->un_ch;
+	if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) {
+		return (-ENXIO);
+	}
+
+	bd = ch->ch_bd;
+	if (!bd || bd->magic != DGAP_BOARD_MAGIC) {
+		return (-ENXIO);
+	}
+
+        bs = ch->ch_bs;
+	if (bs == 0) {
+		return (-ENXIO);
+	}
+
+	DPR_PARAM(("param start: tdev: %x cflags: %x oflags: %x iflags: %x\n",
+		ch->ch_tun.un_dev, ch->ch_c_cflag, ch->ch_c_oflag, ch->ch_c_iflag));
+
+	ts = tty->termios;
+
+	/*
+	 * If baud rate is zero, flush queues, and set mval to drop DTR.
+	 */
+	if ((ch->ch_c_cflag & (CBAUD)) == 0) {
+
+		/* flush rx */
+		head = readw(&(ch->ch_bs->rx_head));
+		writew(head, &(ch->ch_bs->rx_tail));
+
+		/* flush tx */
+		head = readw(&(ch->ch_bs->tx_head));
+		/* Okay to have channel and board locks held calling this */
+		dgap_cmdw( ch, FLUSHTX, (u16) head, 0 );
+
+		/* Drop RTS and DTR */
+		ch->ch_mostat &= ~(D_RTS(ch)|D_DTR(ch));
+		dgap_cmdb( ch, SMODEM, 0, D_DTR(ch)|D_RTS(ch), 0 );
+
+		mval = 0;
+	}
+	else {
+		/*
+		 * Set baud rate, character size, and parity.
+		 */
+
+		/*
+		 * CBAUD has bit position 0x1000 set these days to indicate Linux
+		 * baud rate remap.
+		 * We use a different bit assignment for high speed.  Clear this
+		 * bit out while grabbing the parts of "cflag" we want.
+		 */
+		cflag = ch->ch_c_cflag & ((CBAUD ^ CBAUDEX) | PARODD | PARENB | CSTOPB | CSIZE);
+
+		/*
+		 * HUPCL bit is used by FEP to indicate fast baud
+		 * table is to be used.
+		 */
+		if ((ch->ch_digi.digi_flags & DIGI_FAST) || (ch->ch_c_cflag & CBAUDEX))
+			cflag |= HUPCL;
+
+
+		if ((ch->ch_c_cflag & CBAUDEX) && !(ch->ch_digi.digi_flags & DIGI_FAST)) {
+		/*
+		 * The below code is trying to guarantee that only baud rates
+		 * 115200, 230400, 460800, 921600 are remapped.  We use exclusive or
+		 * because the various baud rates share common bit positions
+		 * and therefore can't be tested for easily.
+		 */
+			tcflag_t tcflag = (ch->ch_c_cflag & CBAUD) | CBAUDEX;
+			int baudpart = 0;
+
+			/* Map high speed requests to index into FEP's baud table */
+			switch (tcflag) {
+			case B57600 :
+				baudpart = 1;
+				break;
+#ifdef B76800
+			case B76800 :
+				baudpart = 2;
+				break;
+#endif
+			case B115200 :
+				baudpart = 3;
+				break;
+			case B230400 :
+				baudpart = 9;
+				break;
+			case B460800 :
+				baudpart = 11;
+				break;
+#ifdef B921600
+			case B921600 :
+				baudpart = 12;
+				break;
+#endif
+			default:
+				baudpart = 0;
+			}
+
+			if (baudpart)
+				cflag = (cflag & ~(CBAUD | CBAUDEX)) | baudpart;
+		}
+
+		cflag &= 0xffff;
+
+		if (cflag != ch->ch_fepcflag) {
+			ch->ch_fepcflag = (u16) (cflag & 0xffff);
+
+			/* Okay to have channel and board locks held calling this */
+			dgap_cmdw(ch, SCFLAG, (u16) cflag, 0);
+		}
+
+		mval = D_DTR(ch) | D_RTS(ch);
+	}
+
+	/*
+	 * Get input flags.
+	 */
+	iflag = ch->ch_c_iflag & (IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP | IXON | IXANY | IXOFF);
+
+	if ((ch->ch_startc == _POSIX_VDISABLE) || (ch->ch_stopc == _POSIX_VDISABLE)) {
+		iflag &= ~(IXON | IXOFF);
+		ch->ch_c_iflag &= ~(IXON | IXOFF);
+	}
+
+	if (ch->ch_digi.digi_flags & DIGI_ALTPIN)
+		iflag |= IALTPIN ;
+
+	if (iflag != ch->ch_fepiflag) {
+		ch->ch_fepiflag = iflag;
+
+		/* Okay to have channel and board locks held calling this */
+		dgap_cmdw(ch, SIFLAG, (u16) ch->ch_fepiflag, 0);
+	}
+
+	/*
+	 * Select hardware handshaking.
+	 */
+	hflow = 0;
+
+	if (ch->ch_c_cflag & CRTSCTS) {
+		hflow |= (D_RTS(ch) | D_CTS(ch));
+	}
+	if (ch->ch_digi.digi_flags & RTSPACE)
+		hflow |= D_RTS(ch);
+	if (ch->ch_digi.digi_flags & DTRPACE)
+		hflow |= D_DTR(ch);
+	if (ch->ch_digi.digi_flags & CTSPACE)
+		hflow |= D_CTS(ch);
+	if (ch->ch_digi.digi_flags & DSRPACE)
+		hflow |= D_DSR(ch);
+	if (ch->ch_digi.digi_flags & DCDPACE)
+		hflow |= D_CD(ch);
+
+	if (hflow != ch->ch_hflow) {
+		ch->ch_hflow = hflow;
+
+		/* Okay to have channel and board locks held calling this */
+		dgap_cmdb(ch, SHFLOW, (uchar) hflow, 0xff, 0);
+        }
+
+	/*
+	 * Set modem control lines.
+	 */
+	mval ^= ch->ch_mforce & (mval ^ ch->ch_mval);
+
+	if (ch->ch_mostat ^ mval) {
+		ch->ch_mostat = mval;
+
+		/* Okay to have channel and board locks held calling this */
+		dgap_cmdb(ch, SMODEM, (uchar) mval, D_RTS(ch)|D_DTR(ch), 0);
+	}
+
+	/*
+	 * Read modem signals, and then call carrier function.
+	 */
+	ch->ch_mistat = readb(&(bs->m_stat));
+	dgap_carrier(ch);
+
+	/*
+	 * Set the start and stop characters.
+	 */
+	if (ch->ch_startc != ch->ch_fepstartc || ch->ch_stopc != ch->ch_fepstopc) {
+		ch->ch_fepstartc = ch->ch_startc;
+		ch->ch_fepstopc =  ch->ch_stopc;
+
+		/* Okay to have channel and board locks held calling this */
+		dgap_cmdb(ch, SFLOWC, ch->ch_fepstartc, ch->ch_fepstopc, 0);
+	}
+
+	/*
+	 * Set the Auxiliary start and stop characters.
+	 */
+	if (ch->ch_astartc != ch->ch_fepastartc || ch->ch_astopc != ch->ch_fepastopc) {
+		ch->ch_fepastartc = ch->ch_astartc;
+		ch->ch_fepastopc = ch->ch_astopc;
+
+		/* Okay to have channel and board locks held calling this */
+		dgap_cmdb(ch, SAFLOWC, ch->ch_fepastartc, ch->ch_fepastopc, 0);
+	}
+
+	DPR_PARAM(("param finish\n"));
+
+	return (0);
+}
+
+
+/*
+ * parity_scan()
+ *
+ * Convert the FEP5 way of reporting parity errors and breaks into
+ * the Linux line discipline way.
+ */
+static void parity_scan(struct channel_t *ch, unsigned char *cbuf, unsigned char *fbuf, int *len)
+{
+	int l = *len;
+	int count = 0;
+	unsigned char *in, *cout, *fout;
+	unsigned char c;
+
+	in = cbuf;
+	cout = cbuf;
+	fout = fbuf;
+
+	DPR_PSCAN(("parity_scan start\n"));
+
+	if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+		return;
+
+	while (l--) {
+		c = *in++;
+		switch (ch->pscan_state) {
+		default:
+			/* reset to sanity and fall through */
+			ch->pscan_state = 0;
+
+		case 0:
+			/* No FF seen yet */
+			if (c == (unsigned char) '\377') {
+				/* delete this character from stream */
+				ch->pscan_state = 1;
+			} else {
+				*cout++ = c;
+				*fout++ = TTY_NORMAL;
+				count += 1;
+			}
+			break;
+
+		case 1:
+			/* first FF seen */
+			if (c == (unsigned char) '\377') {
+				/* doubled ff, transform to single ff */
+				*cout++ = c;
+				*fout++ = TTY_NORMAL;
+				count += 1;
+				ch->pscan_state = 0;
+			} else {
+				/* save value examination in next state */
+				ch->pscan_savechar = c;
+				ch->pscan_state = 2;
+			}
+			break;
+
+		case 2:
+			/* third character of ff sequence */
+
+			*cout++ = c;
+
+			if (ch->pscan_savechar == 0x0) {
+
+				if (c == 0x0) {
+					DPR_PSCAN(("parity_scan in 3rd char of ff seq. c: %x setting break.\n", c));
+					*fout++ = TTY_BREAK;
+				}
+				else {
+					DPR_PSCAN(("parity_scan in 3rd char of ff seq. c: %x setting parity.\n", c));
+					*fout++ = TTY_PARITY;
+				}
+			}
+			else {
+				DPR_PSCAN(("%s:%d Logic Error.\n", __FILE__, __LINE__));
+			}
+
+			count += 1;
+			ch->pscan_state = 0;
+		}
+	}
+	*len = count;
+	DPR_PSCAN(("parity_scan finish\n"));
+}
+
+
+/*=======================================================================
+ *
+ *      dgap_input - Process received data.
+ *
+ *              ch      - Pointer to channel structure.
+ *
+ *=======================================================================*/
+void dgap_input(struct channel_t *ch)
+{
+	struct board_t *bd;
+	struct bs_t	*bs;
+	struct tty_struct *tp;
+	uint	rmask;
+	uint	head;
+	uint	tail;
+	int	data_len;
+	ulong	lock_flags;
+	ulong   lock_flags2;
+	int flip_len;
+	int len = 0;
+	int n = 0;
+	char *buf;
+	uchar tmpchar;
+	int s = 0;
+
+	if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+		return;
+
+	tp = ch->ch_tun.un_tty;
+
+	bs  = ch->ch_bs;
+	if (!bs) {
+		return;
+	}
+
+	bd = ch->ch_bd;
+	if(!bd || bd->magic != DGAP_BOARD_MAGIC)
+		return;
+
+	DPR_READ(("dgap_input start\n"));
+
+	DGAP_LOCK(bd->bd_lock, lock_flags);
+	DGAP_LOCK(ch->ch_lock, lock_flags2);
+
+	/*
+	 *      Figure the number of characters in the buffer.
+	 *      Exit immediately if none.
+	 */
+
+	rmask = ch->ch_rsize - 1;
+
+	head = readw(&(bs->rx_head));
+	head &= rmask;
+	tail = readw(&(bs->rx_tail));
+	tail &= rmask;
+
+	data_len = (head - tail) & rmask;
+
+	if (data_len == 0) {
+		writeb(1, &(bs->idata));
+		DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+		DGAP_UNLOCK(bd->bd_lock, lock_flags);
+		DPR_READ(("No data on port %d\n", ch->ch_portnum));
+		return;
+	}
+
+	/*
+	 *      If the device is not open, or CREAD is off, flush
+	 *      input data and return immediately.
+	 */
+	if (!tp || (tp->magic != TTY_MAGIC) || !(ch->ch_tun.un_flags & UN_ISOPEN) ||
+	    !(tp->termios->c_cflag & CREAD) || (ch->ch_tun.un_flags & UN_CLOSING)) {
+
+		DPR_READ(("input. dropping %d bytes on port %d...\n", data_len, ch->ch_portnum));
+		DPR_READ(("input. tp: %p tp->magic: %x MAGIC:%x ch flags: %x\n",
+			tp, tp ? tp->magic : 0, TTY_MAGIC, ch->ch_tun.un_flags));
+		writew(head, &(bs->rx_tail));
+		writeb(1, &(bs->idata));
+		DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+		DGAP_UNLOCK(bd->bd_lock, lock_flags);
+		return;
+	}
+
+	/*
+	 * If we are throttled, simply don't read any data.
+	 */
+	if (ch->ch_flags & CH_RXBLOCK) {
+		writeb(1, &(bs->idata));
+		DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+		DGAP_UNLOCK(bd->bd_lock, lock_flags);
+		DPR_READ(("Port %d throttled, not reading any data. head: %x tail: %x\n",
+			ch->ch_portnum, head, tail));
+		return;
+	}
+
+	/*
+	 *      Ignore oruns.
+	 */
+	tmpchar = readb(&(bs->orun));
+	if (tmpchar) {
+		writeb(0, &(bs->orun));
+	}
+
+	DPR_READ(("dgap_input start 2\n"));
+
+	/*
+	 * If the rxbuf is empty and we are not throttled, put as much
+	 * as we can directly into the linux TTY flip buffer.
+	 * The dgap_rawreadok case takes advantage of carnal knowledge that
+	 * the char_buf and the flag_buf are next to each other and
+	 * are each of (2 * TTY_FLIPBUF_SIZE) size.
+	 *
+	 * NOTE: if(!tty->real_raw), the call to ldisc.receive_buf
+	 *       actually still uses the flag buffer, so you can't
+	 *       use it for input data
+	 */
+	if (dgap_rawreadok) {
+		if (tp->real_raw) {
+			flip_len = MYFLIPLEN;
+		}
+		else {
+			flip_len = 2 * TTY_FLIPBUF_SIZE;
+		}
+	}
+	else {
+		flip_len = TTY_FLIPBUF_SIZE - tp->flip.count;
+	}
+
+	len = MIN(data_len, flip_len);
+	len = MIN(len, (N_TTY_BUF_SIZE - 1) - tp->read_cnt);
+
+	if (len <= 0) {
+		writeb(1, &(bs->idata));
+		DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+		DGAP_UNLOCK(bd->bd_lock, lock_flags);
+		DPR_READ(("dgap_input 1 - finish\n"));
+		return;
+	}
+
+	/*
+	 * If we're bypassing flip buffers on rx, we can blast it
+	 * right into the beginning of the buffer.
+	 */
+	if (dgap_rawreadok) {
+		if (tp->real_raw) {
+			buf = ch->ch_bd->flipbuf;
+		}
+		else {
+			buf = tp->flip.char_buf;
+		}
+	}
+	else {
+		buf = tp->flip.char_buf_ptr;
+	}
+
+	n = len;
+
+	/*
+	 * n now contains the most amount of data we can copy,
+	 * bounded either by the flip buffer size or the amount
+	 * of data the card actually has pending...
+	 */
+	while (n) {
+
+		s = ((head >= tail) ? head : ch->ch_rsize) - tail;
+		s = MIN(s, n);
+
+		if (s <= 0)
+			break;
+
+
+		memcpy_fromio(buf, (char *) ch->ch_raddr + tail, s);
+
+		tail += s;
+		buf += s;
+		n -= s;
+		/* Flip queue if needed */
+		tail &= rmask;
+	}
+
+	/*
+	 * In high performance mode, we don't have to update
+	 * flag_buf or any of the counts or pointers into flip buf.
+	 */
+	if (!dgap_rawreadok) {
+		if (I_PARMRK(tp)) {
+			parity_scan(ch, tp->flip.char_buf_ptr,
+				tp->flip.flag_buf_ptr, &len);
+		}
+		else {
+			memset(tp->flip.flag_buf_ptr, 0, len);
+		}
+
+		tp->flip.char_buf_ptr += len;
+		tp->flip.flag_buf_ptr += len;
+		tp->flip.count += len;
+
+	}
+	else if (!tp->real_raw) {
+		if (I_PARMRK(tp)) {
+			parity_scan(ch, tp->flip.char_buf,
+				tp->flip.flag_buf, &len);
+		} else {
+			memset(tp->flip.flag_buf, 0, len);
+		}
+	}
+
+	/*
+	 * If we're doing raw reads, jam it right into the
+	 * line disc bypassing the flip buffers.
+	 */
+	if (dgap_rawreadok) {
+		if (tp->real_raw) {
+			writew(tail, &(bs->rx_tail));
+			writeb(1, &(bs->idata));
+
+			/* !!! WE *MUST* LET GO OF ALL LOCKS BEFORE CALLING RECEIVE BUF !!! */
+			DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+			DGAP_UNLOCK(bd->bd_lock, lock_flags);
+
+			DPR_READ(("dgap_input. %d real_raw len:%d calling receive_buf for buffer for board %d\n",
+				 __LINE__, len, ch->ch_bd->boardnum));
+			tp->ldisc.receive_buf(tp, ch->ch_bd->flipbuf, NULL, len);
+		}
+		else {
+			writew(tail, &(bs->rx_tail));
+			writeb(1, &(bs->idata));
+
+			/* !!! WE *MUST* LET GO OF ALL LOCKS BEFORE CALLING RECEIVE BUF !!! */
+			DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+			DGAP_UNLOCK(bd->bd_lock, lock_flags);
+
+			DPR_READ(("dgap_input. %d not real_raw len:%d calling receive_buf for buffer for board %d\n",
+				 __LINE__, len, ch->ch_bd->boardnum));
+			tp->ldisc.receive_buf(tp, tp->flip.char_buf, tp->flip.flag_buf, len);
+		}
+	}
+	else {
+		writew(tail, &(bs->rx_tail));
+		writeb(1, &(bs->idata));
+
+		DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+		DGAP_UNLOCK(bd->bd_lock, lock_flags);
+
+		DPR_READ(("dgap_input. %d not dgap_read raw okay scheduling flip\n", __LINE__));
+		tty_schedule_flip(tp);
+	}
+
+	DPR_READ(("dgap_input - finish\n"));
+}
+
+
+
+/*=======================================================================
+ *
+ *      dgap_event - FEP to host event processing routine.
+ *
+ *              bd     - Board of current event.
+ *
+ *=======================================================================*/
+int dgap_event(struct board_t *bd)
+{
+	struct channel_t *ch;
+	ulong		lock_flags;
+	ulong		lock_flags2;
+	struct bs_t	*bs;
+	uchar		*event;
+	uchar		*vaddr = NULL;
+	struct ev_t	*eaddr = NULL;
+	uint		head;
+	uint		tail;
+	int		port;
+	int		reason;
+	int		modem;
+	int		b1;
+
+	if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+		return (-ENXIO);
+
+	DGAP_LOCK(bd->bd_lock, lock_flags);
+
+	vaddr = bd->re_map_membase;
+
+	if (!vaddr) {
+		DGAP_UNLOCK(bd->bd_lock, lock_flags);
+		return (-ENXIO);
+	}
+
+	eaddr = (struct ev_t *) (vaddr + EVBUF);
+
+	/* Get our head and tail */
+	head = readw(&(eaddr->ev_head));
+	tail = readw(&(eaddr->ev_tail));
+
+	/*
+	 * Forget it if pointers out of range.
+	 */
+
+	if (head >= EVMAX - EVSTART || tail >= EVMAX - EVSTART ||
+	    (head | tail) & 03) {
+		DPR_EVENT(("should be calling xxfail %d\n", __LINE__));
+		/* Let go of board lock */
+		DGAP_UNLOCK(bd->bd_lock, lock_flags);
+		return (-ENXIO);
+	}
+
+	/*
+	 * Loop to process all the events in the buffer.
+	 */
+	while (tail != head) {
+
+		/*
+		 * Get interrupt information.
+		 */
+
+		event = bd->re_map_membase + tail + EVSTART;
+
+		port   = event[0];
+		reason = event[1];
+		modem  = event[2];
+		b1     = event[3];
+
+		DPR_EVENT(("event: jiffies: %ld port: %d reason: %x modem: %x\n",
+			jiffies, port, reason, modem));
+
+		/*
+		 * Make sure the interrupt is valid.
+		 */
+                if ( port >= bd->nasync) {
+			goto next;
+		}
+
+		if (!(reason & (IFMODEM | IFBREAK | IFTLW | IFTEM | IFDATA))) {
+			goto next;
+		}
+
+		ch = bd->channels[port];
+
+		if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) {
+			goto next;
+		}
+
+		/*
+		 * If we have made it here, the event was valid.
+		 * Lock down the channel.
+		 */
+		DGAP_LOCK(ch->ch_lock, lock_flags2);
+
+		bs = ch->ch_bs;
+
+		if (!bs) {
+			DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+			goto next;
+		}
+
+		/*
+		 * Process received data.
+		 */
+		if (reason & IFDATA) {
+
+			/*
+			 * ALL LOCKS *MUST* BE DROPPED BEFORE CALLING INPUT!
+			 * input could send some data to ld, which in turn
+			 * could do a callback to one of our other functions.
+			 */
+			DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+			DGAP_UNLOCK(bd->bd_lock, lock_flags);
+
+			dgap_input(ch);
+
+			DGAP_LOCK(bd->bd_lock, lock_flags);
+			DGAP_LOCK(ch->ch_lock, lock_flags2);
+
+			if (ch->ch_flags & CH_RACTIVE)
+				ch->ch_flags |= CH_RENABLE;
+			else
+				writeb(1, &(bs->idata));
+
+			if (ch->ch_flags & CH_RWAIT) {
+				ch->ch_flags &= ~CH_RWAIT;
+
+				wake_up_interruptible(&ch->ch_tun.un_flags_wait);
+			}
+		}
+
+		/*
+		 * Process Modem change signals.
+		 */
+		if (reason & IFMODEM) {
+			ch->ch_mistat = modem;
+			dgap_carrier(ch);
+		}
+
+		/*
+		 * Process break.
+		 */
+		if (reason & IFBREAK) {
+
+			DPR_EVENT(("got IFBREAK\n"));
+
+			if (ch->ch_tun.un_tty) {
+				/* A break has been indicated */
+				ch->ch_tun.un_tty->flip.count++;
+				*ch->ch_tun.un_tty->flip.flag_buf_ptr++ = TTY_BREAK;
+				*ch->ch_tun.un_tty->flip.char_buf_ptr++ = 0;
+				tty_schedule_flip(ch->ch_tun.un_tty);
+			}
+		}
+
+		/*
+		 * Process Transmit low.
+		 */
+		if (reason & IFTLW) {
+
+			DPR_EVENT(("event: got low event\n"));
+
+			if (ch->ch_tun.un_flags & UN_LOW) {
+				ch->ch_tun.un_flags &= ~UN_LOW;
+
+				if (ch->ch_tun.un_flags & UN_ISOPEN) {
+					if ((ch->ch_tun.un_tty->flags &
+					   (1 << TTY_DO_WRITE_WAKEUP)) &&
+						ch->ch_tun.un_tty->ldisc.write_wakeup)
+					{
+						(ch->ch_tun.un_tty->ldisc.write_wakeup)(ch->ch_tun.un_tty);
+					}
+					wake_up_interruptible(&ch->ch_tun.un_tty->write_wait);
+					wake_up_interruptible(&ch->ch_tun.un_flags_wait);
+
+					DPR_EVENT(("event: Got low event. jiffies: %lu\n", jiffies));
+				}
+			}
+
+			if (ch->ch_pun.un_flags & UN_LOW) {
+				ch->ch_pun.un_flags &= ~UN_LOW;
+				if (ch->ch_pun.un_flags & UN_ISOPEN) {
+					if ((ch->ch_pun.un_tty->flags &
+					   (1 << TTY_DO_WRITE_WAKEUP)) &&
+						ch->ch_pun.un_tty->ldisc.write_wakeup)
+					{
+						(ch->ch_pun.un_tty->ldisc.write_wakeup)(ch->ch_pun.un_tty);
+					}
+					wake_up_interruptible(&ch->ch_pun.un_tty->write_wait);
+					wake_up_interruptible(&ch->ch_pun.un_flags_wait);
+				}
+			}
+
+			if (ch->ch_flags & CH_WLOW) {
+				ch->ch_flags &= ~CH_WLOW;
+				wake_up_interruptible(&ch->ch_flags_wait);
+			}
+		}
+
+		/*
+		 * Process Transmit empty.
+		 */
+		if (reason & IFTEM) {
+			DPR_EVENT(("event: got empty event\n"));
+
+			if (ch->ch_tun.un_flags & UN_EMPTY) {
+				ch->ch_tun.un_flags &= ~UN_EMPTY;
+				if (ch->ch_tun.un_flags & UN_ISOPEN) {
+					if ((ch->ch_tun.un_tty->flags &
+					   (1 << TTY_DO_WRITE_WAKEUP)) &&
+						ch->ch_tun.un_tty->ldisc.write_wakeup)
+					{
+						(ch->ch_tun.un_tty->ldisc.write_wakeup)(ch->ch_tun.un_tty);
+					}
+					wake_up_interruptible(&ch->ch_tun.un_tty->write_wait);
+					wake_up_interruptible(&ch->ch_tun.un_flags_wait);
+				}
+			}
+
+			if (ch->ch_pun.un_flags & UN_EMPTY) {
+				ch->ch_pun.un_flags &= ~UN_EMPTY;
+				if (ch->ch_pun.un_flags & UN_ISOPEN) {
+					if ((ch->ch_pun.un_tty->flags &
+					   (1 << TTY_DO_WRITE_WAKEUP)) &&
+						ch->ch_pun.un_tty->ldisc.write_wakeup)
+					{
+						(ch->ch_pun.un_tty->ldisc.write_wakeup)(ch->ch_pun.un_tty);
+					}
+					wake_up_interruptible(&ch->ch_pun.un_tty->write_wait);
+					wake_up_interruptible(&ch->ch_pun.un_flags_wait);
+				}
+			}
+
+
+			if (ch->ch_flags & CH_WEMPTY) {
+				ch->ch_flags &= ~CH_WEMPTY;
+				wake_up_interruptible(&ch->ch_flags_wait);
+			}
+		}
+
+		DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+
+next:
+		tail = (tail + 4) & (EVMAX - EVSTART - 4);
+	}
+
+	writew(tail, &(eaddr->ev_tail));
+	DGAP_UNLOCK(bd->bd_lock, lock_flags);
+
+	return (0);
+}
+
+
+/************************************************************************
+ * Determines when CARRIER changes state and takes appropriate
+ * action.
+ ************************************************************************/
+void dgap_carrier(struct channel_t *ch)
+{
+	struct board_t *bd;
+
+        int virt_carrier = 0;
+        int phys_carrier = 0;
+
+	DPR_CARR(("dgap_carrier called...\n"));
+
+	if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+		return;
+
+	bd = ch->ch_bd;
+
+	if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+		return;
+
+	/* Make sure altpin is always set correctly */
+	if (ch->ch_digi.digi_flags & DIGI_ALTPIN) {
+		ch->ch_dsr      = DM_CD;
+		ch->ch_cd       = DM_DSR;
+	}
+	else {
+		ch->ch_dsr      = DM_DSR;
+		ch->ch_cd       = DM_CD;
+	}
+
+	if (ch->ch_mistat & D_CD(ch)) {
+		DPR_CARR(("mistat: %x  D_CD: %x\n", ch->ch_mistat, D_CD(ch)));
+		phys_carrier = 1;
+	}
+
+	if (ch->ch_digi.digi_flags & DIGI_FORCEDCD) {
+		virt_carrier = 1;
+	}
+
+	if (ch->ch_c_cflag & CLOCAL) {
+		virt_carrier = 1;
+	}
+
+
+	DPR_CARR(("DCD: physical: %d virt: %d\n", phys_carrier, virt_carrier));
+
+	/*
+	 * Test for a VIRTUAL carrier transition to HIGH.
+	 */
+	if (((ch->ch_flags & CH_FCAR) == 0) && (virt_carrier == 1)) {
+
+		/*
+		 * When carrier rises, wake any threads waiting
+		 * for carrier in the open routine.
+		 */
+
+		DPR_CARR(("carrier: virt DCD rose\n"));
+
+		if (waitqueue_active(&(ch->ch_flags_wait)))
+			wake_up_interruptible(&ch->ch_flags_wait);
+	}
+
+	/*
+	 * Test for a PHYSICAL carrier transition to HIGH.
+	 */
+	if (((ch->ch_flags & CH_CD) == 0) && (phys_carrier == 1)) {
+
+		/*
+		 * When carrier rises, wake any threads waiting
+		 * for carrier in the open routine.
+		 */
+
+		DPR_CARR(("carrier: physical DCD rose\n"));
+
+		if (waitqueue_active(&(ch->ch_flags_wait)))
+			wake_up_interruptible(&ch->ch_flags_wait);
+	}
+
+	/*
+	 *  Test for a PHYSICAL transition to low, so long as we aren't
+	 *  currently ignoring physical transitions (which is what "virtual
+	 *  carrier" indicates).
+	 *
+	 *  The transition of the virtual carrier to low really doesn't
+	 *  matter... it really only means "ignore carrier state", not
+	 *  "make pretend that carrier is there".
+	 */
+	if ((virt_carrier == 0) && ((ch->ch_flags & CH_CD) != 0) &&
+	    (phys_carrier == 0))
+	{
+
+		/*
+		 *   When carrier drops:
+		 *
+		 *   Drop carrier on all open units.
+		 *
+		 *   Flush queues, waking up any task waiting in the
+		 *   line discipline.
+		 *
+		 *   Send a hangup to the control terminal.
+		 *
+		 *   Enable all select calls.
+		 */
+		if (waitqueue_active(&(ch->ch_flags_wait)))
+			wake_up_interruptible(&ch->ch_flags_wait);
+
+		if (ch->ch_tun.un_open_count > 0) {
+			DPR_CARR(("Sending tty hangup\n"));
+			tty_hangup(ch->ch_tun.un_tty);
+		}
+
+		if (ch->ch_pun.un_open_count > 0) {
+			DPR_CARR(("Sending pr hangup\n"));
+			tty_hangup(ch->ch_pun.un_tty);
+		}
+	}
+
+	/*
+	 *  Make sure that our cached values reflect the current reality.
+	 */
+	if (virt_carrier == 1)
+		ch->ch_flags |= CH_FCAR;
+	else
+		ch->ch_flags &= ~CH_FCAR;
+
+	if (phys_carrier == 1)
+		ch->ch_flags |= CH_CD;
+	else
+		ch->ch_flags &= ~CH_CD;
+}
+
+
+/************************************************************************
+ *
+ * TTY Entry points and helper functions
+ *
+ ************************************************************************/
+
+/*
+ * dgap_tty_open()
+ *
+ */
+static int dgap_tty_open(struct tty_struct *tty, struct file *file)
+{
+	struct board_t	*brd;
+	struct channel_t *ch;
+	struct un_t	*un;
+	struct bs_t	*bs;
+	uint		major = 0;
+	uint		minor = 0;
+	int		rc = 0;
+	ulong		lock_flags;
+	ulong		lock_flags2;
+	u32		head;
+
+	DGAP_MOD_INC_USE_COUNT(rc);
+	if (!rc) {
+		return -ENXIO;
+	}
+
+	rc = 0;
+
+	major = DGAP_TTY_MAJOR(tty->device);
+	minor = DGAP_TTY_MINOR(tty->device);
+
+	if (major > 255) {
+		DGAP_MOD_DEC_USE_COUNT;
+		return -ENXIO;
+	}
+
+	/* Get board pointer from our array of majors we have allocated */
+	brd = dgap_BoardsByMajor[major];
+	if (!brd) {
+		DGAP_MOD_DEC_USE_COUNT;
+		return -ENXIO;
+	}
+
+	DGAP_LOCK(brd->bd_lock, lock_flags);
+
+	/* If board is not ready yet, bail. */
+	/* TODO: Maybe sleep here, instead of bailing right away? */
+	if (brd->state != BOARD_READY) {
+		DGAP_UNLOCK(brd->bd_lock, lock_flags);
+		DGAP_MOD_DEC_USE_COUNT;
+		return -ENXIO;
+	}
+
+	/* If opened device is greater than our number of ports, bail. */
+	if (DGAP_TTY_MINOR(tty->device) > brd->nasync) {
+		DGAP_UNLOCK(brd->bd_lock, lock_flags);
+		DGAP_MOD_DEC_USE_COUNT;
+		return -ENXIO;
+	}
+
+	ch = brd->channels[minor];
+	if (!ch) {
+		DGAP_UNLOCK(brd->bd_lock, lock_flags);
+		DGAP_MOD_DEC_USE_COUNT;
+		return -ENXIO;
+	}
+
+	/* Grab channel lock */
+	DGAP_LOCK(ch->ch_lock, lock_flags2);
+
+	/* Figure out our type */
+	if (major == brd->dgap_Serial_Major) {
+		un = &brd->channels[minor]->ch_tun;
+		un->un_type = DGAP_SERIAL;
+	}
+	else if (major == brd->dgap_TransparentPrint_Major) {
+		un = &brd->channels[minor]->ch_pun;
+		un->un_type = DGAP_PRINT;
+	}
+	else {
+		DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+		DGAP_UNLOCK(brd->bd_lock, lock_flags);
+		DPR_OPEN(("%d Unknown TYPE!\n", __LINE__));
+		return -ENXIO;
+	}
+
+	/* Store our unit into driver_data, so we always have it available. */
+	tty->driver_data = un;
+
+	DPR_OPEN(("Open called. MAJOR: %d MINOR:%d unit: %p NAME: %s\n",
+		DGAP_TTY_MAJOR(tty->device), DGAP_TTY_MINOR(tty->device), un, brd->name));
+
+	/*
+	 * Error if channel info pointer is 0.
+	 */
+	if ((bs = ch->ch_bs) == 0) {
+		DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+		DGAP_UNLOCK(brd->bd_lock, lock_flags);
+		DPR_OPEN(("%d BS is 0!\n", __LINE__));
+		return -ENXIO;
+        }
+
+	DPR_OPEN(("%d: tflag=%x  pflag=%x\n", __LINE__, ch->ch_tun.un_flags, ch->ch_pun.un_flags));
+
+	/*
+	 * Initialize tty's
+	 */
+	if (!(un->un_flags & UN_ISOPEN)) {
+		/* Store important variables. */
+		un->un_ch      = brd->channels[minor];
+		un->un_dev     = minor;
+		un->un_tty     = tty;
+
+		/* Maybe do something here to the TTY struct as well? */
+	}
+
+	/*
+	 * Initialize if neither terminal or printer is open.
+	 */
+	if (!((ch->ch_tun.un_flags | ch->ch_pun.un_flags) & UN_ISOPEN)) {
+
+		DPR_OPEN(("dgap_open: initializing channel in open...\n"));
+
+		ch->ch_mforce = 0;
+		ch->ch_mval = 0;
+
+		/*
+		 * Flush input queue.
+		 */
+		head = readw(&(bs->rx_head));
+		writew(head, &(bs->rx_tail));
+
+		ch->ch_flags = 0;
+		ch->pscan_state = 0;
+		ch->pscan_savechar = 0;
+
+		ch->ch_c_cflag   = tty->termios->c_cflag;
+		ch->ch_c_iflag   = tty->termios->c_iflag;
+		ch->ch_c_oflag   = tty->termios->c_oflag;
+		ch->ch_c_lflag   = tty->termios->c_lflag;
+		ch->ch_startc = tty->termios->c_cc[VSTART];
+		ch->ch_stopc  = tty->termios->c_cc[VSTOP];
+
+		/* TODO: flush our TTY struct here? */
+	}
+
+	dgap_carrier(ch);
+	/*
+	 * Run param in case we changed anything
+	 */
+	dgap_param(tty);
+
+	/*
+	 * follow protocol for opening port
+	 */
+
+	DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+	DGAP_UNLOCK(brd->bd_lock, lock_flags);
+
+	rc = block_til_ready(tty, file, ch);
+
+	if (rc) {
+		DPR_OPEN(("dgap_tty_open returning after block_til_ready "
+			"with %d\n", rc));
+	}
+
+	/* No going back now, increment our unit and channel counters */
+	DGAP_LOCK(ch->ch_lock, lock_flags);
+	ch->ch_open_count++;
+	un->un_open_count++;
+	un->un_flags |= (UN_ISOPEN);
+	DGAP_UNLOCK(ch->ch_lock, lock_flags);
+
+	DPR_OPEN(("dgap_tty_open finished\n"));
+	return (rc);
+}
+
+
+/*
+ * block_til_ready()
+ *
+ * Wait for DCD, if needed.
+ */
+static int block_til_ready(struct tty_struct *tty, struct file *file, struct channel_t *ch)
+{
+	int retval = 0;
+	struct un_t *un = NULL;
+	ulong   lock_flags;
+	uint	old_ch_flags = 0;
+
+	if (!tty || tty->magic != TTY_MAGIC || !file || !ch || ch->magic != DGAP_CHANNEL_MAGIC) {
+		return (-ENXIO);
+	}
+
+	un = tty->driver_data;
+	if (!un || un->magic != DGAP_UNIT_MAGIC) {
+		return (-ENXIO);
+	}
+
+	DPR_OPEN(("block_til_ready - before block.\n"));
+
+	DGAP_LOCK(ch->ch_lock, lock_flags);
+
+	ch->ch_wopen++;
+
+	/* Loop forever */
+	while (1) {
+
+		/*
+		 * If board has failed somehow during our sleep, bail with error.
+		 */
+		if (ch->ch_bd->state == BOARD_FAILED) {
+			retval = -ENXIO;
+			break;
+		}
+
+		/* If tty was hung up, break out of loop and set error. */
+		if (tty_hung_up_p(file)) {
+			retval = -EAGAIN;
+			break;
+		}
+
+		/*
+		 * If either unit is in the middle of the fragile part of close,
+		 * we just cannot touch the channel safely.
+		 * Go back to sleep, knowing that when the channel can be
+		 * touched safely, the close routine will signal the
+		 * ch_wait_flags to wake us back up.
+		 */
+		if (!((ch->ch_tun.un_flags | ch->ch_pun.un_flags) & UN_CLOSING)) {
+
+			/*
+			 * Our conditions to leave cleanly and happily:
+			 * 1) NONBLOCKING on the tty is set.
+			 * 2) CLOCAL is set.
+			 * 3) DCD (fake or real) is active.
+			 */
+
+			if (file->f_flags & O_NONBLOCK) {
+				break;
+			}
+
+			if (tty->flags & (1 << TTY_IO_ERROR)) {
+				break;
+			}
+
+			if (ch->ch_flags & CH_CD) {
+				DPR_OPEN(("%d: ch_flags: %x\n", __LINE__, ch->ch_flags));
+				break;
+			}
+
+			if (ch->ch_flags & CH_FCAR) {
+				DPR_OPEN(("%d: ch_flags: %x\n", __LINE__, ch->ch_flags));
+				break;
+			}
+		}
+
+		/*
+		 * If there is a signal pending, the user probably
+		 * interrupted (ctrl-c) us.
+		 * Leave loop with error set.
+		 */
+		if (signal_pending(current)) {
+			DPR_OPEN(("%d: signal pending...\n", __LINE__));
+			retval = -ERESTARTSYS;
+			break;
+		}
+
+		DPR_OPEN(("block_til_ready - blocking.\n"));
+
+		/*
+		 * Store the channel flags before we let go of channel lock
+		 */
+		old_ch_flags = ch->ch_flags;
+
+		/*
+		 * Let go of channel lock before calling schedule.
+		 * Our poller will get any FEP events and wake us up when DCD
+		 * eventually goes active.
+		 */
+
+		DGAP_UNLOCK(ch->ch_lock, lock_flags);
+
+		DPR_OPEN(("Going to sleep...\n"));
+
+		/*
+		 * Wait for something in the ch_flags to change from the current value.
+		 */
+		retval = wait_event_interruptible(ch->ch_flags_wait, (old_ch_flags != ch->ch_flags));
+
+		DPR_OPEN(("After sleep... retval: %x\n", retval));
+
+		/*
+		 * We got woken up for some reason.
+		 * Before looping around, grab our channel lock.
+		 */
+		DGAP_LOCK(ch->ch_lock, lock_flags);
+	}
+
+	ch->ch_wopen--;
+
+	DGAP_UNLOCK(ch->ch_lock, lock_flags);
+
+	DPR_OPEN(("block_til_ready - after blocking.\n"));
+
+	if (retval) {
+		DPR_OPEN(("block_til_ready - done. error. retval: %x\n", retval));
+		return(retval);
+	}
+
+	DPR_OPEN(("block_til_ready - done no error. jiffies: %lu\n", jiffies));
+
+	return(0);
+}
+
+
+/*
+ * dgap_tty_hangup()
+ *
+ * Hangup the port.  Like a close, but don't wait for output to drain.
+ */
+static void dgap_tty_hangup(struct tty_struct *tty)
+{
+	struct board_t	*bd;
+	struct channel_t *ch;
+	struct un_t	*un;
+
+	if (!tty || tty->magic != TTY_MAGIC)
+		return;
+
+	un = tty->driver_data;
+	if (!un || un->magic != DGAP_UNIT_MAGIC)
+		return;
+
+	ch = un->un_ch;
+	if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+		return;
+
+	bd = ch->ch_bd;
+	if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+		return;
+
+	DPR_CLOSE(("dgap_hangup called. ch->ch_open_count: %d un->un_open_count: %d\n",
+		ch->ch_open_count, un->un_open_count));
+
+	/* flush the transmit queues */
+	dgap_tty_flush_buffer(tty);
+
+	DPR_CLOSE(("dgap_hangup finished. ch->ch_open_count: %d un->un_open_count: %d\n",
+		ch->ch_open_count, un->un_open_count));
+}
+
+
+
+/*
+ * dgap_tty_close()
+ *
+ */
+static void dgap_tty_close(struct tty_struct *tty, struct file *file)
+{
+	struct board_t *bd;
+	struct channel_t *ch;
+	struct un_t *un;
+	struct termios *ts;
+	ulong lock_flags;
+	int rc = 0;
+
+	if (!tty || tty->magic != TTY_MAGIC)
+		return;
+
+	un = tty->driver_data;
+	if (!un || un->magic != DGAP_UNIT_MAGIC)
+		return;
+
+	ch = un->un_ch;
+	if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+		return;
+
+	bd = ch->ch_bd;
+	if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+		return;
+
+	ts = tty->termios;
+
+	DPR_CLOSE(("Close called\n"));
+
+	DGAP_LOCK(ch->ch_lock, lock_flags);
+
+	/*
+	 * Determine if this is the last close or not - and if we agree about
+	 * which type of close it is with the Line Discipline
+	 */
+	if ((tty->count == 1) && (un->un_open_count != 1)) {
+		/*
+		 * Uh, oh.  tty->count is 1, which means that the tty
+		 * structure will be freed.  un_open_count should always
+		 * be one in these conditions.  If it's greater than
+		 * one, we've got real problems, since it means the
+		 * serial port won't be shutdown.
+		 */
+		APR(("tty->count is 1, un open count is %d\n", un->un_open_count));
+		un->un_open_count = 1;
+	}
+
+	if (--un->un_open_count < 0) {
+		APR(("bad serial port open count of %d\n", un->un_open_count));
+		un->un_open_count = 0;
+	}
+
+	ch->ch_open_count--;
+
+	if (ch->ch_open_count && un->un_open_count) {
+		DPR_CLOSE(("dgap_tty_close: not last close ch: %d un:%d\n",
+			ch->ch_open_count, un->un_open_count));
+
+		DGAP_MOD_DEC_USE_COUNT;
+		DGAP_UNLOCK(ch->ch_lock, lock_flags);
+                return;
+        }
+
+	/* OK, its the last close on the unit */
+	DPR_CLOSE(("dgap_tty_close - last close on unit procedures\n"));
+
+	un->un_flags |= UN_CLOSING;
+
+	tty->closing = 1;
+
+	/*
+	 * Only officially close channel if count is 0 and
+         * DIGI_PRINTER bit is not set.
+	 */
+	if ((ch->ch_open_count == 0) && !(ch->ch_digi.digi_flags & DIGI_PRINTER)) {
+
+		ch->ch_flags &= ~(CH_RXBLOCK);
+
+		DGAP_UNLOCK(ch->ch_lock, lock_flags);
+
+		/* wait for output to drain */
+		/* This will also return if we take an interrupt */
+
+		DPR_CLOSE(("Calling wait_for_drain\n"));
+		rc = dgap_wait_for_drain(tty);
+		DPR_CLOSE(("After calling wait_for_drain\n"));
+
+		if (rc) {
+			DPR_BASIC(("dgap_tty_close - bad return: %d ", rc));
+		}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+		if (tty->driver->flush_buffer) {
+			DPR_CLOSE(("Calling driver flush buffer\n"));
+			tty->driver->flush_buffer(tty);
+		}
+#else
+		if (tty->driver.flush_buffer) {
+			DPR_CLOSE(("Calling driver flush buffer\n"));
+			tty->driver.flush_buffer(tty);
+		}
+#endif
+		if (tty->ldisc.flush_buffer) {
+			DPR_CLOSE(("Calling ldisc flush buffer\n"));
+			tty->ldisc.flush_buffer(tty);
+		}
+
+		DGAP_LOCK(ch->ch_lock, lock_flags);
+
+		tty->closing = 0;
+
+		/*
+		 * If we have HUPCL set, lower DTR and RTS
+		 */
+		if (ch->ch_c_cflag & HUPCL ) {
+			DPR_CLOSE(("Close. HUPCL set, dropping DTR/RTS\n"));
+			ch->ch_mostat &= ~(D_RTS(ch)|D_DTR(ch));
+			dgap_cmdb( ch, SMODEM, 0, D_DTR(ch)|D_RTS(ch), 0 );
+
+			/*
+			 * Go to sleep to ensure RTS/DTR
+			 * have been dropped for modems to see it.
+			 */
+			if (ch->ch_close_delay) {
+				DPR_CLOSE(("Close. Sleeping for RTS/DTR drop\n"));
+
+				DGAP_UNLOCK(ch->ch_lock, lock_flags);
+				dgap_ms_sleep(ch->ch_close_delay);
+				DGAP_LOCK(ch->ch_lock, lock_flags);
+
+				DPR_CLOSE(("Close. After sleeping for RTS/DTR drop\n"));
+			}
+		}
+
+		ch->pscan_state = 0;
+		ch->pscan_savechar = 0;
+	}
+
+	/*
+	 * turn off print device when closing print device.
+	 */
+	if ((un->un_type == DGAP_PRINT)  && (ch->ch_flags & CH_PRON) ) {
+		dgap_wmove(ch, ch->ch_digi.digi_offstr,
+			(int) ch->ch_digi.digi_offlen);
+		ch->ch_flags &= ~CH_PRON;
+	}
+
+	un->un_tty = 0;
+	un->un_flags &= ~(UN_ISOPEN | UN_CLOSING);
+
+	DPR_CLOSE(("Close. Doing wakeups\n"));
+	wake_up_interruptible(&ch->ch_flags_wait);
+	wake_up_interruptible(&un->un_flags_wait);
+
+	DGAP_MOD_DEC_USE_COUNT;
+
+	DGAP_UNLOCK(ch->ch_lock, lock_flags);
+
+        DPR_BASIC(("dgap_tty_close - complete\n"));
+}
+
+
+/*
+ * dgap_tty_chars_in_buffer()
+ *
+ * Return number of characters that have not been transmitted yet.
+ *
+ * This routine is used by the line discipline to determine if there
+ * is data waiting to be transmitted/drained/flushed or not.
+ */
+static int dgap_tty_chars_in_buffer(struct tty_struct *tty)
+{
+	struct board_t *bd = NULL;
+	struct channel_t *ch = NULL;
+	struct un_t *un = NULL;
+	struct bs_t *bs = NULL;
+	uchar tbusy;
+	u16 thead, ttail, tmask, chead, ctail;
+	u32 chars = 0;
+	ulong   lock_flags = 0;
+	ulong   lock_flags2 = 0;
+
+	if (tty == NULL)
+		return(0);
+
+	un = tty->driver_data;
+	if (!un || un->magic != DGAP_UNIT_MAGIC)
+		return (0);
+
+	ch = un->un_ch;
+	if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+		return (0);
+
+	bd = ch->ch_bd;
+	if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+		return (0);
+
+        bs = ch->ch_bs;
+	if (!bs)
+		return (0);
+
+	DGAP_LOCK(bd->bd_lock, lock_flags);
+	DGAP_LOCK(ch->ch_lock, lock_flags2);
+
+	tmask = (ch->ch_tsize - 1);
+
+	/* Get Transmit queue pointers */
+	thead = readw(&(bs->tx_head)) & tmask;
+	ttail = readw(&(bs->tx_tail)) & tmask;
+
+	/* Get tbusy flag */
+	tbusy = readb(&(bs->tbusy));
+
+	/* Get Command queue pointers */
+	chead = readw(&(ch->ch_cm->cm_head));
+	ctail = readw(&(ch->ch_cm->cm_tail));
+
+	DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+	DGAP_UNLOCK(bd->bd_lock, lock_flags);
+
+	/*
+	 * The only way we know for sure if there is no pending
+	 * data left to be transferred, is if:
+	 * 1) Transmit head and tail are equal (empty).
+	 * 2) Command queue head and tail are equal (empty).
+	 * 3) The "TBUSY" flag is 0. (Transmitter not busy).
+ 	 */
+	if ((ttail == thead) && (tbusy == 0) && (chead == ctail)) {
+		chars = 0;
+	}
+	else {
+		if (thead >= ttail)
+			chars = thead - ttail;
+		else
+			chars = thead - ttail + ch->ch_tsize;
+		/*
+		 * Fudge factor here.
+		 * If chars is zero, we know that the command queue had
+		 * something in it or tbusy was set.  Because we cannot
+		 * be sure if there is still some data to be transmitted,
+		 * lets lie, and tell ld we have 1 byte left.
+		 */
+		if (chars == 0)
+			chars = 1;
+	}
+
+ 	DPR_WRITE(("dgap_tty_chars_in_buffer. Port: %x - %d (head: %d tail: %d tsize: %d)\n",
+		ch->ch_portnum, chars, thead, ttail, ch->ch_tsize));
+
+        return(chars);
+}
+
+
+static int dgap_wait_for_drain(struct tty_struct *tty)
+{
+	struct channel_t *ch;
+	struct un_t *un;
+	struct bs_t *bs;
+	int ret = -EIO;
+	u32 count = 1;
+	ulong   lock_flags = 0;
+
+	if (!tty || tty->magic != TTY_MAGIC)
+		return ret;
+
+	un = tty->driver_data;
+	if (!un || un->magic != DGAP_UNIT_MAGIC)
+		return ret;
+
+	ch = un->un_ch;
+	if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+		return ret;
+
+        bs = ch->ch_bs;
+	if (!bs)
+		return ret;
+
+	ret = 0;
+
+	DPR_DRAIN(("dgap_wait_for_drain start\n"));
+
+	/* Loop until data is drained */
+	while (count != 0) {
+
+		count = dgap_tty_chars_in_buffer(tty);
+
+		if (count == 0)
+			break;
+
+		/* Set flag waiting for drain */
+		DGAP_LOCK(ch->ch_lock, lock_flags);
+		un->un_flags |= UN_EMPTY;
+		writeb(1, &(bs->iempty));
+		DGAP_UNLOCK(ch->ch_lock, lock_flags);
+
+		/* Go to sleep till we get woken up */
+		ret = wait_event_interruptible(un->un_flags_wait, ((un->un_flags & UN_EMPTY) == 0));
+
+		/* If ret is non-zero, user ctrl-c'ed us */
+		if (ret)
+			break;
+	}
+
+	DGAP_LOCK(ch->ch_lock, lock_flags);
+	un->un_flags &= ~(UN_EMPTY);
+	DGAP_UNLOCK(ch->ch_lock, lock_flags);
+
+	DPR_DRAIN(("dgap_wait_for_drain finish\n"));
+
+	return (ret);
+}
+
+
+/*
+ * dgap_maxcps_room
+ *
+ * Reduces bytes_available to the max number of characters
+ * that can be sent currently given the maxcps value, and
+ * returns the new bytes_available.  This only affects printer
+ * output.
+ */
+static int maxcps_room(struct tty_struct *tty, int bytes_available)
+{
+	struct channel_t *ch = NULL;
+	struct un_t *un = NULL;
+
+	if (tty == NULL)
+		return (bytes_available);
+
+	un = tty->driver_data;
+	if (!un || un->magic != DGAP_UNIT_MAGIC)
+		return (bytes_available);
+
+	ch = un->un_ch;
+	if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+		return (bytes_available);
+
+	/*
+	 * If its not the Transparent print device, return
+	 * the full data amount.
+	 */
+	if (un->un_type != DGAP_PRINT)
+		return (bytes_available);
+
+	if (ch->ch_digi.digi_maxcps > 0 && ch->ch_digi.digi_bufsize > 0 ) {
+		int cps_limit = 0;
+		unsigned long current_time = jiffies;
+		unsigned long buffer_time = current_time +
+			(HZ * ch->ch_digi.digi_bufsize) / ch->ch_digi.digi_maxcps;
+
+		if (ch->ch_cpstime < current_time) {
+			/* buffer is empty */
+			ch->ch_cpstime = current_time;            /* reset ch_cpstime */
+			cps_limit = ch->ch_digi.digi_bufsize;
+		}
+		else if (ch->ch_cpstime < buffer_time) {
+			/* still room in the buffer */
+			cps_limit = ((buffer_time - ch->ch_cpstime) * ch->ch_digi.digi_maxcps) / HZ;
+		}
+		else {
+			/* no room in the buffer */
+			cps_limit = 0;
+		}
+
+		bytes_available = MIN(cps_limit, bytes_available);
+	}
+
+	return (bytes_available);
+}
+
+
+/*
+ * dgap_tty_write_room()
+ *
+ * Return space available in Tx buffer
+ */
+static int dgap_tty_write_room(struct tty_struct *tty)
+{
+	struct channel_t *ch = NULL;
+	struct un_t *un = NULL;
+	struct bs_t *bs = NULL;
+	u16 head, tail, tmask;
+	int ret = 0;
+	ulong   lock_flags = 0;
+
+	if (tty == NULL || dgap_TmpWriteBuf == NULL)
+		return(0);
+
+	un = tty->driver_data;
+	if (!un || un->magic != DGAP_UNIT_MAGIC)
+		return (0);
+
+	ch = un->un_ch;
+	if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+		return (0);
+
+        bs = ch->ch_bs;
+	if (!bs)
+		return (0);
+
+	DGAP_LOCK(ch->ch_lock, lock_flags);
+
+	tmask = ch->ch_tsize - 1;
+	head = readw(&(bs->tx_head)) & tmask;
+	tail = readw(&(bs->tx_tail)) & tmask;
+
+        if ((ret = tail - head - 1) < 0)
+                ret += ch->ch_tsize;
+
+	/* Limit printer to maxcps */
+	ret = maxcps_room(tty, ret);
+
+	/*
+	 * If we are printer device, leave space for
+	 * possibly both the on and off strings.
+	 */
+	if (un->un_type == DGAP_PRINT) {
+		if (!(ch->ch_flags & CH_PRON))
+			ret -= ch->ch_digi.digi_onlen;
+		ret -= ch->ch_digi.digi_offlen;
+	}
+	else {
+		if (ch->ch_flags & CH_PRON)
+			ret -= ch->ch_digi.digi_offlen;
+	}
+
+	if (ret < 0)
+		ret = 0;
+
+	if ((ret <= ch->ch_tlw) && ((un->un_flags & UN_LOW) == 0)) {
+		un->un_flags |= UN_LOW;
+		writeb(1, &(bs->ilow));
+	}
+	else if ((un->un_flags & UN_EMPTY) == 0) {
+		un->un_flags |= UN_EMPTY;
+		writeb(1, &(bs->iempty));
+	}
+	else {
+		un->un_flags |= UN_EMPTY;
+		writeb(1, &(bs->iempty));
+	}
+
+	DGAP_UNLOCK(ch->ch_lock, lock_flags);
+
+	DPR_WRITE(("dgap_tty_write_room - %d tail: %d head: %d\n", ret, tail, head));
+
+        return(ret);
+}
+
+
+/*
+ * dgap_tty_put_char()
+ *
+ * Put a character into ch->ch_buf
+ *
+ *      - used by the line discipline for OPOST processing
+ */
+static void dgap_tty_put_char(struct tty_struct *tty, unsigned char c)
+{
+	/*
+	 * Simply call tty_write.
+	 */
+	DPR_WRITE(("dgap_tty_put_char called\n"));
+	dgap_tty_write(tty, 0, &c, 1);
+        return;
+}
+
+
+/*
+ * dgap_tty_write()
+ *
+ * Take data from the user or kernel and send it out to the FEP.
+ * In here exists all the Transparent Print magic as well.
+ */
+static int dgap_tty_write(struct tty_struct *tty, int from_user, const unsigned char *buf,
+		int count)
+{
+	struct channel_t *ch = NULL;
+	struct un_t *un = NULL;
+	struct bs_t *bs = NULL;
+	char *vaddr = NULL;
+	u16 head, tail, tmask, remain;
+	int bufcount = 0, n = 0;
+	int orig_count = 0;
+
+	if (tty == NULL || dgap_TmpWriteBuf == NULL)
+		return(0);
+
+	un = tty->driver_data;
+	if (!un || un->magic != DGAP_UNIT_MAGIC)
+		return (0);
+
+	ch = un->un_ch;
+	if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+		return (0);
+
+        bs = ch->ch_bs;
+	if (!bs)
+		return (0);
+
+	DPR_WRITE(("dgap_tty_write: Port: %x tty=%x user=%d len=%d\n",
+		ch->ch_portnum, (int) tty, from_user, count));
+
+	/*
+	 * Store original amount of characters passed in.
+	 * This helps to figure out if we should ask the FEP
+	 * to send us an event when it has more space available.
+	 */
+	orig_count = count;
+
+	/*
+	 * If data is coming from user space, copy it into a temporary
+	 * buffer so we don't get swapped out while doing the copy to
+	 * the board.
+	 */
+	if (from_user) {
+		/* we're allowed to block if it's from_user */
+		if (down_interruptible(&dgap_TmpWriteSem)) {
+			return(-EINTR);
+		}
+		/* if we get a non-zero return, we couldn't lock, bail. */
+		if (!spin_trylock(&ch->ch_lock)) {
+			DPR_WRITE(("Unable to get lock\n"));
+			up(&dgap_TmpWriteSem);
+			return(-EAGAIN);
+		}
+	}
+	else {
+		/* if we get a non-zero return, we couldn't lock, bail. */
+		if (!spin_trylock(&ch->ch_lock)) {
+			DPR_WRITE(("Unable to get lock\n"));
+			return(-EAGAIN);
+		}
+	}
+
+	/* Get our space available for the channel from the board */
+	tmask = ch->ch_tsize - 1;
+	head = readw(&(bs->tx_head)) & tmask;
+	tail = readw(&(bs->tx_tail)) & tmask;
+
+	if ((bufcount = tail - head - 1) < 0)
+		bufcount += ch->ch_tsize;
+
+	DPR_WRITE(("%d: bufcount: %x count: %x tail: %x head: %x tmask: %x\n",
+		__LINE__, bufcount, count, tail, head, tmask));
+
+	/*
+	 * Limit printer output to maxcps overall, with bursts allowed
+	 * up to bufsize characters.
+	 */
+	bufcount = maxcps_room(tty, bufcount);
+
+	/*
+	 * Take minimum of what the user wants to send, and the
+	 * space available in the FEP buffer.
+	 */
+	count = MIN(count, bufcount);
+
+	/*
+	 * Bail if no space left.
+	 */
+	if (count <= 0)
+		goto out;
+
+	/*
+	 * Output the printer ON string, if we are in terminal mode, but
+	 * need to be in printer mode.
+	 */
+	if ((un->un_type == DGAP_PRINT) && !(ch->ch_flags & CH_PRON)) {
+		dgap_wmove(ch, ch->ch_digi.digi_onstr,
+		    (int) ch->ch_digi.digi_onlen);
+		head = readw(&(bs->tx_head)) & tmask;
+		ch->ch_flags |= CH_PRON;
+	}
+
+	/*
+	 * On the other hand, output the printer OFF string, if we are
+	 * currently in printer mode, but need to output to the terminal.
+	 */
+	if ((un->un_type != DGAP_PRINT) && (ch->ch_flags & CH_PRON)) {
+		dgap_wmove(ch, ch->ch_digi.digi_offstr,
+			(int) ch->ch_digi.digi_offlen);
+		head = readw(&(bs->tx_head)) & tmask;
+		ch->ch_flags &= ~CH_PRON;
+	}
+
+	if (from_user) {
+		/*
+		 * If there is nothing left to copy, or I can't handle any more data, leave.
+		 */
+		if (count <= 0) {
+			goto out;
+		}
+
+		count = MIN(count, WRITEBUFLEN);
+
+		/*
+		 * copy_from_user() returns the number
+		 * of bytes that could *NOT* be copied.
+		 */
+		spin_unlock(&ch->ch_lock);
+		count -= copy_from_user(dgap_TmpWriteBuf, buf, count);
+		spin_lock(&ch->ch_lock);
+		if (!count) {
+			spin_unlock(&ch->ch_lock);
+			up(&dgap_TmpWriteSem);
+			return(-EFAULT);
+		}
+
+		buf = dgap_TmpWriteBuf;
+	}
+
+	n = count;
+
+	/*
+	 * If the write wraps over the top of the circular buffer,
+	 * move the portion up to the wrap point, and reset the
+	 * pointers to the bottom.
+	 */
+	remain = ch->ch_tstart + ch->ch_tsize - head;
+
+	if (n >= remain) {
+		n -= remain;
+		vaddr = ch->ch_taddr + head;
+		memcpy_toio(vaddr, buf, remain);
+		head = ch->ch_tstart;
+		buf += remain;
+	}
+
+	if (n > 0) {
+
+		/*
+		 * Move rest of data.
+		 */
+		vaddr = ch->ch_taddr + head;
+		remain = n;
+
+		memcpy_toio(vaddr, buf, remain);
+		head += remain;
+
+	}
+
+	if (count) {
+		head &= tmask;
+		writew(head, &(bs->tx_head));
+	}
+
+out:
+
+	/*
+	 * If we are doing an incomplete write, we need to tell
+	 * the FEP to send us an event when we have more space available.
+	 *
+	 * If we are over the low water mark, then we can use that.
+	 * If we are under the low water mark, just wait for the empty event.
+	 */
+	if (count != orig_count) {
+
+		if ((bufcount = tail - head - 1) < 0)
+			bufcount += ch->ch_tsize;
+
+		if ((bufcount <= ch->ch_tlw) && ((un->un_flags & UN_LOW) == 0)) {
+			un->un_flags |= UN_LOW;
+			writeb(1, &(bs->ilow));
+		}
+		else if ((un->un_flags & UN_EMPTY) == 0) {
+			un->un_flags |= UN_EMPTY;
+			writeb(1, &(bs->iempty));
+		}
+		else {
+			un->un_flags |= UN_EMPTY;
+			writeb(1, &(bs->iempty));
+		}
+	}
+	else {
+		/*
+		 * If this is the print device, and the
+		 * printer is still on, we need to turn it
+		 * off before going idle.  If the buffer is
+		 * non-empty, wait until it goes empty.
+		 * Otherwise turn it off right now.
+		 */
+		if ((un->un_type == DGAP_PRINT) && (ch->ch_flags & CH_PRON)) {
+			tail = readw(&(bs->tx_tail)) & tmask;
+
+			if (tail != head) {
+				un->un_flags |= UN_EMPTY;
+				writeb(1, &(bs->iempty));
+			}
+			else {
+				dgap_wmove(ch, ch->ch_digi.digi_offstr,
+					(int) ch->ch_digi.digi_offlen);
+				head = readw(&(bs->tx_head)) & tmask;
+				ch->ch_flags &= ~CH_PRON;
+			}
+		}
+	}
+
+	/* Update printer buffer empty time. */
+	if ((un->un_type == DGAP_PRINT) && (ch->ch_digi.digi_maxcps > 0)
+	    && (ch->ch_digi.digi_bufsize > 0)) {
+                ch->ch_cpstime += (HZ * count) / ch->ch_digi.digi_maxcps;
+	}
+
+	if (from_user) {
+		spin_unlock(&ch->ch_lock);
+		up(&dgap_TmpWriteSem);
+	}
+	else {
+		spin_unlock(&ch->ch_lock);
+	}
+
+	DPR_WRITE(("Write finished - Write %d bytes of %d.\n", count, orig_count));
+
+	return (count);
+}
+
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+
+/*
+ * Return modem signals to ld.
+ */
+static int dgap_tty_tiocmget(struct tty_struct *tty, struct file *file)
+{
+	struct channel_t *ch;
+	struct un_t *un;
+	int result = -EIO;
+	uchar mstat = 0;
+	ulong   lock_flags = 0;
+
+	if (!tty || tty->magic != TTY_MAGIC)
+		return result;
+
+	un = tty->driver_data;
+	if (!un || un->magic != DGAP_UNIT_MAGIC)
+		return result;
+
+	ch = un->un_ch;
+	if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+		return result;
+
+	DPR_IOCTL(("dgap_tty_tiocmget start\n"));
+
+	DGAP_LOCK(ch->ch_lock, lock_flags);
+
+	mstat = readb(&(ch->ch_bs->m_stat));
+
+	DGAP_UNLOCK(ch->ch_lock, lock_flags);
+
+	result = 0;
+
+	if (mstat & D_DTR(ch))
+		result |= TIOCM_DTR;
+	if (mstat & D_RTS(ch))
+		result |= TIOCM_RTS;
+	if (mstat & D_CTS(ch))
+		result |= TIOCM_CTS;
+	if (mstat & D_DSR(ch))
+		result |= TIOCM_DSR;
+	if (mstat & D_RI(ch))
+		result |= TIOCM_RI;
+	if (mstat & D_CD(ch))
+		result |= TIOCM_CD;
+
+	DPR_IOCTL(("dgap_tty_tiocmget finish\n"));
+
+	return result;
+}
+
+
+/*
+ * dgap_tty_tiocmset()
+ *
+ * Set modem signals, called by ld.
+ */
+static int dgap_tty_tiocmset(struct tty_struct *tty, struct file *file,
+		unsigned int set, unsigned int clear)
+{
+	struct board_t *bd;
+	struct channel_t *ch;
+	struct un_t *un;
+	int ret = -EIO;
+	int mflag = 0;
+	ulong   lock_flags = 0;
+	ulong   lock_flags2 = 0;
+
+	if (!tty || tty->magic != TTY_MAGIC)
+		return ret;
+
+	un = tty->driver_data;
+	if (!un || un->magic != DGAP_UNIT_MAGIC)
+		return ret;
+
+	ch = un->un_ch;
+	if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+		return ret;
+
+	bd = ch->ch_bd;
+	if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+		return ret;
+
+	DPR_IOCTL(("dgap_tty_tiocmset start\n"));
+
+	ch->ch_mforce = D_DTR(ch)|D_RTS(ch);
+
+	if (set & TIOCM_RTS) {
+		mflag |= D_RTS(ch);
+        }
+
+	if (set & TIOCM_DTR) {
+		mflag |= D_DTR(ch);
+        }
+
+	if (clear & TIOCM_RTS) {
+		mflag &= ~(D_RTS(ch));
+        }
+
+	if (clear & TIOCM_DTR) {
+		mflag &= ~(D_DTR(ch));
+        }
+
+	ch->ch_mval = mflag;
+
+	DGAP_LOCK(bd->bd_lock, lock_flags);
+	DGAP_LOCK(ch->ch_lock, lock_flags2);
+
+	dgap_param(tty);
+
+	DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+	DGAP_UNLOCK(bd->bd_lock, lock_flags);
+
+	DPR_IOCTL(("dgap_tty_tiocmset finish\n"));
+
+	return (0);
+}
+
+#endif /* LINUX 2,6,0 */
+
+
+/*
+ * Return modem signals to ld.
+ */
+static int dgap_get_modem_info(struct channel_t *ch, unsigned int *value)
+{
+	int result = 0;
+	uchar mstat = 0;
+	ulong   lock_flags = 0;
+
+	DPR_IOCTL(("dgap_get_modem_info start\n"));
+
+	if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+		return(-ENXIO);
+
+	DGAP_LOCK(ch->ch_lock, lock_flags);
+
+	mstat = readb(&(ch->ch_bs->m_stat));
+
+	DGAP_UNLOCK(ch->ch_lock, lock_flags);
+
+	result = 0;
+
+	if (mstat & D_DTR(ch))
+		result |= TIOCM_DTR;
+	if (mstat & D_RTS(ch))
+		result |= TIOCM_RTS;
+	if (mstat & D_CTS(ch))
+		result |= TIOCM_CTS;
+	if (mstat & D_DSR(ch))
+		result |= TIOCM_DSR;
+	if (mstat & D_RI(ch))
+		result |= TIOCM_RI;
+	if (mstat & D_CD(ch))
+		result |= TIOCM_CD;
+
+	put_user(result, value);
+
+	DPR_IOCTL(("dgap_get_modem_info finish\n"));
+	return(0);
+}
+
+
+/*
+ * dgap_set_modem_info()
+ *
+ * Set modem signals, called by ld.
+ */
+static int dgap_set_modem_info(struct tty_struct *tty, unsigned int command, unsigned int *value)
+{
+	struct board_t *bd;
+	struct channel_t *ch;
+	struct un_t *un;
+	int ret = -ENXIO;
+	unsigned int arg = 0;
+	ulong   lock_flags = 0;
+	ulong   lock_flags2 = 0;
+
+	if (!tty || tty->magic != TTY_MAGIC)
+		return ret;
+
+	un = tty->driver_data;
+	if (!un || un->magic != DGAP_UNIT_MAGIC)
+		return ret;
+
+	ch = un->un_ch;
+	if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+		return ret;
+
+	bd = ch->ch_bd;
+	if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+		return ret;
+
+	ret = 0;
+
+	DPR_IOCTL(("dgap_set_modem_info() start\n"));
+
+	ret = verify_area(VERIFY_READ, value, sizeof(int));
+	if (ret)
+		return(ret);
+
+	GET_USER(arg, value);
+
+	switch (command) {
+	case TIOCMBIS:
+		if (arg & TIOCM_RTS) {
+			ch->ch_mforce |= D_RTS(ch);
+			ch->ch_mval   |= D_RTS(ch);
+        	}
+
+		if (arg & TIOCM_DTR) {
+			ch->ch_mforce |= D_DTR(ch);
+			ch->ch_mval   |= D_DTR(ch);
+        	}
+
+		break;
+
+	case TIOCMBIC:
+		if (arg & TIOCM_RTS) {
+			ch->ch_mforce |= D_RTS(ch);
+			ch->ch_mval   &= ~(D_RTS(ch));
+        	}
+
+		if (arg & TIOCM_DTR) {
+			ch->ch_mforce |= D_DTR(ch);
+			ch->ch_mval   &= ~(D_DTR(ch));
+        	}
+
+		break;
+
+        case TIOCMSET:
+		ch->ch_mforce = D_DTR(ch)|D_RTS(ch);
+
+		if (arg & TIOCM_RTS) {
+			ch->ch_mval |= D_RTS(ch);
+        	}
+		else {
+			ch->ch_mval &= ~(D_RTS(ch));
+		}
+
+		if (arg & TIOCM_DTR) {
+			ch->ch_mval |= (D_DTR(ch));
+        	}
+		else {
+			ch->ch_mval &= ~(D_DTR(ch));
+		}
+
+		break;
+
+	default:
+		return(-EINVAL);
+	}
+
+	DGAP_LOCK(bd->bd_lock, lock_flags);
+	DGAP_LOCK(ch->ch_lock, lock_flags2);
+
+	dgap_param(tty);
+
+	DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+	DGAP_UNLOCK(bd->bd_lock, lock_flags);
+
+	DPR_IOCTL(("dgap_set_modem_info finish\n"));
+
+	return (0);
+}
+
+
+/*
+ * dgap_tty_digigeta()
+ *
+ * Ioctl to get the information for ditty.
+ *
+ *
+ *
+ */
+static int dgap_tty_digigeta(struct tty_struct *tty, struct digi_t *retinfo)
+{
+	struct channel_t *ch;
+	struct un_t *un;
+	struct digi_t tmp;
+	ulong   lock_flags = 0;
+
+	if (!retinfo)
+		return (-EFAULT);
+
+	if (!tty || tty->magic != TTY_MAGIC)
+		return (-EFAULT);
+
+	un = tty->driver_data;
+	if (!un || un->magic != DGAP_UNIT_MAGIC)
+		return (-EFAULT);
+
+	ch = un->un_ch;
+	if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+		return (-EFAULT);
+
+	memset(&tmp, 0, sizeof(tmp));
+
+	DGAP_LOCK(ch->ch_lock, lock_flags);
+	memcpy(&tmp, &ch->ch_digi, sizeof(tmp));
+	DGAP_UNLOCK(ch->ch_lock, lock_flags);
+
+	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+		return (-EFAULT);
+
+	return (0);
+}
+
+
+/*
+ * dgap_tty_digiseta()
+ *
+ * Ioctl to set the information for ditty.
+ *
+ *
+ *
+ */
+static int dgap_tty_digiseta(struct tty_struct *tty, struct digi_t *new_info)
+{
+	struct board_t *bd;
+	struct channel_t *ch;
+	struct un_t *un;
+	struct digi_t new_digi;
+	ulong   lock_flags = 0;
+	unsigned long lock_flags2;
+
+	DPR_IOCTL(("DIGI_SETA start\n"));
+
+	if (!tty || tty->magic != TTY_MAGIC)
+		return (-EFAULT);
+
+	un = tty->driver_data;
+	if (!un || un->magic != DGAP_UNIT_MAGIC)
+		return (-EFAULT);
+
+	ch = un->un_ch;
+	if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+		return (-EFAULT);
+
+	bd = ch->ch_bd;
+	if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+		return (-EFAULT);
+
+        if (copy_from_user(&new_digi, new_info, sizeof(struct digi_t))) {
+		DPR_IOCTL(("DIGI_SETA failed copy_from_user\n"));
+                return(-EFAULT);
+	}
+
+	DGAP_LOCK(bd->bd_lock, lock_flags);
+	DGAP_LOCK(ch->ch_lock, lock_flags2);
+
+	memcpy(&ch->ch_digi, new_info, sizeof(struct digi_t));
+
+	if (ch->ch_digi.digi_maxcps < 1)
+		ch->ch_digi.digi_maxcps = 1;
+
+	if (ch->ch_digi.digi_maxcps > 10000)
+		ch->ch_digi.digi_maxcps = 10000;
+
+	if (ch->ch_digi.digi_bufsize < 10)
+		ch->ch_digi.digi_bufsize = 10;
+
+	if (ch->ch_digi.digi_bufsize > 100000)
+		ch->ch_digi.digi_bufsize = MIN(100000, USHRT_MAX);
+
+	if (ch->ch_digi.digi_maxchar < 1)
+		ch->ch_digi.digi_maxchar = 1;
+
+	if (ch->ch_digi.digi_maxchar > ch->ch_digi.digi_bufsize)
+		ch->ch_digi.digi_maxchar = ch->ch_digi.digi_bufsize;
+
+	if (ch->ch_digi.digi_onlen > DIGI_PLEN)
+		ch->ch_digi.digi_onlen = DIGI_PLEN;
+
+	if (ch->ch_digi.digi_offlen > DIGI_PLEN)
+		ch->ch_digi.digi_offlen = DIGI_PLEN;
+
+	dgap_param(tty);
+
+	DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+	DGAP_UNLOCK(bd->bd_lock, lock_flags);
+
+	DPR_IOCTL(("DIGI_SETA finish\n"));
+
+	return(0);
+}
+
+
+/*
+ * dgap_set_termios()
+ */
+static void dgap_tty_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+	struct board_t *bd;
+	struct channel_t *ch;
+	struct un_t *un;
+	unsigned long lock_flags;
+	unsigned long lock_flags2;
+
+	if (!tty || tty->magic != TTY_MAGIC)
+		return;
+
+	un = tty->driver_data;
+	if (!un || un->magic != DGAP_UNIT_MAGIC)
+		return;
+
+	ch = un->un_ch;
+	if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+		return;
+
+	bd = ch->ch_bd;
+	if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+		return;
+
+	DGAP_LOCK(bd->bd_lock, lock_flags);
+	DGAP_LOCK(ch->ch_lock, lock_flags2);
+
+	ch->ch_c_cflag   = tty->termios->c_cflag;
+	ch->ch_c_iflag   = tty->termios->c_iflag;
+	ch->ch_c_oflag   = tty->termios->c_oflag;
+	ch->ch_c_lflag   = tty->termios->c_lflag;
+	ch->ch_startc = tty->termios->c_cc[VSTART];
+	ch->ch_stopc  = tty->termios->c_cc[VSTOP];
+
+	dgap_carrier(ch);
+
+	dgap_param(tty);
+
+	DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+	DGAP_UNLOCK(bd->bd_lock, lock_flags);
+}
+
+
+static void dgap_tty_throttle(struct tty_struct *tty)
+{
+	struct channel_t *ch;
+	struct un_t *un;
+	ulong   lock_flags = 0;
+
+	if (!tty || tty->magic != TTY_MAGIC)
+		return;
+
+	un = tty->driver_data;
+	if (!un || un->magic != DGAP_UNIT_MAGIC)
+		return;
+
+        ch = un->un_ch;
+        if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+                return;
+
+	DPR_IOCTL(("dgap_tty_throttle start\n"));
+
+	DGAP_LOCK(ch->ch_lock, lock_flags);
+
+	ch->ch_flags |= (CH_RXBLOCK);
+#if 1
+	dgap_cmdw(ch, RPAUSE, 0, 0);
+#endif
+
+	DGAP_UNLOCK(ch->ch_lock, lock_flags);
+
+	DPR_IOCTL(("dgap_tty_throttle finish\n"));
+}
+
+
+static void dgap_tty_unthrottle(struct tty_struct *tty)
+{
+	struct channel_t *ch;
+	struct un_t *un;
+	ulong   lock_flags = 0;
+
+
+	if (!tty || tty->magic != TTY_MAGIC)
+		return;
+
+	un = tty->driver_data;
+	if (!un || un->magic != DGAP_UNIT_MAGIC)
+		return;
+
+        ch = un->un_ch;
+        if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+                return;
+
+	DPR_IOCTL(("dgap_tty_unthrottle start\n"));
+
+	DGAP_LOCK(ch->ch_lock, lock_flags);
+
+	ch->ch_flags &= ~(CH_RXBLOCK);
+
+#if 1
+	dgap_cmdw(ch, RRESUME, 0, 0);
+#endif
+
+	DGAP_UNLOCK(ch->ch_lock, lock_flags);
+
+	DPR_IOCTL(("dgap_tty_unthrottle finish\n"));
+}
+
+
+static void dgap_tty_start(struct tty_struct *tty)
+{
+	struct board_t *bd;
+	struct channel_t *ch;
+	struct un_t *un;
+	ulong   lock_flags = 0;
+	ulong   lock_flags2 = 0;
+
+	if (!tty || tty->magic != TTY_MAGIC)
+		return;
+
+	un = tty->driver_data;
+	if (!un || un->magic != DGAP_UNIT_MAGIC)
+		return;
+
+        ch = un->un_ch;
+        if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+                return;
+
+	bd = ch->ch_bd;
+	if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+		return;
+
+	DPR_IOCTL(("dgap_tty_start start\n"));
+
+	DGAP_LOCK(bd->bd_lock, lock_flags);
+	DGAP_LOCK(ch->ch_lock, lock_flags2);
+
+	dgap_cmdw(ch, RESUMETX, 0, 0);
+
+	DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+	DGAP_UNLOCK(bd->bd_lock, lock_flags);
+
+	DPR_IOCTL(("dgap_tty_start finish\n"));
+}
+
+
+static void dgap_tty_stop(struct tty_struct *tty)
+{
+	struct board_t *bd;
+	struct channel_t *ch;
+	struct un_t *un;
+	ulong   lock_flags = 0;
+	ulong   lock_flags2 = 0;
+
+	if (!tty || tty->magic != TTY_MAGIC)
+		return;
+
+	un = tty->driver_data;
+	if (!un || un->magic != DGAP_UNIT_MAGIC)
+		return;
+
+        ch = un->un_ch;
+        if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+                return;
+
+	bd = ch->ch_bd;
+	if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+		return;
+
+	DPR_IOCTL(("dgap_tty_stop start\n"));
+
+	DGAP_LOCK(bd->bd_lock, lock_flags);
+	DGAP_LOCK(ch->ch_lock, lock_flags2);
+
+	dgap_cmdw(ch, PAUSETX, 0, 0);
+
+	DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+	DGAP_UNLOCK(bd->bd_lock, lock_flags);
+
+	DPR_IOCTL(("dgap_tty_stop finish\n"));
+}
+
+
+/*
+ * dgap_tty_flush_chars()
+ *
+ * Flush the cook buffer
+ *
+ * Note to self, and any other poor souls who venture here:
+ *
+ * flush in this case DOES NOT mean dispose of the data.
+ * instead, it means "stop buffering and send it if you
+ * haven't already."  Just guess how I figured that out...   SRW 2-Jun-98
+ *
+ * It is also always called in interrupt context - JAR 8-Sept-99
+ */
+static void dgap_tty_flush_chars(struct tty_struct *tty)
+{
+	struct board_t *bd;
+	struct channel_t *ch;
+	struct un_t *un;
+	ulong   lock_flags = 0;
+	ulong   lock_flags2 = 0;
+
+	if (!tty || tty->magic != TTY_MAGIC)
+		return;
+
+	un = tty->driver_data;
+	if (!un || un->magic != DGAP_UNIT_MAGIC)
+		return;
+
+        ch = un->un_ch;
+        if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+                return;
+
+	bd = ch->ch_bd;
+	if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+		return;
+
+	DPR_IOCTL(("dgap_tty_flush_chars start\n"));
+
+	DGAP_LOCK(bd->bd_lock, lock_flags);
+	DGAP_LOCK(ch->ch_lock, lock_flags2);
+
+	/* TODO: Do something here */
+
+	DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+	DGAP_UNLOCK(bd->bd_lock, lock_flags);
+
+	DPR_IOCTL(("dgap_tty_flush_chars finish\n"));
+}
+
+
+
+/*
+ * dgap_tty_flush_buffer()
+ *
+ * Flush Tx buffer (make in == out)
+ */
+static void dgap_tty_flush_buffer(struct tty_struct *tty)
+{
+	struct board_t *bd;
+	struct channel_t *ch;
+	struct un_t *un;
+	ulong   lock_flags = 0;
+	ulong   lock_flags2 = 0;
+	u16	head = 0;
+
+	if (!tty || tty->magic != TTY_MAGIC)
+		return;
+
+	un = tty->driver_data;
+	if (!un || un->magic != DGAP_UNIT_MAGIC)
+		return;
+
+        ch = un->un_ch;
+        if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+                return;
+
+	bd = ch->ch_bd;
+	if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+		return;
+
+	DPR_IOCTL(("dgap_tty_flush_buffer on port: %d start\n", ch->ch_portnum));
+
+	DGAP_LOCK(bd->bd_lock, lock_flags);
+	DGAP_LOCK(ch->ch_lock, lock_flags2);
+
+	ch->ch_flags &= ~CH_STOP;
+	head = readw(&(ch->ch_bs->tx_head));
+	dgap_cmdw(ch, FLUSHTX, (u16) head, 0 );
+	dgap_cmdw(ch, RESUMETX, 0, 0);
+	if (ch->ch_tun.un_flags & (UN_LOW|UN_EMPTY)) {
+		ch->ch_tun.un_flags &= ~(UN_LOW|UN_EMPTY);
+		wake_up_interruptible(&ch->ch_tun.un_flags_wait);
+	}
+	if (ch->ch_pun.un_flags & (UN_LOW|UN_EMPTY)) {
+		ch->ch_pun.un_flags &= ~(UN_LOW|UN_EMPTY);
+		wake_up_interruptible(&ch->ch_pun.un_flags_wait);
+	}
+
+	DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+	DGAP_UNLOCK(bd->bd_lock, lock_flags);
+
+	DPR_IOCTL(("dgap_tty_flush_buffer finish\n"));
+}
+
+
+
+/*****************************************************************************
+ *
+ * The IOCTL function and all of its helpers
+ *
+ *****************************************************************************/
+
+/*
+ * dgap_tty_ioctl()
+ *
+ * The usual assortment of ioctl's
+ */
+static int dgap_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd,
+		unsigned long arg)
+{
+	struct board_t *bd;
+	struct channel_t *ch;
+	struct un_t *un;
+	int rc;
+	u16	head = 0;
+	ulong   lock_flags = 0;
+	ulong   lock_flags2 = 0;
+
+
+	if (!tty || tty->magic != TTY_MAGIC)
+		return (-ENODEV);
+
+	un = tty->driver_data;
+	if (!un || un->magic != DGAP_UNIT_MAGIC)
+		return (-ENODEV);
+
+	ch = un->un_ch;
+	if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+		return (-ENODEV);
+
+	bd = ch->ch_bd;
+	if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+		return (-ENODEV);
+
+	DPR_IOCTL(("dgap_tty_ioctl start on port %d - cmd %s (%x), arg %lx\n",
+		ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg));
+
+	DGAP_LOCK(bd->bd_lock, lock_flags);
+	DGAP_LOCK(ch->ch_lock, lock_flags2);
+
+	if (un->un_open_count <= 0) {
+		DPR_BASIC(("dgap_tty_ioctl - unit not open.\n"));
+		DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+		DGAP_UNLOCK(bd->bd_lock, lock_flags);
+		return(-EIO);
+	}
+
+	switch (cmd) {
+
+	/* Here are all the standard ioctl's that we MUST implement */
+
+	case TCSBRK:
+		/*
+		 * TCSBRK is SVID version: non-zero arg --> no break
+		 * this behaviour is exploited by tcdrain().
+		 *
+		 * According to POSIX.1 spec (7.2.2.1.2) breaks should be
+		 * between 0.25 and 0.5 seconds so we'll ask for something
+		 * in the middle: 0.375 seconds.
+		 */
+		rc = tty_check_change(tty);
+		DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+		DGAP_UNLOCK(bd->bd_lock, lock_flags);
+		if (rc) {
+			return(rc);
+		}
+
+		rc = dgap_wait_for_drain(tty);
+
+		if (rc) {
+			DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc));
+			return(-EINTR);
+		}
+
+		DGAP_LOCK(bd->bd_lock, lock_flags);
+		DGAP_LOCK(ch->ch_lock, lock_flags2);
+
+		if(((cmd == TCSBRK) && (!arg)) || (cmd == TCSBRKP)) {
+			dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0);
+		}
+
+		DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+		DGAP_UNLOCK(bd->bd_lock, lock_flags);
+
+		DPR_IOCTL(("dgap_tty_ioctl finish on port %d - cmd %s (%x), arg %lx\n",
+			ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg));
+
+                return(0);
+
+
+	case TCSBRKP:
+ 		/* support for POSIX tcsendbreak()
+
+		 * According to POSIX.1 spec (7.2.2.1.2) breaks should be
+		 * between 0.25 and 0.5 seconds so we'll ask for something
+		 * in the middle: 0.375 seconds.
+		 */
+		rc = tty_check_change(tty);
+		DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+		DGAP_UNLOCK(bd->bd_lock, lock_flags);
+		if (rc) {
+			return(rc);
+		}
+
+		rc = dgap_wait_for_drain(tty);
+		if (rc) {
+			DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc));
+			return(-EINTR);
+		}
+
+		DGAP_LOCK(bd->bd_lock, lock_flags);
+		DGAP_LOCK(ch->ch_lock, lock_flags2);
+
+		dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0);
+
+		DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+		DGAP_UNLOCK(bd->bd_lock, lock_flags);
+
+		DPR_IOCTL(("dgap_tty_ioctl finish on port %d - cmd %s (%x), arg %lx\n",
+			ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg));
+
+		return(0);
+
+
+	case TIOCGSOFTCAR:
+		rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long));
+		if (rc) {
+			DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+			DGAP_UNLOCK(bd->bd_lock, lock_flags);
+			return(rc);
+		}
+		PUT_USER(C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg);
+
+		DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+		DGAP_UNLOCK(bd->bd_lock, lock_flags);
+		return(0);
+
+	case TIOCSSOFTCAR:
+		GET_USER(arg, (unsigned long *) arg);
+		tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0));
+
+		dgap_param(tty);
+
+		DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+		DGAP_UNLOCK(bd->bd_lock, lock_flags);
+		return(0);
+
+	case TIOCMGET:
+		rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned int));
+		DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+		DGAP_UNLOCK(bd->bd_lock, lock_flags);
+		if (rc) {
+			return(rc);
+		}
+                return(dgap_get_modem_info(ch, (unsigned int *) arg));
+
+	case TIOCMBIS:
+	case TIOCMBIC:
+	case TIOCMSET:
+		DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+		DGAP_UNLOCK(bd->bd_lock, lock_flags);
+		return(dgap_set_modem_info(tty, cmd, (unsigned int *) arg));
+
+		/*
+		 * Here are any additional ioctl's that we want to implement
+		 */
+
+	case TCFLSH:
+		/*
+		 * The linux tty driver doesn't have a flush
+		 * input routine for the driver, assuming all backed
+		 * up data is in the line disc. buffers.  However,
+		 * we all know that's not the case.  Here, we
+		 * act on the ioctl, but then lie and say we didn't
+		 * so the line discipline will process the flush
+		 * also.
+		 */
+		rc = tty_check_change(tty);
+		if (rc) {
+			DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+			DGAP_UNLOCK(bd->bd_lock, lock_flags);
+			return(rc);
+		}
+
+		if ((arg == TCIFLUSH) || (arg == TCIOFLUSH)) {
+			ch->ch_flags &= ~CH_STOP;
+			head = readw(&(ch->ch_bs->tx_head));
+			dgap_cmdw(ch, FLUSHTX, (u16) head, 0 );
+			dgap_cmdw(ch, RESUMETX, 0, 0);
+			if (ch->ch_tun.un_flags & (UN_LOW|UN_EMPTY)) {
+				ch->ch_tun.un_flags &= ~(UN_LOW|UN_EMPTY);
+				wake_up_interruptible(&ch->ch_tun.un_flags_wait);
+			}
+			if (ch->ch_pun.un_flags & (UN_LOW|UN_EMPTY)) {
+				ch->ch_pun.un_flags &= ~(UN_LOW|UN_EMPTY);
+				wake_up_interruptible(&ch->ch_pun.un_flags_wait);
+			}
+		}
+
+		if ((arg == TCOFLUSH) || (arg == TCIOFLUSH)) {
+			if (!(un->un_type == DGAP_PRINT)) {
+				head = readw(&(ch->ch_bs->rx_head));
+				writew(head, &(ch->ch_bs->rx_tail));
+				writeb(0, &(ch->ch_bs->orun));
+			}
+		}
+
+		/* pretend we didn't recognize this IOCTL */
+		DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+		DGAP_UNLOCK(bd->bd_lock, lock_flags);
+		return(-ENOIOCTLCMD);
+
+#ifdef TIOCGETP
+	case TIOCGETP:
+#endif
+	case TCGETS:
+	case TCGETA:
+		if (tty->ldisc.ioctl) {
+			int retval = (-ENXIO);
+
+			DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+			DGAP_UNLOCK(bd->bd_lock, lock_flags);
+
+			if (tty->termios) {
+				retval = ((tty->ldisc.ioctl) (tty, file, cmd, arg));
+			}
+
+			DPR_IOCTL(("dgap_tty_ioctl (LINE:%d) finish on port %d - cmd %s (%x), arg %lx\n",
+				__LINE__, ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg));
+			return(retval);
+		}
+
+		DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+		DGAP_UNLOCK(bd->bd_lock, lock_flags);
+		DPR_IOCTL(("dgap_tty_ioctl (LINE:%d) finish on port %d - cmd %s (%x), arg %lx\n",
+			__LINE__, ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg));
+
+		return(-ENOIOCTLCMD);
+
+	case TCSETSF:
+	case TCSETSW:
+		/*
+		 * The linux tty driver doesn't have a flush
+		 * input routine for the driver, assuming all backed
+		 * up data is in the line disc. buffers.  However,
+		 * we all know that's not the case.  Here, we
+		 * act on the ioctl, but then lie and say we didn't
+		 * so the line discipline will process the flush
+		 * also.
+		 */
+		if (cmd == TCSETSF) {
+			/* flush rx */
+			ch->ch_flags &= ~CH_STOP;
+			head = readw(&(ch->ch_bs->rx_head));
+			writew(head, &(ch->ch_bs->rx_tail));
+		}
+
+		/* now wait for all the output to drain */
+		DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+		DGAP_UNLOCK(bd->bd_lock, lock_flags);
+		rc = dgap_wait_for_drain(tty);
+		if (rc) {
+			DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc));
+			return(-EINTR);
+		}
+
+		DPR_IOCTL(("dgap_tty_ioctl finish on port %d - cmd %s (%x), arg %lx\n",
+			ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg));
+
+		/* pretend we didn't recognize this */
+		return(-ENOIOCTLCMD);
+
+	case TCSETAW:
+
+		DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+		DGAP_UNLOCK(bd->bd_lock, lock_flags);
+		rc = dgap_wait_for_drain(tty);
+		if (rc) {
+			DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc));
+			return(-EINTR);
+		}
+
+		/* pretend we didn't recognize this */
+		return(-ENOIOCTLCMD);
+
+	case TCXONC:
+		/*
+		 * The Linux Line Discipline (LD) would do this for us if we
+		 * let it, but we have the special firmware options to do this
+		 * the "right way" regardless of hardware or software flow
+		 * control so we'll do it outselves instead of letting the LD
+		 * do it.
+		 */
+		rc = tty_check_change(tty);
+		if (rc) {
+			DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+			DGAP_UNLOCK(bd->bd_lock, lock_flags);
+			return(rc);
+		}
+
+		DPR_IOCTL(("dxb_ioctl - in TCXONC - %d\n", cmd));
+		switch (arg) {
+
+		case TCOON:
+			DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+			DGAP_UNLOCK(bd->bd_lock, lock_flags);
+			dgap_tty_start(tty);
+			return(0);
+		case TCOOFF:
+			DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+			DGAP_UNLOCK(bd->bd_lock, lock_flags);
+			dgap_tty_stop(tty);
+			return(0);
+		case TCION:
+			DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+			DGAP_UNLOCK(bd->bd_lock, lock_flags);
+			/* Make the ld do it */
+			return(-ENOIOCTLCMD);
+		case TCIOFF:
+			DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+			DGAP_UNLOCK(bd->bd_lock, lock_flags);
+			/* Make the ld do it */
+			return(-ENOIOCTLCMD);
+		default:
+			DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+			DGAP_UNLOCK(bd->bd_lock, lock_flags);
+			return(-EINVAL);
+		}
+
+		DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+		DGAP_UNLOCK(bd->bd_lock, lock_flags);
+		return(-ENOIOCTLCMD);
+
+	case DIGI_GETA:
+		/* get information for ditty */
+		DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+		DGAP_UNLOCK(bd->bd_lock, lock_flags);
+		return(dgap_tty_digigeta(tty, (struct digi_t *) arg));
+
+	case DIGI_SETAW:
+	case DIGI_SETAF:
+
+		/* set information for ditty */
+		if (cmd == (DIGI_SETAW)) {
+
+			DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+			DGAP_UNLOCK(bd->bd_lock, lock_flags);
+			rc = dgap_wait_for_drain(tty);
+			if (rc) {
+				DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc));
+				return(-EINTR);
+			}
+			DGAP_LOCK(bd->bd_lock, lock_flags);
+			DGAP_LOCK(ch->ch_lock, lock_flags2);
+		}
+		else {
+			if (tty->ldisc.flush_buffer)
+				tty->ldisc.flush_buffer(tty);
+		}
+		/* fall thru */
+
+	case DIGI_SETA:
+		DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+		DGAP_UNLOCK(bd->bd_lock, lock_flags);
+		return(dgap_tty_digiseta(tty, (struct digi_t *) arg));
+
+	default:
+		DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+		DGAP_UNLOCK(bd->bd_lock, lock_flags);
+
+		DPR_IOCTL(("dgap_tty_ioctl - in default\n"));
+		DPR_IOCTL(("dgap_tty_ioctl end - cmd %s (%x), arg %lx\n",
+			dgap_ioctl_name(cmd), cmd, arg));
+
+		return(-ENOIOCTLCMD);
+	}
+
+	DGAP_UNLOCK(ch->ch_lock, lock_flags2);
+	DGAP_UNLOCK(bd->bd_lock, lock_flags);
+
+	DPR_IOCTL(("dgap_tty_ioctl end - cmd %s (%x), arg %lx\n",
+		dgap_ioctl_name(cmd), cmd, arg));
+
+	return(0);
+}
diff -puN /dev/null drivers/char/digi/dgap/dgap_tty.h
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/drivers/char/digi/dgap/dgap_tty.h	Wed Sep 17 12:34:23 2003
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ *	Scott H Kilau <Scott_Kilau at digi dot com>
+ *
+ * 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, EXPRESS OR IMPLIED; 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.
+ *
+ *	NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!!
+ */
+
+#ifndef __DGAP_TTY_H
+#define __DGAP_TTY_H
+
+#include "dgap_driver.h"
+
+int	dgap_tty_register(struct board_t *brd);
+
+int	dgap_tty_preinit(void);
+int     dgap_tty_init(struct board_t *);
+
+void	dgap_tty_post_uninit(void);
+void	dgap_tty_uninit(struct board_t *);
+
+int	dgap_event(struct board_t *);
+
+#endif
diff -puN /dev/null drivers/char/digi/dgap/dgap_types.h
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/drivers/char/digi/dgap/dgap_types.h	Wed Sep 17 12:34:23 2003
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ *	Scott H Kilau <Scott_Kilau at digi dot com>
+ *
+ * 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, EXPRESS OR IMPLIED; 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.
+ *
+ *	NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!!
+ */
+
+#ifndef __DGAP_TYPES_H
+#define __DGAP_TYPES_H
+
+#if defined(__KERNEL__)
+# include <linux/string.h>
+#endif
+
+#include <linux/types.h>
+
+#if !defined (TRUE)
+# define	TRUE		1
+#endif
+
+#if !defined (FALSE)
+# define	FALSE		0
+#endif
+
+#if !defined(NULL)
+# define	NULL		0
+#endif
+
+/* Required for our shared headers! */
+typedef unsigned char		uchar;
+
+#endif
diff -puN /dev/null drivers/char/digi/dgap/digi.h
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/drivers/char/digi/dgap/digi.h	Wed Sep 17 12:34:23 2003
@@ -0,0 +1,369 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ *	Scott H Kilau <Scott_Kilau at digi dot com>
+ *
+ * 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, EXPRESS OR IMPLIED; 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.
+ *
+ * $Id: digi.h,v 1.4 2003/09/03 18:22:56 scottk Exp $
+ *
+ *	NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!!
+ */
+
+#ifndef __DIGI_H
+#define __DIGI_H
+
+/************************************************************************
+ ***	Definitions for Digi ditty(1) command.
+ ************************************************************************/
+
+
+/*
+ * Copyright (c) 1988-96 Digi International Inc., All Rights Reserved.
+ */
+
+/************************************************************************
+ * This module provides application access to special Digi
+ * serial line enhancements which are not standard UNIX(tm) features.
+ ************************************************************************/
+
+#if !defined(TIOCMODG)
+
+#define	TIOCMODG	('d'<<8) | 250		/* get modem ctrl state	*/
+#define	TIOCMODS	('d'<<8) | 251		/* set modem ctrl state	*/
+
+#ifndef TIOCM_LE
+#define		TIOCM_LE	0x01		/* line enable		*/
+#define		TIOCM_DTR	0x02		/* data terminal ready	*/
+#define		TIOCM_RTS	0x04		/* request to send	*/
+#define		TIOCM_ST	0x08		/* secondary transmit	*/
+#define		TIOCM_SR	0x10		/* secondary receive	*/
+#define		TIOCM_CTS	0x20		/* clear to send	*/
+#define		TIOCM_CAR	0x40		/* carrier detect	*/
+#define		TIOCM_RNG	0x80		/* ring	indicator	*/
+#define		TIOCM_DSR	0x100		/* data set ready	*/
+#define		TIOCM_RI	TIOCM_RNG	/* ring (alternate)	*/
+#define		TIOCM_CD	TIOCM_CAR	/* carrier detect (alt)	*/
+#endif
+
+#endif
+
+#if !defined(TIOCMSET)
+#define	TIOCMSET	('d'<<8) | 252		/* set modem ctrl state	*/
+#define	TIOCMGET	('d'<<8) | 253		/* set modem ctrl state	*/
+#endif
+
+#if !defined(TIOCMBIC)
+#define	TIOCMBIC	('d'<<8) | 254		/* set modem ctrl state */
+#define	TIOCMBIS	('d'<<8) | 255		/* set modem ctrl state */
+#endif
+
+
+#if !defined(TIOCSDTR)
+#define	TIOCSDTR	('e'<<8) | 0		/* set DTR		*/
+#define	TIOCCDTR	('e'<<8) | 1		/* clear DTR		*/
+#endif
+
+/************************************************************************
+ * Ioctl command arguments for DIGI parameters.
+ ************************************************************************/
+#define DIGI_GETA	('e'<<8) | 94		/* Read params		*/
+
+#define DIGI_SETA	('e'<<8) | 95		/* Set params		*/
+#define DIGI_SETAW	('e'<<8) | 96		/* Drain & set params	*/
+#define DIGI_SETAF	('e'<<8) | 97		/* Drain, flush & set params */
+
+#define DIGI_KME	('e'<<8) | 98		/* Read/Write Host	*/
+						/* Adapter Memory	*/
+
+#define	DIGI_GETFLOW	('e'<<8) | 99		/* Get startc/stopc flow */
+						/* control characters 	 */
+#define	DIGI_SETFLOW	('e'<<8) | 100		/* Set startc/stopc flow */
+						/* control characters	 */
+#define	DIGI_GETAFLOW	('e'<<8) | 101		/* Get Aux. startc/stopc */
+						/* flow control chars 	 */
+#define	DIGI_SETAFLOW	('e'<<8) | 102		/* Set Aux. startc/stopc */
+						/* flow control chars	 */
+
+#define DIGI_GEDELAY	('d'<<8) | 246		/* Get edelay */
+#define DIGI_SEDELAY	('d'<<8) | 247		/* Set edelay */
+
+struct	digiflow_t {
+	unsigned char	startc;				/* flow cntl start char	*/
+	unsigned char	stopc;				/* flow cntl stop char	*/
+};
+
+
+#ifdef	FLOW_2200
+#define	F2200_GETA	('e'<<8) | 104		/* Get 2x36 flow cntl flags */
+#define	F2200_SETAW	('e'<<8) | 105		/* Set 2x36 flow cntl flags */
+#define		F2200_MASK	0x03		/* 2200 flow cntl bit mask  */
+#define		FCNTL_2200	0x01		/* 2x36 terminal flow cntl  */
+#define		PCNTL_2200	0x02		/* 2x36 printer flow cntl   */
+#define	F2200_XON	0xf8
+#define	P2200_XON	0xf9
+#define	F2200_XOFF	0xfa
+#define	P2200_XOFF	0xfb
+
+#define	FXOFF_MASK	0x03			/* 2200 flow status mask    */
+#define	RCVD_FXOFF	0x01			/* 2x36 Terminal XOFF rcvd  */
+#define	RCVD_PXOFF	0x02			/* 2x36 Printer XOFF rcvd   */
+#endif
+
+/************************************************************************
+ * Values for digi_flags
+ ************************************************************************/
+#define DIGI_IXON	0x0001		/* Handle IXON in the FEP	*/
+#define DIGI_FAST	0x0002		/* Fast baud rates		*/
+#define RTSPACE		0x0004		/* RTS input flow control	*/
+#define CTSPACE		0x0008		/* CTS output flow control	*/
+#define DSRPACE		0x0010		/* DSR output flow control	*/
+#define DCDPACE		0x0020		/* DCD output flow control	*/
+#define DTRPACE		0x0040		/* DTR input flow control	*/
+#define DIGI_COOK	0x0080		/* Cooked processing done in FEP */
+#define DIGI_FORCEDCD	0x0100		/* Force carrier		*/
+#define	DIGI_ALTPIN	0x0200		/* Alternate RJ-45 pin config	*/
+#define	DIGI_AIXON	0x0400		/* Aux flow control in fep	*/
+#define	DIGI_PRINTER	0x0800		/* Hold port open for flow cntrl*/
+#define DIGI_PP_INPUT	0x1000		/* Change parallel port to input*/
+#define	DIGI_422	0x4000		/* for 422/232 selectable panel */
+
+/************************************************************************
+ * These options are not supported on the comxi.
+ ************************************************************************/
+#define	DIGI_COMXI	(DIGI_FAST|DIGI_COOK|DSRPACE|DCDPACE|DTRPACE)
+
+#define DIGI_PLEN	28		/* String length		*/
+#define	DIGI_TSIZ	10		/* Terminal string len		*/
+
+/************************************************************************
+ * Structure used with ioctl commands for DIGI parameters.
+ ************************************************************************/
+struct digi_t {
+	unsigned short	digi_flags;		/* Flags (see above)	*/
+	unsigned short	digi_maxcps;		/* Max printer CPS	*/
+	unsigned short	digi_maxchar;		/* Max chars in print queue */
+	unsigned short	digi_bufsize;		/* Buffer size		*/
+	unsigned char	digi_onlen;		/* Length of ON string	*/
+	unsigned char	digi_offlen;		/* Length of OFF string	*/
+	char		digi_onstr[DIGI_PLEN];	/* Printer on string	*/
+	char		digi_offstr[DIGI_PLEN];	/* Printer off string	*/
+	char		digi_term[DIGI_TSIZ];	/* terminal string	*/
+};
+
+/************************************************************************
+ * KME definitions and structures.
+ ************************************************************************/
+#define	RW_IDLE		0	/* Operation complete			*/
+#define	RW_READ		1	/* Read Concentrator Memory		*/
+#define	RW_WRITE	2	/* Write Concentrator Memory		*/
+
+struct rw_t {
+	unsigned char	rw_req;		/* Request type			*/
+	unsigned char	rw_board;	/* Host Adapter board number	*/
+	unsigned char	rw_conc;	/* Concentrator number		*/
+	unsigned char	rw_reserved;	/* Reserved for expansion	*/
+	unsigned long	rw_addr;	/* Address in concentrator	*/
+	unsigned short	rw_size;	/* Read/write request length	*/
+	unsigned char	rw_data[128];	/* Data to read/write		*/
+};
+
+/***********************************************************************
+ * Shrink Buffer and Board Information definitions and structures.
+
+ ************************************************************************/
+			/* Board type return codes */
+#define	PCXI_TYPE 1     /* Board type at the designated port is a PC/Xi */
+#define PCXM_TYPE 2     /* Board type at the designated port is a PC/Xm */
+#define	PCXE_TYPE 3     /* Board type at the designated port is a PC/Xe */
+#define	MCXI_TYPE 4     /* Board type at the designated port is a MC/Xi */
+#define COMXI_TYPE 5     /* Board type at the designated port is a COM/Xi */
+
+			 /* Non-Zero Result codes. */
+#define RESULT_NOBDFND 1 /* A Digi product at that port is not config installed */
+#define RESULT_NODESCT 2 /* A memory descriptor was not obtainable */
+#define RESULT_NOOSSIG 3 /* FEP/OS signature was not detected on the board */
+#define RESULT_TOOSML  4 /* Too small an area to shrink.  */
+#define RESULT_NOCHAN  5 /* Channel structure for the board was not found */
+
+struct shrink_buf_struct {
+	unsigned long	shrink_buf_vaddr;	/* Virtual address of board */
+	unsigned long	shrink_buf_phys;	/* Physical address of board */
+	unsigned long	shrink_buf_bseg;	/* Amount of board memory */
+	unsigned long	shrink_buf_hseg;	/* '186 Begining of Dual-Port */
+
+	unsigned long	shrink_buf_lseg;	/* '186 Begining of freed memory						*/
+	unsigned long	shrink_buf_mseg;	/* Linear address from start of
+						   dual-port were freed memory
+						   begins, host viewpoint. */
+
+	unsigned long	shrink_buf_bdparam;	/* Parameter for xxmemon and
+						   xxmemoff */
+
+	unsigned long	shrink_buf_reserva;	/* Reserved */
+	unsigned long	shrink_buf_reservb;	/* Reserved */
+	unsigned long	shrink_buf_reservc;	/* Reserved */
+	unsigned long	shrink_buf_reservd;	/* Reserved */
+
+	unsigned char	shrink_buf_result;	/* Reason for call failing
+						   Zero is Good return */
+	unsigned char	shrink_buf_init;	/* Non-Zero if it caused an
+						   xxinit call. */
+
+	unsigned char	shrink_buf_anports;	/* Number of async ports  */
+	unsigned char	shrink_buf_snports; 	/* Number of sync  ports */
+	unsigned char	shrink_buf_type;	/* Board type 1 = PC/Xi,
+							      2 = PC/Xm,
+							      3 = PC/Xe
+							      4 = MC/Xi
+							      5 = COMX/i */
+	unsigned char	shrink_buf_card;	/* Card number */
+
+};
+
+/************************************************************************
+ * Structure to get driver status information
+ ************************************************************************/
+struct digi_dinfo {
+	unsigned long	dinfo_nboards;		/* # boards configured	*/
+	char		dinfo_reserved[12];	/* for future expansion */
+	char		dinfo_version[16];	/* driver version       */
+};
+
+#define	DIGI_GETDD	('d'<<8) | 248		/* get driver info      */
+
+/************************************************************************
+ * Structure used with ioctl commands for per-board information
+ *
+ * physsize and memsize differ when board has "windowed" memory
+ ************************************************************************/
+struct digi_info {
+	unsigned long	info_bdnum;		/* Board number (0 based)  */
+	unsigned long	info_ioport;		/* io port address         */
+	unsigned long	info_physaddr;		/* memory address          */
+	unsigned long	info_physsize;		/* Size of host mem window */
+	unsigned long	info_memsize;		/* Amount of dual-port mem */
+						/* on board                */
+	unsigned short	info_bdtype;		/* Board type              */
+	unsigned short	info_nports;		/* number of ports         */
+	char		info_bdstate;		/* board state             */
+	char		info_reserved[7];	/* for future expansion    */
+};
+
+#define	DIGI_GETBD	('d'<<8) | 249		/* get board info          */
+
+struct digi_stat {
+	unsigned int	info_chan;		/* Channel number (0 based)  */
+	unsigned int	info_brd;		/* Board number (0 based)  */
+	unsigned long	info_cflag;		/* cflag for channel       */
+	unsigned long	info_iflag;		/* iflag for channel       */
+	unsigned long	info_oflag;		/* oflag for channel       */
+	unsigned long	info_mstat;		/* mstat for channel       */
+	unsigned long	info_tx_data;		/* tx_data for channel       */
+	unsigned long	info_rx_data;		/* rx_data for channel       */
+	unsigned long	info_hflow;		/* hflow for channel       */
+	unsigned long	info_reserved[8];	/* for future expansion    */
+};
+
+#define	DIGI_GETSTAT	('d'<<8) | 244		/* get board info          */
+/************************************************************************
+ *
+ * Structure used with ioctl commands for per-channel information
+ *
+ ************************************************************************/
+struct digi_ch {
+	unsigned long	info_bdnum;		/* Board number (0 based)  */
+	unsigned long	info_channel;		/* Channel index number    */
+	unsigned long	info_ch_cflag;		/* Channel cflag   	   */
+	unsigned long	info_ch_iflag;		/* Channel iflag   	   */
+	unsigned long	info_ch_oflag;		/* Channel oflag   	   */
+	unsigned long	info_chsize;		/* Channel structure size  */
+	unsigned long	info_sleep_stat;	/* sleep status		   */
+	dev_t		info_dev;		/* device number	   */
+	unsigned char	info_initstate;		/* Channel init state	   */
+	unsigned char	info_running;		/* Channel running state   */
+	long		reserved[8];		/* reserved for future use */
+};
+
+/*
+* This structure is used with the DIGI_FEPCMD ioctl to
+* tell the driver which port to send the command for.
+*/
+struct digi_cmd {
+	int	cmd;
+	int	word;
+	int	ncmds;
+	int	chan; /* channel index (zero based) */
+	int	bdid; /* board index (zero based) */
+};
+
+/*
+*  info_sleep_stat defines
+*/
+#define INFO_RUNWAIT	0x0001
+#define INFO_WOPEN	0x0002
+#define INFO_TTIOW	0x0004
+#define INFO_CH_RWAIT	0x0008
+#define INFO_CH_WEMPTY	0x0010
+#define INFO_CH_WLOW	0x0020
+#define INFO_XXBUF_BUSY 0x0040
+
+#define	DIGI_GETCH	('d'<<8) | 245		/* get board info          */
+
+/* Board type definitions */
+
+#define	SUBTYPE		0007
+#define	T_PCXI		0000
+#define T_PCXM		0001
+#define T_PCXE		0002
+#define T_PCXR		0003
+#define T_SP		0004
+#define T_SP_PLUS	0005
+#	define T_HERC	0000
+#	define T_HOU	0001
+#	define T_LON	0002
+#	define T_CHA	0003
+#define FAMILY		0070
+#define T_COMXI		0000
+#define T_PCXX		0010
+#define T_CX		0020
+#define T_EPC		0030
+#define	T_PCLITE	0040
+#define	T_SPXX		0050
+#define	T_AVXX		0060
+#define T_DXB		0070
+#define T_A2K_4_8	0070
+#define BUSTYPE		0700
+#define T_ISABUS	0000
+#define T_MCBUS		0100
+#define	T_EISABUS	0200
+#define	T_PCIBUS	0400
+
+/* Board State Definitions */
+
+#define	BD_RUNNING	0x0
+#define	BD_REASON	0x7f
+#define	BD_NOTFOUND	0x1
+#define	BD_NOIOPORT	0x2
+#define	BD_NOMEM	0x3
+#define	BD_NOBIOS	0x4
+#define	BD_NOFEP	0x5
+#define	BD_FAILED	0x6
+#define BD_ALLOCATED	0x7
+#define BD_TRIBOOT	0x8
+#define	BD_BADKME	0x80
+
+#define DIGI_SPOLL            ('d'<<8) | 254  /* change poller rate   */
+
+#endif /* DIGI_H */
diff -puN /dev/null drivers/char/digi/dgap/Makefile
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/drivers/char/digi/dgap/Makefile	Wed Sep 17 12:34:23 2003
@@ -0,0 +1,19 @@
+# Makefile for the in-kernel version of the DGAP driver.
+
+PACKAGE=dgap
+TRUE_VERSION="1.0-2"
+DGAP_PART_NUM=40002347_A-INKERNEL
+SBINDIR=/usr/sbin
+
+RPMNAME := $(PACKAGE)-$(TRUE_VERSION)
+PARTNUM := $(DGAP_PART_NUM)
+
+# Send in some extra things...
+EXTRA_CFLAGS += -DDGAP_TRACER -DSBINDIR=\"$(SBINDIR)\" \
+		-DDG_NAME=\"$(RPMNAME)\" -DDG_PART=\"$(PARTNUM)\"
+
+obj-$(CONFIG_DIGI_DGAP) += dgap.o
+
+dgap-objs :=	dgap_driver.o dgap_mgmt.o\
+		dgap_parse.o dgap_proc.o\
+		dgap_trace.o dgap_tty.o
diff -puN drivers/char/epca.c~dgap drivers/char/epca.c
--- 25/drivers/char/epca.c~dgap	Wed Sep 17 12:34:23 2003
+++ 25-akpm/drivers/char/epca.c	Wed Sep 17 12:34:23 2003
@@ -3,8 +3,10 @@
  
 	Copyright (C) 1996  Digi International.
  
-	For technical support please email digiLinux@dgii.com or
-	call Digi tech support at (612) 912-3456
+	NOTE:	As of the 2.6 Linux kernel, Digi International
+		no longer supports this version of the driver.
+		If you have a PCI EPCA DigiBoard, please use the DGAP
+		driver instead.
 
 	Much of this design and code came from epca.c which was 
 	copyright (C) 1994, 1995 Troy De Jongh, and subsquently 
@@ -44,9 +46,12 @@
 #include <asm/uaccess.h>
 #include <asm/io.h>
 
-#ifdef CONFIG_PCI
-#define ENABLE_PCI
-#endif /* CONFIG_PCI */
+/*
+ * NOTE: DO NOT enable PCI in this driver anymore!
+ * There is a new replacement for this driver for the PCI boards
+ * called DGAP which should be used instead.
+ */
+#undef ENABLE_PCI
 
 #define putUser(arg1, arg2) put_user(arg1, (unsigned long *)arg2)
 #define getUser(arg1, arg2) get_user(arg1, (unsigned int *)arg2)
diff -puN drivers/char/Kconfig~dgap drivers/char/Kconfig
--- 25/drivers/char/Kconfig~dgap	Wed Sep 17 12:34:23 2003
+++ 25-akpm/drivers/char/Kconfig	Wed Sep 17 12:34:23 2003
@@ -143,15 +143,33 @@ config CYZ_INTR
 	  status of the Cyclades-Z ports. The default op mode is polling. If
 	  unsure, say N.
 
+config DIGI_DGAP
+	tristate "Digi International EPCA PCI Acceleport Async Support"
+	depends on SERIAL_NONSTANDARD
+	---help---
+	  This is a driver for Digi International's Xr, Xem, C/X and EPC/X PCI
+	  series of cards which provide multiple serial ports. You would need
+	  something like this to connect more than two modems to your Linux
+	  box, for instance in order to become a dial-in server.
+	  This driver supports only the PCI versions of these cards.
+	  If you have a card like this, say Y here and read the file
+	  <file:Documentation/digidgap.txt>.
+
+	  NOTE: There is another, separate driver for the EPCA ISA boards:
+	  "Digiboard Intelligent ISA Async Support" below.
+
+	  If you want to compile this driver as a module, say M here: the
+	  module will be called dgap.
+
 config DIGIEPCA
-	tristate "Digiboard Intelligent Async Support"
+	tristate "Digiboard Intelligent ISA Async Support"
 	depends on SERIAL_NONSTANDARD && BROKEN_ON_SMP
 	---help---
-	  This is a driver for Digi International's Xx, Xeve, and Xem series
-	  of cards which provide multiple serial ports. You would need
+	  This is a driver for Digi International's Xx, Xeve, and Xem ISA
+	  series of cards which provide multiple serial ports. You would need
 	  something like this to connect more than two modems to your Linux
 	  box, for instance in order to become a dial-in server. This driver
-	  supports the original PC (ISA) boards as well as PCI, and EISA. If
+	  supports the original PC (ISA) boards as well as EISA. If
 	  you have a card like this, say Y here and read the file
 	  <file:Documentation/digiepca.txt>.
 
@@ -159,12 +177,12 @@ config DIGIEPCA
 	  "Digiboard PC/Xx Support" below. You should (and can) only select
 	  one of the two drivers.
 
-	  If you want to compile this driver as a module, say M here and read
-	  <file:Documentation/modules.txt>. The module will be called epca.
+	  If you want to compile this driver as a module, say M here: the
+	  module will be called epca.
 
 config DIGI
 	tristate "Digiboard PC/Xx Support"
-	depends on SERIAL_NONSTANDARD && DIGIEPCA=n && BROKEN_ON_SMP
+	depends on SERIAL_NONSTANDARD && DIGIEPCA=n && BROKEN_ON_SMP && OBSOLETE
 	help
 	  This is a driver for the Digiboard PC/Xe, PC/Xi, and PC/Xeve cards
 	  that give you many serial ports. You would need something like this
@@ -172,8 +190,8 @@ config DIGI
 	  order to become a dial-in server. If you have a card like that, say
 	  Y here and read the file <file:Documentation/digiboard.txt>.
 
-	  If you want to compile this driver as a module, say M here and read
-	  <file:Documentation/modules.txt>. The module will be called pcxx.
+	  If you want to compile this driver as a module, say M here: the
+	  module will be called pcxx.
 
 config ESPSERIAL
 	tristate "Hayes ESP serial port support"
diff -puN drivers/char/Makefile~dgap drivers/char/Makefile
--- 25/drivers/char/Makefile~dgap	Wed Sep 17 12:34:23 2003
+++ 25-akpm/drivers/char/Makefile	Wed Sep 17 12:34:23 2003
@@ -18,6 +18,7 @@ obj-$(CONFIG_MOXA_SMARTIO) += mxser.o
 obj-$(CONFIG_MOXA_INTELLIO) += moxa.o
 obj-$(CONFIG_DIGI) += pcxx.o
 obj-$(CONFIG_DIGIEPCA) += epca.o
+obj-$(CONFIG_DIGI_DGAP) += digi/dgap/
 obj-$(CONFIG_CYCLADES) += cyclades.o
 obj-$(CONFIG_STALLION) += stallion.o
 obj-$(CONFIG_ISTALLION) += istallion.o
diff -puN drivers/char/pcxx.c~dgap drivers/char/pcxx.c
--- 25/drivers/char/pcxx.c~dgap	Wed Sep 17 12:34:23 2003
+++ 25-akpm/drivers/char/pcxx.c	Wed Sep 17 12:34:23 2003
@@ -12,11 +12,12 @@
  *  This driver does NOT support DigiBoard's fastcook FEP option and
  *  does not support the transparent print (i.e. digiprint) option.
  *
- * This Driver is currently maintained by Christoph Lameter (christoph@lameter.com)
  *
- * Please contact digi for support issues at digilnux@dgii.com.
- * Some more information can be found at
- * http://lameter.com/digi.
+ *	NOTE:	As of the 2.6 Linux kernel, Digi International
+ *		no longer supports this version of the driver.
+ *		If you have a PCI EPCA DigiBoard, please use the DGAP
+ *		driver instead.
+ *
  *
  *  1.5.2 Fall 1995 Bug fixes by David Nugent
  *  1.5.3 March 9, 1996 Christoph Lameter: Fixed 115.2K Support. Memory
diff -puN include/linux/major.h~dgap include/linux/major.h
--- 25/include/linux/major.h~dgap	Wed Sep 17 12:34:23 2003
+++ 25-akpm/include/linux/major.h	Wed Sep 17 12:34:23 2003
@@ -40,7 +40,7 @@
 #define MFM_ACORN_MAJOR		21	/* ARM Linux /dev/mfm */
 #define SCSI_GENERIC_MAJOR	21
 #define IDE1_MAJOR		22
-#define DIGICU_MAJOR		22
+#define DIGI_DGAP_MAJOR		22
 #define DIGI_MAJOR		23
 #define MITSUMI_CDROM_MAJOR	23
 #define CDU535_CDROM_MAJOR	24
diff -puN MAINTAINERS~dgap MAINTAINERS
--- 25/MAINTAINERS~dgap	Wed Sep 17 12:34:23 2003
+++ 25-akpm/MAINTAINERS	Wed Sep 17 12:34:23 2003
@@ -607,16 +607,17 @@ S:	Maintained
 DEVICE FILESYSTEM
 S:	Obsolete
 
-DIGI INTL. EPCA DRIVER
+DIGI INTL. EPCA PCI (DGAP) DRIVER
 P:	Digi International, Inc
 M:	Eng.Linux@digi.com
 L:	Eng.Linux@digi.com
 W:	http://www.digi.com
-S:	Maintained
+S:	Supported
 
-DIGI RIGHTSWITCH NETWORK DRIVER
-P:	Rick Richardson
-L:	linux-net@vger.kernel.org
+DIGI INTL. EPCA ISA DRIVER
+P:	Digi International, Inc
+M:	Eng.Linux@digi.com
+L:	Eng.Linux@digi.com
 W:	http://www.digi.com
 S:	Orphaned
 
@@ -627,6 +628,12 @@ W:	http://www.digi.com
 L:	digilnux@dgii.com
 S:	Orphaned
 
+DIGI RIGHTSWITCH NETWORK DRIVER
+P:	Rick Richardson
+L:	linux-net@vger.kernel.org
+W:	http://www.digi.com
+S:	Orphaned
+
 DIRECTORY NOTIFICATION
 P:	Stephen Rothwell
 M:	sfr@canb.auug.org.au

_