Re: [PATCH v2] staging: dgnc - adds dgnc digi driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



I've just realized I made a mistake by adding the Makefile.inc.

Is it possible for me to make a fresh set of patches (adding the files,
building it into the kernel, and fixing the errors and warnings)? Basically
forget about this patch and replace it with a new set of patches?

If its not possible, I'll work around this patch and send my commits to
fit this one.

Lidza

On Thu, Aug 1, 2013 at 5:00 PM,  <lidza.louina@xxxxxxxxx> wrote:
> This patch adds the DGNC driver. This is a TTY Serial Port
> Driver for the Digi International Neo and Classic PCI based product
> line by Digi International <http://www.digi.com>.
>
> This driver isn't hooked up to the build system because it doesn't
> build.
>
> Signed-off-by: Lidza Louina <lidza.louina@xxxxxxxxx>
> ---
>  drivers/staging/dgnc/Kconfig        |    6 +
>  drivers/staging/dgnc/Makefile       |    7 +
>  drivers/staging/dgnc/Makefile.inc   |  133 ++
>  drivers/staging/dgnc/dgnc_cls.c     | 1412 ++++++++++++++
>  drivers/staging/dgnc/dgnc_cls.h     |   90 +
>  drivers/staging/dgnc/dgnc_driver.c  | 1028 ++++++++++
>  drivers/staging/dgnc/dgnc_driver.h  |  566 ++++++
>  drivers/staging/dgnc/dgnc_kcompat.h |   91 +
>  drivers/staging/dgnc/dgnc_mgmt.c    |  313 +++
>  drivers/staging/dgnc/dgnc_mgmt.h    |   37 +
>  drivers/staging/dgnc/dgnc_neo.c     | 1977 +++++++++++++++++++
>  drivers/staging/dgnc/dgnc_neo.h     |  157 ++
>  drivers/staging/dgnc/dgnc_pci.h     |   77 +
>  drivers/staging/dgnc/dgnc_proc.c    | 1551 +++++++++++++++
>  drivers/staging/dgnc/dgnc_proc.h    |  147 ++
>  drivers/staging/dgnc/dgnc_sysfs.c   |  761 ++++++++
>  drivers/staging/dgnc/dgnc_sysfs.h   |   49 +
>  drivers/staging/dgnc/dgnc_trace.c   |  187 ++
>  drivers/staging/dgnc/dgnc_trace.h   |   45 +
>  drivers/staging/dgnc/dgnc_tty.c     | 3648 +++++++++++++++++++++++++++++++++++
>  drivers/staging/dgnc/dgnc_tty.h     |   42 +
>  drivers/staging/dgnc/dgnc_types.h   |   36 +
>  drivers/staging/dgnc/digi.h         |  419 ++++
>  drivers/staging/dgnc/dpacompat.h    |  115 ++
>  24 files changed, 12894 insertions(+)
>  create mode 100644 drivers/staging/dgnc/Kconfig
>  create mode 100644 drivers/staging/dgnc/Makefile
>  create mode 100644 drivers/staging/dgnc/Makefile.inc
>  create mode 100644 drivers/staging/dgnc/dgnc_cls.c
>  create mode 100644 drivers/staging/dgnc/dgnc_cls.h
>  create mode 100644 drivers/staging/dgnc/dgnc_driver.c
>  create mode 100644 drivers/staging/dgnc/dgnc_driver.h
>  create mode 100644 drivers/staging/dgnc/dgnc_kcompat.h
>  create mode 100644 drivers/staging/dgnc/dgnc_mgmt.c
>  create mode 100644 drivers/staging/dgnc/dgnc_mgmt.h
>  create mode 100644 drivers/staging/dgnc/dgnc_neo.c
>  create mode 100644 drivers/staging/dgnc/dgnc_neo.h
>  create mode 100644 drivers/staging/dgnc/dgnc_pci.h
>  create mode 100644 drivers/staging/dgnc/dgnc_proc.c
>  create mode 100644 drivers/staging/dgnc/dgnc_proc.h
>  create mode 100644 drivers/staging/dgnc/dgnc_sysfs.c
>  create mode 100644 drivers/staging/dgnc/dgnc_sysfs.h
>  create mode 100644 drivers/staging/dgnc/dgnc_trace.c
>  create mode 100644 drivers/staging/dgnc/dgnc_trace.h
>  create mode 100644 drivers/staging/dgnc/dgnc_tty.c
>  create mode 100644 drivers/staging/dgnc/dgnc_tty.h
>  create mode 100644 drivers/staging/dgnc/dgnc_types.h
>  create mode 100644 drivers/staging/dgnc/digi.h
>  create mode 100644 drivers/staging/dgnc/dpacompat.h
>
> diff --git a/drivers/staging/dgnc/Kconfig b/drivers/staging/dgnc/Kconfig
> new file mode 100644
> index 0000000..23daaa5
> --- /dev/null
> +++ b/drivers/staging/dgnc/Kconfig
> @@ -0,0 +1,6 @@
> +config DGNC
> +       tristate "Digi Neo and Classic PCI Products"
> +       default n
> +       depends on TTY
> +       ---help---
> +       Driver for the Digi International Neo and Classic PCI based product line.
> diff --git a/drivers/staging/dgnc/Makefile b/drivers/staging/dgnc/Makefile
> new file mode 100644
> index 0000000..c4c96dc
> --- /dev/null
> +++ b/drivers/staging/dgnc/Makefile
> @@ -0,0 +1,7 @@
> +EXTRA_CFLAGS += -DDG_NAME=\"dgnc-1.3-16\" -DDG_PART=\"40002369_F\"
> +
> +obj-$(CONFIG_DGNC) += dgnc.o
> +
> +dgnc-objs :=   dgnc_cls.o dgnc_driver.o\
> +               dgnc_mgmt.o dgnc_neo.o\
> +               dgnc_proc.o dgnc_trace.o dgnc_tty.o dgnc_sysfs.o
> diff --git a/drivers/staging/dgnc/Makefile.inc b/drivers/staging/dgnc/Makefile.inc
> new file mode 100644
> index 0000000..6ca38c7
> --- /dev/null
> +++ b/drivers/staging/dgnc/Makefile.inc
> @@ -0,0 +1,133 @@
> +#
> +# From Makefile.inc
> +#
> +
> +#
> +# Common definitions go here.
> +#
> +
> +#
> +# TRUE_VERSION is the version string used in the driver build,
> +# it is intended to be in the form:
> +#
> +#   2.0-0
> +#
> +# A string noting a particular special modification could be
> +# used as well.  This string will be reported when the driver
> +# is loaded, and will be exposed by its /proc/dgnc/info
> +# interface.
> +#
> +TRUE_VERSION="1.3-16"
> +
> +#
> +# DGNC_PART_NUM is the part number string for the driver package.
> +# It should be in the form:
> +#
> +#   40002369_A
> +#
> +DGNC_PART_NUM=40002369_F
> +
> +#
> +# DGNC_REL_NOTE is the part number string for the driver release
> +# notes.  It should be in the form:
> +#
> +#   93000517_A
> +#
> +DGNC_REL_NOTE=93000517_F
> +
> +#
> +# DGNC_PKG_VER is the "version" number string included in the
> +# various documentation and packaging files.  It should be
> +# in the form:
> +#
> +#   1.0
> +#
> +DGNC_PKG_VER=1.3
> +
> +#
> +# DGNC_PKG_REV is the "revision" of this version.  Together,
> +# a linux module revision is built with:
> +#
> +#   ${DGNC_PKG_VER}-${DGNC_PKG_REV}
> +#
> +DGNC_PKG_REV=16
> +
> +#
> +# DRP_PKG_DATE is the "date" string included in (for now) the
> +# release notes.  It should be in the form:
> +#
> +#   11/04/2003
> +#
> +DGNC_PKG_DATE=10/17/2008
> +
> +INIT_DIR= $(shell \
> +       if [ -d /etc/rc.d/init.d ]; \
> +       then echo "$(RPM_BUILD_ROOT)/etc/rc.d/init.d"; \
> +       else echo "$(RPM_BUILD_ROOT)/etc/init.d"; fi)
> +
> +#
> +#      Miscelaneous path macro's
> +#
> +
> +PACKAGE=       dgnc
> +DEVDIR=                /dev/dg/$(PACKAGE)
> +SRCDIR=                /usr/src/dg/$(PACKAGE)
> +BINDIR=                /usr/bin
> +DRVLIBDIR=     /etc/$(PACKAGE)
> +MANDIR=                /usr/man
> +USRLIBDIR=     /usr/lib
> +DGNCDIR=       /etc/dgnc
> +
> +
> +INIT_DIR= $(shell \
> +       if [ -d /etc/rc.d/init.d ]; \
> +       then echo "/etc/rc.d/init.d"; \
> +       else echo "/etc/init.d"; fi)
> +
> +
> +#
> +# From Makefile
> +#
> +ifndef MYPWD
> +MYPWD = $(shell pwd)
> +endif
> +
> +ifeq ($(KERNDIR),)
> +  KERNVERS       := $(shell uname -r)
> +  KERNDIR         :=/lib/modules/${KERNVERS}/
> +endif
> +
> +# Grab version and other important stuff
> +
> +RPMNAME := $(PACKAGE)-$(TRUE_VERSION)
> +
> +PARTNUM := $(DGNC_PART_NUM)
> +
> +RELNOTES := $(DGNC_REL_NOTE)
> +
> +MODDIR = $(shell echo $(BUILDROOT)/lib/modules/3.4.36-lcrs/misc)
> +LSMOD = /sbin/lsmod
> +RMMOD = /sbin/rmmod
> +INSMOD = /sbin/insmod
> +NEW_TTY_LOCKING = No
> +NEW_TTY_BUFFERING = No
> +REGISTER_TTYS_WITH_SYSFS = No
> +
> +# Send in some extra things...
> +EXTRA_CFLAGS += -I${MYPWD} -I${MYPWD}/include -I${MYPWD}/../../commoninc\
> +       -I${MYPWD}/../../dpa -DLINUX -DDG_NAME=\"$(RPMNAME)\"\
> +       -DDG_PART=\"$(PARTNUM)\" -DDGNC_TRACER
> +
> +ifeq ($(NEW_TTY_LOCKING),Yes)
> +       EXTRA_CFLAGS += -DNEW_TTY_LOCKING
> +endif
> +
> +ifeq ($(NEW_TTY_BUFFERING),Yes)
> +       EXTRA_CFLAGS += -DNEW_TTY_BUFFERING
> +endif
> +
> +ifeq ($(REGISTER_TTYS_WITH_SYSFS),Yes)
> +       EXTRA_CFLAGS += -DREGISTER_TTYS_WITH_SYSFS
> +endif
> +
> +# Conform to correct kbuild conventions...
> diff --git a/drivers/staging/dgnc/dgnc_cls.c b/drivers/staging/dgnc/dgnc_cls.c
> new file mode 100644
> index 0000000..83ded18
> --- /dev/null
> +++ b/drivers/staging/dgnc/dgnc_cls.c
> @@ -0,0 +1,1412 @@
> +/*
> + * 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: dgnc_cls.c,v 1.1.1.1 2009/05/20 12:19:19 markh Exp $
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/version.h>
> +#include <linux/sched.h>       /* For jiffies, task states */
> +#include <linux/interrupt.h>   /* For tasklet and interrupt structs/defines */
> +#include <linux/delay.h>       /* For udelay */
> +#include <asm/io.h>            /* For read[bwl]/write[bwl] */
> +#include <linux/serial.h>      /* For struct async_serial */
> +#include <linux/serial_reg.h>  /* For the various UART offsets */
> +#include <linux/pci.h>
> +
> +#include "dgnc_driver.h"       /* Driver main header file */
> +#include "dgnc_cls.h"
> +#include "dgnc_tty.h"
> +#include "dgnc_trace.h"
> +
> +static inline void cls_parse_isr(struct board_t *brd, uint port);
> +static inline void cls_clear_break(struct channel_t *ch, int force);
> +static inline void cls_set_cts_flow_control(struct channel_t *ch);
> +static inline void cls_set_rts_flow_control(struct channel_t *ch);
> +static inline void cls_set_ixon_flow_control(struct channel_t *ch);
> +static inline void cls_set_ixoff_flow_control(struct channel_t *ch);
> +static inline void cls_set_no_output_flow_control(struct channel_t *ch);
> +static inline void cls_set_no_input_flow_control(struct channel_t *ch);
> +static void cls_parse_modem(struct channel_t *ch, uchar signals);
> +static void cls_tasklet(unsigned long data);
> +static void cls_vpd(struct board_t *brd);
> +static void cls_uart_init(struct channel_t *ch);
> +static void cls_uart_off(struct channel_t *ch);
> +static int cls_drain(struct tty_struct *tty, uint seconds);
> +static void cls_param(struct tty_struct *tty);
> +static void cls_assert_modem_signals(struct channel_t *ch);
> +static void cls_flush_uart_write(struct channel_t *ch);
> +static void cls_flush_uart_read(struct channel_t *ch);
> +static void cls_disable_receiver(struct channel_t *ch);
> +static void cls_enable_receiver(struct channel_t *ch);
> +static void cls_send_break(struct channel_t *ch, int msecs);
> +static void cls_send_start_character(struct channel_t *ch);
> +static void cls_send_stop_character(struct channel_t *ch);
> +static void cls_copy_data_from_uart_to_queue(struct channel_t *ch);
> +static void cls_copy_data_from_queue_to_uart(struct channel_t *ch);
> +static uint cls_get_uart_bytes_left(struct channel_t *ch);
> +static void cls_send_immediate_char(struct channel_t *ch, unsigned char);
> +static irqreturn_t cls_intr(int irq, void *voidbrd);
> +
> +struct board_ops dgnc_cls_ops = {
> +       .tasklet =                      cls_tasklet,
> +       .intr =                         cls_intr,
> +       .uart_init =                    cls_uart_init,
> +       .uart_off =                     cls_uart_off,
> +       .drain =                        cls_drain,
> +       .param =                        cls_param,
> +       .vpd =                          cls_vpd,
> +       .assert_modem_signals =         cls_assert_modem_signals,
> +       .flush_uart_write =             cls_flush_uart_write,
> +       .flush_uart_read =              cls_flush_uart_read,
> +       .disable_receiver =             cls_disable_receiver,
> +       .enable_receiver =              cls_enable_receiver,
> +       .send_break =                   cls_send_break,
> +       .send_start_character =         cls_send_start_character,
> +       .send_stop_character =          cls_send_stop_character,
> +       .copy_data_from_queue_to_uart = cls_copy_data_from_queue_to_uart,
> +       .get_uart_bytes_left =          cls_get_uart_bytes_left,
> +       .send_immediate_char =          cls_send_immediate_char
> +};
> +
> +
> +static inline void cls_set_cts_flow_control(struct channel_t *ch)
> +{
> +       uchar lcrb = readb(&ch->ch_cls_uart->lcr);
> +       uchar ier = readb(&ch->ch_cls_uart->ier);
> +       uchar isr_fcr = 0;
> +
> +       DPR_PARAM(("Setting CTSFLOW\n"));
> +
> +       /*
> +        * The Enhanced Register Set may only be accessed when
> +        * the Line Control Register is set to 0xBFh.
> +        */
> +       writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr);
> +
> +       isr_fcr = readb(&ch->ch_cls_uart->isr_fcr);
> +
> +       /* Turn on CTS flow control, turn off IXON flow control */
> +       isr_fcr |= (UART_EXAR654_EFR_ECB | UART_EXAR654_EFR_CTSDSR);
> +       isr_fcr &= ~(UART_EXAR654_EFR_IXON);
> +
> +       writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr);
> +
> +       /* Write old LCR value back out, which turns enhanced access off */
> +       writeb(lcrb, &ch->ch_cls_uart->lcr);
> +
> +       /* Enable interrupts for CTS flow, turn off interrupts for received XOFF chars */
> +       ier |= (UART_EXAR654_IER_CTSDSR);
> +       ier &= ~(UART_EXAR654_IER_XOFF);
> +       writeb(ier, &ch->ch_cls_uart->ier);
> +
> +       /* Set the usual FIFO values */
> +       writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr);
> +
> +       writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_56 |
> +               UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR),
> +               &ch->ch_cls_uart->isr_fcr);
> +
> +       ch->ch_t_tlevel = 16;
> +
> +}
> +
> +
> +static inline void cls_set_ixon_flow_control(struct channel_t *ch)
> +{
> +       uchar lcrb = readb(&ch->ch_cls_uart->lcr);
> +       uchar ier = readb(&ch->ch_cls_uart->ier);
> +       uchar isr_fcr = 0;
> +
> +       DPR_PARAM(("Setting IXON FLOW\n"));
> +
> +       /*
> +        * The Enhanced Register Set may only be accessed when
> +        * the Line Control Register is set to 0xBFh.
> +        */
> +       writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr);
> +
> +       isr_fcr = readb(&ch->ch_cls_uart->isr_fcr);
> +
> +       /* Turn on IXON flow control, turn off CTS flow control */
> +       isr_fcr |= (UART_EXAR654_EFR_ECB | UART_EXAR654_EFR_IXON);
> +       isr_fcr &= ~(UART_EXAR654_EFR_CTSDSR);
> +
> +       writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr);
> +
> +       /* Now set our current start/stop chars while in enhanced mode */
> +       writeb(ch->ch_startc, &ch->ch_cls_uart->mcr);
> +       writeb(0, &ch->ch_cls_uart->lsr);
> +       writeb(ch->ch_stopc, &ch->ch_cls_uart->msr);
> +       writeb(0, &ch->ch_cls_uart->spr);
> +
> +       /* Write old LCR value back out, which turns enhanced access off */
> +       writeb(lcrb, &ch->ch_cls_uart->lcr);
> +
> +       /* Disable interrupts for CTS flow, turn on interrupts for received XOFF chars */
> +       ier &= ~(UART_EXAR654_IER_CTSDSR);
> +       ier |= (UART_EXAR654_IER_XOFF);
> +       writeb(ier, &ch->ch_cls_uart->ier);
> +
> +       /* Set the usual FIFO values */
> +       writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr);
> +
> +       writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_16 |
> +               UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR),
> +               &ch->ch_cls_uart->isr_fcr);
> +
> +}
> +
> +
> +static inline void cls_set_no_output_flow_control(struct channel_t *ch)
> +{
> +       uchar lcrb = readb(&ch->ch_cls_uart->lcr);
> +       uchar ier = readb(&ch->ch_cls_uart->ier);
> +       uchar isr_fcr = 0;
> +
> +       DPR_PARAM(("Unsetting Output FLOW\n"));
> +
> +       /*
> +        * The Enhanced Register Set may only be accessed when
> +        * the Line Control Register is set to 0xBFh.
> +        */
> +       writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr);
> +
> +       isr_fcr = readb(&ch->ch_cls_uart->isr_fcr);
> +
> +       /* Turn off IXON flow control, turn off CTS flow control */
> +       isr_fcr |= (UART_EXAR654_EFR_ECB);
> +       isr_fcr &= ~(UART_EXAR654_EFR_CTSDSR | UART_EXAR654_EFR_IXON);
> +
> +       writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr);
> +
> +       /* Write old LCR value back out, which turns enhanced access off */
> +       writeb(lcrb, &ch->ch_cls_uart->lcr);
> +
> +       /* Disable interrupts for CTS flow, turn off interrupts for received XOFF chars */
> +       ier &= ~(UART_EXAR654_IER_CTSDSR);
> +       ier &= ~(UART_EXAR654_IER_XOFF);
> +       writeb(ier, &ch->ch_cls_uart->ier);
> +
> +       /* Set the usual FIFO values */
> +       writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr);
> +
> +       writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_16 |
> +               UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR),
> +               &ch->ch_cls_uart->isr_fcr);
> +
> +       ch->ch_r_watermark = 0;
> +        ch->ch_t_tlevel = 16;
> +        ch->ch_r_tlevel = 16;
> +
> +}
> +
> +
> +static inline void cls_set_rts_flow_control(struct channel_t *ch)
> +{
> +       uchar lcrb = readb(&ch->ch_cls_uart->lcr);
> +       uchar ier = readb(&ch->ch_cls_uart->ier);
> +       uchar isr_fcr = 0;
> +
> +       DPR_PARAM(("Setting RTSFLOW\n"));
> +
> +       /*
> +        * The Enhanced Register Set may only be accessed when
> +        * the Line Control Register is set to 0xBFh.
> +        */
> +       writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr);
> +
> +       isr_fcr = readb(&ch->ch_cls_uart->isr_fcr);
> +
> +       /* Turn on RTS flow control, turn off IXOFF flow control */
> +       isr_fcr |= (UART_EXAR654_EFR_ECB | UART_EXAR654_EFR_RTSDTR);
> +       isr_fcr &= ~(UART_EXAR654_EFR_IXOFF);
> +
> +       writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr);
> +
> +       /* Write old LCR value back out, which turns enhanced access off */
> +       writeb(lcrb, &ch->ch_cls_uart->lcr);
> +
> +       /* Enable interrupts for RTS flow */
> +       ier |= (UART_EXAR654_IER_RTSDTR);
> +       writeb(ier, &ch->ch_cls_uart->ier);
> +
> +       /* Set the usual FIFO values */
> +       writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr);
> +
> +       writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_56 |
> +               UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR),
> +               &ch->ch_cls_uart->isr_fcr);
> +
> +
> +       ch->ch_r_watermark = 4;
> +       ch->ch_r_tlevel = 8;
> +
> +}
> +
> +
> +static inline void cls_set_ixoff_flow_control(struct channel_t *ch)
> +{
> +       uchar lcrb = readb(&ch->ch_cls_uart->lcr);
> +       uchar ier = readb(&ch->ch_cls_uart->ier);
> +       uchar isr_fcr = 0;
> +
> +       DPR_PARAM(("Setting IXOFF FLOW\n"));
> +
> +       /*
> +        * The Enhanced Register Set may only be accessed when
> +        * the Line Control Register is set to 0xBFh.
> +        */
> +       writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr);
> +
> +       isr_fcr = readb(&ch->ch_cls_uart->isr_fcr);
> +
> +       /* Turn on IXOFF flow control, turn off RTS flow control */
> +       isr_fcr |= (UART_EXAR654_EFR_ECB | UART_EXAR654_EFR_IXOFF);
> +       isr_fcr &= ~(UART_EXAR654_EFR_RTSDTR);
> +
> +       writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr);
> +
> +       /* Now set our current start/stop chars while in enhanced mode */
> +       writeb(ch->ch_startc, &ch->ch_cls_uart->mcr);
> +       writeb(0, &ch->ch_cls_uart->lsr);
> +       writeb(ch->ch_stopc, &ch->ch_cls_uart->msr);
> +       writeb(0, &ch->ch_cls_uart->spr);
> +
> +       /* Write old LCR value back out, which turns enhanced access off */
> +       writeb(lcrb, &ch->ch_cls_uart->lcr);
> +
> +       /* Disable interrupts for RTS flow */
> +       ier &= ~(UART_EXAR654_IER_RTSDTR);
> +       writeb(ier, &ch->ch_cls_uart->ier);
> +
> +       /* Set the usual FIFO values */
> +       writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr);
> +
> +       writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_16 |
> +               UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR),
> +               &ch->ch_cls_uart->isr_fcr);
> +
> +}
> +
> +
> +static inline void cls_set_no_input_flow_control(struct channel_t *ch)
> +{
> +       uchar lcrb = readb(&ch->ch_cls_uart->lcr);
> +       uchar ier = readb(&ch->ch_cls_uart->ier);
> +       uchar isr_fcr = 0;
> +
> +       DPR_PARAM(("Unsetting Input FLOW\n"));
> +
> +       /*
> +        * The Enhanced Register Set may only be accessed when
> +        * the Line Control Register is set to 0xBFh.
> +        */
> +       writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr);
> +
> +       isr_fcr = readb(&ch->ch_cls_uart->isr_fcr);
> +
> +       /* Turn off IXOFF flow control, turn off RTS flow control */
> +       isr_fcr |= (UART_EXAR654_EFR_ECB);
> +       isr_fcr &= ~(UART_EXAR654_EFR_RTSDTR | UART_EXAR654_EFR_IXOFF);
> +
> +       writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr);
> +
> +       /* Write old LCR value back out, which turns enhanced access off */
> +       writeb(lcrb, &ch->ch_cls_uart->lcr);
> +
> +       /* Disable interrupts for RTS flow */
> +       ier &= ~(UART_EXAR654_IER_RTSDTR);
> +       writeb(ier, &ch->ch_cls_uart->ier);
> +
> +       /* Set the usual FIFO values */
> +       writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr);
> +
> +       writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_16 |
> +               UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR),
> +               &ch->ch_cls_uart->isr_fcr);
> +
> +        ch->ch_t_tlevel = 16;
> +        ch->ch_r_tlevel = 16;
> +
> +}
> +
> +
> +/*
> + * cls_clear_break.
> + * Determines whether its time to shut off break condition.
> + *
> + * No locks are assumed to be held when calling this function.
> + * channel lock is held and released in this function.
> + */
> +static inline void cls_clear_break(struct channel_t *ch, int force)
> +{
> +       ulong lock_flags;
> +
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return;
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       /* Bail if we aren't currently sending a break. */
> +       if (!ch->ch_stop_sending_break) {
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               return;
> +       }
> +
> +       /* Turn break off, and unset some variables */
> +       if (ch->ch_flags & CH_BREAK_SENDING) {
> +               if ((jiffies >= ch->ch_stop_sending_break) || force) {
> +                       uchar temp = readb(&ch->ch_cls_uart->lcr);
> +                       writeb((temp & ~UART_LCR_SBC), &ch->ch_cls_uart->lcr);
> +                       ch->ch_flags &= ~(CH_BREAK_SENDING);
> +                       ch->ch_stop_sending_break = 0;
> +                       DPR_IOCTL(("Finishing UART_LCR_SBC! finished: %lx\n", jiffies));
> +               }
> +       }
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +}
> +
> +
> +/* Parse the ISR register for the specific port */
> +static inline void cls_parse_isr(struct board_t *brd, uint port)
> +{
> +       struct channel_t *ch;
> +       uchar isr = 0;
> +       ulong lock_flags;
> +
> +       /*
> +        * No need to verify board pointer, it was already
> +        * verified in the interrupt routine.
> +        */
> +
> +       if (port > brd->nasync)
> +               return;
> +
> +       ch = brd->channels[port];
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return;
> +
> +       /* Here we try to figure out what caused the interrupt to happen */
> +       while (1) {
> +
> +               isr = readb(&ch->ch_cls_uart->isr_fcr);
> +
> +               /* Bail if no pending interrupt on port */
> +               if (isr & UART_IIR_NO_INT)  {
> +                       break;
> +               }
> +
> +               DPR_INTR(("%s:%d port: %x isr: %x\n", __FILE__, __LINE__, port, isr));
> +
> +               /* Receive Interrupt pending */
> +               if (isr & (UART_IIR_RDI | UART_IIR_RDI_TIMEOUT)) {
> +                       /* Read data from uart -> queue */
> +                       brd->intr_rx++;
> +                       ch->ch_intr_rx++;
> +                       cls_copy_data_from_uart_to_queue(ch);
> +                       dgnc_check_queue_flow_control(ch);
> +               }
> +
> +               /* Transmit Hold register empty pending */
> +               if (isr & UART_IIR_THRI) {
> +                       /* Transfer data (if any) from Write Queue -> UART. */
> +                       DGNC_LOCK(ch->ch_lock, lock_flags);
> +                       ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
> +                       brd->intr_tx++;
> +                       ch->ch_intr_tx++;
> +                       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +                       cls_copy_data_from_queue_to_uart(ch);
> +               }
> +
> +               /* Received Xoff signal/Special character */
> +               if (isr & UART_IIR_XOFF) {
> +                       /* Empty */
> +               }
> +
> +               /* CTS/RTS change of state */
> +               if (isr & UART_IIR_CTSRTS) {
> +                       brd->intr_modem++;
> +                       ch->ch_intr_modem++;
> +                       /*
> +                        * Don't need to do anything, the cls_parse_modem
> +                        * below will grab the updated modem signals.
> +                        */
> +               }
> +
> +               /* Parse any modem signal changes */
> +               DPR_INTR(("MOD_STAT: sending to parse_modem_sigs\n"));
> +               cls_parse_modem(ch, readb(&ch->ch_cls_uart->msr));
> +       }
> +}
> +
> +
> +/*
> + * cls_param()
> + * Send any/all changes to the line to the UART.
> + */
> +static void cls_param(struct tty_struct *tty)
> +{
> +       uchar lcr = 0;
> +       uchar uart_lcr = 0;
> +       uchar ier = 0;
> +       uchar uart_ier = 0;
> +        uint baud = 9600;
> +       int quot = 0;
> +        struct board_t *bd;
> +       struct channel_t *ch;
> +        struct un_t   *un;
> +
> +       if (!tty || tty->magic != TTY_MAGIC) {
> +               return;
> +       }
> +
> +       un = (struct un_t *) tty->driver_data;
> +       if (!un || un->magic != DGNC_UNIT_MAGIC) {
> +               return;
> +       }
> +
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) {
> +               return;
> +       }
> +
> +       bd = ch->ch_bd;
> +       if (!bd || bd->magic != DGNC_BOARD_MAGIC) {
> +               return;
> +       }
> +
> +       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));
> +
> +       /*
> +        * If baud rate is zero, flush queues, and set mval to drop DTR.
> +        */
> +       if ((ch->ch_c_cflag & (CBAUD)) == 0) {
> +               ch->ch_r_head = ch->ch_r_tail = 0;
> +               ch->ch_e_head = ch->ch_e_tail = 0;
> +               ch->ch_w_head = ch->ch_w_tail = 0;
> +
> +               cls_flush_uart_write(ch);
> +                cls_flush_uart_read(ch);
> +
> +               /* The baudrate is B0 so all modem lines are to be dropped. */
> +               ch->ch_flags |= (CH_BAUD0);
> +               ch->ch_mostat &= ~(UART_MCR_RTS | UART_MCR_DTR);
> +               cls_assert_modem_signals(ch);
> +               ch->ch_old_baud = 0;
> +               return;
> +       } else if (ch->ch_custom_speed) {
> +
> +               baud = ch->ch_custom_speed;
> +               /* Handle transition from B0 */
> +               if (ch->ch_flags & CH_BAUD0) {
> +                       ch->ch_flags &= ~(CH_BAUD0);
> +
> +                       /*
> +                        * Bring back up RTS and DTR...
> +                        * Also handle RTS or DTR toggle if set.
> +                        */
> +                       if (!(ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE))
> +                               ch->ch_mostat |= (UART_MCR_RTS);
> +                       if (!(ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE))
> +                               ch->ch_mostat |= (UART_MCR_DTR);
> +               }
> +
> +       } else {
> +               int iindex = 0;
> +               int jindex = 0;
> +
> +               ulong bauds[4][16] = {
> +                       { /* slowbaud */
> +                               0,      50,     75,     110,
> +                               134,    150,    200,    300,
> +                               600,    1200,   1800,   2400,
> +                               4800,   9600,   19200,  38400 },
> +                       { /* slowbaud & CBAUDEX */
> +                               0,      57600,  115200, 230400,
> +                               460800, 150,    200,    921600,
> +                               600,    1200,   1800,   2400,
> +                               4800,   9600,   19200,  38400 },
> +                       { /* fastbaud */
> +                               0,      57600,   76800, 115200,
> +                               131657, 153600, 230400, 460800,
> +                               921600, 1200,   1800,   2400,
> +                               4800,   9600,   19200,  38400 },
> +                       { /* fastbaud & CBAUDEX */
> +                               0,      57600,  115200, 230400,
> +                               460800, 150,    200,    921600,
> +                               600,    1200,   1800,   2400,
> +                               4800,   9600,   19200,  38400 }
> +               };
> +
> +               /* Only use the TXPrint baud rate if the terminal unit is NOT open */
> +               if (!(ch->ch_tun.un_flags & UN_ISOPEN) && (un->un_type == DGNC_PRINT))
> +                       baud = C_BAUD(ch->ch_pun.un_tty) & 0xff;
> +               else
> +                       baud = C_BAUD(ch->ch_tun.un_tty) & 0xff;
> +
> +               if (ch->ch_c_cflag & CBAUDEX)
> +                       iindex = 1;
> +
> +               if (ch->ch_digi.digi_flags & DIGI_FAST)
> +                       iindex += 2;
> +
> +               jindex = baud;
> +
> +               if ((iindex >= 0) && (iindex < 4) && (jindex >= 0) && (jindex < 16)) {
> +                       baud = bauds[iindex][jindex];
> +               } else {
> +                       DPR_IOCTL(("baud indices were out of range (%d)(%d)",
> +                               iindex, jindex));
> +                       baud = 0;
> +               }
> +
> +               if (baud == 0)
> +                       baud = 9600;
> +
> +               /* Handle transition from B0 */
> +               if (ch->ch_flags & CH_BAUD0) {
> +                       ch->ch_flags &= ~(CH_BAUD0);
> +
> +                       /*
> +                        * Bring back up RTS and DTR...
> +                        * Also handle RTS or DTR toggle if set.
> +                        */
> +                       if (!(ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE))
> +                               ch->ch_mostat |= (UART_MCR_RTS);
> +                       if (!(ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE))
> +                               ch->ch_mostat |= (UART_MCR_DTR);
> +               }
> +       }
> +
> +       if (ch->ch_c_cflag & PARENB) {
> +               lcr |= UART_LCR_PARITY;
> +       }
> +
> +       if (!(ch->ch_c_cflag & PARODD)) {
> +               lcr |= UART_LCR_EPAR;
> +       }
> +
> +       /*
> +        * Not all platforms support mark/space parity,
> +        * so this will hide behind an ifdef.
> +        */
> +#ifdef CMSPAR
> +       if (ch->ch_c_cflag & CMSPAR)
> +               lcr |= UART_LCR_SPAR;
> +#endif
> +
> +       if (ch->ch_c_cflag & CSTOPB)
> +               lcr |= UART_LCR_STOP;
> +
> +       switch (ch->ch_c_cflag & CSIZE) {
> +       case CS5:
> +               lcr |= UART_LCR_WLEN5;
> +               break;
> +       case CS6:
> +               lcr |= UART_LCR_WLEN6;
> +               break;
> +       case CS7:
> +               lcr |= UART_LCR_WLEN7;
> +               break;
> +       case CS8:
> +       default:
> +               lcr |= UART_LCR_WLEN8;
> +               break;
> +       }
> +
> +       ier = uart_ier = readb(&ch->ch_cls_uart->ier);
> +       uart_lcr = readb(&ch->ch_cls_uart->lcr);
> +
> +       if (baud == 0)
> +               baud = 9600;
> +
> +       quot = ch->ch_bd->bd_dividend / baud;
> +
> +       if (quot != 0 && ch->ch_old_baud != baud) {
> +               ch->ch_old_baud = baud;
> +               writeb(UART_LCR_DLAB, &ch->ch_cls_uart->lcr);
> +               writeb((quot & 0xff), &ch->ch_cls_uart->txrx);
> +               writeb((quot >> 8), &ch->ch_cls_uart->ier);
> +               writeb(lcr, &ch->ch_cls_uart->lcr);
> +        }
> +
> +       if (uart_lcr != lcr)
> +               writeb(lcr, &ch->ch_cls_uart->lcr);
> +
> +       if (ch->ch_c_cflag & CREAD) {
> +               ier |= (UART_IER_RDI | UART_IER_RLSI);
> +       }
> +       else {
> +               ier &= ~(UART_IER_RDI | UART_IER_RLSI);
> +       }
> +
> +       /*
> +        * Have the UART interrupt on modem signal changes ONLY when
> +        * we are in hardware flow control mode, or CLOCAL/FORCEDCD is not set.
> +        */
> +       if ((ch->ch_digi.digi_flags & CTSPACE) || (ch->ch_digi.digi_flags & RTSPACE) ||
> +               (ch->ch_c_cflag & CRTSCTS) || !(ch->ch_digi.digi_flags & DIGI_FORCEDCD) ||
> +               !(ch->ch_c_cflag & CLOCAL))
> +       {
> +               ier |= UART_IER_MSI;
> +       }
> +       else {
> +               ier &= ~UART_IER_MSI;
> +       }
> +
> +       ier |= UART_IER_THRI;
> +
> +       if (ier != uart_ier)
> +               writeb(ier, &ch->ch_cls_uart->ier);
> +
> +       if (ch->ch_digi.digi_flags & CTSPACE || ch->ch_c_cflag & CRTSCTS) {
> +               cls_set_cts_flow_control(ch);
> +       }
> +       else if (ch->ch_c_iflag & IXON) {
> +               /* If start/stop is set to disable, then we should disable flow control */
> +               if ((ch->ch_startc == _POSIX_VDISABLE) || (ch->ch_stopc == _POSIX_VDISABLE))
> +                       cls_set_no_output_flow_control(ch);
> +               else
> +                       cls_set_ixon_flow_control(ch);
> +       }
> +       else {
> +               cls_set_no_output_flow_control(ch);
> +       }
> +
> +       if (ch->ch_digi.digi_flags & RTSPACE || ch->ch_c_cflag & CRTSCTS) {
> +               cls_set_rts_flow_control(ch);
> +       }
> +       else if (ch->ch_c_iflag & IXOFF) {
> +               /* If start/stop is set to disable, then we should disable flow control */
> +               if ((ch->ch_startc == _POSIX_VDISABLE) || (ch->ch_stopc == _POSIX_VDISABLE))
> +                       cls_set_no_input_flow_control(ch);
> +               else
> +                       cls_set_ixoff_flow_control(ch);
> +       }
> +       else {
> +               cls_set_no_input_flow_control(ch);
> +       }
> +
> +       cls_assert_modem_signals(ch);
> +
> +       /* Get current status of the modem signals now */
> +       cls_parse_modem(ch, readb(&ch->ch_cls_uart->msr));
> +}
> +
> +
> +/*
> + * Our board poller function.
> + */
> +static void cls_tasklet(unsigned long data)
> +{
> +        struct board_t *bd = (struct board_t *) data;
> +       struct channel_t *ch;
> +       ulong  lock_flags;
> +       int i;
> +       int state = 0;
> +       int ports = 0;
> +
> +       if (!bd || bd->magic != DGNC_BOARD_MAGIC) {
> +               APR(("poll_tasklet() - NULL or bad bd.\n"));
> +               return;
> +       }
> +
> +       /* Cache a couple board values */
> +       DGNC_LOCK(bd->bd_lock, lock_flags);
> +       state = bd->state;
> +       ports = bd->nasync;
> +       DGNC_UNLOCK(bd->bd_lock, lock_flags);
> +
> +       /*
> +        * Do NOT allow the interrupt routine to read the intr registers
> +        * Until we release this lock.
> +        */
> +       DGNC_LOCK(bd->bd_intr_lock, lock_flags);
> +
> +       /*
> +        * If board is ready, parse deeper to see if there is anything to do.
> +        */
> +       if ((state == BOARD_READY) && (ports > 0)) {
> +
> +               /* Loop on each port */
> +               for (i = 0; i < ports; i++) {
> +                       ch = bd->channels[i];
> +                       if (!ch)
> +                               continue;
> +
> +                       /*
> +                        * NOTE: Remember you CANNOT hold any channel
> +                        * locks when calling input.
> +                        * During input processing, its possible we
> +                        * will call ld, which might do callbacks back
> +                        * into us.
> +                        */
> +                       dgnc_input(ch);
> +
> +                       /*
> +                        * Channel lock is grabbed and then released
> +                        * inside this routine.
> +                        */
> +                       cls_copy_data_from_queue_to_uart(ch);
> +                       dgnc_wakeup_writes(ch);
> +
> +                       /*
> +                        * Check carrier function.
> +                        */
> +                       dgnc_carrier(ch);
> +
> +                       /*
> +                        * The timing check of turning off the break is done
> +                        * inside clear_break()
> +                        */
> +                       if (ch->ch_stop_sending_break)
> +                               cls_clear_break(ch, 0);
> +               }
> +       }
> +
> +       DGNC_UNLOCK(bd->bd_intr_lock, lock_flags);
> +
> +}
> +
> +
> +/*
> + * cls_intr()
> + *
> + * Classic specific interrupt handler.
> + */
> +static irqreturn_t cls_intr(int irq, void *voidbrd)
> +{
> +       struct board_t *brd = (struct board_t *) voidbrd;
> +       uint i = 0;
> +       uchar poll_reg;
> +       unsigned long lock_flags;
> +
> +       if (!brd) {
> +               APR(("Received interrupt (%d) with null board associated\n", irq));
> +               return IRQ_NONE;
> +       }
> +
> +       /*
> +        * Check to make sure its for us.
> +        */
> +       if (brd->magic != DGNC_BOARD_MAGIC) {
> +               APR(("Received interrupt (%d) with a board pointer that wasn't ours!\n", irq));
> +               return IRQ_NONE;
> +       }
> +
> +       DGNC_LOCK(brd->bd_intr_lock, lock_flags);
> +
> +       brd->intr_count++;
> +
> +       /*
> +        * Check the board's global interrupt offset to see if we
> +        * we actually do have an interrupt pending for us.
> +        */
> +       poll_reg = readb(brd->re_map_membase + UART_CLASSIC_POLL_ADDR_OFFSET);
> +
> +       /* If 0, no interrupts pending */
> +       if (!poll_reg) {
> +               DPR_INTR(("Kernel interrupted to me, but no pending interrupts...\n"));
> +               DGNC_UNLOCK(brd->bd_intr_lock, lock_flags);
> +               return IRQ_NONE;
> +       }
> +
> +       DPR_INTR(("%s:%d poll_reg: %x\n", __FILE__, __LINE__, poll_reg));
> +
> +       /* Parse each port to find out what caused the interrupt */
> +       for (i = 0; i < brd->nasync; i++) {
> +               cls_parse_isr(brd, i);
> +       }
> +
> +       /*
> +        * Schedule tasklet to more in-depth servicing at a better time.
> +        */
> +       tasklet_schedule(&brd->helper_tasklet);
> +
> +       DGNC_UNLOCK(brd->bd_intr_lock, lock_flags);
> +
> +       DPR_INTR(("dgnc_intr finish.\n"));
> +       return IRQ_HANDLED;
> +}
> +
> +
> +static void cls_disable_receiver(struct channel_t *ch)
> +{
> +       uchar tmp = readb(&ch->ch_cls_uart->ier);
> +       tmp &= ~(UART_IER_RDI);
> +       writeb(tmp, &ch->ch_cls_uart->ier);
> +}
> +
> +
> +static void cls_enable_receiver(struct channel_t *ch)
> +{
> +       uchar tmp = readb(&ch->ch_cls_uart->ier);
> +       tmp |= (UART_IER_RDI);
> +       writeb(tmp, &ch->ch_cls_uart->ier);
> +}
> +
> +
> +static void cls_copy_data_from_uart_to_queue(struct channel_t *ch)
> +{
> +        int qleft = 0;
> +        uchar linestatus = 0;
> +       uchar error_mask = 0;
> +       ushort head;
> +       ushort tail;
> +       ulong lock_flags;
> +
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return;
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       /* cache head and tail of queue */
> +       head = ch->ch_r_head;
> +       tail = ch->ch_r_tail;
> +
> +       /* Store how much space we have left in the queue */
> +       if ((qleft = tail - head - 1) < 0)
> +               qleft += RQUEUEMASK + 1;
> +
> +       /*
> +        * Create a mask to determine whether we should
> +        * insert the character (if any) into our queue.
> +        */
> +       if (ch->ch_c_iflag & IGNBRK)
> +               error_mask |= UART_LSR_BI;
> +
> +       while (1) {
> +               linestatus = readb(&ch->ch_cls_uart->lsr);
> +
> +               if (!(linestatus & (UART_LSR_DR)))
> +                       break;
> +
> +               /*
> +                * Discard character if we are ignoring the error mask.
> +               */
> +               if (linestatus & error_mask)  {
> +                       uchar discard;
> +                       linestatus = 0;
> +                       discard = readb(&ch->ch_cls_uart->txrx);
> +                       continue;
> +               }
> +
> +               /*
> +                * If our queue is full, we have no choice but to drop some data.
> +                * The assumption is that HWFLOW or SWFLOW should have stopped
> +                * things way way before we got to this point.
> +                *
> +                * I decided that I wanted to ditch the oldest data first,
> +                * I hope thats okay with everyone? Yes? Good.
> +                */
> +               while (qleft < 1) {
> +                       DPR_READ(("Queue full, dropping DATA:%x LSR:%x\n",
> +                               ch->ch_rqueue[tail], ch->ch_equeue[tail]));
> +
> +                       ch->ch_r_tail = tail = (tail + 1) & RQUEUEMASK;
> +                       ch->ch_err_overrun++;
> +                       qleft++;
> +               }
> +
> +               ch->ch_equeue[head] = linestatus & (UART_LSR_BI | UART_LSR_PE | UART_LSR_FE);
> +               ch->ch_rqueue[head] = readb(&ch->ch_cls_uart->txrx);
> +               dgnc_sniff_nowait_nolock(ch, "UART READ", ch->ch_rqueue + head, 1);
> +
> +               qleft--;
> +
> +               DPR_READ(("DATA/LSR pair: %x %x\n", ch->ch_rqueue[head], ch->ch_equeue[head]));
> +
> +               if (ch->ch_equeue[head] & UART_LSR_PE)
> +                       ch->ch_err_parity++;
> +               if (ch->ch_equeue[head] & UART_LSR_BI)
> +                       ch->ch_err_break++;
> +               if (ch->ch_equeue[head] & UART_LSR_FE)
> +                       ch->ch_err_frame++;
> +
> +               /* Add to, and flip head if needed */
> +               head = (head + 1) & RQUEUEMASK;
> +               ch->ch_rxcount++;
> +       }
> +
> +       /*
> +        * Write new final heads to channel structure.
> +        */
> +       ch->ch_r_head = head & RQUEUEMASK;
> +       ch->ch_e_head = head & EQUEUEMASK;
> +
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +}
> +
> +
> +/*
> + * This function basically goes to sleep for secs, or until
> + * it gets signalled that the port has fully drained.
> + */
> +static int cls_drain(struct tty_struct *tty, uint seconds)
> +{
> +       ulong lock_flags;
> +       struct channel_t *ch;
> +        struct un_t *un;
> +       int rc = 0;
> +
> +       if (!tty || tty->magic != TTY_MAGIC) {
> +               return (-ENXIO);
> +       }
> +
> +       un = (struct un_t *) tty->driver_data;
> +       if (!un || un->magic != DGNC_UNIT_MAGIC) {
> +               return (-ENXIO);
> +       }
> +
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) {
> +               return (-ENXIO);
> +       }
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +       un->un_flags |= UN_EMPTY;
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +       /*
> +        * NOTE: Do something with time passed in.
> +        */
> +       rc = wait_event_interruptible(un->un_flags_wait, ((un->un_flags & UN_EMPTY) == 0));
> +
> +       /* If ret is non-zero, user ctrl-c'ed us */
> +       if (rc)
> +               DPR_IOCTL(("%d Drain - User ctrl c'ed\n", __LINE__));
> +
> +        return (rc);
> +}
> +
> +
> +/* Channel lock MUST be held before calling this function! */
> +static void cls_flush_uart_write(struct channel_t *ch)
> +{
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) {
> +               return;
> +       }
> +
> +       writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_XMIT), &ch->ch_cls_uart->isr_fcr);
> +       udelay(10);
> +
> +       ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
> +}
> +
> +
> +/* Channel lock MUST be held before calling this function! */
> +static void cls_flush_uart_read(struct channel_t *ch)
> +{
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) {
> +               return;
> +       }
> +
> +       /*
> +        * For complete POSIX compatibility, we should be purging the
> +        * read FIFO in the UART here.
> +        *
> +        * However, doing the statement below also incorrectly flushes
> +        * write data as well as just basically trashing the FIFO.
> +        *
> +        * I believe this is a BUG in this UART.
> +        * So for now, we will leave the code #ifdef'ed out...
> +        */
> +#if 0
> +       writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR), &ch->ch_cls_uart->isr_fcr);
> +#endif
> +       udelay(10);
> +}
> +
> +
> +static void cls_copy_data_from_queue_to_uart(struct channel_t *ch)
> +{
> +       ushort head;
> +       ushort tail;
> +       int n;
> +       int qlen;
> +       uint len_written = 0;
> +       ulong lock_flags;
> +
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return;
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       /* No data to write to the UART */
> +       if (ch->ch_w_tail == ch->ch_w_head) {
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               return;
> +       }
> +
> +       /* If port is "stopped", don't send any data to the UART */
> +       if ((ch->ch_flags & CH_FORCED_STOP) || (ch->ch_flags & CH_BREAK_SENDING)) {
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               return;
> +       }
> +
> +       if (!(ch->ch_flags & (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM))) {
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               return;
> +       }
> +
> +       n = 32;
> +
> +        /* cache head and tail of queue */
> +        head = ch->ch_w_head & WQUEUEMASK;
> +        tail = ch->ch_w_tail & WQUEUEMASK;
> +        qlen = (head - tail) & WQUEUEMASK;
> +
> +       /* Find minimum of the FIFO space, versus queue length */
> +       n = min(n, qlen);
> +
> +       while (n > 0) {
> +
> +               /*
> +                * If RTS Toggle mode is on, turn on RTS now if not already set,
> +                * and make sure we get an event when the data transfer has completed.
> +                */
> +               if (ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) {
> +                       if (!(ch->ch_mostat & UART_MCR_RTS)) {
> +                               ch->ch_mostat |= (UART_MCR_RTS);
> +                               cls_assert_modem_signals(ch);
> +                       }
> +                       ch->ch_tun.un_flags |= (UN_EMPTY);
> +               }
> +
> +               /*
> +                * If DTR Toggle mode is on, turn on DTR now if not already set,
> +                * and make sure we get an event when the data transfer has completed.
> +                */
> +               if (ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) {
> +                       if (!(ch->ch_mostat & UART_MCR_DTR)) {
> +                               ch->ch_mostat |= (UART_MCR_DTR);
> +                               cls_assert_modem_signals(ch);
> +                       }
> +                       ch->ch_tun.un_flags |= (UN_EMPTY);
> +               }
> +               writeb(ch->ch_wqueue[ch->ch_w_tail], &ch->ch_cls_uart->txrx);
> +               dgnc_sniff_nowait_nolock(ch, "UART WRITE", ch->ch_wqueue + ch->ch_w_tail, 1);
> +               DPR_WRITE(("Tx data: %x\n", ch->ch_wqueue[ch->ch_w_tail]));
> +               ch->ch_w_tail++;
> +               ch->ch_w_tail &= WQUEUEMASK;
> +               ch->ch_txcount++;
> +               len_written++;
> +               n--;
> +       }
> +
> +       if (len_written > 0)
> +               ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
> +
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +       return;
> +}
> +
> +
> +static void cls_parse_modem(struct channel_t *ch, uchar signals)
> +{
> +       volatile uchar msignals = signals;
> +
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return;
> +
> +       DPR_MSIGS(("cls_parse_modem: port: %d signals: %d\n", ch->ch_portnum, msignals));
> +
> +       /*
> +        * Do altpin switching. Altpin switches DCD and DSR.
> +        * This prolly breaks DSRPACE, so we should be more clever here.
> +        */
> +       if (ch->ch_digi.digi_flags & DIGI_ALTPIN) {
> +               uchar mswap = signals;
> +               if (mswap & UART_MSR_DDCD) {
> +                       msignals &= ~UART_MSR_DDCD;
> +                       msignals |= UART_MSR_DDSR;
> +               }
> +               if (mswap & UART_MSR_DDSR) {
> +                       msignals &= ~UART_MSR_DDSR;
> +                       msignals |= UART_MSR_DDCD;
> +               }
> +               if (mswap & UART_MSR_DCD) {
> +                       msignals &= ~UART_MSR_DCD;
> +                       msignals |= UART_MSR_DSR;
> +               }
> +               if (mswap & UART_MSR_DSR) {
> +                       msignals &= ~UART_MSR_DSR;
> +                       msignals |= UART_MSR_DCD;
> +               }
> +       }
> +
> +       /* Scrub off lower bits. They signify delta's, which I don't care about */
> +       signals &= 0xf0;
> +
> +       if (msignals & UART_MSR_DCD)
> +               ch->ch_mistat |= UART_MSR_DCD;
> +       else
> +               ch->ch_mistat &= ~UART_MSR_DCD;
> +
> +       if (msignals & UART_MSR_DSR)
> +               ch->ch_mistat |= UART_MSR_DSR;
> +       else
> +               ch->ch_mistat &= ~UART_MSR_DSR;
> +
> +       if (msignals & UART_MSR_RI)
> +               ch->ch_mistat |= UART_MSR_RI;
> +       else
> +               ch->ch_mistat &= ~UART_MSR_RI;
> +
> +       if (msignals & UART_MSR_CTS)
> +               ch->ch_mistat |= UART_MSR_CTS;
> +       else
> +               ch->ch_mistat &= ~UART_MSR_CTS;
> +
> +
> +       DPR_MSIGS(("Port: %d DTR: %d RTS: %d CTS: %d DSR: %d " "RI: %d CD: %d\n",
> +               ch->ch_portnum,
> +               !!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_DTR),
> +               !!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_RTS),
> +               !!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_CTS),
> +               !!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_DSR),
> +               !!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_RI),
> +               !!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_DCD)));
> +}
> +
> +
> +/* Make the UART raise any of the output signals we want up */
> +static void cls_assert_modem_signals(struct channel_t *ch)
> +{
> +       uchar out;
> +
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return;
> +
> +       out = ch->ch_mostat;
> +
> +       if (ch->ch_flags & CH_LOOPBACK)
> +               out |= UART_MCR_LOOP;
> +
> +        writeb(out, &ch->ch_cls_uart->mcr);
> +
> +       /* Give time for the UART to actually drop the signals */
> +       udelay(10);
> +}
> +
> +
> +static void cls_send_start_character(struct channel_t *ch)
> +{
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return;
> +
> +       if (ch->ch_startc != _POSIX_VDISABLE) {
> +               ch->ch_xon_sends++;
> +               writeb(ch->ch_startc, &ch->ch_cls_uart->txrx);
> +        }
> +}
> +
> +
> +static void cls_send_stop_character(struct channel_t *ch)
> +{
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return;
> +
> +       if (ch->ch_stopc != _POSIX_VDISABLE) {
> +               ch->ch_xoff_sends++;
> +               writeb(ch->ch_stopc, &ch->ch_cls_uart->txrx);
> +        }
> +}
> +
> +
> +/* Inits UART */
> +static void cls_uart_init(struct channel_t *ch)
> +{
> +       uchar lcrb = readb(&ch->ch_cls_uart->lcr);
> +       uchar isr_fcr = 0;
> +
> +       writeb(0, &ch->ch_cls_uart->ier);
> +
> +       /*
> +        * The Enhanced Register Set may only be accessed when
> +        * the Line Control Register is set to 0xBFh.
> +        */
> +       writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr);
> +
> +       isr_fcr = readb(&ch->ch_cls_uart->isr_fcr);
> +
> +       /* Turn on Enhanced/Extended controls */
> +       isr_fcr |= (UART_EXAR654_EFR_ECB);
> +
> +       writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr);
> +
> +       /* Write old LCR value back out, which turns enhanced access off */
> +       writeb(lcrb, &ch->ch_cls_uart->lcr);
> +
> +        /* Clear out UART and FIFO */
> +       readb(&ch->ch_cls_uart->txrx);
> +
> +       writeb((UART_FCR_ENABLE_FIFO|UART_FCR_CLEAR_RCVR|UART_FCR_CLEAR_XMIT), &ch->ch_cls_uart->isr_fcr);
> +       udelay(10);
> +
> +       ch->ch_flags |= (CH_FIFO_ENABLED | CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
> +
> +       readb(&ch->ch_cls_uart->lsr);
> +       readb(&ch->ch_cls_uart->msr);
> +}
> +
> +
> +/*
> + * Turns off UART.
> + */
> +static void cls_uart_off(struct channel_t *ch)
> +{
> +       writeb(0, &ch->ch_cls_uart->ier);
> +}
> +
> +
> +/*
> + * cls_get_uarts_bytes_left.
> + * Returns 0 is nothing left in the FIFO, returns 1 otherwise.
> + *
> + * The channel lock MUST be held by the calling function.
> + */
> +static uint cls_get_uart_bytes_left(struct channel_t *ch)
> +{
> +       uchar left = 0;
> +       uchar lsr = 0;
> +
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return 0;
> +
> +       lsr = readb(&ch->ch_cls_uart->lsr);
> +
> +       /* Determine whether the Transmitter is empty or not */
> +       if (!(lsr & UART_LSR_TEMT)) {
> +               if (ch->ch_flags & CH_TX_FIFO_EMPTY) {
> +                       tasklet_schedule(&ch->ch_bd->helper_tasklet);
> +               }
> +               left = 1;
> +       }
> +       else {
> +               ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
> +               left = 0;
> +       }
> +
> +       return left;
> +}
> +
> +
> +/*
> + * cls_send_break.
> + * Starts sending a break thru the UART.
> + *
> + * The channel lock MUST be held by the calling function.
> + */
> +static void cls_send_break(struct channel_t *ch, int msecs)
> +{
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return;
> +
> +       /*
> +        * If we receive a time of 0, this means turn off the break.
> +        */
> +       if (msecs == 0) {
> +               /* Turn break off, and unset some variables */
> +               if (ch->ch_flags & CH_BREAK_SENDING) {
> +                       uchar temp = readb(&ch->ch_cls_uart->lcr);
> +                       writeb((temp & ~UART_LCR_SBC), &ch->ch_cls_uart->lcr);
> +                       ch->ch_flags &= ~(CH_BREAK_SENDING);
> +                       ch->ch_stop_sending_break = 0;
> +                       DPR_IOCTL(("Finishing UART_LCR_SBC! finished: %lx\n", jiffies));
> +               }
> +               return;
> +        }
> +
> +       /*
> +        * Set the time we should stop sending the break.
> +        * If we are already sending a break, toss away the existing
> +        * time to stop, and use this new value instead.
> +        */
> +       ch->ch_stop_sending_break = jiffies + dgnc_jiffies_from_ms(msecs);
> +
> +       /* Tell the UART to start sending the break */
> +       if (!(ch->ch_flags & CH_BREAK_SENDING)) {
> +               uchar temp = readb(&ch->ch_cls_uart->lcr);
> +               writeb((temp | UART_LCR_SBC), &ch->ch_cls_uart->lcr);
> +               ch->ch_flags |= (CH_BREAK_SENDING);
> +               DPR_IOCTL(("Port %d. Starting UART_LCR_SBC! start: %lx should end: %lx\n",
> +                       ch->ch_portnum, jiffies, ch->ch_stop_sending_break));
> +       }
> +}
> +
> +
> +/*
> + * cls_send_immediate_char.
> + * Sends a specific character as soon as possible to the UART,
> + * jumping over any bytes that might be in the write queue.
> + *
> + * The channel lock MUST be held by the calling function.
> + */
> +static void cls_send_immediate_char(struct channel_t *ch, unsigned char c)
> +{
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return;
> +
> +       writeb(c, &ch->ch_cls_uart->txrx);
> +}
> +
> +static void cls_vpd(struct board_t *brd)
> +{
> +        ulong           vpdbase;        /* Start of io base of the card */
> +        uchar           *re_map_vpdbase;/* Remapped memory of the card */
> +       int i = 0;
> +
> +
> +       vpdbase = pci_resource_start(brd->pdev, 3);
> +
> +       /* No VPD */
> +       if (!vpdbase)
> +               return;
> +
> +       re_map_vpdbase = ioremap(vpdbase, 0x400);
> +
> +       if (!re_map_vpdbase)
> +               return;
> +
> +        /* Store the VPD into our buffer */
> +        for (i = 0; i < 0x40; i++) {
> +               brd->vpd[i] = readb(re_map_vpdbase + i);
> +               printk("%x ", brd->vpd[i]);
> +        }
> +       printk("\n");
> +
> +       if (re_map_vpdbase)
> +               iounmap(re_map_vpdbase);
> +}
> +
> diff --git a/drivers/staging/dgnc/dgnc_cls.h b/drivers/staging/dgnc/dgnc_cls.h
> new file mode 100644
> index 0000000..dca5ea3
> --- /dev/null
> +++ b/drivers/staging/dgnc/dgnc_cls.h
> @@ -0,0 +1,90 @@
> +/*
> + * 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 __DGNC_CLS_H
> +#define __DGNC_CLS_H
> +
> +#include "dgnc_types.h"
> +
> +
> +/************************************************************************
> + * Per channel/port Classic UART structure                             *
> + ************************************************************************
> + *             Base Structure Entries Usage Meanings to Host           *
> + *                                                                     *
> + *     W = read write          R = read only                           *
> + *                     U = Unused.                                     *
> + ************************************************************************/
> +
> +struct cls_uart_struct {
> +       volatile uchar txrx;            /* WR  RHR/THR - Holding Reg */
> +       volatile uchar ier;             /* WR  IER - Interrupt Enable Reg */
> +       volatile uchar isr_fcr;         /* WR  ISR/FCR - Interrupt Status Reg/Fifo Control Reg */
> +       volatile uchar lcr;             /* WR  LCR - Line Control Reg */
> +       volatile uchar mcr;             /* WR  MCR - Modem Control Reg */
> +       volatile uchar lsr;             /* WR  LSR - Line Status Reg */
> +       volatile uchar msr;             /* WR  MSR - Modem Status Reg */
> +       volatile uchar spr;             /* WR  SPR - Scratch Pad Reg */
> +};
> +
> +/* Where to read the interrupt register (8bits) */
> +#define        UART_CLASSIC_POLL_ADDR_OFFSET   0x40
> +
> +#define UART_EXAR654_ENHANCED_REGISTER_SET 0xBF
> +
> +#define UART_16654_FCR_TXTRIGGER_8     0x0
> +#define UART_16654_FCR_TXTRIGGER_16    0x10
> +#define UART_16654_FCR_TXTRIGGER_32    0x20
> +#define UART_16654_FCR_TXTRIGGER_56    0x30
> +
> +#define UART_16654_FCR_RXTRIGGER_8     0x0
> +#define UART_16654_FCR_RXTRIGGER_16    0x40
> +#define UART_16654_FCR_RXTRIGGER_56    0x80
> +#define UART_16654_FCR_RXTRIGGER_60     0xC0
> +
> +#define UART_IIR_XOFF                  0x10    /* Received Xoff signal/Special character */
> +#define UART_IIR_CTSRTS                        0x20    /* Received CTS/RTS change of state */
> +#define UART_IIR_RDI_TIMEOUT           0x0C    /* Receiver data TIMEOUT */
> +
> +/*
> + * These are the EXTENDED definitions for the Exar 654's Interrupt
> + * Enable Register.
> + */
> +#define UART_EXAR654_EFR_ECB      0x10    /* Enhanced control bit */
> +#define UART_EXAR654_EFR_IXON     0x2     /* Receiver compares Xon1/Xoff1 */
> +#define UART_EXAR654_EFR_IXOFF    0x8     /* Transmit Xon1/Xoff1 */
> +#define UART_EXAR654_EFR_RTSDTR   0x40    /* Auto RTS/DTR Flow Control Enable */
> +#define UART_EXAR654_EFR_CTSDSR   0x80    /* Auto CTS/DSR Flow COntrol Enable */
> +
> +#define UART_EXAR654_XOFF_DETECT  0x1     /* Indicates whether chip saw an incoming XOFF char  */
> +#define UART_EXAR654_XON_DETECT   0x2     /* Indicates whether chip saw an incoming XON char */
> +
> +#define UART_EXAR654_IER_XOFF     0x20    /* Xoff Interrupt Enable */
> +#define UART_EXAR654_IER_RTSDTR   0x40    /* Output Interrupt Enable */
> +#define UART_EXAR654_IER_CTSDSR   0x80    /* Input Interrupt Enable */
> +
> +/*
> + * Our Global Variables
> + */
> +extern struct board_ops dgnc_cls_ops;
> +
> +#endif
> diff --git a/drivers/staging/dgnc/dgnc_driver.c b/drivers/staging/dgnc/dgnc_driver.c
> new file mode 100644
> index 0000000..7c88de7
> --- /dev/null
> +++ b/drivers/staging/dgnc/dgnc_driver.c
> @@ -0,0 +1,1028 @@
> +/*
> + * 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: dgnc_driver.c,v 1.3 2011/06/23 12:47:35 markh Exp $
> + *
> + */
> +
> +
> +#include <linux/kernel.h>
> +#include <linux/version.h>
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +
> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)
> +#include <linux/sched.h>
> +#endif
> +
> +#include "dgnc_driver.h"
> +#include "dgnc_pci.h"
> +#include "dgnc_proc.h"
> +#include "dpacompat.h"
> +#include "dgnc_mgmt.h"
> +#include "dgnc_tty.h"
> +#include "dgnc_trace.h"
> +#include "dgnc_cls.h"
> +#include "dgnc_neo.h"
> +#include "dgnc_sysfs.h"
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Digi International, http://www.digi.com";);
> +MODULE_DESCRIPTION("Driver for the Digi International Neo and Classic PCI based product line");
> +MODULE_SUPPORTED_DEVICE("dgnc");
> +
> +/*
> + * 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,           0644,   "Driver debugging level");
> +PARM_INT(rawreadok,    1,              0644,   "Bypass flip buffers on input");
> +PARM_INT(trcbuf_size,  0x100000,       0644,   "Debugging trace buffer size.");
> +
> +/**************************************************************************
> + *
> + * protos for this file
> + *
> + */
> +static int             dgnc_start(void);
> +static int             dgnc_finalize_board_init(struct board_t *brd);
> +static void            dgnc_init_globals(void);
> +static int             dgnc_found_board(struct pci_dev *pdev, int id);
> +static void            dgnc_cleanup_board(struct board_t *brd);
> +static void            dgnc_poll_handler(ulong dummy);
> +static int             dgnc_init_pci(void);
> +static int             dgnc_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
> +static void            dgnc_remove_one(struct pci_dev *dev);
> +static int             dgnc_probe1(struct pci_dev *pdev, int card_type);
> +static void            dgnc_do_remap(struct board_t *brd);
> +static void            dgnc_mbuf(struct board_t *brd, const char *fmt, ...);
> +
> +
> +/* Driver load/unload functions */
> +int            dgnc_init_module(void);
> +void           dgnc_cleanup_module(void);
> +
> +module_init(dgnc_init_module);
> +module_exit(dgnc_cleanup_module);
> +
> +
> +/*
> + * File operations permitted on Control/Management major.
> + */
> +static struct file_operations dgnc_BoardFops =
> +{
> +       .owner          =       THIS_MODULE,
> +#ifdef HAVE_UNLOCKED_IOCTL
> +        .unlocked_ioctl =      dgnc_mgmt_ioctl,
> +#else
> +        .ioctl          =       dgnc_mgmt_ioctl,
> +#endif
> +       .open           =       dgnc_mgmt_open,
> +       .release        =       dgnc_mgmt_close
> +};
> +
> +
> +/*
> + * Globals
> + */
> +uint                   dgnc_NumBoards;
> +struct board_t         *dgnc_Board[MAXBOARDS];
> +DEFINE_SPINLOCK(dgnc_global_lock);
> +int                    dgnc_driver_state = DRIVER_INITIALIZED;
> +ulong                  dgnc_poll_counter;
> +uint                   dgnc_Major;
> +int                    dgnc_poll_tick = 20;    /* Poll interval - 20 ms */
> +
> +/*
> + * Static vars.
> + */
> +static uint            dgnc_Major_Control_Registered = FALSE;
> +static uint            dgnc_driver_start = FALSE;
> +
> +static struct class *dgnc_class;
> +
> +/*
> + * Poller stuff
> + */
> +static                         DEFINE_SPINLOCK(dgnc_poll_lock);        /* Poll scheduling lock */
> +static ulong           dgnc_poll_time;                         /* Time of next poll */
> +static uint            dgnc_poll_stop;                         /* Used to tell poller to stop */
> +static struct timer_list dgnc_poll_timer;
> +
> +
> +static struct pci_device_id dgnc_pci_tbl[] = {
> +       {       DIGI_VID, PCI_DEVICE_CLASSIC_4_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0,       0 },
> +       {       DIGI_VID, PCI_DEVICE_CLASSIC_4_422_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0,   1 },
> +       {       DIGI_VID, PCI_DEVICE_CLASSIC_8_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0,       2 },
> +       {       DIGI_VID, PCI_DEVICE_CLASSIC_8_422_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0,   3 },
> +       {       DIGI_VID, PCI_DEVICE_NEO_4_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0,           4 },
> +       {       DIGI_VID, PCI_DEVICE_NEO_8_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0,           5 },
> +       {       DIGI_VID, PCI_DEVICE_NEO_2DB9_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0,        6 },
> +       {       DIGI_VID, PCI_DEVICE_NEO_2DB9PRI_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0,     7 },
> +       {       DIGI_VID, PCI_DEVICE_NEO_2RJ45_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0,       8 },
> +       {       DIGI_VID, PCI_DEVICE_NEO_2RJ45PRI_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0,    9 },
> +       {       DIGI_VID, PCI_DEVICE_NEO_1_422_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0,       10 },
> +       {       DIGI_VID, PCI_DEVICE_NEO_1_422_485_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0,   11 },
> +       {       DIGI_VID, PCI_DEVICE_NEO_2_422_485_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0,   12 },
> +       {       DIGI_VID, PCI_DEVICE_NEO_EXPRESS_8_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0,    13 },
> +       {       DIGI_VID, PCI_DEVICE_NEO_EXPRESS_4_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0,   14 },
> +       {       DIGI_VID, PCI_DEVICE_NEO_EXPRESS_4RJ45_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0,       15 },
> +       {       DIGI_VID, PCI_DEVICE_NEO_EXPRESS_8RJ45_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0,       16 },
> +       {0,}                                            /* 0 terminated list. */
> +};
> +MODULE_DEVICE_TABLE(pci, dgnc_pci_tbl);
> +
> +struct board_id {
> +       uchar *name;
> +       uint maxports;
> +       unsigned int is_pci_express;
> +};
> +
> +static struct board_id dgnc_Ids[] =
> +{
> +       {       PCI_DEVICE_CLASSIC_4_PCI_NAME,          4,      0       },
> +       {       PCI_DEVICE_CLASSIC_4_422_PCI_NAME,      4,      0       },
> +       {       PCI_DEVICE_CLASSIC_8_PCI_NAME,          8,      0       },
> +       {       PCI_DEVICE_CLASSIC_8_422_PCI_NAME,      8,      0       },
> +       {       PCI_DEVICE_NEO_4_PCI_NAME,              4,      0       },
> +       {       PCI_DEVICE_NEO_8_PCI_NAME,              8,      0       },
> +       {       PCI_DEVICE_NEO_2DB9_PCI_NAME,           2,      0       },
> +       {       PCI_DEVICE_NEO_2DB9PRI_PCI_NAME,        2,      0       },
> +       {       PCI_DEVICE_NEO_2RJ45_PCI_NAME,          2,      0       },
> +       {       PCI_DEVICE_NEO_2RJ45PRI_PCI_NAME,       2,      0       },
> +       {       PCI_DEVICE_NEO_1_422_PCI_NAME,          1,      0       },
> +       {       PCI_DEVICE_NEO_1_422_485_PCI_NAME,      1,      0       },
> +       {       PCI_DEVICE_NEO_2_422_485_PCI_NAME,      2,      0       },
> +       {       PCI_DEVICE_NEO_EXPRESS_8_PCI_NAME,      8,      1       },
> +       {       PCI_DEVICE_NEO_EXPRESS_4_PCI_NAME,      4,      1       },
> +       {       PCI_DEVICE_NEO_EXPRESS_4RJ45_PCI_NAME,  4,      1       },
> +       {       PCI_DEVICE_NEO_EXPRESS_8RJ45_PCI_NAME,  8,      1       },
> +       {       NULL,                                   0,      0       }
> +};
> +
> +static struct pci_driver dgnc_driver = {
> +       .name           = "dgnc",
> +       .probe          = dgnc_init_one,
> +       .id_table       = dgnc_pci_tbl,
> +       .remove         = dgnc_remove_one,
> +};
> +
> +
> +char *dgnc_state_text[] = {
> +       "Board Failed",
> +       "Board Found",
> +       "Board READY",
> +};
> +
> +char *dgnc_driver_state_text[] = {
> +       "Driver Initialized",
> +       "Driver Ready."
> +};
> +
> +
> +
> +/************************************************************************
> + *
> + * Driver load/unload functions
> + *
> + ************************************************************************/
> +
> +
> +/*
> + * init_module()
> + *
> + * Module load.  This is where it all starts.
> + */
> +int dgnc_init_module(void)
> +{
> +       int rc = 0;
> +
> +       APR(("%s, Digi International Part Number %s\n", DG_NAME, DG_PART));
> +
> +       /*
> +        * Initialize global stuff
> +        */
> +       rc = dgnc_start();
> +
> +       if (rc < 0) {
> +               return(rc);
> +       }
> +
> +       /*
> +        * Find and configure all the cards
> +        */
> +       rc = dgnc_init_pci();
> +
> +       /*
> +        * If something went wrong in the scan, bail out of driver.
> +        */
> +       if (rc < 0) {
> +               /* Only unregister the pci driver if it was actually registered. */
> +               if (dgnc_NumBoards)
> +                       pci_unregister_driver(&dgnc_driver);
> +               else
> +                       printk("WARNING: dgnc driver load failed.  No Digi Neo or Classic boards found.\n");
> +
> +               dgnc_cleanup_module();
> +       }
> +       else {
> +               dgnc_create_driver_sysfiles(&dgnc_driver);
> +       }
> +
> +       DPR_INIT(("Finished init_module. Returning %d\n", rc));
> +       return (rc);
> +}
> +
> +
> +/*
> + * Start of driver.
> + */
> +static int dgnc_start(void)
> +{
> +       int rc = 0;
> +       unsigned long flags;
> +
> +       if (dgnc_driver_start == FALSE) {
> +
> +               dgnc_driver_start = TRUE;
> +
> +               /* make sure that the globals are init'd before we do anything else */
> +               dgnc_init_globals();
> +
> +               dgnc_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 (!dgnc_Major_Control_Registered) {
> +                       /*
> +                        * Register management/dpa devices
> +                        */
> +                       rc = register_chrdev(0, "dgnc", &dgnc_BoardFops);
> +                       if (rc <= 0) {
> +                               APR(("Can't register dgnc driver device (%d)\n", rc));
> +                               rc = -ENXIO;
> +                               return(rc);
> +                       }
> +                       dgnc_Major = rc;
> +
> +                       dgnc_class = class_create(THIS_MODULE, "dgnc_mgmt");
> +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
> +                       device_create_drvdata(dgnc_class, NULL,
> +                               MKDEV(dgnc_Major, 0),
> +                               NULL, "dgnc_mgmt");
> +#else
> +                        device_create(dgnc_class, NULL,
> +                                MKDEV(dgnc_Major, 0),
> +                                NULL, "dgnc_mgmt");
> +#endif
> +
> +                       dgnc_Major_Control_Registered = TRUE;
> +               }
> +
> +               /*
> +                * Register our basic stuff in /proc/dgnc
> +                */
> +               dgnc_proc_register_basic_prescan();
> +
> +               /*
> +                * Init any global tty stuff.
> +                */
> +               rc = dgnc_tty_preinit();
> +
> +               if (rc < 0) {
> +                       APR(("tty preinit - not enough memory (%d)\n", rc));
> +                       return(rc);
> +               }
> +
> +               /* Start the poller */
> +               DGNC_LOCK(dgnc_poll_lock, flags);
> +               init_timer(&dgnc_poll_timer);
> +               dgnc_poll_timer.function = dgnc_poll_handler;
> +               dgnc_poll_timer.data = 0;
> +               dgnc_poll_time = jiffies + dgnc_jiffies_from_ms(dgnc_poll_tick);
> +               dgnc_poll_timer.expires = dgnc_poll_time;
> +               DGNC_UNLOCK(dgnc_poll_lock, flags);
> +
> +               add_timer(&dgnc_poll_timer);
> +
> +               dgnc_driver_state = DRIVER_READY;
> +       }
> +
> +       return(rc);
> +}
> +
> +/*
> + * Register pci driver, and return how many boards we have.
> + */
> +static int dgnc_init_pci(void)
> +{
> +       return pci_register_driver(&dgnc_driver);
> +}
> +
> +
> +/* returns count (>= 0), or negative on error */
> +static int dgnc_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
> +{
> +       int rc;
> +
> +       /* wake up and enable device */
> +       rc = pci_enable_device(pdev);
> +
> +       if (rc < 0) {
> +               rc = -EIO;
> +       } else {
> +               rc = dgnc_probe1(pdev, ent->driver_data);
> +               if (rc == 0) {
> +                       dgnc_NumBoards++;
> +                       DPR_INIT(("Incrementing numboards to %d\n", dgnc_NumBoards));
> +               }
> +       }
> +       return rc;
> +}
> +
> +static int dgnc_probe1(struct pci_dev *pdev, int card_type)
> +{
> +       return dgnc_found_board(pdev, card_type);
> +}
> +
> +
> +static void dgnc_remove_one(struct pci_dev *dev)
> +{
> +       /* Do Nothing */
> +}
> +
> +/*
> + * dgnc_cleanup_module()
> + *
> + * Module unload.  This is where it all ends.
> + */
> +void dgnc_cleanup_module(void)
> +{
> +       int i;
> +       ulong lock_flags;
> +
> +       DGNC_LOCK(dgnc_poll_lock, lock_flags);
> +       dgnc_poll_stop = 1;
> +       DGNC_UNLOCK(dgnc_poll_lock, lock_flags);
> +
> +       /* Turn off poller right away. */
> +       del_timer_sync(&dgnc_poll_timer);
> +
> +       dgnc_proc_unregister_all();
> +
> +       dgnc_remove_driver_sysfiles(&dgnc_driver);
> +
> +       if (dgnc_Major_Control_Registered) {
> +               device_destroy(dgnc_class, MKDEV(dgnc_Major, 0));
> +               class_destroy(dgnc_class);
> +               unregister_chrdev(dgnc_Major, "dgnc");
> +       }
> +
> +       for (i = 0; i < dgnc_NumBoards; ++i) {
> +               dgnc_remove_ports_sysfiles(dgnc_Board[i]);
> +               dgnc_tty_uninit(dgnc_Board[i]);
> +               dgnc_cleanup_board(dgnc_Board[i]);
> +       }
> +
> +       dgnc_tty_post_uninit();
> +
> +#if defined(DGNC_TRACER)
> +       /* last thing, make sure we release the tracebuffer */
> +       dgnc_tracer_free();
> +#endif
> +       if (dgnc_NumBoards)
> +               pci_unregister_driver(&dgnc_driver);
> +}
> +
> +
> +/*
> + * dgnc_cleanup_board()
> + *
> + * Free all the memory associated with a board
> + */
> +static void dgnc_cleanup_board(struct board_t *brd)
> +{
> +       int i = 0;
> +
> +        if(!brd || brd->magic != DGNC_BOARD_MAGIC)
> +                return;
> +
> +       switch (brd->device) {
> +       case PCI_DEVICE_CLASSIC_4_DID:
> +       case PCI_DEVICE_CLASSIC_8_DID:
> +       case PCI_DEVICE_CLASSIC_4_422_DID:
> +       case PCI_DEVICE_CLASSIC_8_422_DID:
> +
> +               /* Tell card not to interrupt anymore. */
> +               outb(0, brd->iobase + 0x4c);
> +               break;
> +
> +       default:
> +               break;
> +       }
> +
> +       if (brd->irq)
> +               free_irq(brd->irq, brd);
> +
> +       tasklet_kill(&brd->helper_tasklet);
> +
> +       if (brd->re_map_membase) {
> +               iounmap(brd->re_map_membase);
> +               brd->re_map_membase = NULL;
> +       }
> +
> +       if (brd->msgbuf_head) {
> +               unsigned long flags;
> +
> +               DGNC_LOCK(dgnc_global_lock, flags);
> +               brd->msgbuf = NULL;
> +               printk(brd->msgbuf_head);
> +               kfree(brd->msgbuf_head);
> +               brd->msgbuf_head = NULL;
> +               DGNC_UNLOCK(dgnc_global_lock, flags);
> +        }
> +
> +       /* Free all allocated channels structs */
> +       for (i = 0; i < MAXPORTS ; i++) {
> +               if (brd->channels[i]) {
> +                       if (brd->channels[i]->ch_rqueue)
> +                               kfree(brd->channels[i]->ch_rqueue);
> +                       if (brd->channels[i]->ch_equeue)
> +                               kfree(brd->channels[i]->ch_equeue);
> +                       if (brd->channels[i]->ch_wqueue)
> +                               kfree(brd->channels[i]->ch_wqueue);
> +
> +                       kfree(brd->channels[i]);
> +                       brd->channels[i] = NULL;
> +               }
> +       }
> +
> +       if (brd->flipbuf)
> +               kfree(brd->flipbuf);
> +
> +       dgnc_Board[brd->boardnum] = NULL;
> +
> +        kfree(brd);
> +}
> +
> +
> +/*
> + * dgnc_found_board()
> + *
> + * A board has been found, init it.
> + */
> +static int dgnc_found_board(struct pci_dev *pdev, int id)
> +{
> +        struct board_t *brd;
> +       unsigned int pci_irq;
> +       int i = 0;
> +       int rc = 0;
> +       unsigned long flags;
> +
> +        /* get the board structure and prep it */
> +        brd = dgnc_Board[dgnc_NumBoards] =
> +        (struct board_t *) dgnc_driver_kzmalloc(sizeof(struct board_t), GFP_KERNEL);
> +       if (!brd) {
> +               APR(("memory allocation for board structure failed\n"));
> +               return(-ENOMEM);
> +       }
> +
> +        /* make a temporary message buffer for the boot messages */
> +        brd->msgbuf = brd->msgbuf_head =
> +                (char *) dgnc_driver_kzmalloc(sizeof(char) * 8192, GFP_KERNEL);
> +        if (!brd->msgbuf) {
> +               kfree(brd);
> +               APR(("memory allocation for board msgbuf failed\n"));
> +               return(-ENOMEM);
> +        }
> +
> +       /* store the info for the board we've found */
> +       brd->magic = DGNC_BOARD_MAGIC;
> +       brd->boardnum = dgnc_NumBoards;
> +       brd->vendor = dgnc_pci_tbl[id].vendor;
> +       brd->device = dgnc_pci_tbl[id].device;
> +       brd->pdev = pdev;
> +       brd->pci_bus = pdev->bus->number;
> +       brd->pci_slot = PCI_SLOT(pdev->devfn);
> +       brd->name = dgnc_Ids[id].name;
> +       brd->maxports = dgnc_Ids[id].maxports;
> +       if (dgnc_Ids[i].is_pci_express)
> +               brd->bd_flags |= BD_IS_PCI_EXPRESS;
> +       brd->dpastatus = BD_NOFEP;
> +       init_waitqueue_head(&brd->state_wait);
> +
> +       DGNC_SPINLOCK_INIT(brd->bd_lock);
> +       DGNC_SPINLOCK_INIT(brd->bd_intr_lock);
> +
> +       brd->state              = BOARD_FOUND;
> +
> +       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;
> +
> +
> +       switch(brd->device) {
> +
> +       case PCI_DEVICE_CLASSIC_4_DID:
> +       case PCI_DEVICE_CLASSIC_8_DID:
> +       case PCI_DEVICE_CLASSIC_4_422_DID:
> +       case PCI_DEVICE_CLASSIC_8_422_DID:
> +
> +               brd->dpatype = T_CLASSIC | T_PCIBUS;
> +
> +                DPR_INIT(("dgnc_found_board - Classic.\n"));
> +
> +               /*
> +                * For PCI ClassicBoards
> +                * PCI Local Address (i.e. "resource" number) space
> +                * 0        PLX Memory Mapped Config
> +                * 1        PLX I/O Mapped Config
> +                * 2        I/O Mapped UARTs and Status
> +                * 3        Memory Mapped VPD
> +                * 4        Memory Mapped UARTs and Status
> +                */
> +
> +
> +               /* get the PCI Base Address Registers */
> +               brd->membase = pci_resource_start(pdev, 4);
> +
> +               if (!brd->membase) {
> +                       APR(("card has no PCI IO resources, failing board.\n"));
> +                       return -ENODEV;
> +               }
> +
> +               brd->membase_end = pci_resource_end(pdev, 4);
> +
> +               if (brd->membase & 1)
> +                       brd->membase &= ~3;
> +               else
> +                       brd->membase &= ~15;
> +
> +               brd->iobase     = pci_resource_start(pdev, 1);
> +               brd->iobase_end = pci_resource_end(pdev, 1);
> +               brd->iobase     = ((unsigned int) (brd->iobase)) & 0xFFFE;
> +
> +               /* Assign the board_ops struct */
> +               brd->bd_ops = &dgnc_cls_ops;
> +
> +               brd->bd_uart_offset = 0x8;
> +               brd->bd_dividend = 921600;
> +
> +               dgnc_do_remap(brd);
> +
> +               /* Get and store the board VPD, if it exists */
> +               brd->bd_ops->vpd(brd);
> +
> +               /*
> +                * Enable Local Interrupt 1               (0x1),
> +                * Local Interrupt 1 Polarity Active high (0x2),
> +                * Enable PCI interrupt                   (0x40)
> +                */
> +               outb(0x43, brd->iobase + 0x4c);
> +
> +               break;
> +
> +
> +       case PCI_DEVICE_NEO_4_DID:
> +       case PCI_DEVICE_NEO_8_DID:
> +       case PCI_DEVICE_NEO_2DB9_DID:
> +       case PCI_DEVICE_NEO_2DB9PRI_DID:
> +       case PCI_DEVICE_NEO_2RJ45_DID:
> +       case PCI_DEVICE_NEO_2RJ45PRI_DID:
> +       case PCI_DEVICE_NEO_1_422_DID:
> +       case PCI_DEVICE_NEO_1_422_485_DID:
> +       case PCI_DEVICE_NEO_2_422_485_DID:
> +       case PCI_DEVICE_NEO_EXPRESS_8_DID:
> +       case PCI_DEVICE_NEO_EXPRESS_4_DID:
> +       case PCI_DEVICE_NEO_EXPRESS_4RJ45_DID:
> +       case PCI_DEVICE_NEO_EXPRESS_8RJ45_DID:
> +
> +               /*
> +                * This chip is set up 100% when we get to it.
> +                * No need to enable global interrupts or anything.
> +                */
> +               if (brd->bd_flags & BD_IS_PCI_EXPRESS)
> +                       brd->dpatype = T_NEO_EXPRESS | T_PCIBUS;
> +               else
> +                       brd->dpatype = T_NEO | T_PCIBUS;
> +
> +                DPR_INIT(("dgnc_found_board - NEO.\n"));
> +
> +               /* get the PCI Base Address Registers */
> +               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;
> +
> +               /* Assign the board_ops struct */
> +               brd->bd_ops = &dgnc_neo_ops;
> +
> +               brd->bd_uart_offset = 0x200;
> +               brd->bd_dividend = 921600;
> +
> +               dgnc_do_remap(brd);
> +
> +               if (brd->re_map_membase) {
> +
> +                       /* After remap is complete, we need to read and store the dvid */
> +                       brd->dvid = readb(brd->re_map_membase + 0x8D);
> +
> +                       /* Get and store the board VPD, if it exists */
> +                       brd->bd_ops->vpd(brd);
> +               }
> +               break;
> +
> +       default:
> +               APR(("Did not find any compatible Neo or Classic PCI boards in system.\n"));
> +               return (-ENXIO);
> +
> +       }
> +
> +       /*
> +        * Do tty device initialization.
> +        */
> +
> +       rc = dgnc_tty_register(brd);
> +       if (rc < 0) {
> +               dgnc_tty_uninit(brd);
> +               APR(("Can't register tty devices (%d)\n", rc));
> +               brd->state = BOARD_FAILED;
> +               brd->dpastatus = BD_NOFEP;
> +               goto failed;
> +       }
> +
> +       rc = dgnc_finalize_board_init(brd);
> +       if (rc < 0) {
> +               APR(("Can't finalize board init (%d)\n", rc));
> +               brd->state = BOARD_FAILED;
> +               brd->dpastatus = BD_NOFEP;
> +
> +               goto failed;
> +       }
> +
> +       rc = dgnc_tty_init(brd);
> +       if (rc < 0) {
> +               dgnc_tty_uninit(brd);
> +               APR(("Can't init tty devices (%d)\n", rc));
> +               brd->state = BOARD_FAILED;
> +               brd->dpastatus = BD_NOFEP;
> +
> +               goto failed;
> +       }
> +
> +       brd->state = BOARD_READY;
> +       brd->dpastatus = BD_RUNNING;
> +
> +       dgnc_create_ports_sysfiles(brd);
> +
> +       /* init our poll helper tasklet */
> +       tasklet_init(&brd->helper_tasklet, brd->bd_ops->tasklet, (unsigned long) brd);
> +
> +        /* Log the information about the board */
> +       dgnc_mbuf(brd, DRVSTR": board %d: %s (rev %d), irq %d\n",
> +               dgnc_NumBoards, brd->name, brd->rev, brd->irq);
> +
> +       DPR_INIT(("dgnc_scan(%d) - printing out the msgbuf\n", i));
> +       DGNC_LOCK(dgnc_global_lock, flags);
> +       brd->msgbuf = NULL;
> +       printk(brd->msgbuf_head);
> +       kfree(brd->msgbuf_head);
> +       brd->msgbuf_head = NULL;
> +       DGNC_UNLOCK(dgnc_global_lock, flags);
> +
> +       /*
> +        * allocate flip buffer for board.
> +        *
> +        * Okay to malloc with GFP_KERNEL, we are not at interrupt
> +        * context, and there are no locks held.
> +        */
> +       brd->flipbuf = dgnc_driver_kzmalloc(MYFLIPLEN, GFP_KERNEL);
> +
> +       dgnc_proc_register_basic_postscan(dgnc_NumBoards);
> +
> +       wake_up_interruptible(&brd->state_wait);
> +
> +        return(0);
> +
> +failed:
> +
> +       return (-ENXIO);
> +
> +}
> +
> +
> +static int dgnc_finalize_board_init(struct board_t *brd) {
> +       int rc = 0;
> +
> +        DPR_INIT(("dgnc_finalize_board_init() - start\n"));
> +
> +       if (!brd || brd->magic != DGNC_BOARD_MAGIC)
> +                return(-ENODEV);
> +
> +        DPR_INIT(("dgnc_finalize_board_init() - start #2\n"));
> +
> +       if (brd->irq) {
> +               rc = request_irq(brd->irq, brd->bd_ops->intr, IRQF_SHARED, "DGNC", brd);
> +
> +               if (rc) {
> +                       printk("Failed to hook IRQ %d\n",brd->irq);
> +                       brd->state = BOARD_FAILED;
> +                       brd->dpastatus = BD_NOFEP;
> +                       rc = -ENODEV;
> +               } else {
> +                       DPR_INIT(("Requested and received usage of IRQ %d\n", brd->irq));
> +               }
> +       }
> +       return(rc);
> +}
> +
> +/*
> + * Remap PCI memory.
> + */
> +static void dgnc_do_remap(struct board_t *brd)
> +{
> +
> +       if (!brd || brd->magic != DGNC_BOARD_MAGIC)
> +               return;
> +
> +       brd->re_map_membase = ioremap(brd->membase, 0x1000);
> +
> +       DPR_INIT(("remapped mem: 0x%p\n", brd->re_map_membase));
> +}
> +
> +
> +/*****************************************************************************
> +*
> +* Function:
> +*
> +*    dgnc_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 dgnc_poll_handler(ulong dummy)
> +{
> +        struct board_t *brd;
> +        unsigned long lock_flags;
> +       int i;
> +       unsigned long new_time;
> +
> +       dgnc_poll_counter++;
> +
> +       /*
> +        * Do not start the board state machine until
> +        * driver tells us its up and running, and has
> +        * everything it needs.
> +        */
> +       if (dgnc_driver_state != DRIVER_READY) {
> +               goto schedule_poller;
> +       }
> +
> +       /* Go thru each board, kicking off a tasklet for each if needed */
> +       for (i = 0; i < dgnc_NumBoards; i++) {
> +               brd = dgnc_Board[i];
> +
> +               DGNC_LOCK(brd->bd_lock, lock_flags);
> +
> +               /* If board is in a failed state, don't bother scheduling a tasklet */
> +               if (brd->state == BOARD_FAILED) {
> +                       DGNC_UNLOCK(brd->bd_lock, lock_flags);
> +                       continue;
> +               }
> +
> +               /* Schedule a poll helper task */
> +               tasklet_schedule(&brd->helper_tasklet);
> +
> +               DGNC_UNLOCK(brd->bd_lock, lock_flags);
> +       }
> +
> +schedule_poller:
> +
> +       /*
> +        * Schedule ourself back at the nominal wakeup interval.
> +        */
> +       DGNC_LOCK(dgnc_poll_lock, lock_flags);
> +       dgnc_poll_time += dgnc_jiffies_from_ms(dgnc_poll_tick);
> +
> +       new_time = dgnc_poll_time - jiffies;
> +
> +       if ((ulong) new_time >= 2 * dgnc_poll_tick) {
> +               dgnc_poll_time = jiffies +  dgnc_jiffies_from_ms(dgnc_poll_tick);
> +       }
> +
> +       init_timer(&dgnc_poll_timer);
> +       dgnc_poll_timer.function = dgnc_poll_handler;
> +       dgnc_poll_timer.data = 0;
> +       dgnc_poll_timer.expires = dgnc_poll_time;
> +       DGNC_UNLOCK(dgnc_poll_lock, lock_flags);
> +
> +       if (!dgnc_poll_stop)
> +               add_timer(&dgnc_poll_timer);
> +}
> +
> +/*
> + * dgnc_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 dgnc_init_globals(void)
> +{
> +       int i = 0;
> +
> +       dgnc_rawreadok          = rawreadok;
> +        dgnc_trcbuf_size       = trcbuf_size;
> +       dgnc_debug              = debug;
> +
> +       for (i = 0; i < MAXBOARDS; i++) {
> +               dgnc_Board[i] = NULL;
> +       }
> +
> +       init_timer(&dgnc_poll_timer);
> +}
> +
> +
> +/************************************************************************
> + *
> + * Utility functions
> + *
> + ************************************************************************/
> +
> +
> +/*
> + * dgnc_driver_kzmalloc()
> + *
> + * Malloc and clear memory,
> + */
> +void *dgnc_driver_kzmalloc(size_t size, int priority)
> +{
> +       void *p = kmalloc(size, priority);
> +       if(p)
> +               memset(p, 0, size);
> +       return(p);
> +}
> +
> +
> +/*
> + * dgnc_mbuf()
> + *
> + * Used to print to the message buffer during board init.
> + */
> +static void dgnc_mbuf(struct board_t *brd, const char *fmt, ...) {
> +       va_list         ap;
> +       char            buf[1024];
> +       int             i;
> +       unsigned long   flags;
> +
> +       DGNC_LOCK(dgnc_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);
> +               DGNC_UNLOCK(dgnc_global_lock, flags);
> +               return;
> +       }
> +
> +       memcpy(brd->msgbuf, buf, strlen(buf));
> +       brd->msgbuf += strlen(buf);
> +       *brd->msgbuf = (char) NULL;
> +
> +       DGNC_UNLOCK(dgnc_global_lock, flags);
> +}
> +
> +
> +/*
> + * dgnc_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 dgnc_ms_sleep(ulong ms)
> +{
> +       current->state = TASK_INTERRUPTIBLE;
> +       schedule_timeout((ms * HZ) / 1000);
> +       return (signal_pending(current));
> +}
> +
> +
> +
> +/*
> + *      dgnc_ioctl_name() : Returns a text version of each ioctl value.
> + */
> +char *dgnc_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");
> +       case DIGI_GETCUSTOMBAUD: return("DIGI_GETCUSTOMBAUD");
> +       case DIGI_SETCUSTOMBAUD: return("DIGI_SETCUSTOMBAUD");
> +       case TIOCMODG:          return("TIOCMODG");
> +       case TIOCMODS:          return("TIOCMODS");
> +       case TIOCSDTR:          return("TIOCSDTR");
> +       case TIOCCDTR:          return("TIOCCDTR");
> +
> +       default:                return("unknown");
> +       }
> +}
> diff --git a/drivers/staging/dgnc/dgnc_driver.h b/drivers/staging/dgnc/dgnc_driver.h
> new file mode 100644
> index 0000000..43177f4
> --- /dev/null
> +++ b/drivers/staging/dgnc/dgnc_driver.h
> @@ -0,0 +1,566 @@
> +/*
> + * 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 __DGNC_DRIVER_H
> +#define __DGNC_DRIVER_H
> +
> +#include <linux/version.h>     /* To get the current Linux version */
> +#include <linux/types.h>       /* To pick up the varions Linux types */
> +#include <linux/tty.h>          /* To pick up the various tty structs/defines */
> +#include <linux/interrupt.h>   /* For irqreturn_t type */
> +
> +#include "dgnc_types.h"                /* Additional types needed by the Digi header files */
> +#include "digi.h"              /* Digi specific ioctl header */
> +#include "dgnc_kcompat.h"      /* Kernel 2.4/2.6 compat includes */
> +#include "dgnc_sysfs.h"                /* Support for SYSFS */
> +
> +/*************************************************************************
> + *
> + * 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 DGNC_TRACER is defined at
> + *                               compile time and dgnc_debug!=0
> + */
> +#define        PROCSTR         "dgnc"                  /* /proc entries         */
> +#define        DEVSTR          "/dev/dg/dgnc"          /* /dev entries          */
> +#define        DRVSTR          "dgnc"                  /* 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)
> +
> +#define TRC_TO_CONSOLE 1
> +
> +/*
> + * Debugging levels can be set using debug insmod variable
> + * They can also be compiled out completely.
> + */
> +
> +#define        DBG_INIT                (dgnc_debug & 0x01)
> +#define        DBG_BASIC               (dgnc_debug & 0x02)
> +#define        DBG_CORE                (dgnc_debug & 0x04)
> +
> +#define        DBG_OPEN                (dgnc_debug & 0x08)
> +#define        DBG_CLOSE               (dgnc_debug & 0x10)
> +#define        DBG_READ                (dgnc_debug & 0x20)
> +#define        DBG_WRITE               (dgnc_debug & 0x40)
> +
> +#define        DBG_IOCTL               (dgnc_debug & 0x80)
> +
> +#define        DBG_PROC                (dgnc_debug & 0x100)
> +#define        DBG_PARAM               (dgnc_debug & 0x200)
> +#define        DBG_PSCAN               (dgnc_debug & 0x400)
> +#define        DBG_EVENT               (dgnc_debug & 0x800)
> +
> +#define        DBG_DRAIN               (dgnc_debug & 0x1000)
> +#define        DBG_MSIGS               (dgnc_debug & 0x2000)
> +
> +#define        DBG_MGMT                (dgnc_debug & 0x4000)
> +#define        DBG_INTR                (dgnc_debug & 0x8000)
> +
> +#define        DBG_CARR                (dgnc_debug & 0x10000)
> +
> +
> +#if defined(DGNC_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) dgnc_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_INTR(ARGS)                if (DBG_INTR)  TRC(ARGS)
> +# define DPR_MSIGS(ARGS)       if (DBG_MSIGS)  TRC(ARGS)
> +
> +# define DPR(ARGS)             if (dgnc_debug) TRC(ARGS)
> +# define P(X)                  dgnc_tracef(#X "=%p\n", X)
> +# define X(X)                  dgnc_tracef(#X "=%x\n", X)
> +
> +#else//!defined DGNC_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_INTR(ARGS)
> +# define DPR_MSIGS(ARGS)
> +
> +# define DPR(args)
> +
> +#endif//DGNC_TRACER
> +
> +/* Number of boards we support at once. */
> +#define        MAXBOARDS       20
> +#define        MAXPORTS        8
> +#define MAXTTYNAMELEN  200
> +
> +/* Our 3 magic numbers for our board, channel and unit structs */
> +#define DGNC_BOARD_MAGIC       0x5c6df104
> +#define DGNC_CHANNEL_MAGIC     0x6c6df104
> +#define DGNC_UNIT_MAGIC                0x7c6df104
> +
> +/* Serial port types */
> +#define DGNC_SERIAL            0
> +#define DGNC_PRINT             1
> +
> +#define        SERIAL_TYPE_NORMAL      1
> +
> +#define PORT_NUM(dev)  ((dev) & 0x7f)
> +#define IS_PRINT(dev)  (((dev) & 0xff) >= 0x80)
> +
> +/* MAX number of stop characters we will send when our read queue is getting full */
> +#define MAX_STOPS_SENT 5
> +
> +/* 4 extra for alignment play space */
> +#define WRITEBUFLEN            ((4096) + 4)
> +#define MYFLIPLEN              N_TTY_BUF_SIZE
> +
> +#define dgnc_jiffies_from_ms(a) (((a) * HZ) / 1000)
> +
> +/*
> + * 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)
> +
> +#ifndef _POSIX_VDISABLE
> +#define   _POSIX_VDISABLE '\0'
> +#endif
> +
> +#define SNIFF_MAX      65536           /* Sniff buffer size (2^n) */
> +#define SNIFF_MASK     (SNIFF_MAX - 1) /* Sniff wrap mask */
> +
> +/*
> + * Lock function/defines.
> + * Makes spotting lock/unlock locations easier.
> + */
> +# define DGNC_SPINLOCK_INIT(x)         spin_lock_init(&(x))
> +# define DGNC_LOCK(x,y)                        spin_lock_irqsave(&(x), y)
> +# define DGNC_UNLOCK(x,y)              spin_unlock_irqrestore(&(x), y)
> +
> +/*
> + * All the possible states the driver can be while being loaded.
> + */
> +enum {
> +       DRIVER_INITIALIZED = 0,
> +       DRIVER_READY
> +};
> +
> +/*
> + * All the possible states the board can be while booting up.
> + */
> +enum {
> +       BOARD_FAILED = 0,
> +       BOARD_FOUND,
> +       BOARD_READY
> +};
> +
> +
> +/*************************************************************************
> + *
> + * Structures and closely related defines.
> + *
> + *************************************************************************/
> +
> +struct board_t;
> +struct channel_t;
> +
> +/************************************************************************
> + * Per board operations structure                                       *
> + ************************************************************************/
> +struct board_ops {
> +       void (*tasklet) (unsigned long data);
> +       irqreturn_t (*intr) (int irq, void *voidbrd);
> +       void (*uart_init) (struct channel_t *ch);
> +       void (*uart_off) (struct channel_t *ch);
> +       int  (*drain) (struct tty_struct *tty, uint seconds);
> +       void (*param) (struct tty_struct *tty);
> +       void (*vpd) (struct board_t *brd);
> +       void (*assert_modem_signals) (struct channel_t *ch);
> +       void (*flush_uart_write) (struct channel_t *ch);
> +       void (*flush_uart_read) (struct channel_t *ch);
> +       void (*disable_receiver) (struct channel_t *ch);
> +       void (*enable_receiver) (struct channel_t *ch);
> +       void (*send_break) (struct channel_t *ch, int);
> +       void (*send_start_character) (struct channel_t *ch);
> +       void (*send_stop_character) (struct channel_t *ch);
> +       void (*copy_data_from_queue_to_uart) (struct channel_t *ch);
> +       uint (*get_uart_bytes_left) (struct channel_t *ch);
> +       void (*send_immediate_char) (struct channel_t *ch, unsigned char);
> +};
> +
> +/************************************************************************
> + * Device flag definitions for bd_flags.
> + ************************************************************************/
> +#define BD_IS_PCI_EXPRESS     0x0001          /* Is a PCI Express board */
> +
> +
> +/*
> + *     Per-board information
> + */
> +struct board_t
> +{
> +       int             magic;          /* Board Magic number.  */
> +       int             boardnum;       /* Board number: 0-32 */
> +
> +       int             type;           /* Type of board */
> +       char            *name;          /* Product Name */
> +       struct pci_dev  *pdev;          /* Pointer to the pci_dev struct */
> +       unsigned long   bd_flags;       /* Board flags */
> +       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 */
> +       uint            pci_bus;        /* PCI bus value */
> +       uint            pci_slot;       /* PCI slot value */
> +       uint            maxports;       /* MAX ports this board can handle */
> +       uchar           dvid;           /* Board specific device id */
> +       uchar           vpd[128];       /* VPD of board, if found */
> +       uchar           serial_num[20]; /* Serial number of board, if found in VPD */
> +
> +       spinlock_t      bd_lock;        /* Used to protect board */
> +
> +       spinlock_t      bd_intr_lock;   /* Used to protect the poller tasklet and
> +                                        * the interrupt routine from each other.
> +                                        */
> +
> +       uint            state;          /* State of card. */
> +       wait_queue_head_t state_wait;   /* Place to sleep on for state change */
> +
> +       struct          tasklet_struct helper_tasklet; /* Poll helper tasklet */
> +
> +       uint            nasync;         /* Number of ports on card */
> +
> +       uint            irq;            /* Interrupt request number */
> +       ulong           intr_count;     /* Count of interrupts */
> +       ulong           intr_modem;     /* Count of interrupts */
> +       ulong           intr_tx;        /* Count of interrupts */
> +       ulong           intr_rx;        /* Count of interrupts */
> +
> +       ulong           membase;        /* Start of base memory of the card */
> +       ulong           membase_end;    /* End of base memory of the card */
> +
> +       uchar           *re_map_membase;/* Remapped memory of the card */
> +
> +       ulong           iobase;         /* Start of io base of the card */
> +       ulong           iobase_end;     /* End of io base of the card */
> +
> +       uint            bd_uart_offset; /* Space between each UART */
> +
> +       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];
> +
> +       uint            dgnc_Major_Serial_Registered;
> +       uint            dgnc_Major_TransparentPrint_Registered;
> +
> +       uint            dgnc_Serial_Major;
> +       uint            dgnc_TransparentPrint_Major;
> +
> +       uint            TtyRefCnt;
> +
> +       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 */
> +
> +       /*
> +        *      Mgmt data.
> +        */
> +        char           *msgbuf_head;
> +        char           *msgbuf;
> +
> +       uint            bd_dividend;    /* Board/UARTs specific dividend */
> +
> +       struct board_ops *bd_ops;
> +
> +       /* /proc/<board> entries */
> +       struct proc_dir_entry *proc_entry_pointer;
> +       struct dgnc_proc_entry *dgnc_board_table;
> +
> +};
> +
> +
> +/************************************************************************
> + * 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                 */
> +
> +struct device;
> +
> +/************************************************************************
> + * Structure for terminal or printer unit.
> + ************************************************************************/
> +struct un_t {
> +       int     magic;          /* Unit Magic Number.                   */
> +       struct  channel_t *un_ch;
> +       ulong   un_time;
> +       uint    un_type;
> +       uint    un_open_count;  /* Counter of opens to port             */
> +       struct tty_struct *un_tty;/* Pointer to unit tty structure      */
> +       uint    un_flags;       /* Unit flags                           */
> +       wait_queue_head_t un_flags_wait; /* Place to sleep to wait on unit */
> +       uint    un_dev;         /* Minor device number                  */
> +       struct device *un_sysfs;
> +};
> +
> +
> +/************************************************************************
> + * Device flag definitions for ch_flags.
> + ************************************************************************/
> +#define CH_PRON                0x0001          /* Printer on string            */
> +#define CH_STOP                0x0002          /* Output is stopped            */
> +#define CH_STOPI       0x0004          /* Input is stopped             */
> +#define CH_CD          0x0008          /* Carrier is present           */
> +#define CH_FCAR                0x0010          /* Carrier forced on            */
> +#define CH_HANGUP       0x0020         /* Hangup received              */
> +
> +#define CH_RECEIVER_OFF        0x0040          /* Receiver is off              */
> +#define CH_OPENING     0x0080          /* Port in fragile open state   */
> +#define CH_CLOSING     0x0100          /* Port in fragile close state  */
> +#define CH_FIFO_ENABLED 0x0200         /* Port has FIFOs enabled       */
> +#define CH_TX_FIFO_EMPTY 0x0400                /* TX Fifo is completely empty  */
> +#define CH_TX_FIFO_LWM  0x0800         /* TX Fifo is below Low Water   */
> +#define CH_BREAK_SENDING 0x1000                /* Break is being sent          */
> +#define CH_LOOPBACK 0x2000             /* Channel is in lookback mode  */
> +#define CH_FLIPBUF_IN_USE 0x4000       /* Channel's flipbuf is in use  */
> +#define CH_BAUD0       0x08000         /* Used for checking B0 transitions */
> +#define CH_FORCED_STOP  0x20000                /* Output is forcibly stopped   */
> +#define CH_FORCED_STOPI 0x40000                /* Input is forcibly stopped    */
> +
> +/*
> + * Definitions for ch_sniff_flags
> + */
> +#define SNIFF_OPEN     0x1
> +#define SNIFF_WAIT_DATA        0x2
> +#define SNIFF_WAIT_SPACE 0x4
> +
> +
> +/* Our Read/Error/Write queue sizes */
> +#define RQUEUEMASK     0x1FFF          /* 8 K - 1 */
> +#define EQUEUEMASK     0x1FFF          /* 8 K - 1 */
> +#define WQUEUEMASK     0x0FFF          /* 4 K - 1 */
> +#define RQUEUESIZE     (RQUEUEMASK + 1)
> +#define EQUEUESIZE     RQUEUESIZE
> +#define WQUEUESIZE     (WQUEUEMASK + 1)
> +
> +
> +/************************************************************************
> + * Channel information structure.
> + ************************************************************************/
> +struct channel_t {
> +       int magic;                      /* Channel Magic Number         */
> +       struct board_t  *ch_bd;         /* Board structure pointer      */
> +       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;
> +
> +       uint            ch_portnum;     /* Port number, 0 offset.       */
> +       uint            ch_open_count;  /* open count                   */
> +       uint            ch_flags;       /* Channel flags                */
> +
> +       ulong           ch_close_delay; /* How long we should drop RTS/DTR for */
> +
> +       ulong           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               */
> +       uchar           ch_stopc;       /* Stop character               */
> +       uchar           ch_startc;      /* Start character              */
> +
> +       uint            ch_old_baud;    /* Cache of the current baud */
> +       uint            ch_custom_speed;/* Custom baud, if set */
> +
> +       uint            ch_wopen;       /* Waiting for open process cnt */
> +
> +       uchar           ch_mostat;      /* FEP output modem status      */
> +       uchar           ch_mistat;      /* FEP input modem status       */
> +
> +       struct neo_uart_struct *ch_neo_uart;    /* Pointer to the "mapped" UART struct */
> +       struct cls_uart_struct *ch_cls_uart;    /* Pointer to the "mapped" UART struct */
> +
> +       uchar           ch_cached_lsr;  /* Cached value of the LSR register */
> +
> +       uchar           *ch_rqueue;     /* Our read queue buffer - malloc'ed */
> +       ushort          ch_r_head;      /* Head location of the read queue */
> +       ushort          ch_r_tail;      /* Tail location of the read queue */
> +
> +       uchar           *ch_equeue;     /* Our error queue buffer - malloc'ed */
> +       ushort          ch_e_head;      /* Head location of the error queue */
> +       ushort          ch_e_tail;      /* Tail location of the error queue */
> +
> +       uchar           *ch_wqueue;     /* Our write queue buffer - malloc'ed */
> +       ushort          ch_w_head;      /* Head location of the write queue */
> +       ushort          ch_w_tail;      /* Tail location of the write queue */
> +
> +       ulong           ch_rxcount;     /* total of data received so far */
> +       ulong           ch_txcount;     /* total of data transmitted so far */
> +
> +       uchar           ch_r_tlevel;    /* Receive Trigger level */
> +       uchar           ch_t_tlevel;    /* Transmit Trigger level */
> +
> +       uchar           ch_r_watermark; /* Receive Watermark */
> +
> +       ulong           ch_stop_sending_break;  /* Time we should STOP sending a break */
> +
> +       uint            ch_stops_sent;  /* How many times I have sent a stop character
> +                                        * to try to stop the other guy sending.
> +                                        */
> +       ulong           ch_err_parity;  /* Count of parity errors on channel */
> +       ulong           ch_err_frame;   /* Count of framing errors on channel */
> +       ulong           ch_err_break;   /* Count of breaks on channel */
> +       ulong           ch_err_overrun; /* Count of overruns on channel */
> +
> +       ulong           ch_xon_sends;   /* Count of xons transmitted */
> +       ulong           ch_xoff_sends;  /* Count of xoffs transmitted */
> +
> +       ulong           ch_intr_modem;  /* Count of interrupts */
> +       ulong           ch_intr_tx;     /* Count of interrupts */
> +       ulong           ch_intr_rx;     /* Count of interrupts */
> +
> +
> +       /* /proc/<board>/<channel> entries */
> +       struct proc_dir_entry *proc_entry_pointer;
> +       struct dgnc_proc_entry *dgnc_channel_table;
> +
> +       uint ch_sniff_in;
> +       uint ch_sniff_out;
> +       char *ch_sniff_buf;             /* Sniff buffer for proc */
> +       ulong ch_sniff_flags;           /* Channel flags                */
> +       wait_queue_head_t ch_sniff_wait;
> +};
> +
> +
> +/*************************************************************************
> + *
> + * Prototypes for non-static functions used in more than one module
> + *
> + *************************************************************************/
> +
> +extern int             dgnc_ms_sleep(ulong ms);
> +extern void            *dgnc_driver_kzmalloc(size_t size, int priority);
> +extern char            *dgnc_ioctl_name(int cmd);
> +
> +/*
> + * Our Global Variables.
> + */
> +extern int             dgnc_driver_state;      /* The state of the driver      */
> +extern uint            dgnc_Major;             /* Our driver/mgmt major        */
> +extern int             dgnc_debug;             /* Debug variable               */
> +extern int             dgnc_rawreadok;         /* Set if user wants rawreads   */
> +extern int             dgnc_poll_tick;         /* Poll interval - 20 ms        */
> +extern int             dgnc_trcbuf_size;       /* Size of the ringbuffer       */
> +extern spinlock_t      dgnc_global_lock;       /* Driver global spinlock       */
> +extern uint            dgnc_NumBoards;         /* Total number of boards       */
> +extern struct board_t  *dgnc_Board[MAXBOARDS]; /* Array of board structs       */
> +extern ulong           dgnc_poll_counter;      /* Times the poller has run     */
> +extern char            *dgnc_state_text[];     /* Array of state text          */
> +extern char            *dgnc_driver_state_text[];/* Array of driver state text */
> +
> +#endif
> diff --git a/drivers/staging/dgnc/dgnc_kcompat.h b/drivers/staging/dgnc/dgnc_kcompat.h
> new file mode 100644
> index 0000000..3f69e1d
> --- /dev/null
> +++ b/drivers/staging/dgnc/dgnc_kcompat.h
> @@ -0,0 +1,91 @@
> +/*
> + * Copyright 2004 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!!!
> + *
> + *************************************************************************
> + *
> + * This file is intended to contain all the kernel "differences" between the
> + * various kernels that we support.
> + *
> + *************************************************************************/
> +
> +#ifndef __DGNC_KCOMPAT_H
> +#define __DGNC_KCOMPAT_H
> +
> +# ifndef KERNEL_VERSION
> +#  define KERNEL_VERSION(a,b,c)  (((a) << 16) + ((b) << 8) + (c))
> +# endif
> +
> +
> +#if !defined(TTY_FLIPBUF_SIZE)
> +# define TTY_FLIPBUF_SIZE 512
> +#endif
> +
> +
> +/* Sparse stuff */
> +# ifndef __user
> +#  define __user
> +#  define __kernel
> +#  define __safe
> +#  define __force
> +#  define __chk_user_ptr(x) (void)0
> +# endif
> +
> +
> +#  define PARM_STR(VAR, INIT, PERM, DESC) \
> +               static char *VAR = INIT; \
> +               char *dgnc_##VAR; \
> +               module_param(VAR, charp, PERM); \
> +               MODULE_PARM_DESC(VAR, DESC);
> +
> +#  define PARM_INT(VAR, INIT, PERM, DESC) \
> +               static int VAR = INIT; \
> +               int dgnc_##VAR; \
> +               module_param(VAR, int, PERM); \
> +               MODULE_PARM_DESC(VAR, DESC);
> +
> +#  define PARM_ULONG(VAR, INIT, PERM, DESC) \
> +               static ulong VAR = INIT; \
> +               ulong dgnc_##VAR; \
> +               module_param(VAR, long, PERM); \
> +               MODULE_PARM_DESC(VAR, DESC);
> +
> +
> +
> +
> +
> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
> +
> +
> +
> +/* NOTHING YET */
> +
> +
> +
> +# else
> +
> +
> +
> +# error "this driver does not support anything below the 2.6.27 kernel series."
> +
> +
> +
> +# endif
> +
> +#endif /* ! __DGNC_KCOMPAT_H */
> diff --git a/drivers/staging/dgnc/dgnc_mgmt.c b/drivers/staging/dgnc/dgnc_mgmt.c
> new file mode 100644
> index 0000000..b8e4792
> --- /dev/null
> +++ b/drivers/staging/dgnc/dgnc_mgmt.c
> @@ -0,0 +1,313 @@
> +/*
> + * 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
> + * Neo and ClassicBoard based product lines.
> + *
> + ************************************************************************
> + * $Id: dgnc_mgmt.c,v 1.2 2010/12/14 20:08:29 markh Exp $
> + */
> +#include <linux/kernel.h>
> +#include <linux/version.h>
> +#include <linux/ctype.h>
> +#include <linux/sched.h>       /* For jiffies, task states */
> +#include <linux/interrupt.h>   /* For tasklet and interrupt structs/defines */
> +#include <linux/serial_reg.h>
> +#include <linux/termios.h>
> +#include <asm/uaccess.h>       /* For copy_from_user/copy_to_user */
> +
> +#include "dgnc_driver.h"
> +#include "dgnc_pci.h"
> +#include "dgnc_proc.h"
> +#include "dgnc_kcompat.h"      /* Kernel 2.4/2.6 compat includes */
> +#include "dgnc_mgmt.h"
> +#include "dpacompat.h"
> +
> +
> +/* Our "in use" variables, to enforce 1 open only */
> +static int dgnc_mgmt_in_use[MAXMGMTDEVICES];
> +
> +
> +/*
> + * dgnc_mgmt_open()
> + *
> + * Open the mgmt/downld/dpa device
> + */
> +int dgnc_mgmt_open(struct inode *inode, struct file *file)
> +{
> +       unsigned long lock_flags;
> +       unsigned int minor = iminor(inode);
> +
> +       DPR_MGMT(("dgnc_mgmt_open start.\n"));
> +
> +       DGNC_LOCK(dgnc_global_lock, lock_flags);
> +
> +       /* mgmt device */
> +       if (minor < MAXMGMTDEVICES) {
> +               /* Only allow 1 open at a time on mgmt device */
> +               if (dgnc_mgmt_in_use[minor]) {
> +                       DGNC_UNLOCK(dgnc_global_lock, lock_flags);
> +                       return (-EBUSY);
> +               }
> +               dgnc_mgmt_in_use[minor]++;
> +       }
> +       else {
> +               DGNC_UNLOCK(dgnc_global_lock, lock_flags);
> +               return (-ENXIO);
> +       }
> +
> +       DGNC_UNLOCK(dgnc_global_lock, lock_flags);
> +
> +       DPR_MGMT(("dgnc_mgmt_open finish.\n"));
> +
> +       return 0;
> +}
> +
> +
> +/*
> + * dgnc_mgmt_close()
> + *
> + * Open the mgmt/dpa device
> + */
> +int dgnc_mgmt_close(struct inode *inode, struct file *file)
> +{
> +       unsigned long lock_flags;
> +       unsigned int minor = iminor(inode);
> +
> +       DPR_MGMT(("dgnc_mgmt_close start.\n"));
> +
> +       DGNC_LOCK(dgnc_global_lock, lock_flags);
> +
> +       /* mgmt device */
> +       if (minor < MAXMGMTDEVICES) {
> +               if (dgnc_mgmt_in_use[minor]) {
> +                       dgnc_mgmt_in_use[minor] = 0;
> +               }
> +       }
> +       DGNC_UNLOCK(dgnc_global_lock, lock_flags);
> +
> +       DPR_MGMT(("dgnc_mgmt_close finish.\n"));
> +
> +       return 0;
> +}
> +
> +
> +/*
> + * dgnc_mgmt_ioctl()
> + *
> + * ioctl the mgmt/dpa device
> + */
> +#ifdef HAVE_UNLOCKED_IOCTL
> +long dgnc_mgmt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> +{
> +       struct inode *inode = file->f_dentry->d_inode;
> +#else
> +int dgnc_mgmt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
> +{
> +#endif
> +       unsigned long lock_flags;
> +       void __user *uarg = (void __user *) arg;
> +
> +       DPR_MGMT(("dgnc_mgmt_ioctl start.\n"));
> +
> +       switch (cmd) {
> +
> +       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;
> +
> +               DGNC_LOCK(dgnc_global_lock, lock_flags);
> +
> +               ddi.dinfo_nboards = dgnc_NumBoards;
> +               sprintf(ddi.dinfo_version, "%s", DG_PART);
> +
> +               DGNC_UNLOCK(dgnc_global_lock, lock_flags);
> +
> +               DPR_MGMT(("DIGI_GETDD returning numboards: %d version: %s\n",
> +                       ddi.dinfo_nboards, ddi.dinfo_version));
> +
> +               if (copy_to_user(uarg, &ddi, sizeof (ddi)))
> +                       return(-EFAULT);
> +
> +               break;
> +       }
> +
> +       case DIGI_GETBD:
> +       {
> +               int brd;
> +
> +               struct digi_info di;
> +
> +               if (copy_from_user(&brd, uarg, sizeof(int))) {
> +                       return(-EFAULT);
> +               }
> +
> +               DPR_MGMT(("DIGI_GETBD asking about board: %d\n", brd));
> +
> +               if ((brd < 0) || (brd > dgnc_NumBoards) || (dgnc_NumBoards == 0))
> +                       return (-ENODEV);
> +
> +               memset(&di, 0, sizeof(di));
> +
> +               di.info_bdnum = brd;
> +
> +               DGNC_LOCK(dgnc_Board[brd]->bd_lock, lock_flags);
> +
> +               di.info_bdtype = dgnc_Board[brd]->dpatype;
> +               di.info_bdstate = dgnc_Board[brd]->dpastatus;
> +               di.info_ioport = 0;
> +               di.info_physaddr = (ulong) dgnc_Board[brd]->membase;
> +               di.info_physsize = (ulong) dgnc_Board[brd]->membase - dgnc_Board[brd]->membase_end;
> +               if (dgnc_Board[brd]->state != BOARD_FAILED)
> +                       di.info_nports = dgnc_Board[brd]->nasync;
> +               else
> +                       di.info_nports = 0;
> +
> +               DGNC_UNLOCK(dgnc_Board[brd]->bd_lock, lock_flags);
> +
> +               DPR_MGMT(("DIGI_GETBD returning type: %x state: %x ports: %x size: %x\n",
> +                       di.info_bdtype, di.info_bdstate, di.info_nports, di.info_physsize));
> +
> +               if (copy_to_user(uarg, &di, sizeof (di)))
> +                       return (-EFAULT);
> +
> +               break;
> +       }
> +
> +       case DIGI_GET_NI_INFO:
> +       {
> +               struct channel_t *ch;
> +               struct ni_info ni;
> +               uchar mstat = 0;
> +               uint board = 0;
> +               uint channel = 0;
> +
> +               if (copy_from_user(&ni, uarg, sizeof(struct ni_info))) {
> +                       return(-EFAULT);
> +               }
> +
> +               DPR_MGMT(("DIGI_GETBD asking about board: %d channel: %d\n",
> +                       ni.board, ni.channel));
> +
> +               board = ni.board;
> +               channel = ni.channel;
> +
> +               /* Verify boundaries on board */
> +               if ((board < 0) || (board > dgnc_NumBoards) || (dgnc_NumBoards == 0))
> +                       return (-ENODEV);
> +
> +               /* Verify boundaries on channel */
> +               if ((channel < 0) || (channel > dgnc_Board[board]->nasync))
> +                       return (-ENODEV);
> +
> +               ch = dgnc_Board[board]->channels[channel];
> +
> +               if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +                       return (-ENODEV);
> +
> +               memset(&ni, 0, sizeof(ni));
> +               ni.board = board;
> +               ni.channel = channel;
> +
> +               DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +               mstat = (ch->ch_mostat | ch->ch_mistat);
> +
> +               if (mstat & UART_MCR_DTR) {
> +                       ni.mstat |= TIOCM_DTR;
> +                       ni.dtr = TIOCM_DTR;
> +               }
> +               if (mstat & UART_MCR_RTS) {
> +                       ni.mstat |= TIOCM_RTS;
> +                       ni.rts = TIOCM_RTS;
> +               }
> +               if (mstat & UART_MSR_CTS) {
> +                       ni.mstat |= TIOCM_CTS;
> +                       ni.cts = TIOCM_CTS;
> +               }
> +               if (mstat & UART_MSR_RI) {
> +                       ni.mstat |= TIOCM_RI;
> +                       ni.ri = TIOCM_RI;
> +               }
> +               if (mstat & UART_MSR_DCD) {
> +                       ni.mstat |= TIOCM_CD;
> +                       ni.dcd = TIOCM_CD;
> +               }
> +               if (mstat & UART_MSR_DSR)
> +                       ni.mstat |= TIOCM_DSR;
> +
> +               ni.iflag = ch->ch_c_iflag;
> +               ni.oflag = ch->ch_c_oflag;
> +               ni.cflag = ch->ch_c_cflag;
> +               ni.lflag = ch->ch_c_lflag;
> +
> +               if (ch->ch_digi.digi_flags & CTSPACE || ch->ch_c_cflag & CRTSCTS)
> +                       ni.hflow = 1;
> +               else
> +                       ni.hflow = 0;
> +
> +               if ((ch->ch_flags & CH_STOPI) || (ch->ch_flags & CH_FORCED_STOPI))
> +                       ni.recv_stopped = 1;
> +               else
> +                       ni.recv_stopped = 0;
> +
> +               if ((ch->ch_flags & CH_STOP) || (ch->ch_flags & CH_FORCED_STOP))
> +                       ni.xmit_stopped = 1;
> +               else
> +                       ni.xmit_stopped = 0;
> +
> +               ni.curtx = ch->ch_txcount;
> +               ni.currx = ch->ch_rxcount;
> +
> +               ni.baud = ch->ch_old_baud;
> +
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +               if (copy_to_user(uarg, &ni, sizeof(ni)))
> +                       return (-EFAULT);
> +
> +               break;
> +       }
> +
> +
> +       }
> +
> +       DPR_MGMT(("dgnc_mgmt_ioctl finish.\n"));
> +
> +       return 0;
> +}
> diff --git a/drivers/staging/dgnc/dgnc_mgmt.h b/drivers/staging/dgnc/dgnc_mgmt.h
> new file mode 100644
> index 0000000..a0d1338
> --- /dev/null
> +++ b/drivers/staging/dgnc/dgnc_mgmt.h
> @@ -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 __DGNC_MGMT_H
> +#define __DGNC_MGMT_H
> +
> +#define MAXMGMTDEVICES 8
> +
> +int dgnc_mgmt_open(struct inode *inode, struct file *file);
> +int dgnc_mgmt_close(struct inode *inode, struct file *file);
> +
> +#ifdef HAVE_UNLOCKED_IOCTL
> +long dgnc_mgmt_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
> +#else
> +int dgnc_mgmt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
> +#endif
> +
> +#endif
> +
> diff --git a/drivers/staging/dgnc/dgnc_neo.c b/drivers/staging/dgnc/dgnc_neo.c
> new file mode 100644
> index 0000000..503db8f
> --- /dev/null
> +++ b/drivers/staging/dgnc/dgnc_neo.c
> @@ -0,0 +1,1977 @@
> +/*
> + * 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: dgnc_neo.c,v 1.1.1.1 2009/05/20 12:19:19 markh Exp $
> + */
> +
> +
> +#include <linux/kernel.h>
> +#include <linux/version.h>
> +#include <linux/sched.h>       /* For jiffies, task states */
> +#include <linux/interrupt.h>    /* For tasklet and interrupt structs/defines */
> +#include <linux/delay.h>       /* For udelay */
> +#include <asm/io.h>            /* For read[bwl]/write[bwl] */
> +#include <linux/serial.h>      /* For struct async_serial */
> +#include <linux/serial_reg.h>  /* For the various UART offsets */
> +
> +#include "dgnc_driver.h"       /* Driver main header file */
> +#include "dgnc_neo.h"          /* Our header file */
> +#include "dgnc_tty.h"
> +#include "dgnc_trace.h"
> +
> +static inline void neo_parse_lsr(struct board_t *brd, uint port);
> +static inline void neo_parse_isr(struct board_t *brd, uint port);
> +static void neo_copy_data_from_uart_to_queue(struct channel_t *ch);
> +static inline void neo_clear_break(struct channel_t *ch, int force);
> +static inline void neo_set_cts_flow_control(struct channel_t *ch);
> +static inline void neo_set_rts_flow_control(struct channel_t *ch);
> +static inline void neo_set_ixon_flow_control(struct channel_t *ch);
> +static inline void neo_set_ixoff_flow_control(struct channel_t *ch);
> +static inline void neo_set_no_output_flow_control(struct channel_t *ch);
> +static inline void neo_set_no_input_flow_control(struct channel_t *ch);
> +static inline void neo_set_new_start_stop_chars(struct channel_t *ch);
> +static void neo_parse_modem(struct channel_t *ch, uchar signals);
> +static void neo_tasklet(unsigned long data);
> +static void neo_vpd(struct board_t *brd);
> +static void neo_uart_init(struct channel_t *ch);
> +static void neo_uart_off(struct channel_t *ch);
> +static int neo_drain(struct tty_struct *tty, uint seconds);
> +static void neo_param(struct tty_struct *tty);
> +static void neo_assert_modem_signals(struct channel_t *ch);
> +static void neo_flush_uart_write(struct channel_t *ch);
> +static void neo_flush_uart_read(struct channel_t *ch);
> +static void neo_disable_receiver(struct channel_t *ch);
> +static void neo_enable_receiver(struct channel_t *ch);
> +static void neo_send_break(struct channel_t *ch, int msecs);
> +static void neo_send_start_character(struct channel_t *ch);
> +static void neo_send_stop_character(struct channel_t *ch);
> +static void neo_copy_data_from_queue_to_uart(struct channel_t *ch);
> +static uint neo_get_uart_bytes_left(struct channel_t *ch);
> +static void neo_send_immediate_char(struct channel_t *ch, unsigned char c);
> +static irqreturn_t neo_intr(int irq, void *voidbrd);
> +
> +
> +struct board_ops dgnc_neo_ops = {
> +       .tasklet =                      neo_tasklet,
> +       .intr =                         neo_intr,
> +       .uart_init =                    neo_uart_init,
> +       .uart_off =                     neo_uart_off,
> +       .drain =                        neo_drain,
> +       .param =                        neo_param,
> +       .vpd =                          neo_vpd,
> +       .assert_modem_signals =         neo_assert_modem_signals,
> +       .flush_uart_write =             neo_flush_uart_write,
> +       .flush_uart_read =              neo_flush_uart_read,
> +       .disable_receiver =             neo_disable_receiver,
> +       .enable_receiver =              neo_enable_receiver,
> +       .send_break =                   neo_send_break,
> +       .send_start_character =         neo_send_start_character,
> +       .send_stop_character =          neo_send_stop_character,
> +       .copy_data_from_queue_to_uart = neo_copy_data_from_queue_to_uart,
> +       .get_uart_bytes_left =          neo_get_uart_bytes_left,
> +       .send_immediate_char =          neo_send_immediate_char
> +};
> +
> +static uint dgnc_offset_table[8] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
> +
> +
> +/*
> + * This function allows calls to ensure that all outstanding
> + * PCI writes have been completed, by doing a PCI read against
> + * a non-destructive, read-only location on the Neo card.
> + *
> + * In this case, we are reading the DVID (Read-only Device Identification)
> + * value of the Neo card.
> + */
> +static inline void neo_pci_posting_flush(struct board_t *bd)
> +{
> +       readb(bd->re_map_membase + 0x8D);
> +}
> +
> +static inline void neo_set_cts_flow_control(struct channel_t *ch)
> +{
> +       uchar ier = readb(&ch->ch_neo_uart->ier);
> +       uchar efr = readb(&ch->ch_neo_uart->efr);
> +
> +
> +       DPR_PARAM(("Setting CTSFLOW\n"));
> +
> +       /* Turn on auto CTS flow control */
> +#if 1
> +       ier |= (UART_17158_IER_CTSDSR);
> +#else
> +       ier &= ~(UART_17158_IER_CTSDSR);
> +#endif
> +
> +       efr |= (UART_17158_EFR_ECB | UART_17158_EFR_CTSDSR);
> +
> +       /* Turn off auto Xon flow control */
> +       efr &= ~(UART_17158_EFR_IXON);
> +
> +       /* Why? Becuz Exar's spec says we have to zero it out before setting it */
> +       writeb(0, &ch->ch_neo_uart->efr);
> +
> +       /* Turn on UART enhanced bits */
> +       writeb(efr, &ch->ch_neo_uart->efr);
> +
> +       /* Turn on table D, with 8 char hi/low watermarks */
> +       writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_4DELAY), &ch->ch_neo_uart->fctr);
> +
> +       /* Feed the UART our trigger levels */
> +       writeb(8, &ch->ch_neo_uart->tfifo);
> +       ch->ch_t_tlevel = 8;
> +
> +       writeb(ier, &ch->ch_neo_uart->ier);
> +
> +       neo_pci_posting_flush(ch->ch_bd);
> +}
> +
> +
> +static inline void neo_set_rts_flow_control(struct channel_t *ch)
> +{
> +       uchar ier = readb(&ch->ch_neo_uart->ier);
> +       uchar efr = readb(&ch->ch_neo_uart->efr);
> +
> +       DPR_PARAM(("Setting RTSFLOW\n"));
> +
> +       /* Turn on auto RTS flow control */
> +#if 1
> +       ier |= (UART_17158_IER_RTSDTR);
> +#else
> +       ier &= ~(UART_17158_IER_RTSDTR);
> +#endif
> +       efr |= (UART_17158_EFR_ECB | UART_17158_EFR_RTSDTR);
> +
> +       /* Turn off auto Xoff flow control */
> +       ier &= ~(UART_17158_IER_XOFF);
> +       efr &= ~(UART_17158_EFR_IXOFF);
> +
> +       /* Why? Becuz Exar's spec says we have to zero it out before setting it */
> +       writeb(0, &ch->ch_neo_uart->efr);
> +
> +       /* Turn on UART enhanced bits */
> +       writeb(efr, &ch->ch_neo_uart->efr);
> +
> +       writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_4DELAY), &ch->ch_neo_uart->fctr);
> +       ch->ch_r_watermark = 4;
> +
> +       writeb(32, &ch->ch_neo_uart->rfifo);
> +       ch->ch_r_tlevel = 32;
> +
> +       writeb(ier, &ch->ch_neo_uart->ier);
> +
> +       /*
> +        * From the Neo UART spec sheet:
> +        * The auto RTS/DTR function must be started by asserting
> +        * RTS/DTR# output pin (MCR bit-0 or 1 to logic 1 after
> +        * it is enabled.
> +        */
> +       ch->ch_mostat |= (UART_MCR_RTS);
> +
> +       neo_pci_posting_flush(ch->ch_bd);
> +}
> +
> +
> +static inline void neo_set_ixon_flow_control(struct channel_t *ch)
> +{
> +       uchar ier = readb(&ch->ch_neo_uart->ier);
> +       uchar efr = readb(&ch->ch_neo_uart->efr);
> +
> +       DPR_PARAM(("Setting IXON FLOW\n"));
> +
> +       /* Turn off auto CTS flow control */
> +       ier &= ~(UART_17158_IER_CTSDSR);
> +       efr &= ~(UART_17158_EFR_CTSDSR);
> +
> +       /* Turn on auto Xon flow control */
> +       efr |= (UART_17158_EFR_ECB | UART_17158_EFR_IXON);
> +
> +       /* Why? Becuz Exar's spec says we have to zero it out before setting it */
> +       writeb(0, &ch->ch_neo_uart->efr);
> +
> +       /* Turn on UART enhanced bits */
> +       writeb(efr, &ch->ch_neo_uart->efr);
> +
> +       writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr);
> +       ch->ch_r_watermark = 4;
> +
> +       writeb(32, &ch->ch_neo_uart->rfifo);
> +       ch->ch_r_tlevel = 32;
> +
> +       /* Tell UART what start/stop chars it should be looking for */
> +       writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1);
> +       writeb(0, &ch->ch_neo_uart->xonchar2);
> +
> +       writeb(ch->ch_stopc, &ch->ch_neo_uart->xoffchar1);
> +       writeb(0, &ch->ch_neo_uart->xoffchar2);
> +
> +       writeb(ier, &ch->ch_neo_uart->ier);
> +
> +       neo_pci_posting_flush(ch->ch_bd);
> +}
> +
> +
> +static inline void neo_set_ixoff_flow_control(struct channel_t *ch)
> +{
> +       uchar ier = readb(&ch->ch_neo_uart->ier);
> +       uchar efr = readb(&ch->ch_neo_uart->efr);
> +
> +       DPR_PARAM(("Setting IXOFF FLOW\n"));
> +
> +       /* Turn off auto RTS flow control */
> +       ier &= ~(UART_17158_IER_RTSDTR);
> +       efr &= ~(UART_17158_EFR_RTSDTR);
> +
> +       /* Turn on auto Xoff flow control */
> +       ier |= (UART_17158_IER_XOFF);
> +       efr |= (UART_17158_EFR_ECB | UART_17158_EFR_IXOFF);
> +
> +       /* Why? Becuz Exar's spec says we have to zero it out before setting it */
> +       writeb(0, &ch->ch_neo_uart->efr);
> +
> +       /* Turn on UART enhanced bits */
> +       writeb(efr, &ch->ch_neo_uart->efr);
> +
> +       /* Turn on table D, with 8 char hi/low watermarks */
> +       writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr);
> +
> +       writeb(8, &ch->ch_neo_uart->tfifo);
> +        ch->ch_t_tlevel = 8;
> +
> +       /* Tell UART what start/stop chars it should be looking for */
> +       writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1);
> +       writeb(0, &ch->ch_neo_uart->xonchar2);
> +
> +       writeb(ch->ch_stopc, &ch->ch_neo_uart->xoffchar1);
> +       writeb(0, &ch->ch_neo_uart->xoffchar2);
> +
> +       writeb(ier, &ch->ch_neo_uart->ier);
> +
> +       neo_pci_posting_flush(ch->ch_bd);
> +}
> +
> +
> +static inline void neo_set_no_input_flow_control(struct channel_t *ch)
> +{
> +       uchar ier = readb(&ch->ch_neo_uart->ier);
> +       uchar efr = readb(&ch->ch_neo_uart->efr);
> +
> +       DPR_PARAM(("Unsetting Input FLOW\n"));
> +
> +       /* Turn off auto RTS flow control */
> +       ier &= ~(UART_17158_IER_RTSDTR);
> +       efr &= ~(UART_17158_EFR_RTSDTR);
> +
> +       /* Turn off auto Xoff flow control */
> +       ier &= ~(UART_17158_IER_XOFF);
> +       if (ch->ch_c_iflag & IXON)
> +               efr &= ~(UART_17158_EFR_IXOFF);
> +       else
> +               efr &= ~(UART_17158_EFR_ECB | UART_17158_EFR_IXOFF);
> +
> +
> +       /* Why? Becuz Exar's spec says we have to zero it out before setting it */
> +       writeb(0, &ch->ch_neo_uart->efr);
> +
> +       /* Turn on UART enhanced bits */
> +       writeb(efr, &ch->ch_neo_uart->efr);
> +
> +       /* Turn on table D, with 8 char hi/low watermarks */
> +       writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr);
> +
> +       ch->ch_r_watermark = 0;
> +
> +       writeb(16, &ch->ch_neo_uart->tfifo);
> +        ch->ch_t_tlevel = 16;
> +
> +       writeb(16, &ch->ch_neo_uart->rfifo);
> +        ch->ch_r_tlevel = 16;
> +
> +       writeb(ier, &ch->ch_neo_uart->ier);
> +
> +       neo_pci_posting_flush(ch->ch_bd);
> +}
> +
> +
> +static inline void neo_set_no_output_flow_control(struct channel_t *ch)
> +{
> +       uchar ier = readb(&ch->ch_neo_uart->ier);
> +       uchar efr = readb(&ch->ch_neo_uart->efr);
> +
> +       DPR_PARAM(("Unsetting Output FLOW\n"));
> +
> +       /* Turn off auto CTS flow control */
> +       ier &= ~(UART_17158_IER_CTSDSR);
> +       efr &= ~(UART_17158_EFR_CTSDSR);
> +
> +       /* Turn off auto Xon flow control */
> +       if (ch->ch_c_iflag & IXOFF)
> +               efr &= ~(UART_17158_EFR_IXON);
> +       else
> +               efr &= ~(UART_17158_EFR_ECB | UART_17158_EFR_IXON);
> +
> +       /* Why? Becuz Exar's spec says we have to zero it out before setting it */
> +       writeb(0, &ch->ch_neo_uart->efr);
> +
> +       /* Turn on UART enhanced bits */
> +       writeb(efr, &ch->ch_neo_uart->efr);
> +
> +       /* Turn on table D, with 8 char hi/low watermarks */
> +       writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr);
> +
> +       ch->ch_r_watermark = 0;
> +
> +       writeb(16, &ch->ch_neo_uart->tfifo);
> +        ch->ch_t_tlevel = 16;
> +
> +       writeb(16, &ch->ch_neo_uart->rfifo);
> +        ch->ch_r_tlevel = 16;
> +
> +       writeb(ier, &ch->ch_neo_uart->ier);
> +
> +       neo_pci_posting_flush(ch->ch_bd);
> +}
> +
> +
> +/* change UARTs start/stop chars */
> +static inline void neo_set_new_start_stop_chars(struct channel_t *ch)
> +{
> +
> +       /* if hardware flow control is set, then skip this whole thing */
> +       if (ch->ch_digi.digi_flags & (CTSPACE | RTSPACE) || ch->ch_c_cflag & CRTSCTS)
> +               return;
> +
> +       DPR_PARAM(("In new start stop chars\n"));
> +
> +       /* Tell UART what start/stop chars it should be looking for */
> +       writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1);
> +       writeb(0, &ch->ch_neo_uart->xonchar2);
> +
> +       writeb(ch->ch_stopc, &ch->ch_neo_uart->xoffchar1);
> +       writeb(0, &ch->ch_neo_uart->xoffchar2);
> +
> +       neo_pci_posting_flush(ch->ch_bd);
> +}
> +
> +
> +/*
> + * No locks are assumed to be held when calling this function.
> + */
> +static inline void neo_clear_break(struct channel_t *ch, int force)
> +{
> +       ulong lock_flags;
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       /* Bail if we aren't currently sending a break. */
> +       if (!ch->ch_stop_sending_break) {
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               return;
> +       }
> +
> +       /* Turn break off, and unset some variables */
> +       if (ch->ch_flags & CH_BREAK_SENDING) {
> +               if ((jiffies >= ch->ch_stop_sending_break) || force) {
> +                       uchar temp = readb(&ch->ch_neo_uart->lcr);
> +                       writeb((temp & ~UART_LCR_SBC), &ch->ch_neo_uart->lcr);
> +                       neo_pci_posting_flush(ch->ch_bd);
> +                       ch->ch_flags &= ~(CH_BREAK_SENDING);
> +                       ch->ch_stop_sending_break = 0;
> +                       DPR_IOCTL(("Finishing UART_LCR_SBC! finished: %lx\n", jiffies));
> +                }
> +        }
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +}
> +
> +
> +/*
> + * Parse the ISR register.
> + */
> +static inline void neo_parse_isr(struct board_t *brd, uint port)
> +{
> +       struct channel_t *ch;
> +       uchar isr;
> +       uchar cause;
> +       ulong lock_flags;
> +
> +       if (!brd || brd->magic != DGNC_BOARD_MAGIC)
> +               return;
> +
> +       if (port > brd->maxports)
> +               return;
> +
> +       ch = brd->channels[port];
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return;
> +
> +       /* Here we try to figure out what caused the interrupt to happen */
> +       while (1) {
> +
> +               isr = readb(&ch->ch_neo_uart->isr_fcr);
> +
> +               /* Bail if no pending interrupt */
> +               if (isr & UART_IIR_NO_INT)  {
> +                       break;
> +               }
> +
> +               /*
> +                * Yank off the upper 2 bits, which just show that the FIFO's are enabled.
> +                */
> +               isr &= ~(UART_17158_IIR_FIFO_ENABLED);
> +
> +               DPR_INTR(("%s:%d isr: %x\n", __FILE__, __LINE__, isr));
> +
> +               if (isr & (UART_17158_IIR_RDI_TIMEOUT | UART_IIR_RDI)) {
> +                       /* Read data from uart -> queue */
> +                       brd->intr_rx++;
> +                       ch->ch_intr_rx++;
> +                       neo_copy_data_from_uart_to_queue(ch);
> +
> +                       /* Call our tty layer to enforce queue flow control if needed. */
> +                       DGNC_LOCK(ch->ch_lock, lock_flags);
> +                       dgnc_check_queue_flow_control(ch);
> +                       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               }
> +
> +               if (isr & UART_IIR_THRI) {
> +                       brd->intr_tx++;
> +                       ch->ch_intr_tx++;
> +                       /* Transfer data (if any) from Write Queue -> UART. */
> +                       DGNC_LOCK(ch->ch_lock, lock_flags);
> +                       ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
> +                       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +                       neo_copy_data_from_queue_to_uart(ch);
> +               }
> +
> +               if (isr & UART_17158_IIR_XONXOFF) {
> +                       cause = readb(&ch->ch_neo_uart->xoffchar1);
> +
> +                       DPR_INTR(("Port %d. Got ISR_XONXOFF: cause:%x\n", port, cause));
> +
> +                       /*
> +                        * Since the UART detected either an XON or
> +                        * XOFF match, we need to figure out which
> +                        * one it was, so we can suspend or resume data flow.
> +                        */
> +                       if (cause == UART_17158_XON_DETECT) {
> +                               /* Is output stopped right now, if so, resume it */
> +                               if (brd->channels[port]->ch_flags & CH_STOP) {
> +                                       DGNC_LOCK(ch->ch_lock, lock_flags);
> +                                       ch->ch_flags &= ~(CH_STOP);
> +                                       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +                               }
> +                               DPR_INTR(("Port %d. XON detected in incoming data\n", port));
> +                       }
> +                       else if (cause == UART_17158_XOFF_DETECT) {
> +                               if (!(brd->channels[port]->ch_flags & CH_STOP)) {
> +                                       DGNC_LOCK(ch->ch_lock, lock_flags);
> +                                       ch->ch_flags |= CH_STOP;
> +                                       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +                                       DPR_INTR(("Setting CH_STOP\n"));
> +                               }
> +                               DPR_INTR(("Port: %d. XOFF detected in incoming data\n", port));
> +                       }
> +               }
> +
> +               if (isr & UART_17158_IIR_HWFLOW_STATE_CHANGE) {
> +                       /*
> +                        * If we get here, this means the hardware is doing auto flow control.
> +                        * Check to see whether RTS/DTR or CTS/DSR caused this interrupt.
> +                        */
> +                       brd->intr_modem++;
> +                       ch->ch_intr_modem++;
> +                       cause = readb(&ch->ch_neo_uart->mcr);
> +                       /* Which pin is doing auto flow? RTS or DTR? */
> +                       if ((cause & 0x4) == 0) {
> +                               if (cause & UART_MCR_RTS) {
> +                                       DGNC_LOCK(ch->ch_lock, lock_flags);
> +                                       ch->ch_mostat |= UART_MCR_RTS;
> +                                       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +                               }
> +                               else {
> +                                       DGNC_LOCK(ch->ch_lock, lock_flags);
> +                                       ch->ch_mostat &= ~(UART_MCR_RTS);
> +                                       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +                               }
> +                       } else {
> +                               if (cause & UART_MCR_DTR) {
> +                                       DGNC_LOCK(ch->ch_lock, lock_flags);
> +                                       ch->ch_mostat |= UART_MCR_DTR;
> +                                       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +                               }
> +                               else {
> +                                       DGNC_LOCK(ch->ch_lock, lock_flags);
> +                                       ch->ch_mostat &= ~(UART_MCR_DTR);
> +                                       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +                               }
> +                       }
> +               }
> +
> +               /* Parse any modem signal changes */
> +               DPR_INTR(("MOD_STAT: sending to parse_modem_sigs\n"));
> +               neo_parse_modem(ch, readb(&ch->ch_neo_uart->msr));
> +       }
> +}
> +
> +
> +static inline void neo_parse_lsr(struct board_t *brd, uint port)
> +{
> +       struct channel_t *ch;
> +       int linestatus;
> +       ulong lock_flags;
> +
> +       if (!brd)
> +               return;
> +
> +       if (brd->magic != DGNC_BOARD_MAGIC)
> +               return;
> +
> +       if (port > brd->maxports)
> +               return;
> +
> +       ch = brd->channels[port];
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return;
> +
> +       linestatus = readb(&ch->ch_neo_uart->lsr);
> +
> +       DPR_INTR(("%s:%d port: %d linestatus: %x\n", __FILE__, __LINE__, port, linestatus));
> +
> +       ch->ch_cached_lsr |= linestatus;
> +
> +       if (ch->ch_cached_lsr & UART_LSR_DR) {
> +               brd->intr_rx++;
> +               ch->ch_intr_rx++;
> +               /* Read data from uart -> queue */
> +               neo_copy_data_from_uart_to_queue(ch);
> +               DGNC_LOCK(ch->ch_lock, lock_flags);
> +               dgnc_check_queue_flow_control(ch);
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +       }
> +
> +       /*
> +        * This is a special flag. It indicates that at least 1
> +        * RX error (parity, framing, or break) has happened.
> +        * Mark this in our struct, which will tell me that I have
> +        *to do the special RX+LSR read for this FIFO load.
> +        */
> +       if (linestatus & UART_17158_RX_FIFO_DATA_ERROR) {
> +               DPR_INTR(("%s:%d Port: %d Got an RX error, need to parse LSR\n",
> +                       __FILE__, __LINE__, port));
> +       }
> +
> +       /*
> +        * The next 3 tests should *NOT* happen, as the above test
> +        * should encapsulate all 3... At least, thats what Exar says.
> +        */
> +
> +       if (linestatus & UART_LSR_PE) {
> +               ch->ch_err_parity++;
> +               DPR_INTR(("%s:%d Port: %d. PAR ERR!\n", __FILE__, __LINE__, port));
> +       }
> +
> +       if (linestatus & UART_LSR_FE) {
> +               ch->ch_err_frame++;
> +               DPR_INTR(("%s:%d Port: %d. FRM ERR!\n", __FILE__, __LINE__, port));
> +       }
> +
> +       if (linestatus & UART_LSR_BI) {
> +               ch->ch_err_break++;
> +               DPR_INTR(("%s:%d Port: %d. BRK INTR!\n", __FILE__, __LINE__, port));
> +       }
> +
> +       if (linestatus & UART_LSR_OE) {
> +               /*
> +                * Rx Oruns. Exar says that an orun will NOT corrupt
> +                * the FIFO. It will just replace the holding register
> +                * with this new data byte. So basically just ignore this.
> +                * Probably we should eventually have an orun stat in our driver...
> +                */
> +               ch->ch_err_overrun++;
> +               DPR_INTR(("%s:%d Port: %d. Rx Overrun!\n", __FILE__, __LINE__, port));
> +       }
> +
> +       if (linestatus & UART_LSR_THRE) {
> +               brd->intr_tx++;
> +               ch->ch_intr_tx++;
> +               DGNC_LOCK(ch->ch_lock, lock_flags);
> +               ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +               /* Transfer data (if any) from Write Queue -> UART. */
> +               neo_copy_data_from_queue_to_uart(ch);
> +       }
> +       else if (linestatus & UART_17158_TX_AND_FIFO_CLR) {
> +               brd->intr_tx++;
> +               ch->ch_intr_tx++;
> +               DGNC_LOCK(ch->ch_lock, lock_flags);
> +               ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +               /* Transfer data (if any) from Write Queue -> UART. */
> +               neo_copy_data_from_queue_to_uart(ch);
> +       }
> +}
> +
> +
> +/*
> + * neo_param()
> + * Send any/all changes to the line to the UART.
> + */
> +static void neo_param(struct tty_struct *tty)
> +{
> +       uchar lcr = 0;
> +       uchar uart_lcr = 0;
> +       uchar ier = 0;
> +       uchar uart_ier = 0;
> +        uint baud = 9600;
> +       int quot = 0;
> +        struct board_t *bd;
> +       struct channel_t *ch;
> +        struct un_t   *un;
> +
> +       if (!tty || tty->magic != TTY_MAGIC) {
> +               return;
> +       }
> +
> +       un = (struct un_t *) tty->driver_data;
> +       if (!un || un->magic != DGNC_UNIT_MAGIC) {
> +               return;
> +       }
> +
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) {
> +               return;
> +       }
> +
> +       bd = ch->ch_bd;
> +       if (!bd || bd->magic != DGNC_BOARD_MAGIC) {
> +               return;
> +       }
> +
> +       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));
> +
> +       /*
> +        * If baud rate is zero, flush queues, and set mval to drop DTR.
> +        */
> +       if ((ch->ch_c_cflag & (CBAUD)) == 0) {
> +               ch->ch_r_head = ch->ch_r_tail = 0;
> +               ch->ch_e_head = ch->ch_e_tail = 0;
> +               ch->ch_w_head = ch->ch_w_tail = 0;
> +
> +               neo_flush_uart_write(ch);
> +               neo_flush_uart_read(ch);
> +
> +               /* The baudrate is B0 so all modem lines are to be dropped. */
> +               ch->ch_flags |= (CH_BAUD0);
> +               ch->ch_mostat &= ~(UART_MCR_RTS | UART_MCR_DTR);
> +               neo_assert_modem_signals(ch);
> +               ch->ch_old_baud = 0;
> +               return;
> +
> +       } else if (ch->ch_custom_speed) {
> +
> +               baud = ch->ch_custom_speed;
> +               /* Handle transition from B0 */
> +               if (ch->ch_flags & CH_BAUD0) {
> +                       ch->ch_flags &= ~(CH_BAUD0);
> +
> +                       /*
> +                        * Bring back up RTS and DTR...
> +                        * Also handle RTS or DTR toggle if set.
> +                        */
> +                       if (!(ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE))
> +                               ch->ch_mostat |= (UART_MCR_RTS);
> +                       if (!(ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE))
> +                               ch->ch_mostat |= (UART_MCR_DTR);
> +               }
> +       } else {
> +               int iindex = 0;
> +               int jindex = 0;
> +
> +               ulong bauds[4][16] = {
> +                       { /* slowbaud */
> +                               0,      50,     75,     110,
> +                               134,    150,    200,    300,
> +                               600,    1200,   1800,   2400,
> +                               4800,   9600,   19200,  38400 },
> +                       { /* slowbaud & CBAUDEX */
> +                               0,      57600,  115200, 230400,
> +                               460800, 150,    200,    921600,
> +                               600,    1200,   1800,   2400,
> +                               4800,   9600,   19200,  38400 },
> +                       { /* fastbaud */
> +                               0,      57600,   76800, 115200,
> +                               131657, 153600, 230400, 460800,
> +                               921600, 1200,   1800,   2400,
> +                               4800,   9600,   19200,  38400 },
> +                       { /* fastbaud & CBAUDEX */
> +                               0,      57600,  115200, 230400,
> +                               460800, 150,    200,    921600,
> +                               600,    1200,   1800,   2400,
> +                               4800,   9600,   19200,  38400 }
> +               };
> +
> +               /* Only use the TXPrint baud rate if the terminal unit is NOT open */
> +               if (!(ch->ch_tun.un_flags & UN_ISOPEN) && (un->un_type == DGNC_PRINT))
> +                       baud = C_BAUD(ch->ch_pun.un_tty) & 0xff;
> +               else
> +                       baud = C_BAUD(ch->ch_tun.un_tty) & 0xff;
> +
> +               if (ch->ch_c_cflag & CBAUDEX)
> +                       iindex = 1;
> +
> +               if (ch->ch_digi.digi_flags & DIGI_FAST)
> +                       iindex += 2;
> +
> +               jindex = baud;
> +
> +               if ((iindex >= 0) && (iindex < 4) && (jindex >= 0) && (jindex < 16)) {
> +                       baud = bauds[iindex][jindex];
> +               } else {
> +                       DPR_IOCTL(("baud indices were out of range (%d)(%d)",
> +                               iindex, jindex));
> +                       baud = 0;
> +               }
> +
> +               if (baud == 0)
> +                       baud = 9600;
> +
> +               /* Handle transition from B0 */
> +               if (ch->ch_flags & CH_BAUD0) {
> +                       ch->ch_flags &= ~(CH_BAUD0);
> +
> +                       /*
> +                        * Bring back up RTS and DTR...
> +                        * Also handle RTS or DTR toggle if set.
> +                        */
> +                       if (!(ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE))
> +                               ch->ch_mostat |= (UART_MCR_RTS);
> +                       if (!(ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE))
> +                               ch->ch_mostat |= (UART_MCR_DTR);
> +               }
> +       }
> +
> +       if (ch->ch_c_cflag & PARENB) {
> +               lcr |= UART_LCR_PARITY;
> +       }
> +
> +       if (!(ch->ch_c_cflag & PARODD)) {
> +               lcr |= UART_LCR_EPAR;
> +       }
> +
> +       /*
> +        * Not all platforms support mark/space parity,
> +        * so this will hide behind an ifdef.
> +        */
> +#ifdef CMSPAR
> +       if (ch->ch_c_cflag & CMSPAR)
> +               lcr |= UART_LCR_SPAR;
> +#endif
> +
> +       if (ch->ch_c_cflag & CSTOPB)
> +               lcr |= UART_LCR_STOP;
> +
> +       switch (ch->ch_c_cflag & CSIZE) {
> +       case CS5:
> +               lcr |= UART_LCR_WLEN5;
> +               break;
> +       case CS6:
> +               lcr |= UART_LCR_WLEN6;
> +               break;
> +       case CS7:
> +               lcr |= UART_LCR_WLEN7;
> +               break;
> +       case CS8:
> +       default:
> +               lcr |= UART_LCR_WLEN8;
> +               break;
> +       }
> +
> +       ier = uart_ier = readb(&ch->ch_neo_uart->ier);
> +       uart_lcr = readb(&ch->ch_neo_uart->lcr);
> +
> +       if (baud == 0)
> +               baud = 9600;
> +
> +       quot = ch->ch_bd->bd_dividend / baud;
> +
> +       if (quot != 0 && ch->ch_old_baud != baud) {
> +               ch->ch_old_baud = baud;
> +               writeb(UART_LCR_DLAB, &ch->ch_neo_uart->lcr);
> +               writeb((quot & 0xff), &ch->ch_neo_uart->txrx);
> +               writeb((quot >> 8), &ch->ch_neo_uart->ier);
> +               writeb(lcr, &ch->ch_neo_uart->lcr);
> +        }
> +
> +       if (uart_lcr != lcr)
> +               writeb(lcr, &ch->ch_neo_uart->lcr);
> +
> +       if (ch->ch_c_cflag & CREAD) {
> +               ier |= (UART_IER_RDI | UART_IER_RLSI);
> +       }
> +       else {
> +               ier &= ~(UART_IER_RDI | UART_IER_RLSI);
> +       }
> +
> +       /*
> +        * Have the UART interrupt on modem signal changes ONLY when
> +        * we are in hardware flow control mode, or CLOCAL/FORCEDCD is not set.
> +        */
> +       if ((ch->ch_digi.digi_flags & CTSPACE) || (ch->ch_digi.digi_flags & RTSPACE) ||
> +               (ch->ch_c_cflag & CRTSCTS) || !(ch->ch_digi.digi_flags & DIGI_FORCEDCD) ||
> +               !(ch->ch_c_cflag & CLOCAL))
> +       {
> +               ier |= UART_IER_MSI;
> +       }
> +       else {
> +               ier &= ~UART_IER_MSI;
> +       }
> +
> +       ier |= UART_IER_THRI;
> +
> +       if (ier != uart_ier)
> +               writeb(ier, &ch->ch_neo_uart->ier);
> +
> +       /* Set new start/stop chars */
> +       neo_set_new_start_stop_chars(ch);
> +
> +       if (ch->ch_digi.digi_flags & CTSPACE || ch->ch_c_cflag & CRTSCTS) {
> +               neo_set_cts_flow_control(ch);
> +       }
> +       else if (ch->ch_c_iflag & IXON) {
> +               /* If start/stop is set to disable, then we should disable flow control */
> +               if ((ch->ch_startc == _POSIX_VDISABLE) || (ch->ch_stopc == _POSIX_VDISABLE))
> +                       neo_set_no_output_flow_control(ch);
> +               else
> +                       neo_set_ixon_flow_control(ch);
> +       }
> +       else {
> +               neo_set_no_output_flow_control(ch);
> +       }
> +
> +       if (ch->ch_digi.digi_flags & RTSPACE || ch->ch_c_cflag & CRTSCTS) {
> +               neo_set_rts_flow_control(ch);
> +       }
> +       else if (ch->ch_c_iflag & IXOFF) {
> +               /* If start/stop is set to disable, then we should disable flow control */
> +               if ((ch->ch_startc == _POSIX_VDISABLE) || (ch->ch_stopc == _POSIX_VDISABLE))
> +                       neo_set_no_input_flow_control(ch);
> +               else
> +                       neo_set_ixoff_flow_control(ch);
> +       }
> +       else {
> +               neo_set_no_input_flow_control(ch);
> +       }
> +
> +       /*
> +        * Adjust the RX FIFO Trigger level if baud is less than 9600.
> +        * Not exactly elegant, but this is needed because of the Exar chip's
> +        * delay on firing off the RX FIFO interrupt on slower baud rates.
> +        */
> +       if (baud < 9600) {
> +               writeb(1, &ch->ch_neo_uart->rfifo);
> +               ch->ch_r_tlevel = 1;
> +       }
> +
> +       neo_assert_modem_signals(ch);
> +
> +       /* Get current status of the modem signals now */
> +       neo_parse_modem(ch, readb(&ch->ch_neo_uart->msr));
> +}
> +
> +
> +/*
> + * Our board poller function.
> + */
> +static void neo_tasklet(unsigned long data)
> +{
> +        struct board_t *bd = (struct board_t *) data;
> +       struct channel_t *ch;
> +       ulong  lock_flags;
> +       int i;
> +       int state = 0;
> +       int ports = 0;
> +
> +       if (!bd || bd->magic != DGNC_BOARD_MAGIC) {
> +               APR(("poll_tasklet() - NULL or bad bd.\n"));
> +               return;
> +       }
> +
> +       /* Cache a couple board values */
> +       DGNC_LOCK(bd->bd_lock, lock_flags);
> +       state = bd->state;
> +       ports = bd->nasync;
> +       DGNC_UNLOCK(bd->bd_lock, lock_flags);
> +
> +       /*
> +        * Do NOT allow the interrupt routine to read the intr registers
> +        * Until we release this lock.
> +        */
> +       DGNC_LOCK(bd->bd_intr_lock, lock_flags);
> +
> +       /*
> +        * If board is ready, parse deeper to see if there is anything to do.
> +        */
> +       if ((state == BOARD_READY) && (ports > 0)) {
> +               /* Loop on each port */
> +               for (i = 0; i < ports; i++) {
> +                       ch = bd->channels[i];
> +
> +                       /* Just being careful... */
> +                       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +                               continue;
> +
> +                       /*
> +                        * NOTE: Remember you CANNOT hold any channel
> +                        * locks when calling the input routine.
> +                        *
> +                        * During input processing, its possible we
> +                        * will call the Linux ld, which might in turn,
> +                        * do a callback right back into us, resulting
> +                        * in us trying to grab the channel lock twice!
> +                        */
> +                       dgnc_input(ch);
> +
> +                       /*
> +                        * Channel lock is grabbed and then released
> +                        * inside both of these routines, but neither
> +                        * call anything else that could call back into us.
> +                        */
> +                       neo_copy_data_from_queue_to_uart(ch);
> +                       dgnc_wakeup_writes(ch);
> +
> +                       /*
> +                        * Call carrier carrier function, in case something
> +                        * has changed.
> +                        */
> +                       dgnc_carrier(ch);
> +
> +                       /*
> +                        * Check to see if we need to turn off a sending break.
> +                        * The timing check is done inside clear_break()
> +                        */
> +                       if (ch->ch_stop_sending_break)
> +                               neo_clear_break(ch, 0);
> +               }
> +       }
> +
> +       /* Allow interrupt routine to access the interrupt register again */
> +       DGNC_UNLOCK(bd->bd_intr_lock, lock_flags);
> +
> +}
> +
> +
> +/*
> + * dgnc_neo_intr()
> + *
> + * Neo specific interrupt handler.
> + */
> +static irqreturn_t neo_intr(int irq, void *voidbrd)
> +{
> +       struct board_t *brd = (struct board_t *) voidbrd;
> +       struct channel_t *ch;
> +       int port = 0;
> +       int type = 0;
> +       int current_port;
> +       u32 tmp;
> +       u32 uart_poll;
> +       unsigned long lock_flags;
> +       unsigned long lock_flags2;
> +
> +       if (!brd) {
> +               APR(("Received interrupt (%d) with null board associated\n", irq));
> +               return IRQ_NONE;
> +       }
> +
> +       /*
> +        * Check to make sure its for us.
> +        */
> +       if (brd->magic != DGNC_BOARD_MAGIC) {
> +               APR(("Received interrupt (%d) with a board pointer that wasn't ours!\n", irq));
> +               return IRQ_NONE;
> +       }
> +
> +       brd->intr_count++;
> +
> +       /* Lock out the slow poller from running on this board. */
> +       DGNC_LOCK(brd->bd_intr_lock, lock_flags);
> +
> +       /*
> +        * Read in "extended" IRQ information from the 32bit Neo register.
> +        * Bits 0-7: What port triggered the interrupt.
> +        * Bits 8-31: Each 3bits indicate what type of interrupt occurred.
> +        */
> +       uart_poll = readl(brd->re_map_membase + UART_17158_POLL_ADDR_OFFSET);
> +
> +       DPR_INTR(("%s:%d uart_poll: %x\n", __FILE__, __LINE__, uart_poll));
> +
> +       /*
> +        * If 0, no interrupts pending.
> +        * This can happen if the IRQ is shared among a couple Neo/Classic boards.
> +        */
> +       if (!uart_poll) {
> +               DPR_INTR(("Kernel interrupted to me, but no pending interrupts...\n"));
> +               DGNC_UNLOCK(brd->bd_intr_lock, lock_flags);
> +               return IRQ_NONE;
> +       }
> +
> +       /* At this point, we have at least SOMETHING to service, dig further... */
> +
> +       current_port = 0;
> +
> +       /* Loop on each port */
> +       while ((uart_poll & 0xff) != 0) {
> +
> +               tmp = uart_poll;
> +
> +               /* Check current port to see if it has interrupt pending */
> +               if ((tmp & dgnc_offset_table[current_port]) != 0) {
> +                       port = current_port;
> +                       type = tmp >> (8 + (port * 3));
> +                       type &= 0x7;
> +               } else {
> +                       current_port++;
> +                       continue;
> +               }
> +
> +               DPR_INTR(("%s:%d port: %x type: %x\n", __FILE__, __LINE__, port, type));
> +
> +               /* Remove this port + type from uart_poll */
> +               uart_poll &= ~(dgnc_offset_table[port]);
> +
> +               if (!type) {
> +                       /* If no type, just ignore it, and move onto next port */
> +                       DPR_INTR(("Interrupt with no type! port: %d\n", port));
> +                       continue;
> +               }
> +
> +               /* Switch on type of interrupt we have */
> +               switch (type) {
> +
> +               case UART_17158_RXRDY_TIMEOUT:
> +                       /*
> +                        * RXRDY Time-out is cleared by reading data in the
> +                        * RX FIFO until it falls below the trigger level.
> +                        */
> +
> +                       /* Verify the port is in range. */
> +                       if (port > brd->nasync)
> +                               continue;
> +
> +                       ch = brd->channels[port];
> +                       neo_copy_data_from_uart_to_queue(ch);
> +
> +                       /* Call our tty layer to enforce queue flow control if needed. */
> +                       DGNC_LOCK(ch->ch_lock, lock_flags2);
> +                       dgnc_check_queue_flow_control(ch);
> +                       DGNC_UNLOCK(ch->ch_lock, lock_flags2);
> +
> +                       continue;
> +
> +               case UART_17158_RX_LINE_STATUS:
> +                       /*
> +                        * RXRDY and RX LINE Status (logic OR of LSR[4:1])
> +                        */
> +                       neo_parse_lsr(brd, port);
> +                       continue;
> +
> +               case UART_17158_TXRDY:
> +                       /*
> +                        * TXRDY interrupt clears after reading ISR register for the UART channel.
> +                        */
> +
> +                       /*
> +                        * Yes, this is odd...
> +                        * Why would I check EVERY possibility of type of
> +                        * interrupt, when we know its TXRDY???
> +                        * Becuz for some reason, even tho we got triggered for TXRDY,
> +                        * it seems to be occassionally wrong. Instead of TX, which
> +                        * it should be, I was getting things like RXDY too. Weird.
> +                        */
> +                       neo_parse_isr(brd, port);
> +                       continue;
> +
> +               case UART_17158_MSR:
> +                       /*
> +                        * MSR or flow control was seen.
> +                        */
> +                       neo_parse_isr(brd, port);
> +                       continue;
> +
> +               default:
> +                       /*
> +                        * The UART triggered us with a bogus interrupt type.
> +                        * It appears the Exar chip, when REALLY bogged down, will throw
> +                        * these once and awhile.
> +                        * Its harmless, just ignore it and move on.
> +                        */
> +                       DPR_INTR(("%s:%d Unknown Interrupt type: %x\n", __FILE__, __LINE__, type));
> +                       continue;
> +               }
> +       }
> +
> +       /*
> +        * Schedule tasklet to more in-depth servicing at a better time.
> +        */
> +       tasklet_schedule(&brd->helper_tasklet);
> +
> +       DGNC_UNLOCK(brd->bd_intr_lock, lock_flags);
> +
> +       DPR_INTR(("dgnc_intr finish.\n"));
> +       return IRQ_HANDLED;
> +}
> +
> +
> +/*
> + * Neo specific way of turning off the receiver.
> + * Used as a way to enforce queue flow control when in
> + * hardware flow control mode.
> + */
> +static void neo_disable_receiver(struct channel_t *ch)
> +{
> +       uchar tmp = readb(&ch->ch_neo_uart->ier);
> +       tmp &= ~(UART_IER_RDI);
> +       writeb(tmp, &ch->ch_neo_uart->ier);
> +       neo_pci_posting_flush(ch->ch_bd);
> +}
> +
> +
> +/*
> + * Neo specific way of turning on the receiver.
> + * Used as a way to un-enforce queue flow control when in
> + * hardware flow control mode.
> + */
> +static void neo_enable_receiver(struct channel_t *ch)
> +{
> +       uchar tmp = readb(&ch->ch_neo_uart->ier);
> +       tmp |= (UART_IER_RDI);
> +       writeb(tmp, &ch->ch_neo_uart->ier);
> +       neo_pci_posting_flush(ch->ch_bd);
> +}
> +
> +
> +static void neo_copy_data_from_uart_to_queue(struct channel_t *ch)
> +{
> +        int qleft = 0;
> +       uchar linestatus = 0;
> +       uchar error_mask = 0;
> +        int n = 0;
> +        int total = 0;
> +       ushort head;
> +       ushort tail;
> +       ulong lock_flags;
> +
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return;
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       /* cache head and tail of queue */
> +       head = ch->ch_r_head & RQUEUEMASK;
> +       tail = ch->ch_r_tail & RQUEUEMASK;
> +
> +       /* Get our cached LSR */
> +       linestatus = ch->ch_cached_lsr;
> +       ch->ch_cached_lsr = 0;
> +
> +       /* Store how much space we have left in the queue */
> +       if ((qleft = tail - head - 1) < 0)
> +               qleft += RQUEUEMASK + 1;
> +
> +       /*
> +        * If the UART is not in FIFO mode, force the FIFO copy to
> +        * NOT be run, by setting total to 0.
> +        *
> +        * On the other hand, if the UART IS in FIFO mode, then ask
> +        * the UART to give us an approximation of data it has RX'ed.
> +        */
> +       if (!(ch->ch_flags & CH_FIFO_ENABLED))
> +               total = 0;
> +       else {
> +               total = readb(&ch->ch_neo_uart->rfifo);
> +
> +               /*
> +                * EXAR chip bug - RX FIFO COUNT - Fudge factor.
> +                *
> +                * This resolves a problem/bug with the Exar chip that sometimes
> +                * returns a bogus value in the rfifo register.
> +                * The count can be any where from 0-3 bytes "off".
> +                * Bizarre, but true.
> +                */
> +               if ((ch->ch_bd->dvid & 0xf0) >= UART_XR17E158_DVID) {
> +                       total -= 1;
> +               }
> +               else {
> +                       total -= 3;
> +               }
> +       }
> +
> +
> +       /*
> +        * Finally, bound the copy to make sure we don't overflow
> +        * our own queue...
> +        * The byte by byte copy loop below this loop this will
> +        * deal with the queue overflow possibility.
> +        */
> +       total = min(total, qleft);
> +
> +       while (total > 0) {
> +
> +               /*
> +                * Grab the linestatus register, we need to check
> +                * to see if there are any errors in the FIFO.
> +                */
> +               linestatus = readb(&ch->ch_neo_uart->lsr);
> +
> +               /*
> +                * Break out if there is a FIFO error somewhere.
> +                * This will allow us to go byte by byte down below,
> +                * finding the exact location of the error.
> +                */
> +               if (linestatus & UART_17158_RX_FIFO_DATA_ERROR)
> +                       break;
> +
> +               /* Make sure we don't go over the end of our queue */
> +               n = min(((uint) total), (RQUEUESIZE - (uint) head));
> +
> +               /*
> +                * Cut down n even further if needed, this is to fix
> +                * a problem with memcpy_fromio() with the Neo on the
> +                * IBM pSeries platform.
> +                * 15 bytes max appears to be the magic number.
> +                */
> +               n = min((uint) n, (uint) 12);
> +
> +               /*
> +                * Since we are grabbing the linestatus register, which
> +                * will reset some bits after our read, we need to ensure
> +                * we don't miss our TX FIFO emptys.
> +                */
> +               if (linestatus & (UART_LSR_THRE | UART_17158_TX_AND_FIFO_CLR)) {
> +                       ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
> +               }
> +
> +               linestatus = 0;
> +
> +               /* Copy data from uart to the queue */
> +               memcpy_fromio(ch->ch_rqueue + head, &ch->ch_neo_uart->txrxburst, n);
> +               dgnc_sniff_nowait_nolock(ch, "UART READ", ch->ch_rqueue + head, n);
> +
> +               /*
> +                * Since RX_FIFO_DATA_ERROR was 0, we are guarenteed
> +                * that all the data currently in the FIFO is free of
> +                * breaks and parity/frame/orun errors.
> +                */
> +               memset(ch->ch_equeue + head, 0, n);
> +
> +               /* Add to and flip head if needed */
> +               head = (head + n) & RQUEUEMASK;
> +               total -= n;
> +               qleft -= n;
> +               ch->ch_rxcount += n;
> +       }
> +
> +       /*
> +        * Create a mask to determine whether we should
> +        * insert the character (if any) into our queue.
> +        */
> +       if (ch->ch_c_iflag & IGNBRK)
> +               error_mask |= UART_LSR_BI;
> +
> +       /*
> +        * Now cleanup any leftover bytes still in the UART.
> +        * Also deal with any possible queue overflow here as well.
> +        */
> +       while (1) {
> +
> +               /*
> +                * Its possible we have a linestatus from the loop above
> +                * this, so we "OR" on any extra bits.
> +                */
> +               linestatus |= readb(&ch->ch_neo_uart->lsr);
> +
> +               /*
> +                * If the chip tells us there is no more data pending to
> +                * be read, we can then leave.
> +                * But before we do, cache the linestatus, just in case.
> +                */
> +               if (!(linestatus & UART_LSR_DR)) {
> +                       ch->ch_cached_lsr = linestatus;
> +                       break;
> +               }
> +
> +               /* No need to store this bit */
> +               linestatus &= ~UART_LSR_DR;
> +
> +               /*
> +                * Since we are grabbing the linestatus register, which
> +                * will reset some bits after our read, we need to ensure
> +                * we don't miss our TX FIFO emptys.
> +                */
> +               if (linestatus & (UART_LSR_THRE | UART_17158_TX_AND_FIFO_CLR)) {
> +                       linestatus &= ~(UART_LSR_THRE | UART_17158_TX_AND_FIFO_CLR);
> +                       ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
> +               }
> +
> +               /*
> +                * Discard character if we are ignoring the error mask.
> +                */
> +               if (linestatus & error_mask)  {
> +                       uchar discard;
> +                       linestatus = 0;
> +                       memcpy_fromio(&discard, &ch->ch_neo_uart->txrxburst, 1);
> +                       continue;
> +               }
> +
> +               /*
> +                * If our queue is full, we have no choice but to drop some data.
> +                * The assumption is that HWFLOW or SWFLOW should have stopped
> +                * things way way before we got to this point.
> +                *
> +                * I decided that I wanted to ditch the oldest data first,
> +                * I hope thats okay with everyone? Yes? Good.
> +                */
> +               while (qleft < 1) {
> +                       DPR_READ(("Queue full, dropping DATA:%x LSR:%x\n",
> +                               ch->ch_rqueue[tail], ch->ch_equeue[tail]));
> +
> +                       ch->ch_r_tail = tail = (tail + 1) & RQUEUEMASK;
> +                       ch->ch_err_overrun++;
> +                       qleft++;
> +               }
> +
> +               memcpy_fromio(ch->ch_rqueue + head, &ch->ch_neo_uart->txrxburst, 1);
> +               ch->ch_equeue[head] = (uchar) linestatus;
> +               dgnc_sniff_nowait_nolock(ch, "UART READ", ch->ch_rqueue + head, 1);
> +
> +               DPR_READ(("DATA/LSR pair: %x %x\n", ch->ch_rqueue[head], ch->ch_equeue[head]));
> +
> +               /* Ditch any remaining linestatus value. */
> +               linestatus = 0;
> +
> +               /* Add to and flip head if needed */
> +               head = (head + 1) & RQUEUEMASK;
> +
> +               qleft--;
> +               ch->ch_rxcount++;
> +       }
> +
> +       /*
> +        * Write new final heads to channel structure.
> +        */
> +       ch->ch_r_head = head & RQUEUEMASK;
> +       ch->ch_e_head = head & EQUEUEMASK;
> +
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +}
> +
> +
> +/*
> + * This function basically goes to sleep for secs, or until
> + * it gets signalled that the port has fully drained.
> + */
> +static int neo_drain(struct tty_struct *tty, uint seconds)
> +{
> +       ulong lock_flags;
> +       struct channel_t *ch;
> +        struct un_t *un;
> +       int rc = 0;
> +
> +       if (!tty || tty->magic != TTY_MAGIC) {
> +               return (-ENXIO);
> +       }
> +
> +       un = (struct un_t *) tty->driver_data;
> +       if (!un || un->magic != DGNC_UNIT_MAGIC) {
> +               return (-ENXIO);
> +       }
> +
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) {
> +               return (-ENXIO);
> +       }
> +
> +       DPR_IOCTL(("%d Drain wait started.\n", __LINE__));
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +       un->un_flags |= UN_EMPTY;
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +       /*
> +        * Go to sleep waiting for the tty layer to wake me back up when
> +        * the empty flag goes away.
> +        *
> +        * NOTE: TODO: Do something with time passed in.
> +        */
> +       rc = wait_event_interruptible(un->un_flags_wait, ((un->un_flags & UN_EMPTY) == 0));
> +
> +       /* If ret is non-zero, user ctrl-c'ed us */
> +       if (rc) {
> +               DPR_IOCTL(("%d Drain - User ctrl c'ed\n", __LINE__));
> +       }
> +       else {
> +               DPR_IOCTL(("%d Drain wait finished.\n", __LINE__));
> +       }
> +
> +        return (rc);
> +}
> +
> +
> +/*
> + * Flush the WRITE FIFO on the Neo.
> + *
> + * NOTE: Channel lock MUST be held before calling this function!
> + */
> +static void neo_flush_uart_write(struct channel_t *ch)
> +{
> +       uchar tmp = 0;
> +       int i = 0;
> +
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) {
> +               return;
> +       }
> +
> +       writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_XMIT), &ch->ch_neo_uart->isr_fcr);
> +       neo_pci_posting_flush(ch->ch_bd);
> +
> +       for (i = 0; i < 10; i++) {
> +
> +               /* Check to see if the UART feels it completely flushed the FIFO. */
> +               tmp = readb(&ch->ch_neo_uart->isr_fcr);
> +               if (tmp & 4) {
> +                       DPR_IOCTL(("Still flushing TX UART... i: %d\n", i));
> +                       udelay(10);
> +               }
> +               else
> +                       break;
> +       }
> +
> +       ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
> +}
> +
> +
> +/*
> + * Flush the READ FIFO on the Neo.
> + *
> + * NOTE: Channel lock MUST be held before calling this function!
> + */
> +static void neo_flush_uart_read(struct channel_t *ch)
> +{
> +       uchar tmp = 0;
> +       int i = 0;
> +
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) {
> +               return;
> +       }
> +
> +       writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR), &ch->ch_neo_uart->isr_fcr);
> +       neo_pci_posting_flush(ch->ch_bd);
> +
> +       for (i = 0; i < 10; i++) {
> +
> +               /* Check to see if the UART feels it completely flushed the FIFO. */
> +               tmp = readb(&ch->ch_neo_uart->isr_fcr);
> +               if (tmp & 2) {
> +                       DPR_IOCTL(("Still flushing RX UART... i: %d\n", i));
> +                       udelay(10);
> +               }
> +               else
> +                       break;
> +       }
> +}
> +
> +
> +static void neo_copy_data_from_queue_to_uart(struct channel_t *ch)
> +{
> +       ushort head;
> +       ushort tail;
> +       int n;
> +       int s;
> +       int qlen;
> +       uint len_written = 0;
> +       ulong lock_flags;
> +
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return;
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       /* No data to write to the UART */
> +       if (ch->ch_w_tail == ch->ch_w_head) {
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               return;
> +       }
> +
> +       /* If port is "stopped", don't send any data to the UART */
> +       if ((ch->ch_flags & CH_FORCED_STOP) || (ch->ch_flags & CH_BREAK_SENDING)) {
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               return;
> +       }
> +
> +       /*
> +        * If FIFOs are disabled. Send data directly to txrx register
> +        */
> +       if (!(ch->ch_flags & CH_FIFO_ENABLED)) {
> +               uchar lsrbits = readb(&ch->ch_neo_uart->lsr);
> +
> +               /* Cache the LSR bits for later parsing */
> +               ch->ch_cached_lsr |= lsrbits;
> +               if (ch->ch_cached_lsr & UART_LSR_THRE) {
> +                       ch->ch_cached_lsr &= ~(UART_LSR_THRE);
> +
> +                       /*
> +                        * If RTS Toggle mode is on, turn on RTS now if not already set,
> +                        * and make sure we get an event when the data transfer has completed.
> +                        */
> +                       if (ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) {
> +                               if (!(ch->ch_mostat & UART_MCR_RTS)) {
> +                                       ch->ch_mostat |= (UART_MCR_RTS);
> +                                       neo_assert_modem_signals(ch);
> +                               }
> +                               ch->ch_tun.un_flags |= (UN_EMPTY);
> +                       }
> +                       /*
> +                        * If DTR Toggle mode is on, turn on DTR now if not already set,
> +                        * and make sure we get an event when the data transfer has completed.
> +                        */
> +                       if (ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) {
> +                               if (!(ch->ch_mostat & UART_MCR_DTR)) {
> +                                       ch->ch_mostat |= (UART_MCR_DTR);
> +                                       neo_assert_modem_signals(ch);
> +                               }
> +                               ch->ch_tun.un_flags |= (UN_EMPTY);
> +                       }
> +
> +                       writeb(ch->ch_wqueue[ch->ch_w_tail], &ch->ch_neo_uart->txrx);
> +                       DPR_WRITE(("Tx data: %x\n", ch->ch_wqueue[ch->ch_w_head]));
> +                       ch->ch_w_tail++;
> +                       ch->ch_w_tail &= WQUEUEMASK;
> +                       ch->ch_txcount++;
> +               }
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               return;
> +       }
> +
> +       /*
> +        * We have to do it this way, because of the EXAR TXFIFO count bug.
> +        */
> +       if ((ch->ch_bd->dvid & 0xf0) < UART_XR17E158_DVID) {
> +               if (!(ch->ch_flags & (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM))) {
> +                       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +                       return;
> +               }
> +
> +               len_written = 0;
> +
> +               n = readb(&ch->ch_neo_uart->tfifo);
> +
> +               if ((unsigned int) n > ch->ch_t_tlevel) {
> +                       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +                       return;
> +               }
> +
> +               n = UART_17158_TX_FIFOSIZE - ch->ch_t_tlevel;
> +       }
> +       else {
> +               n = UART_17158_TX_FIFOSIZE - readb(&ch->ch_neo_uart->tfifo);
> +       }
> +
> +       /* cache head and tail of queue */
> +       head = ch->ch_w_head & WQUEUEMASK;
> +       tail = ch->ch_w_tail & WQUEUEMASK;
> +       qlen = (head - tail) & WQUEUEMASK;
> +
> +       /* Find minimum of the FIFO space, versus queue length */
> +       n = min(n, qlen);
> +
> +       while (n > 0) {
> +
> +               s = ((head >= tail) ? head : WQUEUESIZE) - tail;
> +               s = min(s, n);
> +
> +               if (s <= 0)
> +                       break;
> +
> +               /*
> +                * If RTS Toggle mode is on, turn on RTS now if not already set,
> +                * and make sure we get an event when the data transfer has completed.
> +                */
> +               if (ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) {
> +                       if (!(ch->ch_mostat & UART_MCR_RTS)) {
> +                               ch->ch_mostat |= (UART_MCR_RTS);
> +                               neo_assert_modem_signals(ch);
> +                       }
> +                       ch->ch_tun.un_flags |= (UN_EMPTY);
> +               }
> +
> +               /*
> +                * If DTR Toggle mode is on, turn on DTR now if not already set,
> +                * and make sure we get an event when the data transfer has completed.
> +                */
> +               if (ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) {
> +                       if (!(ch->ch_mostat & UART_MCR_DTR)) {
> +                               ch->ch_mostat |= (UART_MCR_DTR);
> +                               neo_assert_modem_signals(ch);
> +                       }
> +                       ch->ch_tun.un_flags |= (UN_EMPTY);
> +               }
> +
> +               memcpy_toio(&ch->ch_neo_uart->txrxburst, ch->ch_wqueue + tail, s);
> +               dgnc_sniff_nowait_nolock(ch, "UART WRITE", ch->ch_wqueue + tail, s);
> +
> +               /* Add and flip queue if needed */
> +               tail = (tail + s) & WQUEUEMASK;
> +               n -= s;
> +               ch->ch_txcount += s;
> +               len_written += s;
> +       }
> +
> +       /* Update the final tail */
> +       ch->ch_w_tail = tail & WQUEUEMASK;
> +
> +       if (len_written > 0) {
> +               neo_pci_posting_flush(ch->ch_bd);
> +               ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
> +       }
> +
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +}
> +
> +
> +static void neo_parse_modem(struct channel_t *ch, uchar signals)
> +{
> +       volatile uchar msignals = signals;
> +
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return;
> +
> +       DPR_MSIGS(("neo_parse_modem: port: %d msignals: %x\n", ch->ch_portnum, msignals));
> +
> +       /*
> +        * Do altpin switching. Altpin switches DCD and DSR.
> +        * This prolly breaks DSRPACE, so we should be more clever here.
> +        */
> +       if (ch->ch_digi.digi_flags & DIGI_ALTPIN) {
> +               uchar mswap = msignals;
> +
> +               if (mswap & UART_MSR_DDCD) {
> +                       msignals &= ~UART_MSR_DDCD;
> +                       msignals |= UART_MSR_DDSR;
> +               }
> +               if (mswap & UART_MSR_DDSR) {
> +                       msignals &= ~UART_MSR_DDSR;
> +                       msignals |= UART_MSR_DDCD;
> +               }
> +               if (mswap & UART_MSR_DCD) {
> +                       msignals &= ~UART_MSR_DCD;
> +                       msignals |= UART_MSR_DSR;
> +               }
> +               if (mswap & UART_MSR_DSR) {
> +                       msignals &= ~UART_MSR_DSR;
> +                       msignals |= UART_MSR_DCD;
> +               }
> +       }
> +
> +       /* Scrub off lower bits. They signify delta's, which I don't care about */
> +       msignals &= 0xf0;
> +
> +       if (msignals & UART_MSR_DCD)
> +               ch->ch_mistat |= UART_MSR_DCD;
> +       else
> +               ch->ch_mistat &= ~UART_MSR_DCD;
> +
> +       if (msignals & UART_MSR_DSR)
> +               ch->ch_mistat |= UART_MSR_DSR;
> +       else
> +               ch->ch_mistat &= ~UART_MSR_DSR;
> +
> +       if (msignals & UART_MSR_RI)
> +               ch->ch_mistat |= UART_MSR_RI;
> +       else
> +               ch->ch_mistat &= ~UART_MSR_RI;
> +
> +       if (msignals & UART_MSR_CTS)
> +               ch->ch_mistat |= UART_MSR_CTS;
> +       else
> +               ch->ch_mistat &= ~UART_MSR_CTS;
> +
> +       DPR_MSIGS(("Port: %d DTR: %d RTS: %d CTS: %d DSR: %d " "RI: %d CD: %d\n",
> +               ch->ch_portnum,
> +               !!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_DTR),
> +               !!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_RTS),
> +               !!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_CTS),
> +               !!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_DSR),
> +               !!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_RI),
> +               !!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_DCD)));
> +}
> +
> +
> +/* Make the UART raise any of the output signals we want up */
> +static void neo_assert_modem_signals(struct channel_t *ch)
> +{
> +       uchar out;
> +
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return;
> +
> +       out = ch->ch_mostat;
> +
> +       if (ch->ch_flags & CH_LOOPBACK)
> +               out |= UART_MCR_LOOP;
> +
> +       writeb(out, &ch->ch_neo_uart->mcr);
> +       neo_pci_posting_flush(ch->ch_bd);
> +
> +       /* Give time for the UART to actually raise/drop the signals */
> +       udelay(10);
> +}
> +
> +
> +static void neo_send_start_character(struct channel_t *ch)
> +{
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return;
> +
> +       if (ch->ch_startc != _POSIX_VDISABLE) {
> +               ch->ch_xon_sends++;
> +               writeb(ch->ch_startc, &ch->ch_neo_uart->txrx);
> +               neo_pci_posting_flush(ch->ch_bd);
> +               udelay(10);
> +       }
> +}
> +
> +
> +static void neo_send_stop_character(struct channel_t *ch)
> +{
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return;
> +
> +       if (ch->ch_stopc != _POSIX_VDISABLE) {
> +               ch->ch_xoff_sends++;
> +               writeb(ch->ch_stopc, &ch->ch_neo_uart->txrx);
> +               neo_pci_posting_flush(ch->ch_bd);
> +               udelay(10);
> +       }
> +}
> +
> +
> +/*
> + * neo_uart_init
> + */
> +static void neo_uart_init(struct channel_t *ch)
> +{
> +
> +       writeb(0, &ch->ch_neo_uart->ier);
> +       writeb(0, &ch->ch_neo_uart->efr);
> +       writeb(UART_EFR_ECB, &ch->ch_neo_uart->efr);
> +
> +
> +        /* Clear out UART and FIFO */
> +       readb(&ch->ch_neo_uart->txrx);
> +       writeb((UART_FCR_ENABLE_FIFO|UART_FCR_CLEAR_RCVR|UART_FCR_CLEAR_XMIT), &ch->ch_neo_uart->isr_fcr);
> +       readb(&ch->ch_neo_uart->lsr);
> +       readb(&ch->ch_neo_uart->msr);
> +
> +       ch->ch_flags |= CH_FIFO_ENABLED;
> +
> +       /* Assert any signals we want up */
> +       writeb(ch->ch_mostat, &ch->ch_neo_uart->mcr);
> +       neo_pci_posting_flush(ch->ch_bd);
> +}
> +
> +
> +/*
> + * Make the UART completely turn off.
> + */
> +static void neo_uart_off(struct channel_t *ch)
> +{
> +       /* Turn off UART enhanced bits */
> +       writeb(0, &ch->ch_neo_uart->efr);
> +
> +       /* Stop all interrupts from occurring. */
> +       writeb(0, &ch->ch_neo_uart->ier);
> +       neo_pci_posting_flush(ch->ch_bd);
> +}
> +
> +
> +static uint neo_get_uart_bytes_left(struct channel_t *ch)
> +{
> +       uchar left = 0;
> +       uchar lsr = readb(&ch->ch_neo_uart->lsr);
> +
> +       /* We must cache the LSR as some of the bits get reset once read... */
> +       ch->ch_cached_lsr |= lsr;
> +
> +       /* Determine whether the Transmitter is empty or not */
> +       if (!(lsr & UART_LSR_TEMT)) {
> +               if (ch->ch_flags & CH_TX_FIFO_EMPTY) {
> +                       tasklet_schedule(&ch->ch_bd->helper_tasklet);
> +               }
> +               left = 1;
> +       } else {
> +               ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
> +               left = 0;
> +       }
> +
> +       return left;
> +}
> +
> +
> +/* Channel lock MUST be held by the calling function! */
> +static void neo_send_break(struct channel_t *ch, int msecs)
> +{
> +       /*
> +        * If we receive a time of 0, this means turn off the break.
> +        */
> +       if (msecs == 0) {
> +               if (ch->ch_flags & CH_BREAK_SENDING) {
> +                       uchar temp = readb(&ch->ch_neo_uart->lcr);
> +                       writeb((temp & ~UART_LCR_SBC), &ch->ch_neo_uart->lcr);
> +                       neo_pci_posting_flush(ch->ch_bd);
> +                       ch->ch_flags &= ~(CH_BREAK_SENDING);
> +                       ch->ch_stop_sending_break = 0;
> +                       DPR_IOCTL(("Finishing UART_LCR_SBC! finished: %lx\n", jiffies));
> +               }
> +               return;
> +       }
> +
> +       /*
> +        * Set the time we should stop sending the break.
> +        * If we are already sending a break, toss away the existing
> +        * time to stop, and use this new value instead.
> +        */
> +       ch->ch_stop_sending_break = jiffies + dgnc_jiffies_from_ms(msecs);
> +
> +       /* Tell the UART to start sending the break */
> +       if (!(ch->ch_flags & CH_BREAK_SENDING)) {
> +               uchar temp = readb(&ch->ch_neo_uart->lcr);
> +                writeb((temp | UART_LCR_SBC), &ch->ch_neo_uart->lcr);
> +               neo_pci_posting_flush(ch->ch_bd);
> +               ch->ch_flags |= (CH_BREAK_SENDING);
> +               DPR_IOCTL(("Port %d. Starting UART_LCR_SBC! start: %lx should end: %lx\n",
> +                       ch->ch_portnum, jiffies, ch->ch_stop_sending_break));
> +        }
> +}
> +
> +
> +/*
> + * neo_send_immediate_char.
> + *
> + * Sends a specific character as soon as possible to the UART,
> + * jumping over any bytes that might be in the write queue.
> + *
> + * The channel lock MUST be held by the calling function.
> + */
> +static void neo_send_immediate_char(struct channel_t *ch, unsigned char c)
> +{
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return;
> +
> +       writeb(c, &ch->ch_neo_uart->txrx);
> +       neo_pci_posting_flush(ch->ch_bd);
> +}
> +
> +
> +static unsigned int neo_read_eeprom(unsigned char *base, unsigned int address)
> +{
> +       unsigned int enable;
> +       unsigned int bits;
> +       unsigned int databit;
> +       unsigned int val;
> +
> +       /* enable chip select */
> +       writeb(NEO_EECS, base + NEO_EEREG);
> +       /* READ */
> +       enable = (address | 0x180);
> +
> +       for (bits = 9; bits--; ) {
> +               databit = (enable & (1 << bits)) ? NEO_EEDI : 0;
> +               /* Set read address */
> +               writeb(databit | NEO_EECS, base + NEO_EEREG);
> +               writeb(databit | NEO_EECS | NEO_EECK, base + NEO_EEREG);
> +       }
> +
> +       val = 0;
> +
> +       for (bits = 17; bits--; ) {
> +               /* clock to EEPROM */
> +               writeb(NEO_EECS, base + NEO_EEREG);
> +               writeb(NEO_EECS | NEO_EECK, base + NEO_EEREG);
> +               val <<= 1;
> +               /* read EEPROM */
> +               if (readb(base + NEO_EEREG) & NEO_EEDO)
> +                       val |= 1;
> +       }
> +
> +       /* clock falling edge */
> +       writeb(NEO_EECS, base + NEO_EEREG);
> +
> +       /* drop chip select */
> +       writeb(0x00, base + NEO_EEREG);
> +
> +       return val;
> +}
> +
> +
> +static void neo_vpd(struct board_t *brd)
> +{
> +       unsigned int i = 0;
> +       unsigned int a;
> +
> +       if (!brd || brd->magic != DGNC_BOARD_MAGIC)
> +               return;
> +
> +       if (!brd->re_map_membase)
> +               return;
> +
> +       /* Store the VPD into our buffer */
> +       for (i = 0; i < NEO_VPD_IMAGESIZE; i++) {
> +               a = neo_read_eeprom(brd->re_map_membase, i);
> +               brd->vpd[i*2] = a & 0xff;
> +               brd->vpd[(i*2)+1] = (a >> 8) & 0xff;
> +       }
> +
> +       if  (((brd->vpd[0x08] != 0x82)     /* long resource name tag */
> +               &&  (brd->vpd[0x10] != 0x82))   /* long resource name tag (PCI-66 files)*/
> +               ||  (brd->vpd[0x7F] != 0x78))   /* small resource end tag */
> +       {
> +               memset(brd->vpd, '\0', NEO_VPD_IMAGESIZE);
> +       }
> +       else {
> +               /* Search for the serial number */
> +               for (i = 0; i < NEO_VPD_IMAGESIZE * 2; i++) {
> +                       if (brd->vpd[i] == 'S' && brd->vpd[i + 1] == 'N') {
> +                               strncpy(brd->serial_num, &(brd->vpd[i + 3]), 9);
> +                       }
> +               }
> +       }
> +}
> diff --git a/drivers/staging/dgnc/dgnc_neo.h b/drivers/staging/dgnc/dgnc_neo.h
> new file mode 100644
> index 0000000..ffb4209
> --- /dev/null
> +++ b/drivers/staging/dgnc/dgnc_neo.h
> @@ -0,0 +1,157 @@
> +/*
> + * 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 __DGNC_NEO_H
> +#define __DGNC_NEO_H
> +
> +#include "dgnc_types.h"
> +#include "dgnc_driver.h"
> +
> +/************************************************************************
> + * Per channel/port NEO UART structure                                 *
> + ************************************************************************
> + *             Base Structure Entries Usage Meanings to Host           *
> + *                                                                     *
> + *     W = read write          R = read only                           *
> + *                     U = Unused.                                     *
> + ************************************************************************/
> +
> +struct neo_uart_struct {
> +       volatile uchar txrx;            /* WR  RHR/THR - Holding Reg */
> +       volatile uchar ier;             /* WR  IER - Interrupt Enable Reg */
> +       volatile uchar isr_fcr;         /* WR  ISR/FCR - Interrupt Status Reg/Fifo Control Reg */
> +       volatile uchar lcr;             /* WR  LCR - Line Control Reg */
> +       volatile uchar mcr;             /* WR  MCR - Modem Control Reg */
> +       volatile uchar lsr;             /* WR  LSR - Line Status Reg */
> +       volatile uchar msr;             /* WR  MSR - Modem Status Reg */
> +       volatile uchar spr;             /* WR  SPR - Scratch Pad Reg */
> +       volatile uchar fctr;            /* WR  FCTR - Feature Control Reg */
> +       volatile uchar efr;             /* WR  EFR - Enhanced Function Reg */
> +       volatile uchar tfifo;           /* WR  TXCNT/TXTRG - Transmit FIFO Reg */
> +       volatile uchar rfifo;           /* WR  RXCNT/RXTRG - Recieve  FIFO Reg */
> +       volatile uchar xoffchar1;       /* WR  XOFF 1 - XOff Character 1 Reg */
> +       volatile uchar xoffchar2;       /* WR  XOFF 2 - XOff Character 2 Reg */
> +       volatile uchar xonchar1;        /* WR  XON 1 - Xon Character 1 Reg */
> +       volatile uchar xonchar2;        /* WR  XON 2 - XOn Character 2 Reg */
> +
> +       volatile uchar reserved1[0x2ff - 0x200]; /* U   Reserved by Exar */
> +       volatile uchar txrxburst[64];   /* RW  64 bytes of RX/TX FIFO Data */
> +       volatile uchar reserved2[0x37f - 0x340]; /* U   Reserved by Exar */
> +       volatile uchar rxburst_with_errors[64]; /* R  64 bytes of RX FIFO Data + LSR */
> +};
> +
> +/* Where to read the extended interrupt register (32bits instead of 8bits) */
> +#define        UART_17158_POLL_ADDR_OFFSET     0x80
> +
> +/* These are the current dvid's of the Neo boards */
> +#define UART_XR17C158_DVID 0x20
> +#define UART_XR17D158_DVID 0x20
> +#define UART_XR17E158_DVID 0x40
> +
> +#define NEO_EECK  0x10         /* Clock */
> +#define NEO_EECS  0x20         /* Chip Select */
> +#define NEO_EEDI  0x40         /* Data In  is an Output Pin */
> +#define NEO_EEDO  0x80         /* Data Out is an Input Pin */
> +#define NEO_EEREG 0x8E         /* offset to EEPROM control reg */
> +
> +
> +#define NEO_VPD_IMAGESIZE 0x40 /* size of image to read from EEPROM in words */
> +#define NEO_VPD_IMAGEBYTES (NEO_VPD_IMAGESIZE * 2)
> +
> +/*
> + * These are the redefinitions for the FCTR on the XR17C158, since
> + * Exar made them different than their earlier design. (XR16C854)
> + */
> +
> +/* These are only applicable when table D is selected */
> +#define UART_17158_FCTR_RTS_NODELAY    0x00
> +#define UART_17158_FCTR_RTS_4DELAY     0x01
> +#define UART_17158_FCTR_RTS_6DELAY     0x02
> +#define UART_17158_FCTR_RTS_8DELAY     0x03
> +#define UART_17158_FCTR_RTS_12DELAY    0x12
> +#define UART_17158_FCTR_RTS_16DELAY    0x05
> +#define UART_17158_FCTR_RTS_20DELAY    0x13
> +#define UART_17158_FCTR_RTS_24DELAY    0x06
> +#define UART_17158_FCTR_RTS_28DELAY    0x14
> +#define UART_17158_FCTR_RTS_32DELAY    0x07
> +#define UART_17158_FCTR_RTS_36DELAY    0x16
> +#define UART_17158_FCTR_RTS_40DELAY    0x08
> +#define UART_17158_FCTR_RTS_44DELAY    0x09
> +#define UART_17158_FCTR_RTS_48DELAY    0x10
> +#define UART_17158_FCTR_RTS_52DELAY    0x11
> +
> +#define UART_17158_FCTR_RTS_IRDA       0x10
> +#define UART_17158_FCTR_RS485          0x20
> +#define UART_17158_FCTR_TRGA           0x00
> +#define UART_17158_FCTR_TRGB           0x40
> +#define UART_17158_FCTR_TRGC           0x80
> +#define UART_17158_FCTR_TRGD           0xC0
> +
> +/* 17158 trigger table selects.. */
> +#define UART_17158_FCTR_BIT6           0x40
> +#define UART_17158_FCTR_BIT7           0x80
> +
> +/* 17158 TX/RX memmapped buffer offsets */
> +#define UART_17158_RX_FIFOSIZE         64
> +#define UART_17158_TX_FIFOSIZE         64
> +
> +/* 17158 Extended IIR's */
> +#define UART_17158_IIR_RDI_TIMEOUT     0x0C    /* Receiver data TIMEOUT */
> +#define UART_17158_IIR_XONXOFF         0x10    /* Received an XON/XOFF char */
> +#define UART_17158_IIR_HWFLOW_STATE_CHANGE 0x20        /* CTS/DSR or RTS/DTR state change */
> +#define UART_17158_IIR_FIFO_ENABLED    0xC0    /* 16550 FIFOs are Enabled */
> +
> +/*
> + * These are the extended interrupts that get sent
> + * back to us from the UART's 32bit interrupt register
> + */
> +#define UART_17158_RX_LINE_STATUS      0x1     /* RX Ready */
> +#define UART_17158_RXRDY_TIMEOUT       0x2     /* RX Ready Timeout */
> +#define UART_17158_TXRDY               0x3     /* TX Ready */
> +#define UART_17158_MSR                 0x4     /* Modem State Change */
> +#define UART_17158_TX_AND_FIFO_CLR     0x40    /* Transmitter Holding Reg Empty */
> +#define UART_17158_RX_FIFO_DATA_ERROR  0x80    /* UART detected an RX FIFO Data error */
> +
> +/*
> + * These are the EXTENDED definitions for the 17C158's Interrupt
> + * Enable Register.
> + */
> +#define UART_17158_EFR_ECB     0x10    /* Enhanced control bit */
> +#define UART_17158_EFR_IXON    0x2     /* Receiver compares Xon1/Xoff1 */
> +#define UART_17158_EFR_IXOFF   0x8     /* Transmit Xon1/Xoff1 */
> +#define UART_17158_EFR_RTSDTR  0x40    /* Auto RTS/DTR Flow Control Enable */
> +#define UART_17158_EFR_CTSDSR  0x80    /* Auto CTS/DSR Flow COntrol Enable */
> +
> +#define UART_17158_XOFF_DETECT 0x1     /* Indicates whether chip saw an incoming XOFF char  */
> +#define UART_17158_XON_DETECT  0x2     /* Indicates whether chip saw an incoming XON char */
> +
> +#define UART_17158_IER_RSVD1   0x10    /* Reserved by Exar */
> +#define UART_17158_IER_XOFF    0x20    /* Xoff Interrupt Enable */
> +#define UART_17158_IER_RTSDTR  0x40    /* Output Interrupt Enable */
> +#define UART_17158_IER_CTSDSR  0x80    /* Input Interrupt Enable */
> +
> +/*
> + * Our Global Variables
> + */
> +extern struct board_ops dgnc_neo_ops;
> +
> +#endif
> diff --git a/drivers/staging/dgnc/dgnc_pci.h b/drivers/staging/dgnc/dgnc_pci.h
> new file mode 100644
> index 0000000..5550707
> --- /dev/null
> +++ b/drivers/staging/dgnc/dgnc_pci.h
> @@ -0,0 +1,77 @@
> +/*
> + * 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: dgnc_pci.h,v 1.1.1.1 2009/05/20 12:19:19 markh Exp $ */
> +
> +#ifndef __DGNC_PCI_H
> +#define __DGNC_PCI_H
> +
> +#define PCIMAX 32                      /* maximum number of PCI boards */
> +
> +#define DIGI_VID                               0x114F
> +
> +#define PCI_DEVICE_CLASSIC_4_DID               0x0028
> +#define PCI_DEVICE_CLASSIC_8_DID               0x0029
> +#define PCI_DEVICE_CLASSIC_4_422_DID           0x00D0
> +#define PCI_DEVICE_CLASSIC_8_422_DID           0x00D1
> +#define PCI_DEVICE_NEO_4_DID                   0x00B0
> +#define PCI_DEVICE_NEO_8_DID                   0x00B1
> +#define PCI_DEVICE_NEO_2DB9_DID                        0x00C8
> +#define PCI_DEVICE_NEO_2DB9PRI_DID             0x00C9
> +#define PCI_DEVICE_NEO_2RJ45_DID               0x00CA
> +#define PCI_DEVICE_NEO_2RJ45PRI_DID            0x00CB
> +#define PCI_DEVICE_NEO_1_422_DID               0x00CC
> +#define PCI_DEVICE_NEO_1_422_485_DID           0x00CD
> +#define PCI_DEVICE_NEO_2_422_485_DID           0x00CE
> +#define PCI_DEVICE_NEO_EXPRESS_8_DID           0x00F0
> +#define PCI_DEVICE_NEO_EXPRESS_4_DID           0x00F1
> +#define PCI_DEVICE_NEO_EXPRESS_4RJ45_DID       0x00F2
> +#define PCI_DEVICE_NEO_EXPRESS_8RJ45_DID       0x00F3
> +#define PCI_DEVICE_NEO_EXPRESS_4_IBM_DID       0x00F4
> +
> +#define PCI_DEVICE_CLASSIC_4_PCI_NAME          "ClassicBoard 4 PCI"
> +#define PCI_DEVICE_CLASSIC_8_PCI_NAME          "ClassicBoard 8 PCI"
> +#define PCI_DEVICE_CLASSIC_4_422_PCI_NAME      "ClassicBoard 4 422 PCI"
> +#define PCI_DEVICE_CLASSIC_8_422_PCI_NAME      "ClassicBoard 8 422 PCI"
> +#define PCI_DEVICE_NEO_4_PCI_NAME              "Neo 4 PCI"
> +#define PCI_DEVICE_NEO_8_PCI_NAME              "Neo 8 PCI"
> +#define PCI_DEVICE_NEO_2DB9_PCI_NAME           "Neo 2 - DB9 Universal PCI"
> +#define PCI_DEVICE_NEO_2DB9PRI_PCI_NAME                "Neo 2 - DB9 Universal PCI - Powered Ring Indicator"
> +#define PCI_DEVICE_NEO_2RJ45_PCI_NAME          "Neo 2 - RJ45 Universal PCI"
> +#define PCI_DEVICE_NEO_2RJ45PRI_PCI_NAME       "Neo 2 - RJ45 Universal PCI - Powered Ring Indicator"
> +#define PCI_DEVICE_NEO_1_422_PCI_NAME          "Neo 1 422 PCI"
> +#define PCI_DEVICE_NEO_1_422_485_PCI_NAME      "Neo 1 422/485 PCI"
> +#define PCI_DEVICE_NEO_2_422_485_PCI_NAME      "Neo 2 422/485 PCI"
> +
> +#define PCI_DEVICE_NEO_EXPRESS_8_PCI_NAME      "Neo 8 PCI Express"
> +#define PCI_DEVICE_NEO_EXPRESS_4_PCI_NAME      "Neo 4 PCI Express"
> +#define PCI_DEVICE_NEO_EXPRESS_4RJ45_PCI_NAME  "Neo 4 PCI Express RJ45"
> +#define PCI_DEVICE_NEO_EXPRESS_8RJ45_PCI_NAME  "Neo 8 PCI Express RJ45"
> +#define PCI_DEVICE_NEO_EXPRESS_4_IBM_PCI_NAME  "Neo 4 PCI Express IBM"
> +
> +
> +/* Size of Memory and I/O for PCI (4 K) */
> +#define PCI_RAM_SIZE                           0x1000
> +
> +/* Size of Memory (2MB) */
> +#define PCI_MEM_SIZE                           0x1000
> +
> +#endif
> diff --git a/drivers/staging/dgnc/dgnc_proc.c b/drivers/staging/dgnc/dgnc_proc.c
> new file mode 100644
> index 0000000..8fbaf3b
> --- /dev/null
> +++ b/drivers/staging/dgnc/dgnc_proc.c
> @@ -0,0 +1,1551 @@
> +/*
> + * 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: dgnc_proc.c,v 1.3 2011/06/22 12:16:35 markh Exp $
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/version.h>
> +#include <linux/sched.h>       /* For jiffies, task states */
> +#include <linux/interrupt.h>   /* For tasklet and interrupt structs/defines */
> +#include <linux/module.h>
> +#include <linux/ctype.h>
> +#include <linux/proc_fs.h>
> +#include <linux/serial_reg.h>
> +#include <linux/sched.h>               /* For in_egroup_p() */
> +#include <linux/string.h>
> +#include <asm/uaccess.h>               /* For copy_from_user/copy_to_user */
> +
> +#include "dgnc_driver.h"
> +#include "dgnc_proc.h"
> +#include "dgnc_mgmt.h"
> +
> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)
> +#define init_MUTEX(sem)         sema_init(sem, 1)
> +#define DECLARE_MUTEX(name)     \
> +        struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1)
> +#endif
> +
> +
> +/* The /proc/dgnc directory */
> +static struct proc_dir_entry *ProcDGNC;
> +
> +
> +/* File operation declarations */
> +static int     dgnc_gen_proc_open(struct inode *, struct file *);
> +static int     dgnc_gen_proc_close(struct inode *, struct file *);
> +static ssize_t dgnc_gen_proc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos);
> +static ssize_t dgnc_gen_proc_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos);
> +
> +static int dgnc_proc_chk_perm(struct inode *, int);
> +
> +static const struct file_operations dgnc_proc_file_ops =
> +{
> +       .owner =        THIS_MODULE,
> +       .read =         dgnc_gen_proc_read,     /* read         */
> +       .write =        dgnc_gen_proc_write,    /* write        */
> +       .open =         dgnc_gen_proc_open,     /* open         */
> +       .release =      dgnc_gen_proc_close,    /* release      */
> +};
> +
> +
> +static struct inode_operations dgnc_proc_inode_ops =
> +{
> +       .permission =   dgnc_proc_chk_perm
> +};
> +
> +
> +static void dgnc_register_proc_table(struct dgnc_proc_entry *, struct proc_dir_entry *);
> +static void dgnc_unregister_proc_table(struct dgnc_proc_entry *, struct proc_dir_entry *);
> +static void dgnc_remove_proc_entry(struct proc_dir_entry *pde);
> +
> +
> +/* Stuff in /proc/ */
> +static int dgnc_read_info(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               char __user *buffer, ssize_t *lenp, loff_t *ppos);
> +static int dgnc_write_info(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               const char __user *buffer, ssize_t *lenp, loff_t *ppos);
> +
> +static int dgnc_read_mknod(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               char __user *buffer, ssize_t *lenp, loff_t *ppos);
> +
> +static struct dgnc_proc_entry dgnc_table[] = {
> +       {DGNC_INFO,     "info", 0600, NULL, NULL, NULL, &dgnc_read_info, &dgnc_write_info,
> +                       NULL, __SEMAPHORE_INITIALIZER(dgnc_table[0].excl_sem, 1), 0, NULL },
> +       {DGNC_MKNOD, "mknod", 0600, NULL, NULL, NULL, &dgnc_read_mknod, NULL,
> +                       NULL, __SEMAPHORE_INITIALIZER(dgnc_table[1].excl_sem, 1), 0, NULL },
> +       {0}
> +};
> +
> +
> +/* Stuff in /proc/<board>/ */
> +static int dgnc_read_board_info(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               char __user *buffer, ssize_t *lenp, loff_t *ppos);
> +static int dgnc_read_board_vpd(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               char __user *buffer, ssize_t *lenp, loff_t *ppos);
> +static int dgnc_read_board_mknod(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               char __user *buffer, ssize_t *lenp, loff_t *ppos);
> +static int dgnc_read_board_ttystats(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               char __user *buffer, ssize_t *lenp, loff_t *ppos);
> +static int dgnc_read_board_ttyintr(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               char __user *buffer, ssize_t *lenp, loff_t *ppos);
> +static int dgnc_read_board_ttyflags(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               char __user *buffer, ssize_t *lenp, loff_t *ppos);
> +
> +static struct dgnc_proc_entry dgnc_board_table[] = {
> +       {DGNC_BOARD_INFO, "info", 0600, NULL, NULL, NULL, &dgnc_read_board_info, NULL,
> +                       NULL, __SEMAPHORE_INITIALIZER(dgnc_board_table[0].excl_sem, 1), 0, NULL },
> +       {DGNC_BOARD_VPD, "vpd", 0600, NULL, NULL, NULL, &dgnc_read_board_vpd, NULL,
> +                       NULL, __SEMAPHORE_INITIALIZER(dgnc_board_table[1].excl_sem, 1), 0, NULL },
> +       {DGNC_BOARD_TTYSTATS, "stats", 0600, NULL, NULL, NULL, &dgnc_read_board_ttystats, NULL,
> +                       NULL, __SEMAPHORE_INITIALIZER(dgnc_board_table[2].excl_sem, 1), 0, NULL },
> +       {DGNC_BOARD_TTYINTR, "intr", 0600, NULL, NULL, NULL, &dgnc_read_board_ttyintr, NULL,
> +                       NULL, __SEMAPHORE_INITIALIZER(dgnc_board_table[3].excl_sem, 1), 0, NULL },
> +       {DGNC_BOARD_TTYFLAGS, "flags", 0600, NULL, NULL, NULL, &dgnc_read_board_ttyflags, NULL,
> +                       NULL, __SEMAPHORE_INITIALIZER(dgnc_board_table[4].excl_sem, 1), 0, NULL },
> +       {DGNC_BOARD_MKNOD, "mknod", 0600, NULL, NULL, NULL, &dgnc_read_board_mknod, NULL,
> +                       NULL, __SEMAPHORE_INITIALIZER(dgnc_board_table[5].excl_sem, 1), 0, NULL },
> +       {0}
> +};
> +
> +
> +/* Stuff in /proc/<board>/<channel> */
> +static int dgnc_read_channel_info(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               char __user *buffer, ssize_t *lenp, loff_t *ppos);
> +static int dgnc_open_channel_sniff(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               void *buffer, ssize_t *lenp, loff_t *ppos);
> +static int dgnc_close_channel_sniff(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               void *buffer, ssize_t *lenp, loff_t *ppos);
> +static int dgnc_read_channel_sniff(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               char __user  *buffer, ssize_t *lenp, loff_t *ppos);
> +static int dgnc_read_channel_custom_ttyname(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               char __user *buffer, ssize_t *lenp, loff_t *ppos);
> +static int dgnc_read_channel_custom_prname(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               char __user *buffer, ssize_t *lenp, loff_t *ppos);
> +
> +static struct dgnc_proc_entry dgnc_channel_table[] = {
> +       {DGNC_PORT_INFO, "info", 0600, NULL, NULL, NULL, &dgnc_read_channel_info, NULL,
> +               NULL, __SEMAPHORE_INITIALIZER(dgnc_channel_table[0].excl_sem, 1), 0, NULL },
> +       {DGNC_PORT_SNIFF, "sniff", 0600, NULL, &dgnc_open_channel_sniff, &dgnc_close_channel_sniff, &dgnc_read_channel_sniff, NULL,
> +               NULL, __SEMAPHORE_INITIALIZER(dgnc_channel_table[1].excl_sem, 1), 0, NULL},
> +        {DGNC_PORT_CUSTOM_TTYNAME, "ttyname", 0600, NULL, NULL, NULL, &dgnc_read_channel_custom_ttyname, NULL,
> +               NULL, __SEMAPHORE_INITIALIZER(dgnc_channel_table[2].excl_sem, 1), 0, NULL },
> +        {DGNC_PORT_CUSTOM_PRNAME, "prname", 0600, NULL, NULL, NULL, &dgnc_read_channel_custom_prname, NULL,
> +               NULL, __SEMAPHORE_INITIALIZER(dgnc_channel_table[3].excl_sem, 1), 0, NULL },
> +       {0}
> +};
> +
> +
> +/*
> + * dgnc_test_perm does NOT grant the superuser all rights automatically, because
> + * some entries are readonly even to root.
> + */
> +static inline int dgnc_test_perm(int mode, int op)
> +{
> +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
> +       if (!current->euid)
> +#else
> +       if (!current_euid())
> +#endif
> +               mode >>= 6;
> +       else if (in_egroup_p(0))
> +               mode >>= 3;
> +       if ((mode & op & 0007) == op)
> +               return 0;
> +       if (capable(CAP_SYS_ADMIN))
> +               return 0;
> +       return -EACCES;
> +}
> +
> +
> +/*
> + * /proc/sys support
> + */
> +static inline int dgnc_proc_match(int len, const char *name, struct proc_dir_entry *de)
> +{
> +       if (!de || !de->low_ino)
> +               return 0;
> +       if (de->namelen != len)
> +               return 0;
> +       return !memcmp(name, de->name, len);
> +}
> +
> +
> +/*
> + *  Scan the entries in table and add them all to /proc at the position
> + *  referred to by "root"
> + */
> +static void dgnc_register_proc_table(struct dgnc_proc_entry *table, struct proc_dir_entry *root)
> +{
> +       struct proc_dir_entry *de;
> +       int len;
> +       mode_t mode;
> +
> +       for (; table->magic; table++) {
> +               /* Can't do anything without a proc name. */
> +               if (!table->name) {
> +                       DPR_PROC(("dgnc_register_proc_table, no name...\n"));
> +                       continue;
> +               }
> +
> +               /* Maybe we can't do anything with it... */
> +               if (!table->read_handler && !table->write_handler && !table->child) {
> +                       DPR_PROC((KERN_WARNING "DGNC PROC: Can't register %s\n", table->name));
> +                       continue;
> +               }
> +
> +               len = strlen(table->name);
> +               mode = table->mode;
> +               de = NULL;
> +
> +               if (!table->child) {
> +                       mode |= S_IFREG;
> +               } else {
> +                       mode |= S_IFDIR;
> +                       for (de = root->subdir; de; de = de->next) {
> +                               if (dgnc_proc_match(len, table->name, de))
> +                                       break;
> +                       }
> +
> +                       /* If the subdir exists already, de is non-NULL */
> +               }
> +
> +               if (!de) {
> +                       de = create_proc_entry(table->name, mode, root);
> +                       if (!de)
> +                               continue;
> +                       de->data = (void *) table;
> +                       if (!table->child) {
> +                               de->proc_iops = &dgnc_proc_inode_ops;
> +                               de->proc_fops = &dgnc_proc_file_ops;
> +                       }
> +               }
> +
> +               table->de = de;
> +
> +               if (de->mode & S_IFDIR)
> +                       dgnc_register_proc_table(table->child, de);
> +
> +       }
> +}
> +
> +
> +
> +/*
> + * Unregister a /proc sysctl table and any subdirectories.
> + */
> +static void dgnc_unregister_proc_table(struct dgnc_proc_entry *table, struct proc_dir_entry *root)
> +{
> +       struct proc_dir_entry *de;
> +
> +       for (; table->magic; table++) {
> +               if (!(de = table->de))
> +                       continue;
> +
> +               if (de->mode & S_IFDIR) {
> +                       if (!table->child) {
> +                               DPR_PROC((KERN_ALERT "Help - malformed sysctl tree on free\n"));
> +                               continue;
> +                       }
> +
> +                       /* recurse down into subdirectory... */
> +                       DPR_PROC(("Recursing down a directory...\n"));
> +                       dgnc_unregister_proc_table(table->child, de);
> +
> +                       /* Don't unregister directories which still have entries.. */
> +                       if (de->subdir)
> +                               continue;
> +               }
> +
> +               /* Don't unregister proc entries that are still being used.. */
> +               if ((atomic_read(&de->count)) != 1) {
> +                       DPR_PROC(("proc entry in use... Not removing...\n"));
> +                       continue;
> +               }
> +
> +               dgnc_remove_proc_entry(de);
> +               table->de = NULL;
> +       }
> +}
> +
> +
> +
> +static int dgnc_gen_proc_open(struct inode *inode, struct file *file)
> +{
> +       struct proc_dir_entry *de;
> +       struct dgnc_proc_entry *entry;
> +       int (*handler) (struct dgnc_proc_entry *table, int dir, struct file *filp,
> +               void *buffer, ssize_t *lenp, loff_t *ppos);
> +       int ret = 0, error = 0;
> +
> +       de = (struct proc_dir_entry *) PDE(file->f_dentry->d_inode);
> +       if (!de || !de->data) {
> +               ret = -ENXIO;
> +               goto done;
> +       }
> +
> +       entry = (struct dgnc_proc_entry *) de->data;
> +       if (!entry) {
> +               ret = -ENXIO;
> +               goto done;
> +       }
> +
> +       down(&entry->excl_sem);
> +
> +       if (entry->excl_cnt) {
> +               ret = -EBUSY;
> +       } else {
> +               entry->excl_cnt++;
> +
> +               handler = entry->open_handler;
> +               if (handler) {
> +                       error = (*handler) (entry, OUTBOUND, file, NULL, NULL, NULL);
> +                       if (error) {
> +                               entry->excl_cnt--;
> +                               ret = error;
> +                       }
> +               }
> +       }
> +
> +       up(&entry->excl_sem);
> +
> +done:
> +
> +       return ret;
> +}
> +
> +
> +static int dgnc_gen_proc_close(struct inode *inode, struct file *file)
> +{
> +       struct proc_dir_entry *de;
> +       int (*handler) (struct dgnc_proc_entry *table, int dir, struct file *filp,
> +               void *buffer, ssize_t *lenp, loff_t *ppos);
> +       struct dgnc_proc_entry *entry;
> +       int error = 0;
> +
> +       de = (struct proc_dir_entry *) PDE(file->f_dentry->d_inode);
> +       if (!de || !de->data)
> +               goto done;
> +
> +       entry = (struct dgnc_proc_entry *) de->data;
> +       if (!entry)
> +               goto done;
> +
> +       down(&entry->excl_sem);
> +
> +       if (entry->excl_cnt)
> +               entry->excl_cnt = 0;
> +
> +
> +       handler = entry->close_handler;
> +       if (handler) {
> +               error = (*handler) (entry, OUTBOUND, file, NULL, NULL, NULL);
> +       }
> +
> +       up(&entry->excl_sem);
> +
> +done:
> +       return 0;
> +}
> +
> +
> +static ssize_t dgnc_gen_proc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
> +{
> +       struct proc_dir_entry *de;
> +       struct dgnc_proc_entry *entry;
> +       int (*handler) (struct dgnc_proc_entry *table, int dir, struct file *filp,
> +               char __user *buffer, ssize_t *lenp, loff_t *ppos2);
> +       ssize_t res;
> +       ssize_t error;
> +
> +       de = (struct proc_dir_entry*) PDE(file->f_dentry->d_inode);
> +       if (!de || !de->data)
> +               return -ENXIO;
> +
> +       entry = (struct dgnc_proc_entry *) de->data;
> +       if (!entry)
> +               return -ENXIO;
> +
> +       /* Test for read permission */
> +       if (dgnc_test_perm(entry->mode, 4))
> +               return -EPERM;
> +
> +       res = count;
> +
> +       handler = entry->read_handler;
> +       if (!handler)
> +               return -ENXIO;
> +
> +       error = (*handler) (entry, OUTBOUND, file, buf, &res, ppos);
> +       if (error)
> +               return error;
> +
> +       return res;
> +}
> +
> +
> +static ssize_t dgnc_gen_proc_write(struct file *file, const char __user *buf,
> +                                   size_t count, loff_t *ppos)
> +{
> +       struct proc_dir_entry *de;
> +       struct dgnc_proc_entry *entry;
> +       int (*handler) (struct dgnc_proc_entry *table, int dir, struct file *filp,
> +               const char __user *buffer, ssize_t *lenp, loff_t *ppos2);
> +       ssize_t res;
> +       ssize_t error;
> +
> +       de = (struct proc_dir_entry *) PDE(file->f_dentry->d_inode);
> +       if (!de || !de->data)
> +               return -ENXIO;
> +
> +       entry = (struct dgnc_proc_entry *) de->data;
> +       if (!entry)
> +               return -ENXIO;
> +
> +       /* Test for write permission */
> +       if (dgnc_test_perm(entry->mode, 2))
> +               return -EPERM;
> +
> +       res = count;
> +
> +       handler = entry->write_handler;
> +       if (!handler)
> +               return -ENXIO;
> +
> +       error = (*handler) (entry, INBOUND, file, buf, &res, ppos);
> +       if (error)
> +               return error;
> +
> +       return res;
> +}
> +
> +
> +static int dgnc_proc_chk_perm(struct inode *inode, int op)
> +{
> +       return dgnc_test_perm(inode->i_mode, op);
> +}
> +
> +
> +/*
> + *  Return what is (hopefully) useful information about the
> + *  driver.
> + */
> +static int dgnc_read_info(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               char __user *buffer, ssize_t *lenp, loff_t *ppos)
> +{
> +       static int done = 0;
> +       static char buf[4096];
> +       char *p = buf;
> +
> +       DPR_PROC(("dgnc_proc_info\n"));
> +
> +       if (done) {
> +               done = 0;
> +               *lenp = 0;
> +               return 0;
> +       }
> +
> +       p += sprintf(p, "Driver:\t\t%s\n", DG_NAME);
> +       p += sprintf(p, "\n");
> +       p += sprintf(p, "Debug:\t\t0x%x\n", dgnc_debug);
> +       p += sprintf(p, "Sysfs Support:\t0x1\n");
> +       p += sprintf(p, "Rawreadok:\t0x%x\n", dgnc_rawreadok);
> +       p += sprintf(p, "Max Boards:\t%d\n", MAXBOARDS);
> +       p += sprintf(p, "Total Boards:\t%d\n", dgnc_NumBoards);
> +       p += sprintf(p, "Poll rate:\t%dms\n", dgnc_poll_tick);
> +       p += sprintf(p, "Poll counter:\t%ld\n", dgnc_poll_counter);
> +       p += sprintf(p, "State:\t\t%s\n", dgnc_driver_state_text[dgnc_driver_state]);
> +
> +       if (copy_to_user(buffer, buf, (p - (char *) buf)))
> +               return -EFAULT;
> +
> +       *lenp = p - (char *) buf;
> +       *ppos += p - (char *) buf;
> +       done = 1;
> +       return 0;
> +}
> +
> +
> +/*
> + *  When writing to the "info" entry point, I actually allow one
> + *  to modify certain variables.  This may be a sleazy overload
> + *  of this /proc entry, but I don't want:
> + *
> + *     a. to clutter /proc more than I have to
> + *     b. to overload the "config" entry, which would be somewhat
> + *        more natural
> + *     c. necessarily advertise the fact this ability exists
> + *
> + *  The continued support of this feature has not yet been
> + *  guaranteed.
> + *
> + *  Writing operates on a "state machine" principle.
> + *
> + *  State 0: waiting for a symbol to start.  Waiting for anything
> + *           which isn't " ' = or whitespace.
> + *  State 1: reading a symbol.  If the character is a space, move
> + *           to state 2.  If =, move to state 3.  If " or ', move
> + *           to state 0.
> + *  State 2: Waiting for =... suck whitespace.  If anything other
> + *           than whitespace, drop to state 0.
> + *  State 3: Got =.  Suck whitespace waiting for value to start.
> + *           If " or ', go to state 4 (and remember which quote it
> + *           was).  Otherwise, go to state 5.
> + *  State 4: Reading value, within quotes.  Everything is added to
> + *           value up until the matching quote.  When you hit the
> + *           matching quote, try to set the variable, then state 0.
> + *  State 5: Reading value, outside quotes.  Everything not " ' =
> + *           or whitespace goes in value.  Hitting one of the
> + *           terminators tosses us back to state 0 after trying to
> + *           set the variable.
> + */
> +typedef enum {
> +       INFO_NONE, INFO_INT, INFO_CHAR, INFO_SHORT,
> +       INFO_LONG, INFO_PTR, INFO_STRING, INFO_END
> +} info_proc_var_val;
> +
> +static struct {
> +       char              *name;
> +       info_proc_var_val  type;
> +       int                rw;       /* 0=readonly */
> +       void              *val_ptr;
> +} dgnc_info_vars[] = {
> +       { "rawreadok",   INFO_INT,    1, (void *) &dgnc_rawreadok },
> +        { "pollrate",    INFO_INT,    1, (void *) &dgnc_poll_tick },
> +       { NULL, INFO_NONE, 0, NULL },
> +       { "debug",   INFO_LONG,   1, (void *) &dgnc_debug },
> +       { NULL, INFO_END, 0, NULL }
> +};
> +
> +static void dgnc_set_info_var(char *name, char *val)
> +{
> +       int i;
> +       unsigned long newval;
> +       unsigned char charval;
> +       unsigned short shortval;
> +       unsigned int intval;
> +
> +       for (i = 0; dgnc_info_vars[i].type != INFO_END; i++) {
> +               if (dgnc_info_vars[i].name)
> +                       if (!strcmp(name, dgnc_info_vars[i].name))
> +                               break;
> +       }
> +
> +       if (dgnc_info_vars[i].type == INFO_END)
> +               return;
> +       if (dgnc_info_vars[i].rw == 0)
> +               return;
> +       if (dgnc_info_vars[i].val_ptr == NULL)
> +               return;
> +
> +       newval = simple_strtoul(val, NULL, 0 );
> +
> +       switch (dgnc_info_vars[i].type) {
> +       case INFO_CHAR:
> +               charval = newval & 0xff;
> +               APR(("Modifying %s (%lx) <= 0x%02x  (%d)\n",
> +                          name, (long)(dgnc_info_vars[i].val_ptr ),
> +                          charval, charval));
> +               *(uchar *)(dgnc_info_vars[i].val_ptr) = charval;
> +               break;
> +       case INFO_SHORT:
> +               shortval = newval & 0xffff;
> +               APR(("Modifying %s (%lx) <= 0x%04x  (%d)\n",
> +                          name, (long)(dgnc_info_vars[i].val_ptr),
> +                          shortval, shortval));
> +               *(ushort *)(dgnc_info_vars[i].val_ptr) = shortval;
> +               break;
> +       case INFO_INT:
> +               intval = newval & 0xffffffff;
> +               APR(("Modifying %s (%lx) <= 0x%08x  (%d)\n",
> +                          name, (long)(dgnc_info_vars[i].val_ptr),
> +                          intval, intval));
> +               *(uint *)(dgnc_info_vars[i].val_ptr) = intval;
> +               break;
> +       case INFO_LONG:
> +               APR(("Modifying %s (%lx) <= 0x%lx  (%ld)\n",
> +                          name, (long)(dgnc_info_vars[i].val_ptr),
> +                          newval, newval));
> +               *(ulong *)(dgnc_info_vars[i].val_ptr) = newval;
> +               break;
> +       case INFO_PTR:
> +       case INFO_STRING:
> +       case INFO_END:
> +       case INFO_NONE:
> +       default:
> +               break;
> +       }
> +}
> +
> +static int dgnc_write_info(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               const char __user *buffer, ssize_t *lenp, loff_t *ppos)
> +{
> +       static int state = 0;
> +       #define MAXSYM 255
> +       static int sympos, valpos;
> +       static char sym[MAXSYM + 1];
> +       static char val[MAXSYM + 1];
> +       static int quotchar = 0;
> +
> +       int i;
> +
> +       long len;
> +       #define INBUFLEN 256
> +       char inbuf[INBUFLEN];
> +
> +       if (*ppos == 0) {
> +               state = 0;
> +               sympos = 0; sym[0] = 0;
> +               valpos = 0; val[0] = 0;
> +               quotchar = 0;
> +       }
> +
> +       if ((!*lenp) || (dir != INBOUND)) {
> +               *lenp = 0;
> +               return 0;
> +       }
> +
> +       len = *lenp;
> +
> +       if (len > INBUFLEN - 1)
> +               len = INBUFLEN - 1;
> +
> +       if (copy_from_user(inbuf, buffer, len))
> +               return -EFAULT;
> +
> +       inbuf[len] = 0;
> +
> +       for (i = 0; i < len; i++) {
> +               unsigned char c = inbuf[i];
> +
> +               switch (state) {
> +               case 0:
> +                       quotchar = sympos = valpos = sym[0] = val[0] = 0;
> +                       if (!isspace(c) && (c != '\"') &&
> +                           (c != '\'') && (c != '=')) {
> +                               sym[sympos++] = c;
> +                               state = 1;
> +                               break;
> +                       }
> +                       break;
> +               case 1:
> +                       if (isspace(c)) {
> +                               sym[sympos] = 0;
> +                               state = 2;
> +                               break;
> +                       }
> +                       if (c == '=') {
> +                               sym[sympos] = 0;
> +                               state = 3;
> +                               break;
> +                       }
> +                       if ((c == '\"' ) || ( c == '\'' )) {
> +                               state = 0;
> +                               break;
> +                       }
> +                       if (sympos < MAXSYM) sym[sympos++] = c;
> +                       break;
> +               case 2:
> +                       if (isspace(c)) break;
> +                       if (c == '=') {
> +                               state = 3;
> +                               break;
> +                       }
> +                       if ((c != '\"') && (c != '\'')) {
> +                               quotchar = sympos = valpos = sym[0] = val[0] = 0;
> +                               sym[sympos++] = c;
> +                               state = 1;
> +                               break;
> +                       }
> +                       state = 0;
> +                       break;
> +               case 3:
> +                       if (isspace(c)) break;
> +                       if (c == '=') {
> +                               state = 0;
> +                               break;
> +                       }
> +                       if ((c == '\"') || (c == '\'')) {
> +                               state = 4;
> +                               quotchar = c;
> +                               break;
> +                       }
> +                       val[valpos++] = c;
> +                       state = 5;
> +                       break;
> +               case 4:
> +                       if (c == quotchar) {
> +                               val[valpos] = 0;
> +                               dgnc_set_info_var(sym, val);
> +                               state = 0;
> +                               break;
> +                       }
> +                       if (valpos < MAXSYM) val[valpos++] = c;
> +                       break;
> +               case 5:
> +                       if (isspace(c) || (c == '\"') ||
> +                           (c == '\'') || (c == '=')) {
> +                               val[valpos] = 0;
> +                               dgnc_set_info_var(sym, val);
> +                               state = 0;
> +                               break;
> +                       }
> +                       if (valpos < MAXSYM) val[valpos++] = c;
> +                       break;
> +               default:
> +                       break;
> +               }
> +       }
> +
> +       *lenp = len;
> +       *ppos += len;
> +
> +       return len;
> +}
> +
> +
> +/*
> + *  Return mknod information for the driver's devices.
> + */
> +static int dgnc_read_mknod(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               char __user *buffer, ssize_t *lenp, loff_t *ppos)
> +{
> +       static int done = 0;
> +       static char buf[4096];
> +       char *p = buf;
> +       int i = 0;
> +
> +       DPR_PROC(("dgnc_proc_info\n"));
> +
> +       if (done) {
> +               done = 0;
> +               *lenp = 0;
> +               return 0;
> +       }
> +
> +       DPR_PROC(("dgnc_proc_mknod\n"));
> +
> +       p += sprintf(p, "#\tCreate the management devices.\n");
> +
> +       for (i = 0; i < MAXMGMTDEVICES; i++) {
> +               char tmp[100];
> +               sprintf(tmp, "/dev/dg/dgnc/mgmt%d", i);
> +               p += sprintf(p, "%s\t%d\t%d\t%d\n",
> +                       tmp, dgnc_Major, i, 1);
> +       }
> +
> +       if (copy_to_user(buffer, buf, (p - (char *) buf)))
> +               return -EFAULT;
> +
> +       *lenp = p - (char *) buf;
> +       *ppos += p - (char *) buf;
> +       done = 1;
> +       return 0;
> +}
> +
> +
> +/*
> + *  Return what is (hopefully) useful information about the specific board.
> + */
> +static int dgnc_read_board_info(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               char __user *buffer, ssize_t *lenp, loff_t *ppos)
> +{
> +       struct board_t  *brd;
> +       static int done = 0;
> +       static char buf[4096];
> +       char *p = buf;
> +       char *name;
> +
> +       DPR_PROC(("dgnc_proc_brd_info\n"));
> +
> +       brd = (struct board_t *) table->data;
> +
> +       if (done || !brd || (brd->magic != DGNC_BOARD_MAGIC)) {
> +               done = 0;
> +               *lenp = 0;
> +               return 0;
> +       }
> +
> +       name = brd->name;
> +
> +       p += sprintf(p, "Board Name = %s\n", name);
> +       if (brd->serial_num[0] == '\0')
> +               p += sprintf(p, "Serial number = <UNKNOWN>\n");
> +       else
> +               p += sprintf(p, "Serial number = %s\n", brd->serial_num);
> +
> +       p += sprintf(p, "Board Type = %d\n", brd->type);
> +       p += sprintf(p, "Number of Ports = %d\n", brd->nasync);
> +
> +       /*
> +        * 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);
> +        p += sprintf(p, "Bus = %d\n", brd->pci_bus);
> +        p += sprintf(p, "Slot = %d\n", brd->pci_slot);
> +
> +       /*
> +        * report the physical addresses assigned to us when we got
> +        * registered
> +        */
> +        p += sprintf(p, "Memory Base Address = 0x%lx\n", brd->membase);
> +        p += sprintf(p, "Remapped Memory Base Address = 0x%p\n", brd->re_map_membase);
> +
> +        p += sprintf(p, "Current state of board = %s\n", dgnc_state_text[brd->state]);
> +        p += sprintf(p, "Interrupt #: %d. Times interrupted: %ld\n",
> +               brd->irq, brd->intr_count);
> +
> +        p += sprintf(p, "TX interrupts: %ld  RX interrupts: %ld\n",
> +               brd->intr_tx, brd->intr_rx);
> +        p += sprintf(p, "Modem interrupts: %ld\n", brd->intr_modem);
> +
> +        p += sprintf(p, "Majors allocated to board = TTY: %d PR: %d\n",
> +               brd->SerialDriver.major, brd->PrintDriver.major);
> +
> +
> +       if (copy_to_user(buffer, buf, (p - (char *) buf)))
> +               return -EFAULT;
> +
> +       *lenp = p - (char *) buf;
> +       *ppos += p - (char *) buf;
> +       done = 1;
> +       return 0;
> +}
> +
> +
> +
> +static int dgnc_read_board_vpd(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               char __user *buffer, ssize_t *lenp, loff_t *ppos)
> +{
> +       struct board_t  *brd;
> +       static int done = 0;
> +       static char buf[4096];
> +       int i = 0, j = 0;
> +       char *p = buf;
> +
> +       DPR_PROC(("dgnc_proc_brd_info\n"));
> +
> +       brd = (struct board_t *) table->data;
> +
> +       if (done || !brd || (brd->magic != DGNC_BOARD_MAGIC)) {
> +               done = 0;
> +               *lenp = 0;
> +               return 0;
> +       }
> +
> +       p += sprintf(p, "\n      0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F          ASCII\n");
> +
> +       for (i = 0; i < 0x40 * 2; i++) {
> +               j = i;
> +               if (!(i % 16)) {
> +                       if (j > 0) {
> +                               p += sprintf(p, "    ");
> +                               for (j = i - 16; j < i; j++) {
> +                                       if (0x20 <= brd->vpd[j] && brd->vpd[j] <= 0x7e)
> +                                               p += sprintf(p, "%c", brd->vpd[j]);
> +                                       else
> +                                               p += sprintf(p, ".");
> +                               }
> +                               p += sprintf(p, "\n");
> +                       }
> +                       p += sprintf(p, "%04X ", i);
> +               }
> +               p += sprintf(p, "%02X ", brd->vpd[i]);
> +       }
> +       if (!(i % 16)) {
> +               p += sprintf(p, "    ");
> +               for (j = i - 16; j < i; j++) {
> +                       if (0x20 <= brd->vpd[j] && brd->vpd[j] <= 0x7e)
> +                               p += sprintf(p, "%c", brd->vpd[j]);
> +                       else
> +                               p += sprintf(p, ".");
> +               }
> +               p += sprintf(p, "\n");
> +       }
> +
> +       p += sprintf(p, "\n");
> +
> +       if (copy_to_user(buffer, buf, (p - (char *) buf)))
> +               return -EFAULT;
> +
> +       *lenp = p - (char *) buf;
> +       *ppos += p - (char *) buf;
> +       done = 1;
> +       return 0;
> +}
> +
> +
> +/*
> + *  Return what is (hopefully) useful stats about the specific board's ttys
> + */
> +static int dgnc_read_board_ttystats(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               char __user *buffer, ssize_t *lenp, loff_t *ppos)
> +{
> +       struct board_t  *brd;
> +       static int done = 0;
> +       static char buf[4096];
> +       char *p = buf;
> +       int i = 0;
> +
> +       DPR_PROC(("dgnc_proc_brd_info\n"));
> +
> +       brd = (struct board_t *) table->data;
> +
> +       if (done || !brd || (brd->magic != DGNC_BOARD_MAGIC)) {
> +               done = 0;
> +               *lenp = 0;
> +               return 0;
> +       }
> +
> +       /* Prepare the Header Labels */
> +       p += sprintf(p, "%2s %10s %23s %10s %9s\n",
> +               "Ch", "Chars Rx", "  Rx Par--Brk--Frm--Ovr",
> +               "Chars Tx", "XON XOFF");
> +
> +        for (i = 0; i < brd->nasync; i++) {
> +
> +               struct channel_t *ch = brd->channels[i];
> +
> +               p += sprintf(p, "%2d ", i);
> +               p += sprintf(p, "%10ld ", ch->ch_rxcount);
> +               p += sprintf(p, "    %4ld %4ld %4ld %4ld ", ch->ch_err_parity,
> +                       ch->ch_err_break, ch->ch_err_frame, ch->ch_err_overrun);
> +               p += sprintf(p, "%10ld ", ch->ch_txcount);
> +               p += sprintf(p, "%4ld %4ld ", ch->ch_xon_sends, ch->ch_xoff_sends);
> +
> +               p += sprintf(p, "\n");
> +       }
> +
> +       if (copy_to_user(buffer, buf, (p - (char *) buf)))
> +               return -EFAULT;
> +
> +       *lenp = p - (char *) buf;
> +       *ppos += p - (char *) buf;
> +       done = 1;
> +       return 0;
> +}
> +
> +
> +/*
> + *  Return what is (hopefully) useful stats about the specific board's tty intrs
> + */
> +static int dgnc_read_board_ttyintr(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               char __user *buffer, ssize_t *lenp, loff_t *ppos)
> +{
> +       struct board_t  *brd;
> +       static int done = 0;
> +       static char buf[4096];
> +       char *p = buf;
> +       int i = 0;
> +
> +       DPR_PROC(("dgnc_proc_brd_info\n"));
> +
> +       brd = (struct board_t *) table->data;
> +
> +       if (done || !brd || (brd->magic != DGNC_BOARD_MAGIC)) {
> +               done = 0;
> +               *lenp = 0;
> +               return 0;
> +       }
> +
> +       /* Prepare the Header Labels */
> +       p += sprintf(p, "%2s     %14s  %14s  %14s\n",
> +               "Ch", "TX interrupts", "RX interrupts", "Modem interrupts");
> +
> +        for (i = 0; i < brd->nasync; i++) {
> +
> +               struct channel_t *ch = brd->channels[i];
> +
> +               p += sprintf(p, "%2d ", i);
> +
> +               p += sprintf(p, "    %14ld  %14ld    %14ld",
> +                       ch->ch_intr_tx, ch->ch_intr_rx, ch->ch_intr_modem);
> +
> +               p += sprintf(p, "\n");
> +       }
> +
> +       if (copy_to_user(buffer, buf, (p - (char *) buf)))
> +               return -EFAULT;
> +
> +       *lenp = p - (char *) buf;
> +       *ppos += p - (char *) buf;
> +       done = 1;
> +       return 0;
> +}
> +
> +
> +/*
> + *  Return what is (hopefully) useful flags about the specific board's ttys
> + */
> +static int dgnc_read_board_ttyflags(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               char __user *buffer, ssize_t *lenp, loff_t *ppos)
> +{
> +       struct board_t  *brd;
> +       static int done = 0;
> +       static char buf[4096];
> +       char *p = buf;
> +       int i = 0;
> +
> +       DPR_PROC(("dgnc_proc_brd_info\n"));
> +
> +       brd = (struct board_t *) table->data;
> +
> +       if (done || !brd || (brd->magic != DGNC_BOARD_MAGIC)) {
> +               done = 0;
> +               *lenp = 0;
> +               return 0;
> +       }
> +
> +       /* Prepare the Header Labels */
> +       p += sprintf(p, "%2s %5s %5s %5s %5s %5s %10s  Line Status Flags\n",
> +               "Ch", "CFlag", "IFlag", "OFlag", "LFlag", "DFlag", "Baud");
> +
> +        for (i = 0; i < brd->nasync; i++) {
> +
> +               struct channel_t *ch = brd->channels[i];
> +
> +               p += sprintf(p, "%2d ", i);
> +               p += sprintf(p, "%5x ", ch->ch_c_cflag);
> +               p += sprintf(p, "%5x ", ch->ch_c_iflag);
> +               p += sprintf(p, "%5x ", ch->ch_c_oflag);
> +               p += sprintf(p, "%5x ", ch->ch_c_lflag);
> +               p += sprintf(p, "%5x ", ch->ch_digi.digi_flags);
> +               p += sprintf(p, "%10d ", ch->ch_old_baud);
> +
> +               if (!ch->ch_open_count) {
> +                       p += sprintf(p, " -- -- -- -- -- -- --") ;
> +               } else {
> +                       p += sprintf(p, " op %s %s %s %s %s %s",
> +                               (ch->ch_mostat & UART_MCR_RTS) ? "rs" : "--",
> +                               (ch->ch_mistat & UART_MSR_CTS) ? "cs" : "--",
> +                               (ch->ch_mostat & UART_MCR_DTR) ? "tr" : "--",
> +                               (ch->ch_mistat & UART_MSR_DSR) ? "mr" : "--",
> +                               (ch->ch_mistat & UART_MSR_DCD) ? "cd" : "--",
> +                               (ch->ch_mistat & UART_MSR_RI)  ? "ri" : "--");
> +               }
> +
> +               p += sprintf(p, "\n");
> +       }
> +       if (copy_to_user(buffer, buf, (p - (char *) buf)))
> +               return -EFAULT;
> +
> +       *lenp = p - (char *) buf;
> +       *ppos += p - (char *) buf;
> +       done = 1;
> +       return 0;
> +}
> +
> +
> +/*
> + *  Return mknod information for the board's devices.
> + */
> +static int dgnc_read_board_mknod(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               char __user *buffer, ssize_t *lenp, loff_t *ppos)
> +{
> +       struct board_t  *brd;
> +       static int done = 0;
> +       static char buf[4096];
> +       char str[MAXTTYNAMELEN];
> +       char *p = buf;
> +
> +       DPR_PROC(("dgnc_proc_brd_info\n"));
> +
> +       brd = (struct board_t *) table->data;
> +
> +       if (done || !brd || (brd->magic != DGNC_BOARD_MAGIC)) {
> +               done = 0;
> +               *lenp = 0;
> +               return 0;
> +       }
> +
> +       /*
> +        * For each board, output the device information in
> +        * a handy table format...
> +        */
> +       p += sprintf(p, "# Create the TTY and PR devices\n");
> +
> +       /* TTY devices */
> +       sprintf(str, "ttyn%d%%p", brd->boardnum + 1);
> +       p += sprintf(p, "%s\t\t\t%d\t%d\t%d\n", str,
> +               brd->dgnc_Serial_Major, 0, brd->maxports);
> +
> +       /* PR devices */
> +       sprintf(str, "prn%d%%p", brd->boardnum + 1);
> +       p += sprintf(p, "%s\t\t\t%d\t%d\t%d\n", str,
> +               brd->dgnc_TransparentPrint_Major, 128, brd->maxports);
> +
> +       if (copy_to_user(buffer, buf, (p - (char *) buf)))
> +               return -EFAULT;
> +
> +       *lenp = p - (char *) buf;
> +       *ppos += p - (char *) buf;
> +       done = 1;
> +       return 0;
> +}
> +
> +
> +/*
> + *  Return what is (hopefully) useful information about the specific channel.
> + */
> +static int dgnc_read_channel_info(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               char __user *buffer, ssize_t *lenp, loff_t *ppos)
> +{
> +       struct channel_t *ch;
> +       static int done = 0;
> +       static char buf[4096];
> +       char *p = buf;
> +
> +       DPR_PROC(("dgnc_proc_info\n"));
> +
> +       ch = (struct channel_t *) table->data;
> +
> +       if (done || !ch || (ch->magic != DGNC_CHANNEL_MAGIC)) {
> +               done = 0;
> +               *lenp = 0;
> +               return 0;
> +       }
> +
> +       p += sprintf(p, "Port number:\t\t%d\n", ch->ch_portnum);
> +       p += sprintf(p, "\n");
> +
> +       /* Prepare the Header Labels */
> +       p += sprintf(p, "%10s %23s %10s %9s\n",
> +               "Chars Rx", "  Rx Par--Brk--Frm--Ovr",
> +               "Chars Tx", "XON XOFF");
> +       p += sprintf(p, "%10ld ", ch->ch_rxcount);
> +       p += sprintf(p, "    %4ld %4ld %4ld %4ld ", ch->ch_err_parity,
> +               ch->ch_err_break, ch->ch_err_frame, ch->ch_err_overrun);
> +       p += sprintf(p, "%10ld ", ch->ch_txcount);
> +       p += sprintf(p, "%4ld %4ld ", ch->ch_xon_sends, ch->ch_xoff_sends);
> +       p += sprintf(p, "\n\n");
> +
> +       /* Prepare the Header Labels */
> +       p += sprintf(p, "%5s %5s %5s %5s %5s %10s  Line Status Flags\n",
> +               "CFlag", "IFlag", "OFlag", "LFlag", "DFlag", "Baud");
> +
> +       p += sprintf(p, "%5x ", ch->ch_c_cflag);
> +       p += sprintf(p, "%5x ", ch->ch_c_iflag);
> +       p += sprintf(p, "%5x ", ch->ch_c_oflag);
> +       p += sprintf(p, "%5x ", ch->ch_c_lflag);
> +       p += sprintf(p, "%5x ", ch->ch_digi.digi_flags);
> +       p += sprintf(p, "%10d ", ch->ch_old_baud);
> +       if (!ch->ch_open_count) {
> +               p += sprintf(p, " -- -- -- -- -- -- --") ;
> +       } else {
> +               p += sprintf(p, " op %s %s %s %s %s %s",
> +                       (ch->ch_mostat & UART_MCR_RTS) ? "rs" : "--",
> +                       (ch->ch_mistat & UART_MSR_CTS) ? "cs" : "--",
> +                       (ch->ch_mostat & UART_MCR_DTR) ? "tr" : "--",
> +                       (ch->ch_mistat & UART_MSR_DSR) ? "mr" : "--",
> +                       (ch->ch_mistat & UART_MSR_DCD) ? "cd" : "--",
> +                       (ch->ch_mistat & UART_MSR_RI)  ? "ri" : "--");
> +       }
> +       p += sprintf(p, "\n\n");
> +
> +       if (copy_to_user(buffer, buf, (p - (char *) buf)))
> +               return -EFAULT;
> +
> +       *lenp = p - (char *) buf;
> +       *ppos += p - (char *) buf;
> +       done = 1;
> +       return 0;
> +}
> +
> +
> +/*
> + *  Return mknod information for the board's devices.
> + */
> +static int dgnc_read_channel_custom_ttyname(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               char __user *buffer, ssize_t *lenp, loff_t *ppos)
> +{
> +       struct channel_t *ch;
> +       struct board_t  *brd;
> +       static int done = 0;
> +       static char buf[4096];
> +       char *p = buf;
> +       int     cn;
> +       int     bn;
> +
> +       DPR_PROC(("dgnc_proc_brd_info\n"));
> +
> +       ch = (struct channel_t *) table->data;
> +
> +       if (done || !ch || (ch->magic != DGNC_CHANNEL_MAGIC)) {
> +               done = 0;
> +               *lenp = 0;
> +               return 0;
> +       }
> +
> +       brd = ch->ch_bd;
> +
> +       if (done || !brd || (brd->magic != DGNC_BOARD_MAGIC)) {
> +               done = 0;
> +               *lenp = 0;
> +               return 0;
> +       }
> +
> +       bn = brd->boardnum;
> +       cn = ch->ch_portnum;
> +
> +       p += sprintf(p, "ttyn%d%c\n", bn + 1, 'a' + cn);
> +
> +       if (copy_to_user(buffer, buf, (p - (char *) buf)))
> +               return -EFAULT;
> +
> +       *lenp = p - (char *) buf;
> +       *ppos += p - (char *) buf;
> +       done = 1;
> +       return 0;
> +}
> +
> +
> +
> +
> +/*
> + *  Return mknod information for the board's devices.
> + */
> +static int dgnc_read_channel_custom_prname(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               char __user *buffer, ssize_t *lenp, loff_t *ppos)
> +{
> +       struct channel_t *ch;
> +       struct board_t  *brd;
> +       static int done = 0;
> +       static char buf[4096];
> +       char *p = buf;
> +       int     cn;
> +       int     bn;
> +
> +       DPR_PROC(("dgnc_proc_brd_info\n"));
> +
> +       ch = (struct channel_t *) table->data;
> +
> +       if (done || !ch || (ch->magic != DGNC_CHANNEL_MAGIC)) {
> +               done = 0;
> +               *lenp = 0;
> +               return 0;
> +       }
> +
> +       brd = ch->ch_bd;
> +
> +       if (done || !brd || (brd->magic != DGNC_BOARD_MAGIC)) {
> +               done = 0;
> +               *lenp = 0;
> +               return 0;
> +       }
> +
> +       bn = brd->boardnum;
> +       cn = ch->ch_portnum;
> +
> +       p += sprintf(p, "prn%d%c\n", bn + 1, 'a' + cn);
> +
> +       if (copy_to_user(buffer, buf, (p - (char *) buf)))
> +               return -EFAULT;
> +
> +       *lenp = p - (char *) buf;
> +       *ppos += p - (char *) buf;
> +       done = 1;
> +       return 0;
> +}
> +
> +
> +static int dgnc_open_channel_sniff(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               void *buffer, ssize_t *lenp, loff_t *ppos)
> +{
> +       struct channel_t *ch;
> +       ulong  lock_flags;
> +
> +       ch = (struct channel_t *) table->data;
> +
> +       if (!ch || (ch->magic != DGNC_CHANNEL_MAGIC))
> +               return 0;
> +
> +       ch->ch_sniff_buf = dgnc_driver_kzmalloc(SNIFF_MAX, GFP_KERNEL);
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +       ch->ch_sniff_flags |= SNIFF_OPEN;
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +       return 0;
> +}
> +
> +static int dgnc_close_channel_sniff(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               void *buffer, ssize_t *lenp, loff_t *ppos)
> +{
> +       struct channel_t *ch;
> +       ulong  lock_flags;
> +
> +       ch = (struct channel_t *) table->data;
> +
> +       if (!ch || (ch->magic != DGNC_CHANNEL_MAGIC))
> +               return 0;
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +       ch->ch_sniff_flags &= ~(SNIFF_OPEN);
> +       kfree(ch->ch_sniff_buf);
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +       return 0;
> +}
> +
> +
> +/*
> + *    Copy data from the monitoring buffer to the user, freeing space
> + *    in the monitoring buffer for more messages
> + *
> + */
> +static int dgnc_read_channel_sniff(struct dgnc_proc_entry *table, int dir, struct file *filp,
> +                               char __user *buffer, ssize_t *lenp, loff_t *ppos)
> +{
> +       struct channel_t *ch;
> +       int n;
> +       int r;
> +       int offset = 0;
> +       int res = 0;
> +       ssize_t rtn = 0;
> +       ulong  lock_flags;
> +
> +       ch = (struct channel_t *) table->data;
> +
> +       if (!ch || (ch->magic != DGNC_CHANNEL_MAGIC)) {
> +               rtn = -ENXIO;
> +               goto done;
> +       }
> +
> +       /*
> +        *  Wait for some data to appear in the buffer.
> +        */
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       for (;;) {
> +               n = (ch->ch_sniff_in - ch->ch_sniff_out) & SNIFF_MASK;
> +
> +               if (n != 0)
> +                       break;
> +
> +               ch->ch_sniff_flags |= SNIFF_WAIT_DATA;
> +
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +               /*
> +                * Go to sleep waiting until the condition becomes true.
> +                */
> +               rtn = wait_event_interruptible(ch->ch_sniff_wait,
> +                       ((ch->ch_sniff_flags & SNIFF_WAIT_DATA) == 0));
> +
> +               if (rtn)
> +                       goto done;
> +
> +               DGNC_LOCK(ch->ch_lock, lock_flags);
> +       }
> +
> +       /*
> +        *  Read whatever is there.
> +        */
> +
> +       if (n > *lenp)
> +               n = *lenp;
> +
> +       res = n;
> +
> +       r = SNIFF_MAX - ch->ch_sniff_out;
> +
> +       if (r <= n) {
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               rtn = copy_to_user(buffer, ch->ch_sniff_buf + ch->ch_sniff_out, r);
> +               if (rtn) {
> +                       rtn = -EFAULT;
> +                       goto done;
> +               }
> +
> +               DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +               ch->ch_sniff_out = 0;
> +               n -= r;
> +               offset = r;
> +       }
> +
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +       rtn = copy_to_user(buffer + offset, ch->ch_sniff_buf + ch->ch_sniff_out, n);
> +       if (rtn) {
> +               rtn = -EFAULT;
> +               goto done;
> +       }
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       ch->ch_sniff_out += n;
> +       *ppos += res;
> +       rtn = res;
> +//     rtn = 0;
> +
> +       /*
> +        *  Wakeup any thread waiting for buffer space.
> +        */
> +
> +       if (ch->ch_sniff_flags & SNIFF_WAIT_SPACE) {
> +               ch->ch_sniff_flags &= ~SNIFF_WAIT_SPACE;
> +               wake_up_interruptible(&ch->ch_sniff_wait);
> +       }
> +
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +done:
> +       return rtn;
> +}
> +
> +
> +/*
> + * Register the basic /proc/dgnc files that appear whenever
> + * the driver is loaded.
> + */
> +void dgnc_proc_register_basic_prescan(void)
> +{
> +       /*
> +        *      Register /proc/dgnc
> +        */
> +       ProcDGNC = proc_create("dgnc", (0700 | S_IFDIR), NULL, &dgnc_proc_file_ops);
> +       dgnc_register_proc_table(dgnc_table, ProcDGNC);
> +}
> +
> +
> +/*
> + * Register the basic /proc/dgnc files that appear whenever
> + * the driver is loaded.
> + */
> +void dgnc_proc_register_basic_postscan(int board_num)
> +{
> +       int i, j;
> +       char board[10];
> +       sprintf(board, "%d", board_num);
> +
> +       /* Set proc board entry pointer */
> +       dgnc_Board[board_num]->proc_entry_pointer = create_proc_entry(board, (0700 | S_IFDIR), ProcDGNC);
> +
> +       /* Create a new copy of the board_table... */
> +       dgnc_Board[board_num]->dgnc_board_table = dgnc_driver_kzmalloc(sizeof(dgnc_board_table),
> +               GFP_KERNEL);
> +
> +       /* Now copy the default table into that memory */
> +       memcpy(dgnc_Board[board_num]->dgnc_board_table, dgnc_board_table, sizeof(dgnc_board_table));
> +
> +       /* Initialize semaphores in each table slot */
> +       for (i = 0; i < 999; i++) {
> +               if (!dgnc_Board[board_num]->dgnc_board_table[i].magic) {
> +                       break;
> +               }
> +
> +               init_MUTEX(&(dgnc_Board[board_num]->dgnc_board_table[i].excl_sem));
> +               dgnc_Board[board_num]->dgnc_board_table[i].data = dgnc_Board[board_num];
> +
> +       }
> +
> +       /* Register board table into proc */
> +       dgnc_register_proc_table(dgnc_Board[board_num]->dgnc_board_table,
> +               dgnc_Board[board_num]->proc_entry_pointer);
> +
> +       /*
> +        * Add new entries for each port.
> +        */
> +       for (i = 0; i < dgnc_Board[board_num]->nasync; i++) {
> +
> +               char channel[10];
> +               sprintf(channel, "%d", i);
> +
> +               /* Set proc channel entry pointer */
> +               dgnc_Board[board_num]->channels[i]->proc_entry_pointer =
> +                       create_proc_entry(channel, (0700 | S_IFDIR),
> +                       dgnc_Board[board_num]->proc_entry_pointer);
> +
> +               /* Create a new copy of the channel_table... */
> +               dgnc_Board[board_num]->channels[i]->dgnc_channel_table =
> +                       dgnc_driver_kzmalloc(sizeof(dgnc_channel_table), GFP_KERNEL);
> +
> +               /* Now copy the default table into that memory */
> +               memcpy(dgnc_Board[board_num]->channels[i]->dgnc_channel_table,
> +                       dgnc_channel_table, sizeof(dgnc_channel_table));
> +
> +               /* Initialize semaphores in each table slot */
> +               for (j = 0; j < 999; j++) {
> +                       if (!dgnc_Board[board_num]->channels[i]->dgnc_channel_table[j].magic) {
> +                               break;
> +                       }
> +
> +                       init_MUTEX(&(dgnc_Board[board_num]->channels[i]->dgnc_channel_table[j].excl_sem));
> +                       dgnc_Board[board_num]->channels[i]->dgnc_channel_table[j].data =
> +                               dgnc_Board[board_num]->channels[i];
> +               }
> +
> +               /* Register channel table into proc */
> +               dgnc_register_proc_table(dgnc_Board[board_num]->channels[i]->dgnc_channel_table,
> +                       dgnc_Board[board_num]->channels[i]->proc_entry_pointer);
> +       }
> +}
> +
> +
> +static void dgnc_remove_proc_entry(struct proc_dir_entry *pde)
> +{
> +       if (!pde) {
> +               DPR_PROC(("dgnc_remove_proc_entry... NULL entry... not removing...\n"));
> +               return;
> +       }
> +
> +       remove_proc_entry(pde->name, pde->parent);
> +}
> +
> +
> +void dgnc_proc_unregister_all(void)
> +{
> +       int i = 0, j = 0;
> +
> +       /* Walk each board, blowing away their proc entries... */
> +       for (i = 0; i < dgnc_NumBoards; i++) {
> +
> +               /* Walk each channel, blowing away their proc entries... */
> +               for (j = 0; j < dgnc_Board[i]->nasync; j++) {
> +
> +                       dgnc_unregister_proc_table(dgnc_Board[i]->channels[j]->dgnc_channel_table,
> +                               dgnc_Board[i]->channels[j]->proc_entry_pointer);
> +                       dgnc_remove_proc_entry(dgnc_Board[i]->channels[j]->proc_entry_pointer);
> +                       kfree(dgnc_Board[i]->channels[j]->dgnc_channel_table);
> +               }
> +
> +               dgnc_unregister_proc_table(dgnc_Board[i]->dgnc_board_table,
> +                       dgnc_Board[i]->proc_entry_pointer);
> +               dgnc_remove_proc_entry(dgnc_Board[i]->proc_entry_pointer);
> +               kfree(dgnc_Board[i]->dgnc_board_table);
> +       }
> +
> +       /* Blow away the top proc entry */
> +       dgnc_unregister_proc_table(dgnc_table, ProcDGNC);
> +       dgnc_remove_proc_entry(ProcDGNC);
> +}
> diff --git a/drivers/staging/dgnc/dgnc_proc.h b/drivers/staging/dgnc/dgnc_proc.h
> new file mode 100644
> index 0000000..19670e2
> --- /dev/null
> +++ b/drivers/staging/dgnc/dgnc_proc.h
> @@ -0,0 +1,147 @@
> +/*
> + * 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: dgnc_proc.h,v 1.1.1.1 2009/05/20 12:19:19 markh Exp $
> + *
> + *  Description:
> + *
> + *     Describes the private structures used to manipulate the "special"
> + *     proc constructs (not read-only) used by the Digi Neo software.
> + *     The concept is borrowed heavily from the "sysctl" interface of
> + *     the kernel.  I decided not to use the structures and functions
> + *     provided by the kernel for two reasons:
> + *
> + *       1. Due to the planned use of "/proc" in the Neo driver, many
> + *          of the functions of the "sysctl" interface would go unused.
> + *          A simpler interface will be easier to maintain.
> + *
> + *       2. I'd rather divorce our "added package" from the kernel internals.
> + *          If the "sysctl" structures should change, I will be insulated
> + *          from those changes.  These "/proc" entries won't be under the
> + *          "sys" tree anyway, so there is no need to maintain a strict
> + *          dependence relationship.
> + *
> + *  Author:
> + *
> + *     Scott H Kilau
> + *
> + */
> +
> +#ifndef _DGNC_RW_PROC_H
> +#define _DGNC_RW_PROC_H
> +
> +/*
> + *  The list of DGNC entries with r/w capabilities.
> + *  These magic numbers are used for identification purposes.
> + */
> +enum {
> +       DGNC_INFO = 1,                  /* Get info about the running module    */
> +       DGNC_MKNOD = 2,                 /* Get info about driver devices        */
> +       DGNC_BOARD_INFO = 3,            /* Get info about the specific board    */
> +       DGNC_BOARD_VPD = 4,             /* Get info about the board's VPD       */
> +       DGNC_BOARD_TTYSTATS = 5,        /* Get info about the board's tty stats */
> +       DGNC_BOARD_TTYINTR = 6,         /* Get info about the board's tty intrs */
> +       DGNC_BOARD_TTYFLAGS = 7,        /* Get info about the board's tty flags */
> +       DGNC_BOARD_MKNOD = 8,           /* Get info about board devices         */
> +       DGNC_PORT_INFO = 9,             /* Get info about the specific port     */
> +       DGNC_PORT_SNIFF = 10,           /* Sniff data in/out of specific port   */
> +       DGNC_PORT_CUSTOM_TTYNAME = 11,  /* Get info about UDEV tty name         */
> +       DGNC_PORT_CUSTOM_PRNAME = 12,   /* Get info about UDEV pr name          */
> +};
> +
> +/*
> + *  Directions for proc handlers
> + */
> +enum {
> +        INBOUND = 1,           /* Data being written to kernel */
> +        OUTBOUND = 2,          /* Data being read from the kernel */
> +};
> +
> +/*
> + *  Each entry in a DGNC proc directory is described with a
> + *  "dgnc_proc_entry" structure.  A collection of these
> + *  entries (in an array) represents the members associated
> + *  with a particular "/proc" directory, and is referred to
> + *  as a table.  All "tables" are terminated by an entry with
> + *  zeros for every member.
> + *
> + *  The structure members are as follows:
> + *
> + *    int magic              -- ID number associated with this particular
> + *                              entry.  Should be unique across all of
> + *                              DGNC.
> + *
> + *    const char *name       -- ASCII name associated with the /proc entry.
> + *
> + *    mode_t mode            -- File access permisssions for the /proc entry.
> + *
> + *    dgnc_proc_entry *child -- When set, this entry refers to a directory,
> + *                              and points to the table which describes the
> + *                              entries in the subdirectory
> + *
> + *    dgnc_proc_handler *open_handler -- When set, points to the fxn which
> + *                                       does any "extra" open stuff.
> + *
> + *    dgnc_proc_handler *close_handler -- When set, points to the fxn which
> + *                                        does any "extra" close stuff.
> + *
> + *    dgnc_proc_handler *read_handler -- When set, points to the fxn which
> + *                                       handle outbound data flow
> + *
> + *    dgnc_proc_handler *write_handler -- When set, points to the fxn which
> + *                                        handles inbound data flow
> + *
> + *    struct proc_dir_entry *de -- Pointer to the directory entry for this
> + *                                 object once registered.  Used to grab
> + *                                 the handle of the object for
> + *                                 unregistration
> + *
> + *    void *data;                 When set, points to the parent structure
> + *
> + */
> +
> +struct dgnc_proc_entry {
> +       int             magic;          /* Integer identifier   */
> +       const char      *name;          /* ASCII identifier     */
> +       mode_t          mode;           /* File access permissions */
> +       struct dgnc_proc_entry *child;  /* Child pointer        */
> +
> +       int (*open_handler) (struct dgnc_proc_entry *table, int dir, struct file *filp,
> +               void *buffer, ssize_t *lenp, loff_t *ppos);
> +       int (*close_handler) (struct dgnc_proc_entry *table, int dir, struct file *filp,
> +               void *buffer, ssize_t *lenp, loff_t *ppos);
> +       int (*read_handler) (struct dgnc_proc_entry *table, int dir, struct file *filp,
> +               char __user *buffer, ssize_t *lenp, loff_t *ppos);
> +       int (*write_handler) (struct dgnc_proc_entry *table, int dir, struct file *filp,
> +               const char __user *buffer, ssize_t *lenp, loff_t *ppos);
> +
> +       struct proc_dir_entry *de;      /* proc entry pointer   */
> +       struct semaphore excl_sem;      /* Protects exclusive access var        */
> +       int             excl_cnt;       /* Counts number of curr accesses       */
> +       void            *data;          /* Allows storing a pointer to parent   */
> +};
> +
> +void dgnc_proc_register_basic_prescan(void);
> +void dgnc_proc_register_basic_postscan(int board_num);
> +void dgnc_proc_unregister_all(void);
> +
> +
> +#endif /* _DGNC_RW_PROC_H */
> diff --git a/drivers/staging/dgnc/dgnc_sysfs.c b/drivers/staging/dgnc/dgnc_sysfs.c
> new file mode 100644
> index 0000000..af49e44
> --- /dev/null
> +++ b/drivers/staging/dgnc/dgnc_sysfs.c
> @@ -0,0 +1,761 @@
> +/*
> + * Copyright 2004 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: dgnc_sysfs.c,v 1.1.1.1 2009/05/20 12:19:19 markh Exp $
> + */
> +
> +
> +#include <linux/kernel.h>
> +#include <linux/version.h>
> +#include <linux/module.h>
> +#include <linux/ctype.h>
> +#include <linux/string.h>
> +#include <linux/serial_reg.h>
> +#include <linux/device.h>
> +#include <linux/pci.h>
> +#include <linux/kdev_t.h>
> +
> +#include "dgnc_driver.h"
> +#include "dgnc_proc.h"
> +#include "dgnc_mgmt.h"
> +
> +
> +static ssize_t dgnc_driver_version_show(struct device_driver *ddp, char *buf)
> +{
> +       return snprintf(buf, PAGE_SIZE, "%s\n", DG_PART);
> +}
> +static DRIVER_ATTR(version, S_IRUSR, dgnc_driver_version_show, NULL);
> +
> +
> +static ssize_t dgnc_driver_boards_show(struct device_driver *ddp, char *buf)
> +{
> +       return snprintf(buf, PAGE_SIZE, "%d\n", dgnc_NumBoards);
> +}
> +static DRIVER_ATTR(boards, S_IRUSR, dgnc_driver_boards_show, NULL);
> +
> +
> +static ssize_t dgnc_driver_maxboards_show(struct device_driver *ddp, char *buf)
> +{
> +       return snprintf(buf, PAGE_SIZE, "%d\n", MAXBOARDS);
> +}
> +static DRIVER_ATTR(maxboards, S_IRUSR, dgnc_driver_maxboards_show, NULL);
> +
> +
> +static ssize_t dgnc_driver_pollcounter_show(struct device_driver *ddp, char *buf)
> +{
> +       return snprintf(buf, PAGE_SIZE, "%ld\n", dgnc_poll_counter);
> +}
> +static DRIVER_ATTR(pollcounter, S_IRUSR, dgnc_driver_pollcounter_show, NULL);
> +
> +
> +static ssize_t dgnc_driver_state_show(struct device_driver *ddp, char *buf)
> +{
> +       return snprintf(buf, PAGE_SIZE, "%s\n", dgnc_driver_state_text[dgnc_driver_state]);
> +}
> +static DRIVER_ATTR(state, S_IRUSR, dgnc_driver_state_show, NULL);
> +
> +
> +static ssize_t dgnc_driver_debug_show(struct device_driver *ddp, char *buf)
> +{
> +       return snprintf(buf, PAGE_SIZE, "0x%x\n", dgnc_debug);
> +}
> +
> +static ssize_t dgnc_driver_debug_store(struct device_driver *ddp, const char *buf, size_t count)
> +{
> +       sscanf(buf, "0x%x\n", &dgnc_debug);
> +       return count;
> +}
> +static DRIVER_ATTR(debug, (S_IRUSR | S_IWUSR), dgnc_driver_debug_show, dgnc_driver_debug_store);
> +
> +
> +static ssize_t dgnc_driver_rawreadok_show(struct device_driver *ddp, char *buf)
> +{
> +       return snprintf(buf, PAGE_SIZE, "0x%x\n", dgnc_rawreadok);
> +}
> +
> +static ssize_t dgnc_driver_rawreadok_store(struct device_driver *ddp, const char *buf, size_t count)
> +{
> +       sscanf(buf, "0x%x\n", &dgnc_rawreadok);
> +       return count;
> +}
> +static DRIVER_ATTR(rawreadok, (S_IRUSR | S_IWUSR), dgnc_driver_rawreadok_show, dgnc_driver_rawreadok_store);
> +
> +
> +static ssize_t dgnc_driver_pollrate_show(struct device_driver *ddp, char *buf)
> +{
> +       return snprintf(buf, PAGE_SIZE, "%dms\n", dgnc_poll_tick);
> +}
> +
> +static ssize_t dgnc_driver_pollrate_store(struct device_driver *ddp, const char *buf, size_t count)
> +{
> +       sscanf(buf, "%d\n", &dgnc_poll_tick);
> +       return count;
> +}
> +static DRIVER_ATTR(pollrate, (S_IRUSR | S_IWUSR), dgnc_driver_pollrate_show, dgnc_driver_pollrate_store);
> +
> +
> +void dgnc_create_driver_sysfiles(struct pci_driver *dgnc_driver)
> +{
> +       int rc = 0;
> +       struct device_driver *driverfs = &dgnc_driver->driver;
> +
> +       rc |= driver_create_file(driverfs, &driver_attr_version);
> +       rc |= driver_create_file(driverfs, &driver_attr_boards);
> +       rc |= driver_create_file(driverfs, &driver_attr_maxboards);
> +       rc |= driver_create_file(driverfs, &driver_attr_debug);
> +       rc |= driver_create_file(driverfs, &driver_attr_rawreadok);
> +       rc |= driver_create_file(driverfs, &driver_attr_pollrate);
> +       rc |= driver_create_file(driverfs, &driver_attr_pollcounter);
> +       rc |= driver_create_file(driverfs, &driver_attr_state);
> +       if (rc) {
> +               printk(KERN_ERR "DGNC: sysfs driver_create_file failed!\n");
> +       }
> +}
> +
> +
> +void dgnc_remove_driver_sysfiles(struct pci_driver *dgnc_driver)
> +{
> +       struct device_driver *driverfs = &dgnc_driver->driver;
> +       driver_remove_file(driverfs, &driver_attr_version);
> +       driver_remove_file(driverfs, &driver_attr_boards);
> +       driver_remove_file(driverfs, &driver_attr_maxboards);
> +       driver_remove_file(driverfs, &driver_attr_debug);
> +       driver_remove_file(driverfs, &driver_attr_rawreadok);
> +       driver_remove_file(driverfs, &driver_attr_pollrate);
> +       driver_remove_file(driverfs, &driver_attr_pollcounter);
> +       driver_remove_file(driverfs, &driver_attr_state);
> +}
> +
> +
> +#define DGNC_VERIFY_BOARD(p, bd)                       \
> +       if (!p)                                         \
> +               return (0);                             \
> +                                                       \
> +       bd = dev_get_drvdata(p);                        \
> +       if (!bd || bd->magic != DGNC_BOARD_MAGIC)       \
> +               return (0);                             \
> +       if (bd->state != BOARD_READY)                   \
> +               return (0);                             \
> +
> +
> +
> +static ssize_t dgnc_vpd_show(struct device *p, struct device_attribute *attr, char *buf)
> +{
> +       struct board_t *bd;
> +       int count = 0;
> +       int i = 0;
> +
> +       DGNC_VERIFY_BOARD(p, bd);
> +
> +       count += sprintf(buf + count, "\n      0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F");
> +       for (i = 0; i < 0x40 * 2; i++) {
> +               if (!(i % 16))
> +                       count += sprintf(buf + count, "\n%04X ", i * 2);
> +               count += sprintf(buf + count, "%02X ", bd->vpd[i]);
> +       }
> +       count += sprintf(buf + count, "\n");
> +
> +       return count;
> +}
> +static DEVICE_ATTR(vpd, S_IRUSR, dgnc_vpd_show, NULL);
> +
> +static ssize_t dgnc_serial_number_show(struct device *p, struct device_attribute *attr, char *buf)
> +{
> +       struct board_t *bd;
> +       int count = 0;
> +
> +       DGNC_VERIFY_BOARD(p, bd);
> +
> +       if (bd->serial_num[0] == '\0')
> +               count += sprintf(buf + count, "<UNKNOWN>\n");
> +       else
> +               count += sprintf(buf + count, "%s\n", bd->serial_num);
> +
> +       return count;
> +}
> +static DEVICE_ATTR(serial_number, S_IRUSR, dgnc_serial_number_show, NULL);
> +
> +
> +static ssize_t dgnc_ports_state_show(struct device *p, struct device_attribute *attr, char *buf)
> +{
> +       struct board_t *bd;
> +       int count = 0;
> +       int i = 0;
> +
> +       DGNC_VERIFY_BOARD(p, bd);
> +
> +       for (i = 0; i < bd->nasync; i++) {
> +               count += snprintf(buf + count, PAGE_SIZE - count,
> +                       "%d %s\n", bd->channels[i]->ch_portnum,
> +                       bd->channels[i]->ch_open_count ? "Open" : "Closed");
> +       }
> +       return count;
> +}
> +static DEVICE_ATTR(ports_state, S_IRUSR, dgnc_ports_state_show, NULL);
> +
> +
> +static ssize_t dgnc_ports_baud_show(struct device *p, struct device_attribute *attr, char *buf)
> +{
> +       struct board_t *bd;
> +       int count = 0;
> +       int i = 0;
> +
> +       DGNC_VERIFY_BOARD(p, bd);
> +
> +       for (i = 0; i < bd->nasync; i++) {
> +               count +=  snprintf(buf + count, PAGE_SIZE - count,
> +                       "%d %d\n", bd->channels[i]->ch_portnum, bd->channels[i]->ch_old_baud);
> +       }
> +       return count;
> +}
> +static DEVICE_ATTR(ports_baud, S_IRUSR, dgnc_ports_baud_show, NULL);
> +
> +
> +static ssize_t dgnc_ports_msignals_show(struct device *p, struct device_attribute *attr, char *buf)
> +{
> +       struct board_t *bd;
> +       int count = 0;
> +       int i = 0;
> +
> +       DGNC_VERIFY_BOARD(p, bd);
> +
> +       for (i = 0; i < bd->nasync; i++) {
> +               if (bd->channels[i]->ch_open_count) {
> +                       count += snprintf(buf + count, PAGE_SIZE - count,
> +                               "%d %s %s %s %s %s %s\n", bd->channels[i]->ch_portnum,
> +                               (bd->channels[i]->ch_mostat & UART_MCR_RTS) ? "RTS" : "",
> +                               (bd->channels[i]->ch_mistat & UART_MSR_CTS) ? "CTS" : "",
> +                               (bd->channels[i]->ch_mostat & UART_MCR_DTR) ? "DTR" : "",
> +                               (bd->channels[i]->ch_mistat & UART_MSR_DSR) ? "DSR" : "",
> +                               (bd->channels[i]->ch_mistat & UART_MSR_DCD) ? "DCD" : "",
> +                               (bd->channels[i]->ch_mistat & UART_MSR_RI)  ? "RI"  : "");
> +               } else {
> +                       count += snprintf(buf + count, PAGE_SIZE - count,
> +                               "%d\n", bd->channels[i]->ch_portnum);
> +               }
> +       }
> +       return count;
> +}
> +static DEVICE_ATTR(ports_msignals, S_IRUSR, dgnc_ports_msignals_show, NULL);
> +
> +
> +static ssize_t dgnc_ports_iflag_show(struct device *p, struct device_attribute *attr, char *buf)
> +{
> +       struct board_t *bd;
> +       int count = 0;
> +       int i = 0;
> +
> +       DGNC_VERIFY_BOARD(p, bd);
> +
> +       for (i = 0; i < bd->nasync; i++) {
> +               count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n",
> +                       bd->channels[i]->ch_portnum, bd->channels[i]->ch_c_iflag);
> +       }
> +       return count;
> +}
> +static DEVICE_ATTR(ports_iflag, S_IRUSR, dgnc_ports_iflag_show, NULL);
> +
> +
> +static ssize_t dgnc_ports_cflag_show(struct device *p, struct device_attribute *attr, char *buf)
> +{
> +       struct board_t *bd;
> +       int count = 0;
> +       int i = 0;
> +
> +       DGNC_VERIFY_BOARD(p, bd);
> +
> +       for (i = 0; i < bd->nasync; i++) {
> +               count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n",
> +                       bd->channels[i]->ch_portnum, bd->channels[i]->ch_c_cflag);
> +       }
> +       return count;
> +}
> +static DEVICE_ATTR(ports_cflag, S_IRUSR, dgnc_ports_cflag_show, NULL);
> +
> +
> +static ssize_t dgnc_ports_oflag_show(struct device *p, struct device_attribute *attr, char *buf)
> +{
> +       struct board_t *bd;
> +       int count = 0;
> +       int i = 0;
> +
> +       DGNC_VERIFY_BOARD(p, bd);
> +
> +       for (i = 0; i < bd->nasync; i++) {
> +               count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n",
> +                       bd->channels[i]->ch_portnum, bd->channels[i]->ch_c_oflag);
> +       }
> +       return count;
> +}
> +static DEVICE_ATTR(ports_oflag, S_IRUSR, dgnc_ports_oflag_show, NULL);
> +
> +
> +static ssize_t dgnc_ports_lflag_show(struct device *p, struct device_attribute *attr, char *buf)
> +{
> +       struct board_t *bd;
> +       int count = 0;
> +       int i = 0;
> +
> +       DGNC_VERIFY_BOARD(p, bd);
> +
> +       for (i = 0; i < bd->nasync; i++) {
> +               count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n",
> +                       bd->channels[i]->ch_portnum, bd->channels[i]->ch_c_lflag);
> +       }
> +       return count;
> +}
> +static DEVICE_ATTR(ports_lflag, S_IRUSR, dgnc_ports_lflag_show, NULL);
> +
> +
> +static ssize_t dgnc_ports_digi_flag_show(struct device *p, struct device_attribute *attr, char *buf)
> +{
> +       struct board_t *bd;
> +       int count = 0;
> +       int i = 0;
> +
> +       DGNC_VERIFY_BOARD(p, bd);
> +
> +       for (i = 0; i < bd->nasync; i++) {
> +               count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n",
> +                       bd->channels[i]->ch_portnum, bd->channels[i]->ch_digi.digi_flags);
> +       }
> +       return count;
> +}
> +static DEVICE_ATTR(ports_digi_flag, S_IRUSR, dgnc_ports_digi_flag_show, NULL);
> +
> +
> +static ssize_t dgnc_ports_rxcount_show(struct device *p, struct device_attribute *attr, char *buf)
> +{
> +       struct board_t *bd;
> +       int count = 0;
> +       int i = 0;
> +
> +       DGNC_VERIFY_BOARD(p, bd);
> +
> +       for (i = 0; i < bd->nasync; i++) {
> +               count += snprintf(buf + count, PAGE_SIZE - count, "%d %ld\n",
> +                       bd->channels[i]->ch_portnum, bd->channels[i]->ch_rxcount);
> +       }
> +       return count;
> +}
> +static DEVICE_ATTR(ports_rxcount, S_IRUSR, dgnc_ports_rxcount_show, NULL);
> +
> +
> +static ssize_t dgnc_ports_txcount_show(struct device *p, struct device_attribute *attr, char *buf)
> +{
> +       struct board_t *bd;
> +       int count = 0;
> +       int i = 0;
> +
> +       DGNC_VERIFY_BOARD(p, bd);
> +
> +       for (i = 0; i < bd->nasync; i++) {
> +               count += snprintf(buf + count, PAGE_SIZE - count, "%d %ld\n",
> +                       bd->channels[i]->ch_portnum, bd->channels[i]->ch_txcount);
> +       }
> +       return count;
> +}
> +static DEVICE_ATTR(ports_txcount, S_IRUSR, dgnc_ports_txcount_show, NULL);
> +
> +
> +/* this function creates the sys files that will export each signal status
> + * to sysfs each value will be put in a separate filename
> + */
> +void dgnc_create_ports_sysfiles(struct board_t *bd)
> +{
> +       int rc = 0;
> +
> +       dev_set_drvdata(&bd->pdev->dev, bd);
> +       rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_state);
> +       rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_baud);
> +       rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_msignals);
> +       rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_iflag);
> +       rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_cflag);
> +       rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_oflag);
> +       rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_lflag);
> +       rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_digi_flag);
> +       rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_rxcount);
> +       rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_txcount);
> +       rc |= device_create_file(&(bd->pdev->dev), &dev_attr_vpd);
> +       rc |= device_create_file(&(bd->pdev->dev), &dev_attr_serial_number);
> +       if (rc) {
> +               printk(KERN_ERR "DGNC: sysfs device_create_file failed!\n");
> +       }
> +}
> +
> +
> +/* removes all the sys files created for that port */
> +void dgnc_remove_ports_sysfiles(struct board_t *bd)
> +{
> +       device_remove_file(&(bd->pdev->dev), &dev_attr_ports_state);
> +       device_remove_file(&(bd->pdev->dev), &dev_attr_ports_baud);
> +       device_remove_file(&(bd->pdev->dev), &dev_attr_ports_msignals);
> +       device_remove_file(&(bd->pdev->dev), &dev_attr_ports_iflag);
> +       device_remove_file(&(bd->pdev->dev), &dev_attr_ports_cflag);
> +       device_remove_file(&(bd->pdev->dev), &dev_attr_ports_oflag);
> +       device_remove_file(&(bd->pdev->dev), &dev_attr_ports_lflag);
> +       device_remove_file(&(bd->pdev->dev), &dev_attr_ports_digi_flag);
> +       device_remove_file(&(bd->pdev->dev), &dev_attr_ports_rxcount);
> +       device_remove_file(&(bd->pdev->dev), &dev_attr_ports_txcount);
> +       device_remove_file(&(bd->pdev->dev), &dev_attr_vpd);
> +       device_remove_file(&(bd->pdev->dev), &dev_attr_serial_number);
> +}
> +
> +
> +static ssize_t dgnc_tty_state_show(struct device *d, struct device_attribute *attr, char *buf)
> +{
> +       struct board_t *bd;
> +       struct channel_t *ch;
> +       struct un_t *un;
> +
> +       if (!d)
> +               return (0);
> +       un = (struct un_t *) dev_get_drvdata(d);
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return (0);
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return (0);
> +       bd = ch->ch_bd;
> +       if (!bd || bd->magic != DGNC_BOARD_MAGIC)
> +               return (0);
> +       if (bd->state != BOARD_READY)
> +               return (0);
> +
> +       return snprintf(buf, PAGE_SIZE, "%s", un->un_open_count ? "Open" : "Closed");
> +}
> +static DEVICE_ATTR(state, S_IRUSR, dgnc_tty_state_show, NULL);
> +
> +
> +static ssize_t dgnc_tty_baud_show(struct device *d, struct device_attribute *attr, char *buf)
> +{
> +       struct board_t *bd;
> +       struct channel_t *ch;
> +       struct un_t *un;
> +
> +       if (!d)
> +               return (0);
> +       un = (struct un_t *) dev_get_drvdata(d);
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return (0);
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return (0);
> +       bd = ch->ch_bd;
> +       if (!bd || bd->magic != DGNC_BOARD_MAGIC)
> +               return (0);
> +       if (bd->state != BOARD_READY)
> +               return (0);
> +
> +       return snprintf(buf, PAGE_SIZE, "%d\n", ch->ch_old_baud);
> +}
> +static DEVICE_ATTR(baud, S_IRUSR, dgnc_tty_baud_show, NULL);
> +
> +
> +static ssize_t dgnc_tty_msignals_show(struct device *d, struct device_attribute *attr, char *buf)
> +{
> +       struct board_t *bd;
> +       struct channel_t *ch;
> +       struct un_t *un;
> +
> +       if (!d)
> +               return (0);
> +       un = (struct un_t *) dev_get_drvdata(d);
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return (0);
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return (0);
> +       bd = ch->ch_bd;
> +       if (!bd || bd->magic != DGNC_BOARD_MAGIC)
> +               return (0);
> +       if (bd->state != BOARD_READY)
> +               return (0);
> +
> +       if (ch->ch_open_count) {
> +               return snprintf(buf, PAGE_SIZE, "%s %s %s %s %s %s\n",
> +                       (ch->ch_mostat & UART_MCR_RTS) ? "RTS" : "",
> +                       (ch->ch_mistat & UART_MSR_CTS) ? "CTS" : "",
> +                       (ch->ch_mostat & UART_MCR_DTR) ? "DTR" : "",
> +                       (ch->ch_mistat & UART_MSR_DSR) ? "DSR" : "",
> +                       (ch->ch_mistat & UART_MSR_DCD) ? "DCD" : "",
> +                       (ch->ch_mistat & UART_MSR_RI)  ? "RI"  : "");
> +       }
> +       return 0;
> +}
> +static DEVICE_ATTR(msignals, S_IRUSR, dgnc_tty_msignals_show, NULL);
> +
> +
> +static ssize_t dgnc_tty_iflag_show(struct device *d, struct device_attribute *attr, char *buf)
> +{
> +       struct board_t *bd;
> +       struct channel_t *ch;
> +       struct un_t *un;
> +
> +       if (!d)
> +               return (0);
> +       un = (struct un_t *) dev_get_drvdata(d);
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return (0);
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return (0);
> +       bd = ch->ch_bd;
> +       if (!bd || bd->magic != DGNC_BOARD_MAGIC)
> +               return (0);
> +       if (bd->state != BOARD_READY)
> +               return (0);
> +
> +       return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_iflag);
> +}
> +static DEVICE_ATTR(iflag, S_IRUSR, dgnc_tty_iflag_show, NULL);
> +
> +
> +static ssize_t dgnc_tty_cflag_show(struct device *d, struct device_attribute *attr, char *buf)
> +{
> +       struct board_t *bd;
> +       struct channel_t *ch;
> +       struct un_t *un;
> +
> +       if (!d)
> +               return (0);
> +       un = (struct un_t *) dev_get_drvdata(d);
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return (0);
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return (0);
> +       bd = ch->ch_bd;
> +       if (!bd || bd->magic != DGNC_BOARD_MAGIC)
> +               return (0);
> +       if (bd->state != BOARD_READY)
> +               return (0);
> +
> +       return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_cflag);
> +}
> +static DEVICE_ATTR(cflag, S_IRUSR, dgnc_tty_cflag_show, NULL);
> +
> +
> +static ssize_t dgnc_tty_oflag_show(struct device *d, struct device_attribute *attr, char *buf)
> +{
> +       struct board_t *bd;
> +       struct channel_t *ch;
> +       struct un_t *un;
> +
> +       if (!d)
> +               return (0);
> +       un = (struct un_t *) dev_get_drvdata(d);
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return (0);
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return (0);
> +       bd = ch->ch_bd;
> +       if (!bd || bd->magic != DGNC_BOARD_MAGIC)
> +               return (0);
> +       if (bd->state != BOARD_READY)
> +               return (0);
> +
> +       return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_oflag);
> +}
> +static DEVICE_ATTR(oflag, S_IRUSR, dgnc_tty_oflag_show, NULL);
> +
> +
> +static ssize_t dgnc_tty_lflag_show(struct device *d, struct device_attribute *attr, char *buf)
> +{
> +       struct board_t *bd;
> +       struct channel_t *ch;
> +       struct un_t *un;
> +
> +       if (!d)
> +               return (0);
> +       un = (struct un_t *) dev_get_drvdata(d);
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return (0);
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return (0);
> +       bd = ch->ch_bd;
> +       if (!bd || bd->magic != DGNC_BOARD_MAGIC)
> +               return (0);
> +       if (bd->state != BOARD_READY)
> +               return (0);
> +
> +       return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_lflag);
> +}
> +static DEVICE_ATTR(lflag, S_IRUSR, dgnc_tty_lflag_show, NULL);
> +
> +
> +static ssize_t dgnc_tty_digi_flag_show(struct device *d, struct device_attribute *attr, char *buf)
> +{
> +       struct board_t *bd;
> +       struct channel_t *ch;
> +       struct un_t *un;
> +
> +       if (!d)
> +               return (0);
> +       un = (struct un_t *) dev_get_drvdata(d);
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return (0);
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return (0);
> +       bd = ch->ch_bd;
> +       if (!bd || bd->magic != DGNC_BOARD_MAGIC)
> +               return (0);
> +       if (bd->state != BOARD_READY)
> +               return (0);
> +
> +       return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_digi.digi_flags);
> +}
> +static DEVICE_ATTR(digi_flag, S_IRUSR, dgnc_tty_digi_flag_show, NULL);
> +
> +
> +static ssize_t dgnc_tty_rxcount_show(struct device *d, struct device_attribute *attr, char *buf)
> +{
> +       struct board_t *bd;
> +       struct channel_t *ch;
> +       struct un_t *un;
> +
> +       if (!d)
> +               return (0);
> +       un = (struct un_t *) dev_get_drvdata(d);
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return (0);
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return (0);
> +       bd = ch->ch_bd;
> +       if (!bd || bd->magic != DGNC_BOARD_MAGIC)
> +               return (0);
> +       if (bd->state != BOARD_READY)
> +               return (0);
> +
> +       return snprintf(buf, PAGE_SIZE, "%ld\n", ch->ch_rxcount);
> +}
> +static DEVICE_ATTR(rxcount, S_IRUSR, dgnc_tty_rxcount_show, NULL);
> +
> +
> +static ssize_t dgnc_tty_txcount_show(struct device *d, struct device_attribute *attr, char *buf)
> +{
> +       struct board_t *bd;
> +       struct channel_t *ch;
> +       struct un_t *un;
> +
> +       if (!d)
> +               return (0);
> +       un = (struct un_t *) dev_get_drvdata(d);
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return (0);
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return (0);
> +       bd = ch->ch_bd;
> +       if (!bd || bd->magic != DGNC_BOARD_MAGIC)
> +               return (0);
> +       if (bd->state != BOARD_READY)
> +               return (0);
> +
> +       return snprintf(buf, PAGE_SIZE, "%ld\n", ch->ch_txcount);
> +}
> +static DEVICE_ATTR(txcount, S_IRUSR, dgnc_tty_txcount_show, NULL);
> +
> +
> +static ssize_t dgnc_tty_name_show(struct device *d, struct device_attribute *attr, char *buf)
> +{
> +       struct board_t *bd;
> +       struct channel_t *ch;
> +       struct un_t *un;
> +
> +       if (!d)
> +               return (0);
> +       un = (struct un_t *) dev_get_drvdata(d);
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return (0);
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return (0);
> +       bd = ch->ch_bd;
> +       if (!bd || bd->magic != DGNC_BOARD_MAGIC)
> +               return (0);
> +       if (bd->state != BOARD_READY)
> +               return (0);
> +
> +       return snprintf(buf, PAGE_SIZE, "%sn%d%c\n",
> +               (un->un_type == DGNC_PRINT) ? "pr" : "tty",
> +               bd->boardnum + 1, 'a' + ch->ch_portnum);
> +}
> +static DEVICE_ATTR(custom_name, S_IRUSR, dgnc_tty_name_show, NULL);
> +
> +
> +static struct attribute *dgnc_sysfs_tty_entries[] = {
> +       &dev_attr_state.attr,
> +       &dev_attr_baud.attr,
> +       &dev_attr_msignals.attr,
> +       &dev_attr_iflag.attr,
> +       &dev_attr_cflag.attr,
> +       &dev_attr_oflag.attr,
> +       &dev_attr_lflag.attr,
> +       &dev_attr_digi_flag.attr,
> +       &dev_attr_rxcount.attr,
> +       &dev_attr_txcount.attr,
> +       &dev_attr_custom_name.attr,
> +       NULL
> +};
> +
> +
> +static struct attribute_group dgnc_tty_attribute_group = {
> +        .name = NULL,
> +        .attrs = dgnc_sysfs_tty_entries,
> +};
> +
> +
> +void dgnc_create_tty_sysfs(struct un_t *un, struct device *c)
> +{
> +       int ret;
> +
> +       ret = sysfs_create_group(&c->kobj, &dgnc_tty_attribute_group);
> +       if (ret) {
> +               printk(KERN_ERR "dgnc: failed to create sysfs tty device attributes.\n");
> +               sysfs_remove_group(&c->kobj, &dgnc_tty_attribute_group);
> +               return;
> +       }
> +
> +       dev_set_drvdata(c, un);
> +
> +}
> +
> +
> +void dgnc_remove_tty_sysfs(struct device *c)
> +{
> +       sysfs_remove_group(&c->kobj, &dgnc_tty_attribute_group);
> +}
> +
> diff --git a/drivers/staging/dgnc/dgnc_sysfs.h b/drivers/staging/dgnc/dgnc_sysfs.h
> new file mode 100644
> index 0000000..fe99110
> --- /dev/null
> +++ b/drivers/staging/dgnc/dgnc_sysfs.h
> @@ -0,0 +1,49 @@
> +/*
> + * 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 __DGNC_SYSFS_H
> +#define __DGNC_SYSFS_H
> +
> +#include "dgnc_driver.h"
> +
> +#include <linux/device.h>
> +
> +struct board_t;
> +struct channel_t;
> +struct un_t;
> +struct pci_driver;
> +struct class_device;
> +
> +extern void dgnc_create_ports_sysfiles(struct board_t *bd);
> +extern void dgnc_remove_ports_sysfiles(struct board_t *bd);
> +
> +extern void dgnc_create_driver_sysfiles(struct pci_driver *);
> +extern void dgnc_remove_driver_sysfiles(struct pci_driver *);
> +
> +extern int dgnc_tty_class_init(void);
> +extern int dgnc_tty_class_destroy(void);
> +
> +extern void dgnc_create_tty_sysfs(struct un_t *un, struct device *c);
> +extern void dgnc_remove_tty_sysfs(struct device *c);
> +
> +
> +
> +#endif
> diff --git a/drivers/staging/dgnc/dgnc_trace.c b/drivers/staging/dgnc/dgnc_trace.c
> new file mode 100644
> index 0000000..ea710e5
> --- /dev/null
> +++ b/drivers/staging/dgnc/dgnc_trace.c
> @@ -0,0 +1,187 @@
> +/*
> + * 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: dgnc_trace.c,v 1.1.1.1 2009/05/20 12:19:19 markh Exp $ */
> +
> +#include <linux/kernel.h>
> +#include <linux/version.h>
> +#include <linux/sched.h>       /* For jiffies, task states */
> +#include <linux/interrupt.h>   /* For tasklet and interrupt structs/defines */
> +#include <linux/vmalloc.h>
> +
> +#include "dgnc_driver.h"
> +
> +#define TRC_TO_CONSOLE 1
> +
> +/* file level globals */
> +static char *dgnc_trcbuf;              /* the ringbuffer */
> +
> +#if defined(TRC_TO_KMEM)
> +static int dgnc_trcbufi = 0;           /* index of the tilde at the end of */
> +#endif
> +
> +#if defined(TRC_TO_KMEM)
> +static DEFINE_SPINLOCK(dgnc_tracef_lock);
> +#endif
> +
> +
> +#if 0
> +
> +#if !defined(TRC_TO_KMEM) && !defined(TRC_TO_CONSOLE)
> +
> +void dgnc_tracef(const char *fmt, ...)
> +{
> +       return;
> +}
> +
> +#else /* !defined(TRC_TO_KMEM) && !defined(TRC_TO_CONSOLE) */
> +
> +void dgnc_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)
> +       DGNC_LOCK(dgnc_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) {
> +                       dgnc_trcbuf = (char *) vmalloc(dgnc_trcbuf_size);
> +                       if(!dgnc_trcbuf) {
> +                               failed = TRUE;
> +                               printk("dgnc: tracing init failed!\n");
> +                               return;
> +                       }
> +
> +                       memset(dgnc_trcbuf, '\0',  dgnc_trcbuf_size);
> +                       dgnc_trcbufi = 0;
> +                       initd++;
> +
> +                       printk("dgnc: tracing enabled - " TRC_DTRC
> +                               " 0x%lx 0x%x\n",
> +                               (unsigned long)dgnc_trcbuf,
> +                               dgnc_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 (dgnc_trcbufi + lenbuf >= dgnc_trcbuf_size) {
> +                       /* We are wrapping, so wipe out the last tilde. */
> +                       dgnc_trcbuf[dgnc_trcbufi] = '\0';
> +                       /* put the new string at the beginning of the buffer */
> +                       dgnc_trcbufi = 0;
> +               }
> +
> +               strcpy(&dgnc_trcbuf[dgnc_trcbufi], buf);
> +               dgnc_trcbufi += lenbuf;
> +               dgnc_trcbuf[dgnc_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(&dgnc_trcbuf[dgnc_trcbufi], buf);
> +               dgnc_trcbufi += lenbuf;
> +               dgnc_trcbuf[dgnc_trcbufi] = '~';
> +               dgnc_trcbuf[dgnc_trcbufi+1] = '\0';
> +
> +               /* If we're near the end of the trace buffer... */
> +               if (dgnc_trcbufi > (dgnc_trcbuf_size/8)*7) {
> +                       /* Wipe out the first eighth to make some more room. */
> +                       strcpy(dgnc_trcbuf, &dgnc_trcbuf[dgnc_trcbuf_size/8]);
> +                       dgnc_trcbufi = strlen(dgnc_trcbuf)-1;
> +                       /* Plop overflow message at the top of the buffer. */
> +                       bcopy(TRC_OVERFLOW, dgnc_trcbuf, strlen(TRC_OVERFLOW));
> +               }
> +#  else
> +#   error "TRC_ON_OVERFLOW_WRAP_AROUND or TRC_ON_OVERFLOW_SHIFT_BUFFER?"
> +#  endif
> +       }
> +       DGNC_UNLOCK(dgnc_tracef_lock, flags);
> +
> +# endif /* defined(TRC_TO_KMEM) */
> +}
> +
> +#endif /* !defined(TRC_TO_KMEM) && !defined(TRC_TO_CONSOLE) */
> +
> +#endif
> +
> +
> +/*
> + * dgnc_tracer_free()
> + *
> + *
> + */
> +void dgnc_tracer_free(void)
> +{
> +       if(dgnc_trcbuf)
> +               vfree(dgnc_trcbuf);
> +}
> diff --git a/drivers/staging/dgnc/dgnc_trace.h b/drivers/staging/dgnc/dgnc_trace.h
> new file mode 100644
> index 0000000..1e8870b
> --- /dev/null
> +++ b/drivers/staging/dgnc/dgnc_trace.h
> @@ -0,0 +1,45 @@
> +/*
> + * 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 dgnc_trace.c
> + *
> + * $Id: dgnc_trace.h,v 1.1.1.1 2009/05/20 12:19:19 markh Exp $
> + */
> +
> +#ifndef __DGNC_TRACE_H
> +#define __DGNC_TRACE_H
> +
> +#include "dgnc_driver.h"
> +
> +#if 0
> +
> +# if !defined(TRC_TO_KMEM) && !defined(TRC_TO_CONSOLE)
> +   void dgnc_tracef(const char *fmt, ...);
> +# else
> +   void dgnc_tracef(const char *fmt, ...);
> +# endif
> +
> +#endif
> +
> +void dgnc_tracer_free(void);
> +
> +#endif
> +
> diff --git a/drivers/staging/dgnc/dgnc_tty.c b/drivers/staging/dgnc/dgnc_tty.c
> new file mode 100644
> index 0000000..461e881
> --- /dev/null
> +++ b/drivers/staging/dgnc/dgnc_tty.c
> @@ -0,0 +1,3648 @@
> +/*
> + * 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
> + * Neo and ClassicBoard PCI based product lines.
> + *
> + ************************************************************************
> + *
> + * $Id: dgnc_tty.c,v 1.5 2013/04/30 19:18:30 markh Exp $
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/version.h>
> +#include <linux/sched.h>       /* For jiffies, task states */
> +#include <linux/interrupt.h>   /* For tasklet and interrupt structs/defines */
> +#include <linux/module.h>
> +#include <linux/ctype.h>
> +#include <linux/tty.h>
> +#include <linux/tty_flip.h>
> +#include <linux/serial_reg.h>
> +#include <linux/slab.h>
> +#include <linux/delay.h>        /* For udelay */
> +#include <asm/uaccess.h>       /* For copy_from_user/copy_to_user */
> +#include <linux/pci.h>
> +
> +#include "dgnc_driver.h"
> +#include "dgnc_tty.h"
> +#include "dgnc_types.h"
> +#include "dgnc_trace.h"
> +#include "dgnc_neo.h"
> +#include "dgnc_cls.h"
> +#include "dpacompat.h"
> +#include "dgnc_sysfs.h"
> +
> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)
> +#define init_MUTEX(sem)         sema_init(sem, 1)
> +#define DECLARE_MUTEX(name)     \
> +        struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1)
> +#endif
> +
> +/*
> + * internal variables
> + */
> +static struct board_t  *dgnc_BoardsByMajor[256];
> +static uchar           *dgnc_TmpWriteBuf = NULL;
> +static DECLARE_MUTEX(dgnc_TmpWriteSem);
> +
> +/*
> + * Default transparent print information.
> + */
> +static struct digi_t dgnc_digi_init = {
> +       .digi_flags =   DIGI_COOK,      /* Flags                        */
> +       .digi_maxcps =  100,            /* Max CPS                      */
> +       .digi_maxchar = 50,             /* Max chars in print queue     */
> +       .digi_bufsize = 100,            /* Printer buffer size          */
> +       .digi_onlen =   4,              /* size of printer on string    */
> +       .digi_offlen =  4,              /* size of printer off string   */
> +       .digi_onstr =   "\033[5i",      /* ANSI printer on string ]     */
> +       .digi_offstr =  "\033[4i",      /* ANSI printer off string ]    */
> +       .digi_term =    "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 ktermios DgncDefaultTermios =
> +{
> +       .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 int dgnc_tty_open(struct tty_struct *tty, struct file *file);
> +static void dgnc_tty_close(struct tty_struct *tty, struct file *file);
> +static int dgnc_block_til_ready(struct tty_struct *tty, struct file *file, struct channel_t *ch);
> +static int dgnc_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg);
> +static int dgnc_tty_digigeta(struct tty_struct *tty, struct digi_t __user *retinfo);
> +static int dgnc_tty_digiseta(struct tty_struct *tty, struct digi_t __user *new_info);
> +static int dgnc_tty_write_room(struct tty_struct* tty);
> +static int dgnc_tty_put_char(struct tty_struct *tty, unsigned char c);
> +static int dgnc_tty_chars_in_buffer(struct tty_struct* tty);
> +static void dgnc_tty_start(struct tty_struct *tty);
> +static void dgnc_tty_stop(struct tty_struct *tty);
> +static void dgnc_tty_throttle(struct tty_struct *tty);
> +static void dgnc_tty_unthrottle(struct tty_struct *tty);
> +static void dgnc_tty_flush_chars(struct tty_struct *tty);
> +static void dgnc_tty_flush_buffer(struct tty_struct *tty);
> +static void dgnc_tty_hangup(struct tty_struct *tty);
> +static int dgnc_set_modem_info(struct tty_struct *tty, unsigned int command, unsigned int __user *value);
> +static int dgnc_get_modem_info(struct channel_t *ch, unsigned int __user *value);
> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)
> +static int dgnc_tty_tiocmget(struct tty_struct *tty);
> +static int dgnc_tty_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear);
> +#else
> +static int dgnc_tty_tiocmget(struct tty_struct *tty, struct file *file);
> +static int dgnc_tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear);
> +#endif
> +static int dgnc_tty_send_break(struct tty_struct *tty, int msec);
> +static void dgnc_tty_wait_until_sent(struct tty_struct *tty, int timeout);
> +static int dgnc_tty_write(struct tty_struct *tty, const unsigned char *buf, int count);
> +static void dgnc_tty_set_termios(struct tty_struct *tty, struct ktermios *old_termios);
> +static void dgnc_tty_send_xchar(struct tty_struct *tty, char ch);
> +
> +
> +static const struct tty_operations dgnc_tty_ops = {
> +       .open = dgnc_tty_open,
> +       .close = dgnc_tty_close,
> +       .write = dgnc_tty_write,
> +       .write_room = dgnc_tty_write_room,
> +       .flush_buffer = dgnc_tty_flush_buffer,
> +       .chars_in_buffer = dgnc_tty_chars_in_buffer,
> +       .flush_chars = dgnc_tty_flush_chars,
> +       .ioctl = dgnc_tty_ioctl,
> +       .set_termios = dgnc_tty_set_termios,
> +       .stop = dgnc_tty_stop,
> +       .start = dgnc_tty_start,
> +       .throttle = dgnc_tty_throttle,
> +       .unthrottle = dgnc_tty_unthrottle,
> +       .hangup = dgnc_tty_hangup,
> +       .put_char = dgnc_tty_put_char,
> +       .tiocmget = dgnc_tty_tiocmget,
> +       .tiocmset = dgnc_tty_tiocmset,
> +       .break_ctl = dgnc_tty_send_break,
> +       .wait_until_sent = dgnc_tty_wait_until_sent,
> +       .send_xchar = dgnc_tty_send_xchar
> +};
> +
> +/************************************************************************
> + *
> + * TTY Initialization/Cleanup Functions
> + *
> + ************************************************************************/
> +
> +/*
> + * dgnc_tty_preinit()
> + *
> + * Initialize any global tty related data before we download any boards.
> + */
> +int dgnc_tty_preinit(void)
> +{
> +       /*
> +        * Allocate a buffer for doing the copy from user space to
> +        * kernel space in dgnc_write().  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.
> +        *
> +        * We are okay to sleep in the malloc, as this routine
> +        * is only called during module load, (not in interrupt context),
> +        * and with no locks held.
> +        */
> +       dgnc_TmpWriteBuf = kmalloc(WRITEBUFLEN, GFP_KERNEL);
> +
> +       if (!dgnc_TmpWriteBuf) {
> +               DPR_INIT(("unable to allocate tmp write buf"));
> +               return (-ENOMEM);
> +       }
> +
> +       return(0);
> +}
> +
> +
> +/*
> + * dgnc_tty_register()
> + *
> + * Init the tty subsystem for this board.
> + */
> +int dgnc_tty_register(struct board_t *brd)
> +{
> +       int rc = 0;
> +
> +       DPR_INIT(("tty_register start\n"));
> +
> +        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_dgnc_%d_", brd->boardnum);
> +
> +       brd->SerialDriver.name = brd->SerialName;
> +       brd->SerialDriver.name_base = 0;
> +       brd->SerialDriver.major = 0;
> +       brd->SerialDriver.minor_start = 0;
> +       brd->SerialDriver.num = brd->maxports;
> +       brd->SerialDriver.type = TTY_DRIVER_TYPE_SERIAL;
> +       brd->SerialDriver.subtype = SERIAL_TYPE_NORMAL;
> +       brd->SerialDriver.init_termios = DgncDefaultTermios;
> +       brd->SerialDriver.driver_name = DRVSTR;
> +       brd->SerialDriver.flags = (TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_HARDWARE_BREAK);
> +
> +       /*
> +        * The kernel wants space to store pointers to
> +        * tty_struct's and termios's.
> +        */
> +       brd->SerialDriver.ttys = dgnc_driver_kzmalloc(brd->maxports * sizeof(struct tty_struct *), GFP_KERNEL);
> +       if (!brd->SerialDriver.ttys)
> +               return(-ENOMEM);
> +
> +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
> +        brd->SerialDriver.refcount = brd->TtyRefCnt;
> +#else
> +        kref_init(&brd->SerialDriver.kref);
> +#endif
> +
> +       brd->SerialDriver.termios = dgnc_driver_kzmalloc(brd->maxports * sizeof(struct ktermios *), GFP_KERNEL);
> +       if (!brd->SerialDriver.termios)
> +               return(-ENOMEM);
> +
> +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)
> +       brd->SerialDriver.termios_locked = dgnc_driver_kzmalloc(brd->maxports * sizeof(struct ktermios *), GFP_KERNEL);
> +       if (!brd->SerialDriver.termios_locked)
> +               return(-ENOMEM);
> +#endif
> +       /*
> +        * Entry points for driver.  Called by the kernel from
> +        * tty_io.c and n_tty.c.
> +        */
> +       tty_set_operations(&brd->SerialDriver, &dgnc_tty_ops);
> +
> +       if (!brd->dgnc_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->dgnc_Major_Serial_Registered = TRUE;
> +       }
> +
> +       /*
> +        * 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 dgnc_tty_open() routine.
> +        */
> +       brd->PrintDriver.magic = TTY_DRIVER_MAGIC;
> +       snprintf(brd->PrintName, MAXTTYNAMELEN, "pr_dgnc_%d_", brd->boardnum);
> +
> +       brd->PrintDriver.name = brd->PrintName;
> +       brd->PrintDriver.name_base = 0;
> +       brd->PrintDriver.major = brd->SerialDriver.major;
> +       brd->PrintDriver.minor_start = 0x80;
> +       brd->PrintDriver.num = brd->maxports;
> +       brd->PrintDriver.type = TTY_DRIVER_TYPE_SERIAL;
> +       brd->PrintDriver.subtype = SERIAL_TYPE_NORMAL;
> +       brd->PrintDriver.init_termios = DgncDefaultTermios;
> +       brd->PrintDriver.driver_name = DRVSTR;
> +       brd->PrintDriver.flags = (TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_HARDWARE_BREAK);
> +
> +       /*
> +        * 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
> +        */
> +       brd->PrintDriver.ttys = dgnc_driver_kzmalloc(brd->maxports * sizeof(struct tty_struct *), GFP_KERNEL);
> +       if (!brd->PrintDriver.ttys)
> +               return(-ENOMEM);
> +
> +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
> +        brd->PrintDriver.refcount = brd->TtyRefCnt;
> +#else
> +        kref_init(&brd->PrintDriver.kref);
> +#endif
> +
> +       brd->PrintDriver.termios = dgnc_driver_kzmalloc(brd->maxports * sizeof(struct ktermios *), GFP_KERNEL);
> +       if (!brd->PrintDriver.termios)
> +               return(-ENOMEM);
> +
> +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)
> +       brd->PrintDriver.termios_locked = dgnc_driver_kzmalloc(brd->maxports * sizeof(struct ktermios *), GFP_KERNEL);
> +       if (!brd->PrintDriver.termios_locked)
> +               return(-ENOMEM);
> +#endif
> +
> +       /*
> +        * Entry points for driver.  Called by the kernel from
> +        * tty_io.c and n_tty.c.
> +        */
> +       tty_set_operations(&brd->PrintDriver, &dgnc_tty_ops);
> +
> +       if (!brd->dgnc_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->dgnc_Major_TransparentPrint_Registered = TRUE;
> +       }
> +
> +       dgnc_BoardsByMajor[brd->SerialDriver.major] = brd;
> +       brd->dgnc_Serial_Major = brd->SerialDriver.major;
> +       brd->dgnc_TransparentPrint_Major = brd->PrintDriver.major;
> +
> +       DPR_INIT(("DGNC REGISTER TTY: MAJOR: %d\n", brd->SerialDriver.major));
> +
> +       return (rc);
> +}
> +
> +
> +/*
> + * dgnc_tty_init()
> + *
> + * Init the tty subsystem.  Called once per board after board has been
> + * downloaded and init'ed.
> + */
> +int dgnc_tty_init(struct board_t *brd)
> +{
> +       int i;
> +       uchar *vaddr;
> +       struct channel_t *ch;
> +
> +       if (!brd)
> +               return (-ENXIO);
> +
> +       DPR_INIT(("dgnc_tty_init start\n"));
> +
> +       /*
> +        * Initialize board structure elements.
> +        */
> +
> +       vaddr = brd->re_map_membase;
> +
> +       brd->nasync = brd->maxports;
> +
> +       /*
> +        * 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]) {
> +
> +                       /*
> +                        * Okay to malloc with GFP_KERNEL, we are not at
> +                        * interrupt context, and there are no locks held.
> +                        */
> +                       brd->channels[i] = dgnc_driver_kzmalloc(sizeof(struct channel_t), GFP_KERNEL);
> +                       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;
> +
> +       /* Set up channel variables */
> +       for (i = 0; i < brd->nasync; i++, ch = brd->channels[i]) {
> +
> +               if (!brd->channels[i])
> +                       continue;
> +
> +               DGNC_SPINLOCK_INIT(ch->ch_lock);
> +
> +               /* Store all our magic numbers */
> +               ch->magic = DGNC_CHANNEL_MAGIC;
> +               ch->ch_tun.magic = DGNC_UNIT_MAGIC;
> +               ch->ch_tun.un_ch = ch;
> +               ch->ch_tun.un_type = DGNC_SERIAL;
> +               ch->ch_tun.un_dev = i;
> +
> +               ch->ch_pun.magic = DGNC_UNIT_MAGIC;
> +               ch->ch_pun.un_ch = ch;
> +               ch->ch_pun.un_type = DGNC_PRINT;
> +               ch->ch_pun.un_dev = i + 128;
> +
> +               if (brd->bd_uart_offset == 0x200)
> +                       ch->ch_neo_uart = (struct neo_uart_struct *) ((ulong) vaddr + (brd->bd_uart_offset * i));
> +               else
> +                       ch->ch_cls_uart = (struct cls_uart_struct *) ((ulong) vaddr + (brd->bd_uart_offset * i));
> +
> +               ch->ch_bd = brd;
> +               ch->ch_portnum = i;
> +               ch->ch_digi = dgnc_digi_init;
> +
> +               /* .25 second delay */
> +               ch->ch_close_delay = 250;
> +
> +               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);
> +               init_waitqueue_head(&ch->ch_sniff_wait);
> +
> +               {
> +                       struct device *classp;
> +                       classp = tty_register_device(&brd->SerialDriver, i,
> +                               &(ch->ch_bd->pdev->dev));
> +                       ch->ch_tun.un_sysfs = classp;
> +                       dgnc_create_tty_sysfs(&ch->ch_tun, classp);
> +
> +                       classp = tty_register_device(&brd->PrintDriver, i,
> +                               &(ch->ch_bd->pdev->dev));
> +                       ch->ch_pun.un_sysfs = classp;
> +                       dgnc_create_tty_sysfs(&ch->ch_pun, classp);
> +               }
> +
> +       }
> +
> +       DPR_INIT(("dgnc_tty_init finish\n"));
> +
> +       return (0);
> +}
> +
> +
> +/*
> + * dgnc_tty_post_uninit()
> + *
> + * UnInitialize any global tty related data.
> + */
> +void dgnc_tty_post_uninit(void)
> +{
> +       if (dgnc_TmpWriteBuf) {
> +               kfree(dgnc_TmpWriteBuf);
> +               dgnc_TmpWriteBuf = NULL;
> +       }
> +}
> +
> +
> +/*
> + * dgnc_tty_uninit()
> + *
> + * Uninitialize the TTY portion of this driver.  Free all memory and
> + * resources.
> + */
> +void dgnc_tty_uninit(struct board_t *brd)
> +{
> +       int i = 0;
> +
> +       if (brd->dgnc_Major_Serial_Registered) {
> +               dgnc_BoardsByMajor[brd->SerialDriver.major] = NULL;
> +               brd->dgnc_Serial_Major = 0;
> +               for (i = 0; i < brd->nasync; i++) {
> +                       dgnc_remove_tty_sysfs(brd->channels[i]->ch_tun.un_sysfs);
> +                       tty_unregister_device(&brd->SerialDriver, i);
> +               }
> +               tty_unregister_driver(&brd->SerialDriver);
> +               brd->dgnc_Major_Serial_Registered = FALSE;
> +       }
> +
> +       if (brd->dgnc_Major_TransparentPrint_Registered) {
> +               dgnc_BoardsByMajor[brd->PrintDriver.major] = NULL;
> +               brd->dgnc_TransparentPrint_Major = 0;
> +               for (i = 0; i < brd->nasync; i++) {
> +                       dgnc_remove_tty_sysfs(brd->channels[i]->ch_pun.un_sysfs);
> +                       tty_unregister_device(&brd->PrintDriver, i);
> +               }
> +               tty_unregister_driver(&brd->PrintDriver);
> +               brd->dgnc_Major_TransparentPrint_Registered = FALSE;
> +       }
> +
> +       if (brd->SerialDriver.ttys) {
> +               kfree(brd->SerialDriver.ttys);
> +               brd->SerialDriver.ttys = NULL;
> +       }
> +       if (brd->PrintDriver.ttys) {
> +               kfree(brd->PrintDriver.ttys);
> +               brd->PrintDriver.ttys = NULL;
> +        }
> +}
> +
> +
> +#define TMPBUFLEN (1024)
> +
> +/*
> + * dgnc_sniff - Dump data out to the "sniff" buffer if the
> + * proc sniff file is opened...
> + */
> +void dgnc_sniff_nowait_nolock(struct channel_t *ch, uchar *text, uchar *buf, int len)
> +{
> +       struct timeval tv;
> +       int n;
> +       int r;
> +       int nbuf;
> +       int i;
> +       int tmpbuflen;
> +       char tmpbuf[TMPBUFLEN];
> +       char *p = tmpbuf;
> +       int too_much_data;
> +
> +       /* Leave if sniff not open */
> +       if (!(ch->ch_sniff_flags & SNIFF_OPEN))
> +               return;
> +
> +       do_gettimeofday(&tv);
> +
> +       /* Create our header for data dump */
> +       p += sprintf(p, "<%ld %ld><%s><", tv.tv_sec, tv.tv_usec, text);
> +       tmpbuflen = p - tmpbuf;
> +
> +       do {
> +               too_much_data = 0;
> +
> +               for (i = 0; i < len && tmpbuflen < (TMPBUFLEN - 4); i++) {
> +                       p += sprintf(p, "%02x ", *buf);
> +                       buf++;
> +                       tmpbuflen = p - tmpbuf;
> +               }
> +
> +               if (tmpbuflen < (TMPBUFLEN - 4)) {
> +                       if (i > 0)
> +                               p += sprintf(p - 1, "%s\n", ">");
> +                       else
> +                               p += sprintf(p, "%s\n", ">");
> +               } else {
> +                       too_much_data = 1;
> +                       len -= i;
> +               }
> +
> +               nbuf = strlen(tmpbuf);
> +               p = tmpbuf;
> +
> +               /*
> +                *  Loop while data remains.
> +                */
> +               while (nbuf > 0 && ch->ch_sniff_buf != 0) {
> +                       /*
> +                        *  Determine the amount of available space left in the
> +                        *  buffer.  If there's none, wait until some appears.
> +                        */
> +                       n = (ch->ch_sniff_out - ch->ch_sniff_in - 1) & SNIFF_MASK;
> +
> +                       /*
> +                        * If there is no space left to write to in our sniff buffer,
> +                        * we have no choice but to drop the data.
> +                        * We *cannot* sleep here waiting for space, because this
> +                        * function was probably called by the interrupt/timer routines!
> +                        */
> +                       if (n == 0) {
> +                               return;
> +                       }
> +
> +                       /*
> +                        * Copy as much data as will fit.
> +                        */
> +
> +                       if (n > nbuf)
> +                               n = nbuf;
> +
> +                       r = SNIFF_MAX - ch->ch_sniff_in;
> +
> +                       if (r <= n) {
> +                               memcpy(ch->ch_sniff_buf + ch->ch_sniff_in, p, r);
> +
> +                               n -= r;
> +                               ch->ch_sniff_in = 0;
> +                               p += r;
> +                               nbuf -= r;
> +                       }
> +
> +                       memcpy(ch->ch_sniff_buf + ch->ch_sniff_in, p, n);
> +
> +                       ch->ch_sniff_in += n;
> +                       p += n;
> +                       nbuf -= n;
> +
> +                       /*
> +                        *  Wakeup any thread waiting for data
> +                        */
> +                       if (ch->ch_sniff_flags & SNIFF_WAIT_DATA) {
> +                               ch->ch_sniff_flags &= ~SNIFF_WAIT_DATA;
> +                               wake_up_interruptible(&ch->ch_sniff_wait);
> +                       }
> +               }
> +
> +               /*
> +                * If the user sent us too much data to push into our tmpbuf,
> +                * we need to keep looping around on all the data.
> +                */
> +               if (too_much_data) {
> +                       p = tmpbuf;
> +                       tmpbuflen = 0;
> +               }
> +
> +       } while (too_much_data);
> +}
> +
> +
> +/*=======================================================================
> + *
> + *     dgnc_wmove - Write data to transmit queue.
> + *
> + *             ch      - Pointer to channel structure.
> + *             buf     - Poiter to characters to be moved.
> + *             n       - Number of characters to move.
> + *
> + *=======================================================================*/
> +static void dgnc_wmove(struct channel_t *ch, char *buf, uint n)
> +{
> +       int     remain;
> +       uint    head;
> +
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return;
> +
> +       head = ch->ch_w_head & WQUEUEMASK;
> +
> +       /*
> +        * 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 = WQUEUESIZE - head;
> +
> +       if (n >= remain) {
> +               n -= remain;
> +               memcpy(ch->ch_wqueue + head, buf, remain);
> +               head = 0;
> +               buf += remain;
> +       }
> +
> +       if (n > 0) {
> +               /*
> +                * Move rest of data.
> +                */
> +               remain = n;
> +               memcpy(ch->ch_wqueue + head, buf, remain);
> +               head += remain;
> +       }
> +
> +       head &= WQUEUEMASK;
> +       ch->ch_w_head = head;
> +}
> +
> +
> +
> +
> +/*=======================================================================
> + *
> + *      dgnc_input - Process received data.
> + *
> + *              ch      - Pointer to channel structure.
> + *
> + *=======================================================================*/
> +void dgnc_input(struct channel_t *ch)
> +{
> +       struct board_t *bd;
> +       struct tty_struct *tp;
> +       struct tty_ldisc *ld;
> +       uint    rmask;
> +       ushort  head;
> +       ushort  tail;
> +       int     data_len;
> +       ulong   lock_flags;
> +       int flip_len;
> +       int len = 0;
> +       int n = 0;
> +       char *buf = NULL;
> +       int s = 0;
> +       int i = 0;
> +
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return;
> +
> +       tp = ch->ch_tun.un_tty;
> +
> +       bd = ch->ch_bd;
> +       if(!bd || bd->magic != DGNC_BOARD_MAGIC)
> +               return;
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       /*
> +        *      Figure the number of characters in the buffer.
> +        *      Exit immediately if none.
> +        */
> +       rmask = RQUEUEMASK;
> +       head = ch->ch_r_head & rmask;
> +       tail = ch->ch_r_tail & rmask;
> +       data_len = (head - tail) & rmask;
> +
> +       if (data_len == 0) {
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               return;
> +       }
> +
> +       DPR_READ(("dgnc_input start\n"));
> +
> +       /*
> +        * 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));
> +
> +               ch->ch_r_head = tail;
> +
> +               /* Force queue flow control to be released, if needed */
> +               dgnc_check_queue_flow_control(ch);
> +
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               return;
> +       }
> +
> +       /*
> +        * If we are throttled, simply don't read any data.
> +        */
> +       if (ch->ch_flags & CH_FORCED_STOPI) {
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               DPR_READ(("Port %d throttled, not reading any data. head: %x tail: %x\n",
> +                       ch->ch_portnum, head, tail));
> +               return;
> +       }
> +
> +       DPR_READ(("dgnc_input start 2\n"));
> +
> +       /* Decide how much data we can send into the tty layer */
> +       if (dgnc_rawreadok && tp->real_raw)
> +               flip_len = MYFLIPLEN;
> +       else
> +               flip_len = TTY_FLIPBUF_SIZE;
> +
> +       /* Chop down the length, if needed */
> +       len = min(data_len, flip_len);
> +       len = min(len, (N_TTY_BUF_SIZE - 1) - tp->read_cnt);
> +
> +       ld = tty_ldisc_ref(tp);
> +
> +#ifdef TTY_DONT_FLIP
> +       /*
> +        * If the DONT_FLIP flag is on, don't flush our buffer, and act
> +        * like the ld doesn't have any space to put the data right now.
> +        */
> +       if (test_bit(TTY_DONT_FLIP, &tp->flags))
> +               len = 0;
> +#endif
> +
> +       /*
> +        * If we were unable to get a reference to the ld,
> +        * don't flush our buffer, and act like the ld doesn't
> +        * have any space to put the data right now.
> +        */
> +       if (!ld) {
> +               len = 0;
> +       } else {
> +               /*
> +                * If ld doesn't have a pointer to a receive_buf function,
> +                * flush the data, then act like the ld doesn't have any
> +                * space to put the data right now.
> +                */
> +               if (!ld->ops->receive_buf) {
> +                       ch->ch_r_head = ch->ch_r_tail;
> +                       len = 0;
> +               }
> +       }
> +
> +       if (len <= 0) {
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               if (ld)
> +                       tty_ldisc_deref(ld);
> +               return;
> +       }
> +
> +       /*
> +        * The tty layer in the kernel has changed in 2.6.16+.
> +        *
> +        * The flip buffers in the tty structure are no longer exposed,
> +        * and probably will be going away eventually.
> +        *
> +        * If we are completely raw, we don't need to go through a lot
> +        * of the tty layers that exist.
> +        * In this case, we take the shortest and fastest route we
> +        * can to relay the data to the user.
> +        *
> +        * On the other hand, if we are not raw, we need to go through
> +        * the new 2.6.16+ tty layer, which has its API more well defined.
> +        */
> +       if (dgnc_rawreadok && tp->real_raw) {
> +
> +               if (ch->ch_flags & CH_FLIPBUF_IN_USE) {
> +                       DPR_READ(("DGNC - FLIPBUF in use. delaying input\n"));
> +                       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +                       if (ld)
> +                               tty_ldisc_deref(ld);
> +                       return;
> +               }
> +
> +               ch->ch_flags |= CH_FLIPBUF_IN_USE;
> +               buf = ch->ch_bd->flipbuf;
> +
> +               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 : RQUEUESIZE) - tail;
> +                       s = min(s, n);
> +
> +                       if (s <= 0)
> +                               break;
> +
> +                       memcpy(buf, ch->ch_rqueue + tail, s);
> +                       dgnc_sniff_nowait_nolock(ch, "USER READ", ch->ch_rqueue + tail, s);
> +
> +                       tail += s;
> +                       buf += s;
> +
> +                       n -= s;
> +                       /* Flip queue if needed */
> +                       tail &= rmask;
> +               }
> +
> +               ch->ch_r_tail = tail & rmask;
> +               ch->ch_e_tail = tail & rmask;
> +
> +               dgnc_check_queue_flow_control(ch);
> +
> +               /* !!! WE *MUST* LET GO OF ALL LOCKS BEFORE CALLING RECEIVE BUF !!! */
> +
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +               DPR_READ(("dgnc_input. %d real_raw len:%d calling receive_buf for buffer for board %d\n",
> +                        __LINE__, len, ch->ch_bd->boardnum));
> +
> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
> +               tp->ldisc->ops->receive_buf(tp, ch->ch_bd->flipbuf, NULL, len);
> +#else
> +               tp->ldisc.ops->receive_buf(tp, ch->ch_bd->flipbuf, NULL, len);
> +#endif
> +
> +               /* Allow use of channel flip buffer again */
> +               DGNC_LOCK(ch->ch_lock, lock_flags);
> +               ch->ch_flags &= ~CH_FLIPBUF_IN_USE;
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +       }
> +       else {
> +               len = tty_buffer_request_room(tp, len);
> +               n = len;
> +
> +               /*
> +                * n now contains the most amount of data we can copy,
> +                * bounded either by how much the Linux tty layer can handle,
> +                * or the amount of data the card actually has pending...
> +                */
> +               while (n) {
> +                       s = ((head >= tail) ? head : RQUEUESIZE) - tail;
> +                       s = min(s, n);
> +
> +                       if (s <= 0)
> +                               break;
> +
> +                       /*
> +                        * If conditions are such that ld needs to see all
> +                        * UART errors, we will have to walk each character
> +                        * and error byte and send them to the buffer one at
> +                        * a time.
> +                        */
> +                       if (I_PARMRK(tp) || I_BRKINT(tp) || I_INPCK(tp)) {
> +                               for (i = 0; i < s; i++) {
> +                                       if (*(ch->ch_equeue + tail + i) & UART_LSR_BI)
> +                                               tty_insert_flip_char(tp, *(ch->ch_rqueue + tail + i), TTY_BREAK);
> +                                       else if (*(ch->ch_equeue + tail + i) & UART_LSR_PE)
> +                                               tty_insert_flip_char(tp, *(ch->ch_rqueue + tail + i), TTY_PARITY);
> +                                       else if (*(ch->ch_equeue + tail + i) & UART_LSR_FE)
> +                                               tty_insert_flip_char(tp, *(ch->ch_rqueue + tail + i), TTY_FRAME);
> +                                       else
> +                                               tty_insert_flip_char(tp, *(ch->ch_rqueue + tail + i), TTY_NORMAL);
> +                               }
> +                       }
> +                       else {
> +                               tty_insert_flip_string(tp, ch->ch_rqueue + tail, s);
> +                       }
> +
> +                       dgnc_sniff_nowait_nolock(ch, "USER READ", ch->ch_rqueue + tail, s);
> +
> +                       tail += s;
> +                       n -= s;
> +                       /* Flip queue if needed */
> +                       tail &= rmask;
> +               }
> +
> +               ch->ch_r_tail = tail & rmask;
> +               ch->ch_e_tail = tail & rmask;
> +               dgnc_check_queue_flow_control(ch);
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +               /* Tell the tty layer its okay to "eat" the data now */
> +               tty_flip_buffer_push(tp);
> +       }
> +
> +       if (ld)
> +               tty_ldisc_deref(ld);
> +
> +       DPR_READ(("dgnc_input - finish\n"));
> +}
> +
> +
> +/************************************************************************
> + * Determines when CARRIER changes state and takes appropriate
> + * action.
> + ************************************************************************/
> +void dgnc_carrier(struct channel_t *ch)
> +{
> +       struct board_t *bd;
> +
> +        int virt_carrier = 0;
> +        int phys_carrier = 0;
> +
> +       DPR_CARR(("dgnc_carrier called...\n"));
> +
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return;
> +
> +       bd = ch->ch_bd;
> +
> +       if (!bd || bd->magic != DGNC_BOARD_MAGIC)
> +               return;
> +
> +       if (ch->ch_mistat & UART_MSR_DCD) {
> +               DPR_CARR(("mistat: %x  D_CD: %x\n", ch->ch_mistat, ch->ch_mistat & UART_MSR_DCD));
> +               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;
> +}
> +
> +/*
> + *  Assign the custom baud rate to the channel structure
> + */
> +static void dgnc_set_custom_speed(struct channel_t *ch, uint newrate)
> +{
> +       int testdiv;
> +       int testrate_high;
> +       int testrate_low;
> +       int deltahigh;
> +       int deltalow;
> +
> +       if (newrate < 0)
> +               newrate = 0;
> +
> +       /*
> +        *  Since the divisor is stored in a 16-bit integer, we make sure
> +        *  we don't allow any rates smaller than a 16-bit integer would allow.
> +        *  And of course, rates above the dividend won't fly.
> +        */
> +       if (newrate && newrate < ((ch->ch_bd->bd_dividend / 0xFFFF) + 1))
> +               newrate = ((ch->ch_bd->bd_dividend / 0xFFFF) + 1);
> +
> +       if (newrate && newrate > ch->ch_bd->bd_dividend)
> +                newrate = ch->ch_bd->bd_dividend;
> +
> +       while (newrate > 0) {
> +               testdiv = ch->ch_bd->bd_dividend / newrate;
> +
> +               /*
> +                *  If we try to figure out what rate the board would use
> +                *  with the test divisor, it will be either equal or higher
> +                *  than the requested baud rate.  If we then determine the
> +                *  rate with a divisor one higher, we will get the next lower
> +                *  supported rate below the requested.
> +                */
> +               testrate_high = ch->ch_bd->bd_dividend / testdiv;
> +               testrate_low  = ch->ch_bd->bd_dividend / (testdiv + 1);
> +
> +               /*
> +                *  If the rate for the requested divisor is correct, just
> +                *  use it and be done.
> +                */
> +               if (testrate_high == newrate )
> +                       break;
> +
> +               /*
> +                *  Otherwise, pick the rate that is closer (i.e. whichever rate
> +                *  has a smaller delta).
> +                */
> +               deltahigh = testrate_high - newrate;
> +               deltalow = newrate - testrate_low;
> +
> +               if (deltahigh < deltalow) {
> +                       newrate = testrate_high;
> +                } else {
> +                       newrate = testrate_low;
> +               }
> +
> +               break;
> +       }
> +
> +       ch->ch_custom_speed = newrate;
> +
> +       return;
> +}
> +
> +
> +void dgnc_check_queue_flow_control(struct channel_t *ch)
> +{
> +       int qleft = 0;
> +
> +       /* Store how much space we have left in the queue */
> +       if ((qleft = ch->ch_r_tail - ch->ch_r_head - 1) < 0)
> +               qleft += RQUEUEMASK + 1;
> +
> +       /*
> +        * Check to see if we should enforce flow control on our queue because
> +        * the ld (or user) isn't reading data out of our queue fast enuf.
> +        *
> +        * NOTE: This is done based on what the current flow control of the
> +        * port is set for.
> +        *
> +        * 1) HWFLOW (RTS) - Turn off the UART's Receive interrupt.
> +        *      This will cause the UART's FIFO to back up, and force
> +        *      the RTS signal to be dropped.
> +        * 2) SWFLOW (IXOFF) - Keep trying to send a stop character to
> +        *      the other side, in hopes it will stop sending data to us.
> +        * 3) NONE - Nothing we can do.  We will simply drop any extra data
> +        *      that gets sent into us when the queue fills up.
> +        */
> +       if (qleft < 256) {
> +               /* HWFLOW */
> +               if (ch->ch_digi.digi_flags & CTSPACE || ch->ch_c_cflag & CRTSCTS) {
> +                       if(!(ch->ch_flags & CH_RECEIVER_OFF)) {
> +                               ch->ch_bd->bd_ops->disable_receiver(ch);
> +                               ch->ch_flags |= (CH_RECEIVER_OFF);
> +                               DPR_READ(("Internal queue hit hilevel mark (%d)! Turning off interrupts.\n",
> +                                       qleft));
> +                       }
> +               }
> +               /* SWFLOW */
> +               else if (ch->ch_c_iflag & IXOFF) {
> +                       if (ch->ch_stops_sent <= MAX_STOPS_SENT) {
> +                               ch->ch_bd->bd_ops->send_stop_character(ch);
> +                               ch->ch_stops_sent++;
> +                               DPR_READ(("Sending stop char!  Times sent: %x\n", ch->ch_stops_sent));
> +                       }
> +               }
> +               /* No FLOW */
> +               else {
> +                       /* Empty... Can't do anything about the impending overflow... */
> +               }
> +       }
> +
> +       /*
> +        * Check to see if we should unenforce flow control because
> +        * ld (or user) finally read enuf data out of our queue.
> +        *
> +        * NOTE: This is done based on what the current flow control of the
> +        * port is set for.
> +        *
> +        * 1) HWFLOW (RTS) - Turn back on the UART's Receive interrupt.
> +        *      This will cause the UART's FIFO to raise RTS back up,
> +        *      which will allow the other side to start sending data again.
> +        * 2) SWFLOW (IXOFF) - Send a start character to
> +        *      the other side, so it will start sending data to us again.
> +        * 3) NONE - Do nothing. Since we didn't do anything to turn off the
> +        *      other side, we don't need to do anything now.
> +        */
> +       if (qleft > (RQUEUESIZE / 2)) {
> +               /* HWFLOW */
> +               if (ch->ch_digi.digi_flags & RTSPACE || ch->ch_c_cflag & CRTSCTS) {
> +                       if (ch->ch_flags & CH_RECEIVER_OFF) {
> +                               ch->ch_bd->bd_ops->enable_receiver(ch);
> +                               ch->ch_flags &= ~(CH_RECEIVER_OFF);
> +                               DPR_READ(("Internal queue hit lowlevel mark (%d)! Turning on interrupts.\n",
> +                                       qleft));
> +                       }
> +               }
> +               /* SWFLOW */
> +               else if (ch->ch_c_iflag & IXOFF && ch->ch_stops_sent) {
> +                       ch->ch_stops_sent = 0;
> +                       ch->ch_bd->bd_ops->send_start_character(ch);
> +                       DPR_READ(("Sending start char!\n"));
> +               }
> +               /* No FLOW */
> +               else {
> +                       /* Nothing needed. */
> +               }
> +       }
> +}
> +
> +
> +void dgnc_wakeup_writes(struct channel_t *ch)
> +{
> +       int qlen = 0;
> +       ulong lock_flags;
> +
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return;
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       /*
> +        * If channel now has space, wake up anyone waiting on the condition.
> +        */
> +       if ((qlen = ch->ch_w_head - ch->ch_w_tail) < 0)
> +                qlen += WQUEUESIZE;
> +
> +       if (qlen >= (WQUEUESIZE - 256)) {
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               return;
> +       }
> +
> +       if (ch->ch_tun.un_flags & UN_ISOPEN) {
> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
> +                if ((ch->ch_tun.un_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
> +                        ch->ch_tun.un_tty->ldisc->ops->write_wakeup)
> +                {
> +                        DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +                        (ch->ch_tun.un_tty->ldisc->ops->write_wakeup)(ch->ch_tun.un_tty);
> +                        DGNC_LOCK(ch->ch_lock, lock_flags);
> +                }
> +#else
> +               if ((ch->ch_tun.un_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
> +                       ch->ch_tun.un_tty->ldisc.ops->write_wakeup)
> +               {
> +                       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +                       (ch->ch_tun.un_tty->ldisc.ops->write_wakeup)(ch->ch_tun.un_tty);
> +                       DGNC_LOCK(ch->ch_lock, lock_flags);
> +               }
> +#endif
> +
> +               wake_up_interruptible(&ch->ch_tun.un_tty->write_wait);
> +
> +               /*
> +                * If unit is set to wait until empty, check to make sure
> +                * the queue AND FIFO are both empty.
> +                */
> +               if (ch->ch_tun.un_flags & UN_EMPTY) {
> +                       if ((qlen == 0) && (ch->ch_bd->bd_ops->get_uart_bytes_left(ch) == 0)) {
> +                               ch->ch_tun.un_flags &= ~(UN_EMPTY);
> +
> +                               /*
> +                                * If RTS Toggle mode is on, whenever
> +                                * the queue and UART is empty, keep RTS low.
> +                                */
> +                               if (ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) {
> +                                       ch->ch_mostat &= ~(UART_MCR_RTS);
> +                                       ch->ch_bd->bd_ops->assert_modem_signals(ch);
> +                               }
> +
> +                               /*
> +                                * If DTR Toggle mode is on, whenever
> +                                * the queue and UART is empty, keep DTR low.
> +                                */
> +                               if (ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) {
> +                                       ch->ch_mostat &= ~(UART_MCR_DTR);
> +                                       ch->ch_bd->bd_ops->assert_modem_signals(ch);
> +                               }
> +                       }
> +               }
> +
> +               wake_up_interruptible(&ch->ch_tun.un_flags_wait);
> +       }
> +
> +       if (ch->ch_pun.un_flags & UN_ISOPEN) {
> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
> +                if ((ch->ch_pun.un_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
> +                        ch->ch_pun.un_tty->ldisc->ops->write_wakeup)
> +                {
> +                        DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +                        (ch->ch_pun.un_tty->ldisc->ops->write_wakeup)(ch->ch_pun.un_tty);
> +                        DGNC_LOCK(ch->ch_lock, lock_flags);
> +                }
> +#else
> +               if ((ch->ch_pun.un_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
> +                       ch->ch_pun.un_tty->ldisc.ops->write_wakeup)
> +               {
> +                       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +                       (ch->ch_pun.un_tty->ldisc.ops->write_wakeup)(ch->ch_pun.un_tty);
> +                       DGNC_LOCK(ch->ch_lock, lock_flags);
> +               }
> +#endif
> +
> +               wake_up_interruptible(&ch->ch_pun.un_tty->write_wait);
> +
> +               /*
> +                * If unit is set to wait until empty, check to make sure
> +                * the queue AND FIFO are both empty.
> +                */
> +               if (ch->ch_pun.un_flags & UN_EMPTY) {
> +                       if ((qlen == 0) && (ch->ch_bd->bd_ops->get_uart_bytes_left(ch) == 0)) {
> +                               ch->ch_pun.un_flags &= ~(UN_EMPTY);
> +                       }
> +               }
> +
> +               wake_up_interruptible(&ch->ch_pun.un_flags_wait);
> +       }
> +
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +}
> +
> +
> +
> +/************************************************************************
> + *
> + * TTY Entry points and helper functions
> + *
> + ************************************************************************/
> +
> +/*
> + * dgnc_tty_open()
> + *
> + */
> +static int dgnc_tty_open(struct tty_struct *tty, struct file *file)
> +{
> +       struct board_t  *brd;
> +       struct channel_t *ch;
> +       struct un_t     *un;
> +       uint            major = 0;
> +       uint            minor = 0;
> +       int             rc = 0;
> +       ulong           lock_flags;
> +
> +       rc = 0;
> +
> +       major = MAJOR(tty_devnum(tty));
> +       minor = MINOR(tty_devnum(tty));
> +
> +       if (major > 255) {
> +               return -ENXIO;
> +       }
> +
> +       /* Get board pointer from our array of majors we have allocated */
> +       brd = dgnc_BoardsByMajor[major];
> +       if (!brd) {
> +               return -ENXIO;
> +       }
> +
> +       /*
> +        * If board is not yet up to a state of READY, go to
> +        * sleep waiting for it to happen or they cancel the open.
> +        */
> +       rc = wait_event_interruptible(brd->state_wait,
> +               (brd->state & BOARD_READY));
> +
> +       if (rc) {
> +               return rc;
> +       }
> +
> +       DGNC_LOCK(brd->bd_lock, lock_flags);
> +
> +       /* If opened device is greater than our number of ports, bail. */
> +       if (PORT_NUM(minor) > brd->nasync) {
> +               DGNC_UNLOCK(brd->bd_lock, lock_flags);
> +               return -ENXIO;
> +       }
> +
> +       ch = brd->channels[PORT_NUM(minor)];
> +       if (!ch) {
> +               DGNC_UNLOCK(brd->bd_lock, lock_flags);
> +               return -ENXIO;
> +       }
> +
> +       /* Drop board lock */
> +       DGNC_UNLOCK(brd->bd_lock, lock_flags);
> +
> +       /* Grab channel lock */
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       /* Figure out our type */
> +       if (!IS_PRINT(minor)) {
> +               un = &brd->channels[PORT_NUM(minor)]->ch_tun;
> +               un->un_type = DGNC_SERIAL;
> +       }
> +       else if (IS_PRINT(minor)) {
> +               un = &brd->channels[PORT_NUM(minor)]->ch_pun;
> +               un->un_type = DGNC_PRINT;
> +       }
> +       else {
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               DPR_OPEN(("%d Unknown TYPE!\n", __LINE__));
> +               return -ENXIO;
> +       }
> +
> +       /*
> +        * If the port is still in a previous open, and in a state
> +        * where we simply cannot safely keep going, wait until the
> +        * state clears.
> +        */
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +       rc = wait_event_interruptible(ch->ch_flags_wait, ((ch->ch_flags & CH_OPENING) == 0));
> +
> +       /* If ret is non-zero, user ctrl-c'ed us */
> +       if (rc) {
> +               DPR_OPEN(("%d User ctrl c'ed\n", __LINE__));
> +               return -EINTR;
> +       }
> +
> +       /*
> +        * If either unit is in the middle of the fragile part of close,
> +        * we just cannot touch the channel safely.
> +        * Go to sleep, knowing that when the channel can be
> +        * touched safely, the close routine will signal the
> +        * ch_flags_wait to wake us back up.
> +        */
> +       rc = wait_event_interruptible(ch->ch_flags_wait,
> +               (((ch->ch_tun.un_flags | ch->ch_pun.un_flags) & UN_CLOSING) == 0));
> +
> +       /* If ret is non-zero, user ctrl-c'ed us */
> +       if (rc) {
> +               DPR_OPEN(("%d User ctrl c'ed\n", __LINE__));
> +               return -EINTR;
> +       }
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +
> +       /* Store our unit into driver_data, so we always have it available. */
> +       tty->driver_data = un;
> +
> +       DPR_OPEN(("Open called. MAJOR: %d MINOR:%d PORT_NUM: %x unit: %p NAME: %s\n",
> +               MAJOR(tty_devnum(tty)), MINOR(tty_devnum(tty)), PORT_NUM(minor), un, brd->name));
> +
> +       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_tty     = tty;
> +
> +               /* Maybe do something here to the TTY struct as well? */
> +       }
> +
> +
> +       /*
> +        * Allocate channel buffers for read/write/error.
> +        * Set flag, so we don't get trounced on.
> +        */
> +       ch->ch_flags |= (CH_OPENING);
> +
> +       /* Drop locks, as malloc with GFP_KERNEL can sleep */
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +       if (!ch->ch_rqueue)
> +               ch->ch_rqueue = dgnc_driver_kzmalloc(RQUEUESIZE, GFP_KERNEL);
> +       if (!ch->ch_equeue)
> +               ch->ch_equeue = dgnc_driver_kzmalloc(EQUEUESIZE, GFP_KERNEL);
> +       if (!ch->ch_wqueue)
> +               ch->ch_wqueue = dgnc_driver_kzmalloc(WQUEUESIZE, GFP_KERNEL);
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       ch->ch_flags &= ~(CH_OPENING);
> +       wake_up_interruptible(&ch->ch_flags_wait);
> +
> +       /*
> +        * Initialize if neither terminal or printer is open.
> +        */
> +       if (!((ch->ch_tun.un_flags | ch->ch_pun.un_flags) & UN_ISOPEN)) {
> +
> +               DPR_OPEN(("dgnc_open: initializing channel in open...\n"));
> +
> +               /*
> +                * Flush input queues.
> +                */
> +               ch->ch_r_head = ch->ch_r_tail = 0;
> +               ch->ch_e_head = ch->ch_e_tail = 0;
> +               ch->ch_w_head = ch->ch_w_tail = 0;
> +
> +               brd->bd_ops->flush_uart_write(ch);
> +               brd->bd_ops->flush_uart_read(ch);
> +
> +               ch->ch_flags = 0;
> +               ch->ch_cached_lsr = 0;
> +               ch->ch_stop_sending_break = 0;
> +               ch->ch_stops_sent = 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];
> +
> +               /*
> +                * Bring up RTS and DTR...
> +                * Also handle RTS or DTR toggle if set.
> +                */
> +               if (!(ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE))
> +                       ch->ch_mostat |= (UART_MCR_RTS);
> +               if (!(ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE))
> +                       ch->ch_mostat |= (UART_MCR_DTR);
> +
> +               /* Tell UART to init itself */
> +               brd->bd_ops->uart_init(ch);
> +       }
> +
> +       /*
> +        * Run param in case we changed anything
> +        */
> +       brd->bd_ops->param(tty);
> +
> +       dgnc_carrier(ch);
> +
> +       /*
> +        * follow protocol for opening port
> +        */
> +
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +       rc = dgnc_block_til_ready(tty, file, ch);
> +
> +       if (rc) {
> +               DPR_OPEN(("dgnc_tty_open returning after dgnc_block_til_ready "
> +                       "with %d\n", rc));
> +       }
> +
> +       /* No going back now, increment our unit and channel counters */
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +       ch->ch_open_count++;
> +       un->un_open_count++;
> +       un->un_flags |= (UN_ISOPEN);
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +       DPR_OPEN(("dgnc_tty_open finished\n"));
> +       return (rc);
> +}
> +
> +
> +/*
> + * dgnc_block_til_ready()
> + *
> + * Wait for DCD, if needed.
> + */
> +static int dgnc_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_flags = 0;
> +       int     sleep_on_un_flags = 0;
> +
> +       if (!tty || tty->magic != TTY_MAGIC || !file || !ch || ch->magic != DGNC_CHANNEL_MAGIC) {
> +               return (-ENXIO);
> +       }
> +
> +       un = tty->driver_data;
> +       if (!un || un->magic != DGNC_UNIT_MAGIC) {
> +               return (-ENXIO);
> +       }
> +
> +       DPR_OPEN(("dgnc_block_til_ready - before block.\n"));
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       ch->ch_wopen++;
> +
> +       /* Loop forever */
> +       while (1) {
> +
> +               sleep_on_un_flags = 0;
> +
> +               /*
> +                * 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)) {
> +                               retval = -EIO;
> +                               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;
> +                       }
> +               }
> +               else {
> +                       sleep_on_un_flags = 1;
> +               }
> +
> +               /*
> +                * 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(("dgnc_block_til_ready - blocking.\n"));
> +
> +               /*
> +                * Store the flags before we let go of channel lock
> +                */
> +               if (sleep_on_un_flags)
> +                       old_flags = ch->ch_tun.un_flags | ch->ch_pun.un_flags;
> +               else
> +                       old_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.
> +                */
> +
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +               DPR_OPEN(("Going to sleep on %s flags...\n",
> +                       (sleep_on_un_flags ? "un" : "ch")));
> +
> +               /*
> +                * Wait for something in the flags to change from the current value.
> +                */
> +               if (sleep_on_un_flags) {
> +                       retval = wait_event_interruptible(un->un_flags_wait,
> +                               (old_flags != (ch->ch_tun.un_flags | ch->ch_pun.un_flags)));
> +               }
> +               else {
> +                       retval = wait_event_interruptible(ch->ch_flags_wait,
> +                               (old_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.
> +                */
> +               DGNC_LOCK(ch->ch_lock, lock_flags);
> +       }
> +
> +       ch->ch_wopen--;
> +
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +       DPR_OPEN(("dgnc_block_til_ready - after blocking.\n"));
> +
> +       if (retval) {
> +               DPR_OPEN(("dgnc_block_til_ready - done. error. retval: %x\n", retval));
> +               return(retval);
> +       }
> +
> +       DPR_OPEN(("dgnc_block_til_ready - done no error. jiffies: %lu\n", jiffies));
> +
> +       return(0);
> +}
> +
> +
> +/*
> + * dgnc_tty_hangup()
> + *
> + * Hangup the port.  Like a close, but don't wait for output to drain.
> + */
> +static void dgnc_tty_hangup(struct tty_struct *tty)
> +{
> +       struct un_t     *un;
> +
> +       if (!tty || tty->magic != TTY_MAGIC)
> +               return;
> +
> +       un = tty->driver_data;
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return;
> +
> +       DPR_CLOSE(("dgnc_hangup called. ch->ch_open_count: %d un->un_open_count: %d\n",
> +               un->un_ch->ch_open_count, un->un_open_count));
> +
> +       /* flush the transmit queues */
> +       dgnc_tty_flush_buffer(tty);
> +
> +       DPR_CLOSE(("dgnc_hangup finished. ch->ch_open_count: %d un->un_open_count: %d\n",
> +               un->un_ch->ch_open_count, un->un_open_count));
> +}
> +
> +
> +/*
> + * dgnc_tty_close()
> + *
> + */
> +static void dgnc_tty_close(struct tty_struct *tty, struct file *file)
> +{
> +       struct ktermios *ts;
> +       struct board_t *bd;
> +       struct channel_t *ch;
> +       struct un_t *un;
> +       ulong lock_flags;
> +       int rc = 0;
> +
> +       if (!tty || tty->magic != TTY_MAGIC)
> +               return;
> +
> +       un = tty->driver_data;
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return;
> +
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return;
> +
> +       bd = ch->ch_bd;
> +       if (!bd || bd->magic != DGNC_BOARD_MAGIC)
> +               return;
> +
> +       ts = tty->termios;
> +
> +       DPR_CLOSE(("Close called\n"));
> +
> +       DGNC_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(("dgnc_tty_close: not last close ch: %d un:%d\n",
> +                       ch->ch_open_count, un->un_open_count));
> +
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +                return;
> +        }
> +
> +       /* OK, its the last close on the unit */
> +       DPR_CLOSE(("dgnc_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_STOPI | CH_FORCED_STOPI);
> +
> +               /*
> +                * turn off print device when closing print device.
> +                */
> +               if ((un->un_type == DGNC_PRINT) && (ch->ch_flags & CH_PRON) ) {
> +                       dgnc_wmove(ch, ch->ch_digi.digi_offstr,
> +                               (int) ch->ch_digi.digi_offlen);
> +                       ch->ch_flags &= ~CH_PRON;
> +               }
> +
> +               DGNC_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 = bd->bd_ops->drain(tty, 0);
> +
> +               DPR_CLOSE(("After calling wait_for_drain\n"));
> +
> +               if (rc) {
> +                       DPR_BASIC(("dgnc_tty_close - bad return: %d ", rc));
> +               }
> +
> +               dgnc_tty_flush_buffer(tty);
> +               tty_ldisc_flush(tty);
> +
> +               DGNC_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"));
> +
> +                       /* Drop RTS/DTR */
> +                       ch->ch_mostat &= ~(UART_MCR_DTR | UART_MCR_RTS);
> +                       bd->bd_ops->assert_modem_signals(ch);
> +
> +                       /*
> +                        * 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"));
> +
> +                               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +                               dgnc_ms_sleep(ch->ch_close_delay);
> +                               DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +                               DPR_CLOSE(("Close. After sleeping for RTS/DTR drop\n"));
> +                       }
> +               }
> +
> +               ch->ch_old_baud = 0;
> +
> +               /* Turn off UART interrupts for this port */
> +               ch->ch_bd->bd_ops->uart_off(ch);
> +       }
> +       else {
> +               /*
> +                * turn off print device when closing print device.
> +                */
> +               if ((un->un_type == DGNC_PRINT) && (ch->ch_flags & CH_PRON) ) {
> +                       dgnc_wmove(ch, ch->ch_digi.digi_offstr,
> +                               (int) ch->ch_digi.digi_offlen);
> +                       ch->ch_flags &= ~CH_PRON;
> +               }
> +       }
> +
> +       un->un_tty = NULL;
> +       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);
> +
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +        DPR_BASIC(("dgnc_tty_close - complete\n"));
> +}
> +
> +
> +/*
> + * dgnc_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 dgnc_tty_chars_in_buffer(struct tty_struct *tty)
> +{
> +       struct channel_t *ch = NULL;
> +       struct un_t *un = NULL;
> +       ushort thead;
> +       ushort ttail;
> +       uint tmask;
> +       uint chars = 0;
> +       ulong   lock_flags = 0;
> +
> +       if (tty == NULL)
> +               return(0);
> +
> +       un = tty->driver_data;
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return (0);
> +
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return (0);
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       tmask = WQUEUEMASK;
> +       thead = ch->ch_w_head & tmask;
> +       ttail = ch->ch_w_tail & tmask;
> +
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +       if (ttail == thead) {
> +               chars = 0;
> +       } else {
> +               if (thead >= ttail)
> +                       chars = thead - ttail;
> +               else
> +                       chars = thead - ttail + WQUEUESIZE;
> +       }
> +
> +       DPR_WRITE(("dgnc_tty_chars_in_buffer. Port: %x - %d (head: %d tail: %d)\n",
> +               ch->ch_portnum, chars, thead, ttail));
> +
> +        return(chars);
> +}
> +
> +
> +/*
> + * dgnc_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 dgnc_maxcps_room(struct tty_struct *tty, int bytes_available)
> +{
> +       struct channel_t *ch = NULL;
> +       struct un_t *un = NULL;
> +
> +       if (!tty)
> +               return (bytes_available);
> +
> +       un = tty->driver_data;
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return (bytes_available);
> +
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return (bytes_available);
> +
> +       /*
> +        * If its not the Transparent print device, return
> +        * the full data amount.
> +        */
> +       if (un->un_type != DGNC_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);
> +}
> +
> +
> +/*
> + * dgnc_tty_write_room()
> + *
> + * Return space available in Tx buffer
> + */
> +static int dgnc_tty_write_room(struct tty_struct *tty)
> +{
> +       struct channel_t *ch = NULL;
> +       struct un_t *un = NULL;
> +       ushort head;
> +       ushort tail;
> +       ushort tmask;
> +       int ret = 0;
> +       ulong   lock_flags = 0;
> +
> +       if (tty == NULL || dgnc_TmpWriteBuf == NULL)
> +               return(0);
> +
> +       un = tty->driver_data;
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return (0);
> +
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return (0);
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       tmask = WQUEUEMASK;
> +       head = (ch->ch_w_head) & tmask;
> +       tail = (ch->ch_w_tail) & tmask;
> +
> +       if ((ret = tail - head - 1) < 0)
> +               ret += WQUEUESIZE;
> +
> +       /* Limit printer to maxcps */
> +       ret = dgnc_maxcps_room(tty, ret);
> +
> +       /*
> +        * If we are printer device, leave space for
> +        * possibly both the on and off strings.
> +        */
> +       if (un->un_type == DGNC_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;
> +
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +       DPR_WRITE(("dgnc_tty_write_room - %d tail: %d head: %d\n", ret, tail, head));
> +
> +        return(ret);
> +}
> +
> +
> +/*
> + * dgnc_tty_put_char()
> + *
> + * Put a character into ch->ch_buf
> + *
> + *      - used by the line discipline for OPOST processing
> + */
> +static int dgnc_tty_put_char(struct tty_struct *tty, unsigned char c)
> +{
> +       /*
> +        * Simply call tty_write.
> +        */
> +       DPR_WRITE(("dgnc_tty_put_char called\n"));
> +       dgnc_tty_write(tty, &c, 1);
> +       return 1;
> +}
> +
> +
> +/*
> + * dgnc_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 dgnc_tty_write(struct tty_struct *tty,
> +               const unsigned char *buf, int count)
> +{
> +       struct channel_t *ch = NULL;
> +       struct un_t *un = NULL;
> +       int bufcount = 0, n = 0;
> +       int orig_count = 0;
> +       ulong lock_flags;
> +       ushort head;
> +       ushort tail;
> +       ushort tmask;
> +       uint remain;
> +       int from_user = 0;
> +
> +       if (tty == NULL || dgnc_TmpWriteBuf == NULL)
> +               return(0);
> +
> +       un = tty->driver_data;
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return(0);
> +
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return(0);
> +
> +       if (!count)
> +               return(0);
> +
> +       DPR_WRITE(("dgnc_tty_write: Port: %x tty=%p user=%d len=%d\n",
> +               ch->ch_portnum, 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;
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       /* Get our space available for the channel from the board */
> +       tmask = WQUEUEMASK;
> +       head = (ch->ch_w_head) & tmask;
> +       tail = (ch->ch_w_tail) & tmask;
> +
> +       if ((bufcount = tail - head - 1) < 0)
> +               bufcount += WQUEUESIZE;
> +
> +       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 = dgnc_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) {
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               return(0);
> +       }
> +
> +       /*
> +        * Output the printer ON string, if we are in terminal mode, but
> +        * need to be in printer mode.
> +        */
> +       if ((un->un_type == DGNC_PRINT) && !(ch->ch_flags & CH_PRON)) {
> +               dgnc_wmove(ch, ch->ch_digi.digi_onstr,
> +                   (int) ch->ch_digi.digi_onlen);
> +               head = (ch->ch_w_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 != DGNC_PRINT) && (ch->ch_flags & CH_PRON)) {
> +               dgnc_wmove(ch, ch->ch_digi.digi_offstr,
> +                       (int) ch->ch_digi.digi_offlen);
> +               head = (ch->ch_w_head) & tmask;
> +               ch->ch_flags &= ~CH_PRON;
> +       }
> +
> +       /*
> +        * If there is nothing left to copy, or I can't handle any more data, leave.
> +        */
> +       if (count <= 0) {
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               return(0);
> +       }
> +
> +       if (from_user) {
> +
> +               count = min(count, WRITEBUFLEN);
> +
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +               /*
> +                * 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.
> +                */
> +               /* we're allowed to block if it's from_user */
> +               if (down_interruptible(&dgnc_TmpWriteSem)) {
> +                       return (-EINTR);
> +               }
> +
> +               /*
> +                * copy_from_user() returns the number
> +                * of bytes that could *NOT* be copied.
> +                */
> +               count -= copy_from_user(dgnc_TmpWriteBuf, (const uchar __user *) buf, count);
> +
> +               if (!count) {
> +                       up(&dgnc_TmpWriteSem);
> +                       return(-EFAULT);
> +               }
> +
> +               DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +               buf = dgnc_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 = WQUEUESIZE - head;
> +
> +       if (n >= remain) {
> +               n -= remain;
> +               memcpy(ch->ch_wqueue + head, buf, remain);
> +               dgnc_sniff_nowait_nolock(ch, "USER WRITE", ch->ch_wqueue + head, remain);
> +               head = 0;
> +               buf += remain;
> +       }
> +
> +       if (n > 0) {
> +               /*
> +                * Move rest of data.
> +                */
> +               remain = n;
> +               memcpy(ch->ch_wqueue + head, buf, remain);
> +               dgnc_sniff_nowait_nolock(ch, "USER WRITE", ch->ch_wqueue + head, remain);
> +               head += remain;
> +       }
> +
> +       if (count) {
> +               head &= tmask;
> +               ch->ch_w_head = head;
> +       }
> +
> +#if 0
> +       /*
> +        * If this is the print device, and the
> +        * printer is still on, we need to turn it
> +        * off before going idle.
> +        */
> +       if (count == orig_count) {
> +               if ((un->un_type == DGNC_PRINT) && (ch->ch_flags & CH_PRON)) {
> +                       head &= tmask;
> +                       ch->ch_w_head = head;
> +                       dgnc_wmove(ch, ch->ch_digi.digi_offstr,
> +                               (int) ch->ch_digi.digi_offlen);
> +                       head = (ch->ch_w_head) & tmask;
> +                       ch->ch_flags &= ~CH_PRON;
> +               }
> +       }
> +#endif
> +
> +       /* Update printer buffer empty time. */
> +       if ((un->un_type == DGNC_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) {
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               up(&dgnc_TmpWriteSem);
> +       } else {
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +       }
> +
> +       DPR_WRITE(("Write finished - Write %d bytes of %d.\n", count, orig_count));
> +
> +       if (count) {
> +               /*
> +                * Channel lock is grabbed and then released
> +                * inside this routine.
> +                */
> +               ch->ch_bd->bd_ops->copy_data_from_queue_to_uart(ch);
> +       }
> +
> +       return (count);
> +}
> +
> +
> +/*
> + * Return modem signals to ld.
> + */
> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)
> +static int dgnc_tty_tiocmget(struct tty_struct *tty)
> +#else
> +static int dgnc_tty_tiocmget(struct tty_struct *tty, struct file *file)
> +#endif
> +{
> +       struct channel_t *ch;
> +       struct un_t *un;
> +       int result = -EIO;
> +       uchar mstat = 0;
> +       ulong   lock_flags;
> +
> +       if (!tty || tty->magic != TTY_MAGIC)
> +               return result;
> +
> +       un = tty->driver_data;
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return result;
> +
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return result;
> +
> +       DPR_IOCTL(("dgnc_tty_tiocmget start\n"));
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       mstat = (ch->ch_mostat | ch->ch_mistat);
> +
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +       result = 0;
> +
> +       if (mstat & UART_MCR_DTR)
> +               result |= TIOCM_DTR;
> +       if (mstat & UART_MCR_RTS)
> +               result |= TIOCM_RTS;
> +       if (mstat & UART_MSR_CTS)
> +               result |= TIOCM_CTS;
> +       if (mstat & UART_MSR_DSR)
> +               result |= TIOCM_DSR;
> +       if (mstat & UART_MSR_RI)
> +               result |= TIOCM_RI;
> +       if (mstat & UART_MSR_DCD)
> +               result |= TIOCM_CD;
> +
> +       DPR_IOCTL(("dgnc_tty_tiocmget finish\n"));
> +
> +       return result;
> +}
> +
> +
> +/*
> + * dgnc_tty_tiocmset()
> + *
> + * Set modem signals, called by ld.
> + */
> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)
> +static int dgnc_tty_tiocmset(struct tty_struct *tty,
> +                unsigned int set, unsigned int clear)
> +#else
> +static int dgnc_tty_tiocmset(struct tty_struct *tty, struct file *file,
> +               unsigned int set, unsigned int clear)
> +#endif
> +{
> +       struct board_t *bd;
> +       struct channel_t *ch;
> +       struct un_t *un;
> +       int ret = -EIO;
> +       ulong   lock_flags;
> +
> +       if (!tty || tty->magic != TTY_MAGIC)
> +               return ret;
> +
> +       un = tty->driver_data;
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return ret;
> +
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return ret;
> +
> +       bd = ch->ch_bd;
> +       if (!bd || bd->magic != DGNC_BOARD_MAGIC)
> +               return ret;
> +
> +       DPR_IOCTL(("dgnc_tty_tiocmset start\n"));
> +
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       if (set & TIOCM_RTS) {
> +               ch->ch_mostat |= UART_MCR_RTS;
> +        }
> +
> +       if (set & TIOCM_DTR) {
> +               ch->ch_mostat |= UART_MCR_DTR;
> +        }
> +
> +       if (clear & TIOCM_RTS) {
> +               ch->ch_mostat &= ~(UART_MCR_RTS);
> +        }
> +
> +       if (clear & TIOCM_DTR) {
> +               ch->ch_mostat &= ~(UART_MCR_DTR);
> +        }
> +
> +       ch->ch_bd->bd_ops->assert_modem_signals(ch);
> +
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +       DPR_IOCTL(("dgnc_tty_tiocmset finish\n"));
> +
> +       return (0);
> +}
> +
> +
> +/*
> + * dgnc_tty_send_break()
> + *
> + * Send a Break, called by ld.
> + */
> +static int dgnc_tty_send_break(struct tty_struct *tty, int msec)
> +{
> +       struct board_t *bd;
> +       struct channel_t *ch;
> +       struct un_t *un;
> +       int ret = -EIO;
> +       ulong   lock_flags;
> +
> +       if (!tty || tty->magic != TTY_MAGIC)
> +               return ret;
> +
> +       un = tty->driver_data;
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return ret;
> +
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return ret;
> +
> +       bd = ch->ch_bd;
> +       if (!bd || bd->magic != DGNC_BOARD_MAGIC)
> +               return ret;
> +
> +       switch (msec) {
> +       case -1:
> +               msec = 0xFFFF;
> +               break;
> +       case 0:
> +               msec = 0;
> +               break;
> +       default:
> +               break;
> +       }
> +
> +       DPR_IOCTL(("dgnc_tty_send_break start 1.  %lx\n", jiffies));
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       ch->ch_bd->bd_ops->send_break(ch, msec);
> +
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +       DPR_IOCTL(("dgnc_tty_send_break finish\n"));
> +
> +       return (0);
> +
> +}
> +
> +
> +/*
> + * dgnc_tty_wait_until_sent()
> + *
> + * wait until data has been transmitted, called by ld.
> + */
> +static void dgnc_tty_wait_until_sent(struct tty_struct *tty, int timeout)
> +{
> +       struct board_t *bd;
> +       struct channel_t *ch;
> +       struct un_t *un;
> +       int rc;
> +
> +       if (!tty || tty->magic != TTY_MAGIC)
> +               return;
> +
> +       un = tty->driver_data;
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return;
> +
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return;
> +
> +       bd = ch->ch_bd;
> +       if (!bd || bd->magic != DGNC_BOARD_MAGIC)
> +               return;
> +
> +       rc = bd->bd_ops->drain(tty, 0);
> +       if (rc) {
> +               DPR_IOCTL(("dgnc_tty_ioctl - bad return: %d ", rc));
> +               return;
> +       }
> +       return;
> +}
> +
> +
> +/*
> + * dgnc_send_xchar()
> + *
> + * send a high priority character, called by ld.
> + */
> +static void dgnc_tty_send_xchar(struct tty_struct *tty, char c)
> +{
> +       struct board_t *bd;
> +       struct channel_t *ch;
> +       struct un_t *un;
> +       ulong   lock_flags;
> +
> +       if (!tty || tty->magic != TTY_MAGIC)
> +               return;
> +
> +       un = tty->driver_data;
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return;
> +
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return;
> +
> +       bd = ch->ch_bd;
> +       if (!bd || bd->magic != DGNC_BOARD_MAGIC)
> +               return;
> +
> +       DPR_IOCTL(("dgnc_tty_send_xchar start\n"));
> +       printk("dgnc_tty_send_xchar start\n");
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +       bd->bd_ops->send_immediate_char(ch, c);
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +       DPR_IOCTL(("dgnc_tty_send_xchar finish\n"));
> +       printk("dgnc_tty_send_xchar finish\n");
> +       return;
> +}
> +
> +
> +
> +
> +/*
> + * Return modem signals to ld.
> + */
> +static inline int dgnc_get_mstat(struct channel_t *ch)
> +{
> +       unsigned char mstat;
> +       int result = -EIO;
> +       ulong   lock_flags;
> +
> +       DPR_IOCTL(("dgnc_getmstat start\n"));
> +
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return(-ENXIO);
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       mstat = (ch->ch_mostat | ch->ch_mistat);
> +
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +       result = 0;
> +
> +       if (mstat & UART_MCR_DTR)
> +               result |= TIOCM_DTR;
> +       if (mstat & UART_MCR_RTS)
> +               result |= TIOCM_RTS;
> +       if (mstat & UART_MSR_CTS)
> +               result |= TIOCM_CTS;
> +       if (mstat & UART_MSR_DSR)
> +               result |= TIOCM_DSR;
> +       if (mstat & UART_MSR_RI)
> +               result |= TIOCM_RI;
> +       if (mstat & UART_MSR_DCD)
> +               result |= TIOCM_CD;
> +
> +       DPR_IOCTL(("dgnc_getmstat finish\n"));
> +
> +       return(result);
> +}
> +
> +
> +
> +/*
> + * Return modem signals to ld.
> + */
> +static int dgnc_get_modem_info(struct channel_t *ch, unsigned int  __user *value)
> +{
> +       int result;
> +       int rc;
> +
> +       DPR_IOCTL(("dgnc_get_modem_info start\n"));
> +
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return(-ENXIO);
> +
> +       result = dgnc_get_mstat(ch);
> +
> +       if (result < 0)
> +               return (-ENXIO);
> +
> +       rc = put_user(result, value);
> +
> +       DPR_IOCTL(("dgnc_get_modem_info finish\n"));
> +       return(rc);
> +}
> +
> +
> +/*
> + * dgnc_set_modem_info()
> + *
> + * Set modem signals, called by ld.
> + */
> +static int dgnc_set_modem_info(struct tty_struct *tty, unsigned int command, unsigned int __user *value)
> +{
> +       struct board_t *bd;
> +       struct channel_t *ch;
> +       struct un_t *un;
> +       int ret = -ENXIO;
> +       unsigned int arg = 0;
> +       ulong   lock_flags;
> +
> +       if (!tty || tty->magic != TTY_MAGIC)
> +               return ret;
> +
> +       un = tty->driver_data;
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return ret;
> +
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return ret;
> +
> +       bd = ch->ch_bd;
> +       if (!bd || bd->magic != DGNC_BOARD_MAGIC)
> +               return ret;
> +
> +       ret = 0;
> +
> +       DPR_IOCTL(("dgnc_set_modem_info() start\n"));
> +
> +       ret = get_user(arg, value);
> +       if (ret)
> +               return(ret);
> +
> +       switch (command) {
> +       case TIOCMBIS:
> +               if (arg & TIOCM_RTS) {
> +                       ch->ch_mostat |= UART_MCR_RTS;
> +               }
> +
> +               if (arg & TIOCM_DTR) {
> +                       ch->ch_mostat |= UART_MCR_DTR;
> +               }
> +
> +               break;
> +
> +       case TIOCMBIC:
> +               if (arg & TIOCM_RTS) {
> +                       ch->ch_mostat &= ~(UART_MCR_RTS);
> +               }
> +
> +               if (arg & TIOCM_DTR) {
> +                       ch->ch_mostat &= ~(UART_MCR_DTR);
> +               }
> +
> +               break;
> +
> +        case TIOCMSET:
> +
> +               if (arg & TIOCM_RTS) {
> +                       ch->ch_mostat |= UART_MCR_RTS;
> +               }
> +               else {
> +                       ch->ch_mostat &= ~(UART_MCR_RTS);
> +               }
> +
> +               if (arg & TIOCM_DTR) {
> +                       ch->ch_mostat |= UART_MCR_DTR;
> +               }
> +               else {
> +                       ch->ch_mostat &= ~(UART_MCR_DTR);
> +               }
> +
> +               break;
> +
> +       default:
> +               return(-EINVAL);
> +       }
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       ch->ch_bd->bd_ops->assert_modem_signals(ch);
> +
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +       DPR_IOCTL(("dgnc_set_modem_info finish\n"));
> +
> +       return (0);
> +}
> +
> +
> +/*
> + * dgnc_tty_digigeta()
> + *
> + * Ioctl to get the information for ditty.
> + *
> + *
> + *
> + */
> +static int dgnc_tty_digigeta(struct tty_struct *tty, struct digi_t __user *retinfo)
> +{
> +       struct channel_t *ch;
> +       struct un_t *un;
> +       struct digi_t tmp;
> +       ulong   lock_flags;
> +
> +       if (!retinfo)
> +               return (-EFAULT);
> +
> +       if (!tty || tty->magic != TTY_MAGIC)
> +               return (-EFAULT);
> +
> +       un = tty->driver_data;
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return (-EFAULT);
> +
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return (-EFAULT);
> +
> +       memset(&tmp, 0, sizeof(tmp));
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +       memcpy(&tmp, &ch->ch_digi, sizeof(tmp));
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +       if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
> +               return (-EFAULT);
> +
> +       return (0);
> +}
> +
> +
> +/*
> + * dgnc_tty_digiseta()
> + *
> + * Ioctl to set the information for ditty.
> + *
> + *
> + *
> + */
> +static int dgnc_tty_digiseta(struct tty_struct *tty, struct digi_t __user *new_info)
> +{
> +       struct board_t *bd;
> +       struct channel_t *ch;
> +       struct un_t *un;
> +       struct digi_t new_digi;
> +       ulong lock_flags;
> +
> +       DPR_IOCTL(("DIGI_SETA start\n"));
> +
> +       if (!tty || tty->magic != TTY_MAGIC)
> +               return (-EFAULT);
> +
> +       un = tty->driver_data;
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return (-EFAULT);
> +
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return (-EFAULT);
> +
> +       bd = ch->ch_bd;
> +       if (!bd || bd->magic != DGNC_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);
> +       }
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       /*
> +        * Handle transistions to and from RTS Toggle.
> +        */
> +       if (!(ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) && (new_digi.digi_flags & DIGI_RTS_TOGGLE))
> +               ch->ch_mostat &= ~(UART_MCR_RTS);
> +       if ((ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) && !(new_digi.digi_flags & DIGI_RTS_TOGGLE))
> +               ch->ch_mostat |= (UART_MCR_RTS);
> +
> +       /*
> +        * Handle transistions to and from DTR Toggle.
> +        */
> +       if (!(ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) && (new_digi.digi_flags & DIGI_DTR_TOGGLE))
> +               ch->ch_mostat &= ~(UART_MCR_DTR);
> +       if ((ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) && !(new_digi.digi_flags & DIGI_DTR_TOGGLE))
> +               ch->ch_mostat |= (UART_MCR_DTR);
> +
> +       memcpy(&ch->ch_digi, &new_digi, 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_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;
> +
> +       ch->ch_bd->bd_ops->param(tty);
> +
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +       DPR_IOCTL(("DIGI_SETA finish\n"));
> +
> +       return(0);
> +}
> +
> +
> +/*
> + * dgnc_set_termios()
> + */
> +static void dgnc_tty_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
> +{
> +       struct board_t *bd;
> +       struct channel_t *ch;
> +       struct un_t *un;
> +       unsigned long lock_flags;
> +
> +       if (!tty || tty->magic != TTY_MAGIC)
> +               return;
> +
> +       un = tty->driver_data;
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return;
> +
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return;
> +
> +       bd = ch->ch_bd;
> +       if (!bd || bd->magic != DGNC_BOARD_MAGIC)
> +               return;
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       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];
> +
> +       ch->ch_bd->bd_ops->param(tty);
> +       dgnc_carrier(ch);
> +
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +}
> +
> +
> +static void dgnc_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 != DGNC_UNIT_MAGIC)
> +               return;
> +
> +        ch = un->un_ch;
> +        if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +                return;
> +
> +       DPR_IOCTL(("dgnc_tty_throttle start\n"));
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       ch->ch_flags |= (CH_FORCED_STOPI);
> +
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +       DPR_IOCTL(("dgnc_tty_throttle finish\n"));
> +}
> +
> +
> +static void dgnc_tty_unthrottle(struct tty_struct *tty)
> +{
> +       struct channel_t *ch;
> +       struct un_t *un;
> +       ulong   lock_flags;
> +
> +       if (!tty || tty->magic != TTY_MAGIC)
> +               return;
> +
> +       un = tty->driver_data;
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return;
> +
> +        ch = un->un_ch;
> +        if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +                return;
> +
> +       DPR_IOCTL(("dgnc_tty_unthrottle start\n"));
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       ch->ch_flags &= ~(CH_FORCED_STOPI);
> +
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +       DPR_IOCTL(("dgnc_tty_unthrottle finish\n"));
> +}
> +
> +
> +static void dgnc_tty_start(struct tty_struct *tty)
> +{
> +       struct board_t *bd;
> +       struct channel_t *ch;
> +       struct un_t *un;
> +       ulong lock_flags;
> +
> +       if (!tty || tty->magic != TTY_MAGIC)
> +               return;
> +
> +       un = tty->driver_data;
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return;
> +
> +        ch = un->un_ch;
> +        if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +                return;
> +
> +       bd = ch->ch_bd;
> +       if (!bd || bd->magic != DGNC_BOARD_MAGIC)
> +               return;
> +
> +       DPR_IOCTL(("dgcn_tty_start start\n"));
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       ch->ch_flags &= ~(CH_FORCED_STOP);
> +
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +       DPR_IOCTL(("dgnc_tty_start finish\n"));
> +}
> +
> +
> +static void dgnc_tty_stop(struct tty_struct *tty)
> +{
> +       struct board_t *bd;
> +       struct channel_t *ch;
> +       struct un_t *un;
> +       ulong lock_flags;
> +
> +       if (!tty || tty->magic != TTY_MAGIC)
> +               return;
> +
> +       un = tty->driver_data;
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return;
> +
> +        ch = un->un_ch;
> +        if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +                return;
> +
> +       bd = ch->ch_bd;
> +       if (!bd || bd->magic != DGNC_BOARD_MAGIC)
> +               return;
> +
> +       DPR_IOCTL(("dgnc_tty_stop start\n"));
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       ch->ch_flags |= (CH_FORCED_STOP);
> +
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +       DPR_IOCTL(("dgnc_tty_stop finish\n"));
> +}
> +
> +
> +/*
> + * dgnc_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 dgnc_tty_flush_chars(struct tty_struct *tty)
> +{
> +       struct board_t *bd;
> +       struct channel_t *ch;
> +       struct un_t *un;
> +       ulong lock_flags;
> +
> +       if (!tty || tty->magic != TTY_MAGIC)
> +               return;
> +
> +       un = tty->driver_data;
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return;
> +
> +        ch = un->un_ch;
> +        if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +                return;
> +
> +       bd = ch->ch_bd;
> +       if (!bd || bd->magic != DGNC_BOARD_MAGIC)
> +               return;
> +
> +       DPR_IOCTL(("dgnc_tty_flush_chars start\n"));
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       /* Do something maybe here */
> +
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +       DPR_IOCTL(("dgnc_tty_flush_chars finish\n"));
> +}
> +
> +
> +
> +/*
> + * dgnc_tty_flush_buffer()
> + *
> + * Flush Tx buffer (make in == out)
> + */
> +static void dgnc_tty_flush_buffer(struct tty_struct *tty)
> +{
> +       struct channel_t *ch;
> +       struct un_t *un;
> +       ulong lock_flags;
> +
> +       if (!tty || tty->magic != TTY_MAGIC)
> +               return;
> +
> +       un = tty->driver_data;
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return;
> +
> +        ch = un->un_ch;
> +        if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +                return;
> +
> +       DPR_IOCTL(("dgnc_tty_flush_buffer on port: %d start\n", ch->ch_portnum));
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       ch->ch_flags &= ~CH_STOP;
> +
> +       /* Flush our write queue */
> +       ch->ch_w_head = ch->ch_w_tail;
> +
> +       /* Flush UARTs transmit FIFO */
> +       ch->ch_bd->bd_ops->flush_uart_write(ch);
> +
> +       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);
> +       }
> +
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +       DPR_IOCTL(("dgnc_tty_flush_buffer finish\n"));
> +}
> +
> +
> +
> +/*****************************************************************************
> + *
> + * The IOCTL function and all of its helpers
> + *
> + *****************************************************************************/
> +
> +/*
> + * dgnc_tty_ioctl()
> + *
> + * The usual assortment of ioctl's
> + */
> +static int dgnc_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;
> +       ulong lock_flags;
> +       void __user *uarg = (void __user *) arg;
> +
> +       if (!tty || tty->magic != TTY_MAGIC)
> +               return (-ENODEV);
> +
> +       un = tty->driver_data;
> +       if (!un || un->magic != DGNC_UNIT_MAGIC)
> +               return (-ENODEV);
> +
> +       ch = un->un_ch;
> +       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
> +               return (-ENODEV);
> +
> +       bd = ch->ch_bd;
> +       if (!bd || bd->magic != DGNC_BOARD_MAGIC)
> +               return (-ENODEV);
> +
> +       DPR_IOCTL(("dgnc_tty_ioctl start on port %d - cmd %s (%x), arg %lx\n",
> +               ch->ch_portnum, dgnc_ioctl_name(cmd), cmd, arg));
> +
> +       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +       if (un->un_open_count <= 0) {
> +               DPR_BASIC(("dgnc_tty_ioctl - unit not open.\n"));
> +               DGNC_UNLOCK(ch->ch_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);
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               if (rc) {
> +                       return(rc);
> +               }
> +
> +               rc = ch->ch_bd->bd_ops->drain(tty, 0);
> +
> +               if (rc) {
> +                       DPR_IOCTL(("dgnc_tty_ioctl - bad return: %d ", rc));
> +                       return(-EINTR);
> +               }
> +
> +               DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +               if(((cmd == TCSBRK) && (!arg)) || (cmd == TCSBRKP)) {
> +                       ch->ch_bd->bd_ops->send_break(ch, 250);
> +               }
> +
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +               DPR_IOCTL(("dgnc_tty_ioctl finish on port %d - cmd %s (%x), arg %lx\n",
> +                       ch->ch_portnum, dgnc_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);
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               if (rc) {
> +                       return(rc);
> +               }
> +
> +               rc = ch->ch_bd->bd_ops->drain(tty, 0);
> +               if (rc) {
> +                       DPR_IOCTL(("dgnc_tty_ioctl - bad return: %d ", rc));
> +                       return(-EINTR);
> +               }
> +
> +               DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +               ch->ch_bd->bd_ops->send_break(ch, 250);
> +
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +               DPR_IOCTL(("dgnc_tty_ioctl finish on port %d - cmd %s (%x), arg %lx\n",
> +                       ch->ch_portnum, dgnc_ioctl_name(cmd), cmd, arg));
> +
> +               return(0);
> +
> +       case TIOCSBRK:
> +               rc = tty_check_change(tty);
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               if (rc) {
> +                       return(rc);
> +               }
> +
> +               rc = ch->ch_bd->bd_ops->drain(tty, 0);
> +               if (rc) {
> +                       DPR_IOCTL(("dgnc_tty_ioctl - bad return: %d ", rc));
> +                       return(-EINTR);
> +               }
> +
> +               DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +               ch->ch_bd->bd_ops->send_break(ch, 250);
> +
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +               DPR_IOCTL(("dgnc_tty_ioctl finish on port %d - cmd %s (%x), arg %lx\n",
> +                       ch->ch_portnum, dgnc_ioctl_name(cmd), cmd, arg));
> +
> +               return(0);
> +
> +       case TIOCCBRK:
> +               /* Do Nothing */
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               return 0;
> +
> +       case TIOCGSOFTCAR:
> +
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +               rc = put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long __user *) arg);
> +               return(rc);
> +
> +       case TIOCSSOFTCAR:
> +
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               rc = get_user(arg, (unsigned long __user *) arg);
> +               if (rc)
> +                       return(rc);
> +
> +               DGNC_LOCK(ch->ch_lock, lock_flags);
> +               tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0));
> +               ch->ch_bd->bd_ops->param(tty);
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +               return(0);
> +
> +       case TIOCMGET:
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +                return(dgnc_get_modem_info(ch, uarg));
> +
> +       case TIOCMBIS:
> +       case TIOCMBIC:
> +       case TIOCMSET:
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               return(dgnc_set_modem_info(tty, cmd, uarg));
> +
> +               /*
> +                * 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) {
> +                       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +                       return(rc);
> +               }
> +
> +               if ((arg == TCIFLUSH) || (arg == TCIOFLUSH)) {
> +                       ch->ch_r_head = ch->ch_r_tail;
> +                       ch->ch_bd->bd_ops->flush_uart_read(ch);
> +                       /* Force queue flow control to be released, if needed */
> +                       dgnc_check_queue_flow_control(ch);
> +               }
> +
> +               if ((arg == TCOFLUSH) || (arg == TCIOFLUSH)) {
> +                       if (!(un->un_type == DGNC_PRINT)) {
> +                               ch->ch_w_head = ch->ch_w_tail;
> +                               ch->ch_bd->bd_ops->flush_uart_write(ch);
> +
> +                               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);
> +                               }
> +
> +                       }
> +               }
> +
> +               /* pretend we didn't recognize this IOCTL */
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               return(-ENOIOCTLCMD);
> +
> +#ifdef TIOCGETP
> +       case TIOCGETP:
> +#endif
> +       case TCGETS:
> +       case TCGETA:
> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
> +               if (tty->ldisc->ops->ioctl) {
> +#else
> +               if (tty->ldisc.ops->ioctl) {
> +#endif
> +                       int retval = (-ENXIO);
> +
> +                       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +                       if (tty->termios) {
> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
> +                               retval = ((tty->ldisc->ops->ioctl) (tty, file, cmd, arg));
> +#else
> +                               retval = ((tty->ldisc.ops->ioctl) (tty, file, cmd, arg));
> +#endif
> +                       }
> +
> +                       DPR_IOCTL(("dgnc_tty_ioctl (LINE:%d) finish on port %d - cmd %s (%x), arg %lx\n",
> +                               __LINE__, ch->ch_portnum, dgnc_ioctl_name(cmd), cmd, arg));
> +                       return(retval);
> +               }
> +
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               DPR_IOCTL(("dgnc_tty_ioctl (LINE:%d) finish on port %d - cmd %s (%x), arg %lx\n",
> +                       __LINE__, ch->ch_portnum, dgnc_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;
> +                       ch->ch_r_head = ch->ch_r_tail;
> +                       ch->ch_bd->bd_ops->flush_uart_read(ch);
> +                       /* Force queue flow control to be released, if needed */
> +                       dgnc_check_queue_flow_control(ch);
> +               }
> +
> +               /* now wait for all the output to drain */
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               rc = ch->ch_bd->bd_ops->drain(tty, 0);
> +               if (rc) {
> +                       DPR_IOCTL(("dgnc_tty_ioctl - bad return: %d\n", rc));
> +                       return(-EINTR);
> +               }
> +
> +               DPR_IOCTL(("dgnc_tty_ioctl finish on port %d - cmd %s (%x), arg %lx\n",
> +                       ch->ch_portnum, dgnc_ioctl_name(cmd), cmd, arg));
> +
> +               /* pretend we didn't recognize this */
> +               return(-ENOIOCTLCMD);
> +
> +       case TCSETAW:
> +
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               rc = ch->ch_bd->bd_ops->drain(tty, 0);
> +               if (rc) {
> +                       DPR_IOCTL(("dgnc_tty_ioctl - bad return: %d ", rc));
> +                       return(-EINTR);
> +               }
> +
> +               /* pretend we didn't recognize this */
> +               return(-ENOIOCTLCMD);
> +
> +       case TCXONC:
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               /* Make the ld do it */
> +               return(-ENOIOCTLCMD);
> +
> +       case DIGI_GETA:
> +               /* get information for ditty */
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               return(dgnc_tty_digigeta(tty, uarg));
> +
> +       case DIGI_SETAW:
> +       case DIGI_SETAF:
> +
> +               /* set information for ditty */
> +               if (cmd == (DIGI_SETAW)) {
> +
> +                       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +                       rc = ch->ch_bd->bd_ops->drain(tty, 0);
> +                       if (rc) {
> +                               DPR_IOCTL(("dgnc_tty_ioctl - bad return: %d ", rc));
> +                               return(-EINTR);
> +                       }
> +                       DGNC_LOCK(ch->ch_lock, lock_flags);
> +               }
> +               else {
> +                       tty_ldisc_flush(tty);
> +               }
> +               /* fall thru */
> +
> +       case DIGI_SETA:
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               return(dgnc_tty_digiseta(tty, uarg));
> +
> +       case DIGI_LOOPBACK:
> +               {
> +                       uint loopback = 0;
> +                       /* Let go of locks when accessing user space, could sleep */
> +                       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +                       rc = get_user(loopback, (unsigned int __user *) arg);
> +                       if (rc)
> +                               return(rc);
> +                       DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +                       /* Enable/disable internal loopback for this port */
> +                       if (loopback)
> +                               ch->ch_flags |= CH_LOOPBACK;
> +                       else
> +                               ch->ch_flags &= ~(CH_LOOPBACK);
> +
> +                       ch->ch_bd->bd_ops->param(tty);
> +                       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +                       return(0);
> +                }
> +
> +       case DIGI_GETCUSTOMBAUD:
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               rc = put_user(ch->ch_custom_speed, (unsigned int __user *) arg);
> +               return(rc);
> +
> +       case DIGI_SETCUSTOMBAUD:
> +       {
> +               uint new_rate;
> +               /* Let go of locks when accessing user space, could sleep */
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               rc = get_user(new_rate, (unsigned int __user *) arg);
> +               if (rc)
> +                       return(rc);
> +               DGNC_LOCK(ch->ch_lock, lock_flags);
> +               dgnc_set_custom_speed(ch, new_rate);
> +               ch->ch_bd->bd_ops->param(tty);
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               return(0);
> +        }
> +
> +       /*
> +        * This ioctl allows insertion of a character into the front
> +        * of any pending data to be transmitted.
> +        *
> +        * This ioctl is to satify the "Send Character Immediate"
> +        * call that the RealPort protocol spec requires.
> +        */
> +       case DIGI_REALPORT_SENDIMMEDIATE:
> +       {
> +               unsigned char c;
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               rc = get_user(c, (unsigned char __user *) arg);
> +               if (rc)
> +                       return(rc);
> +               DGNC_LOCK(ch->ch_lock, lock_flags);
> +               ch->ch_bd->bd_ops->send_immediate_char(ch, c);
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               return(0);
> +       }
> +
> +       /*
> +        * This ioctl returns all the current counts for the port.
> +        *
> +        * This ioctl is to satify the "Line Error Counters"
> +        * call that the RealPort protocol spec requires.
> +        */
> +       case DIGI_REALPORT_GETCOUNTERS:
> +       {
> +               struct digi_getcounter buf;
> +
> +               buf.norun = ch->ch_err_overrun;
> +               buf.noflow = 0;         /* The driver doesn't keep this stat */
> +               buf.nframe = ch->ch_err_frame;
> +               buf.nparity = ch->ch_err_parity;
> +               buf.nbreak = ch->ch_err_break;
> +               buf.rbytes = ch->ch_rxcount;
> +               buf.tbytes = ch->ch_txcount;
> +
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +               if (copy_to_user(uarg, &buf, sizeof(struct digi_getcounter))) {
> +                       return (-EFAULT);
> +               }
> +               return(0);
> +       }
> +
> +       /*
> +        * This ioctl returns all current events.
> +        *
> +        * This ioctl is to satify the "Event Reporting"
> +        * call that the RealPort protocol spec requires.
> +         */
> +       case DIGI_REALPORT_GETEVENTS:
> +       {
> +               unsigned int events = 0;
> +
> +               /* NOTE: MORE EVENTS NEEDS TO BE ADDED HERE */
> +               if (ch->ch_flags & CH_BREAK_SENDING)
> +                       events |= EV_TXB;
> +               if ((ch->ch_flags & CH_STOP) || (ch->ch_flags & CH_FORCED_STOP)) {
> +                       events |= (EV_OPU | EV_OPS);
> +               }
> +               if ((ch->ch_flags & CH_STOPI) || (ch->ch_flags & CH_FORCED_STOPI)) {
> +                       events |= (EV_IPU | EV_IPS);
> +               }
> +
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +               rc = put_user(events, (unsigned int __user *) arg);
> +               return(rc);
> +       }
> +
> +       /*
> +        * This ioctl returns TOUT and TIN counters based
> +        * upon the values passed in by the RealPort Server.
> +        * It also passes back whether the UART Transmitter is
> +        * empty as well.
> +         */
> +       case DIGI_REALPORT_GETBUFFERS:
> +       {
> +               struct digi_getbuffer buf;
> +               int tdist;
> +               int count;
> +
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +               /*
> +                * Get data from user first.
> +                */
> +               if (copy_from_user(&buf, uarg, sizeof(struct digi_getbuffer))) {
> +                       return (-EFAULT);
> +               }
> +
> +               DGNC_LOCK(ch->ch_lock, lock_flags);
> +
> +               /*
> +                * Figure out how much data is in our RX and TX queues.
> +                */
> +               buf.rxbuf = (ch->ch_r_head - ch->ch_r_tail) & RQUEUEMASK;
> +               buf.txbuf = (ch->ch_w_head - ch->ch_w_tail) & WQUEUEMASK;
> +
> +               /*
> +                * Is the UART empty? Add that value to whats in our TX queue.
> +                */
> +               count = buf.txbuf + ch->ch_bd->bd_ops->get_uart_bytes_left(ch);
> +
> +               /*
> +                * Figure out how much data the RealPort Server believes should
> +                * be in our TX queue.
> +                */
> +               tdist = (buf.tIn - buf.tOut) & 0xffff;
> +
> +               /*
> +                * If we have more data than the RealPort Server believes we
> +                * should have, reduce our count to its amount.
> +                *
> +                * This count difference CAN happen because the Linux LD can
> +                * insert more characters into our queue for OPOST processing
> +                * that the RealPort Server doesn't know about.
> +                */
> +               if (buf.txbuf > tdist) {
> +                       buf.txbuf = tdist;
> +               }
> +
> +               /*
> +                * Report whether our queue and UART TX are completely empty.
> +                */
> +               if (count) {
> +                       buf.txdone = 0;
> +               } else {
> +                       buf.txdone = 1;
> +               }
> +
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +               if (copy_to_user(uarg, &buf, sizeof(struct digi_getbuffer))) {
> +                       return (-EFAULT);
> +               }
> +               return(0);
> +       }
> +       default:
> +               DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +               DPR_IOCTL(("dgnc_tty_ioctl - in default\n"));
> +               DPR_IOCTL(("dgnc_tty_ioctl end - cmd %s (%x), arg %lx\n",
> +                       dgnc_ioctl_name(cmd), cmd, arg));
> +
> +               return(-ENOIOCTLCMD);
> +       }
> +
> +       DGNC_UNLOCK(ch->ch_lock, lock_flags);
> +
> +       DPR_IOCTL(("dgnc_tty_ioctl end - cmd %s (%x), arg %lx\n",
> +               dgnc_ioctl_name(cmd), cmd, arg));
> +
> +       return(0);
> +}
> diff --git a/drivers/staging/dgnc/dgnc_tty.h b/drivers/staging/dgnc/dgnc_tty.h
> new file mode 100644
> index 0000000..deb388d
> --- /dev/null
> +++ b/drivers/staging/dgnc/dgnc_tty.h
> @@ -0,0 +1,42 @@
> +/*
> + * 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 __DGNC_TTY_H
> +#define __DGNC_TTY_H
> +
> +#include "dgnc_driver.h"
> +
> +int    dgnc_tty_register(struct board_t *brd);
> +
> +int    dgnc_tty_preinit(void);
> +int     dgnc_tty_init(struct board_t *);
> +
> +void   dgnc_tty_post_uninit(void);
> +void   dgnc_tty_uninit(struct board_t *);
> +
> +void   dgnc_input(struct channel_t *ch);
> +void   dgnc_carrier(struct channel_t *ch);
> +void   dgnc_wakeup_writes(struct channel_t *ch);
> +void   dgnc_check_queue_flow_control(struct channel_t *ch);
> +
> +void   dgnc_sniff_nowait_nolock(struct channel_t *ch, uchar *text, uchar *buf, int nbuf);
> +
> +#endif
> diff --git a/drivers/staging/dgnc/dgnc_types.h b/drivers/staging/dgnc/dgnc_types.h
> new file mode 100644
> index 0000000..4fa3585
> --- /dev/null
> +++ b/drivers/staging/dgnc/dgnc_types.h
> @@ -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!!!
> + */
> +
> +#ifndef __DGNC_TYPES_H
> +#define __DGNC_TYPES_H
> +
> +#ifndef TRUE
> +# define TRUE 1
> +#endif
> +
> +#ifndef FALSE
> +# define FALSE 0
> +#endif
> +
> +/* Required for our shared headers! */
> +typedef unsigned char uchar;
> +
> +#endif
> diff --git a/drivers/staging/dgnc/digi.h b/drivers/staging/dgnc/digi.h
> new file mode 100644
> index 0000000..ab90382
> --- /dev/null
> +++ b/drivers/staging/dgnc/digi.h
> @@ -0,0 +1,419 @@
> +/*
> + * 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.1.1.1 2009/05/20 12:19:19 markh 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_DTR_TOGGLE        0x2000          /* Support DTR Toggle           */
> +#define DIGI_422       0x4000          /* for 422/232 selectable panel */
> +#define DIGI_RTS_TOGGLE        0x8000          /* Support RTS Toggle           */
> +
> +/************************************************************************
> + * 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 int    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 int    shrink_buf_vaddr;       /* Virtual address of board */
> +       unsigned int    shrink_buf_phys;        /* Physical address of board */
> +       unsigned int    shrink_buf_bseg;        /* Amount of board memory */
> +       unsigned int    shrink_buf_hseg;        /* '186 Begining of Dual-Port */
> +
> +       unsigned int    shrink_buf_lseg;        /* '186 Begining of freed memory */
> +       unsigned int    shrink_buf_mseg;        /* Linear address from start of
> +                                                  dual-port were freed memory
> +                                                  begins, host viewpoint. */
> +
> +       unsigned int    shrink_buf_bdparam;     /* Parameter for xxmemon and
> +                                                  xxmemoff */
> +
> +       unsigned int    shrink_buf_reserva;     /* Reserved */
> +       unsigned int    shrink_buf_reservb;     /* Reserved */
> +       unsigned int    shrink_buf_reservc;     /* Reserved */
> +       unsigned int    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 int    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 int    info_bdnum;             /* Board number (0 based)  */
> +       unsigned int    info_ioport;            /* io port address         */
> +       unsigned int    info_physaddr;          /* memory address          */
> +       unsigned int    info_physsize;          /* Size of host mem window */
> +       unsigned int    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 int    info_cflag;             /* cflag for channel       */
> +       unsigned int    info_iflag;             /* iflag for channel       */
> +       unsigned int    info_oflag;             /* oflag for channel       */
> +       unsigned int    info_mstat;             /* mstat for channel       */
> +       unsigned int    info_tx_data;           /* tx_data for channel       */
> +       unsigned int    info_rx_data;           /* rx_data for channel       */
> +       unsigned int    info_hflow;             /* hflow for channel       */
> +       unsigned int    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 int    info_bdnum;             /* Board number (0 based)  */
> +       unsigned int    info_channel;           /* Channel index number    */
> +       unsigned int    info_ch_cflag;          /* Channel cflag           */
> +       unsigned int    info_ch_iflag;          /* Channel iflag           */
> +       unsigned int    info_ch_oflag;          /* Channel oflag           */
> +       unsigned int    info_chsize;            /* Channel structure size  */
> +       unsigned int    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   */
> +       int             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) */
> +};
> +
> +
> +struct digi_getbuffer /* Struct for holding buffer use counts */
> +{
> +       unsigned long tIn;
> +       unsigned long tOut;
> +       unsigned long rxbuf;
> +       unsigned long txbuf;
> +       unsigned long txdone;
> +};
> +
> +struct digi_getcounter
> +{
> +       unsigned long norun;            /* number of UART overrun errors */
> +       unsigned long noflow;           /* number of buffer overflow errors */
> +       unsigned long nframe;           /* number of framing errors */
> +       unsigned long nparity;          /* number of parity errors */
> +       unsigned long nbreak;           /* number of breaks received */
> +       unsigned long rbytes;           /* number of received bytes */
> +       unsigned long tbytes;           /* number of bytes transmitted fully */
> +};
> +
> +/*
> +*  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   */
> +
> +#define DIGI_SETCUSTOMBAUD     _IOW('e', 106, int)     /* Set integer baud rate */
> +#define DIGI_GETCUSTOMBAUD     _IOR('e', 107, int)     /* Get integer baud rate */
> +
> +#define DIGI_REALPORT_GETBUFFERS ('e'<<8 ) | 108
> +#define DIGI_REALPORT_SENDIMMEDIATE ('e'<<8 ) | 109
> +#define DIGI_REALPORT_GETCOUNTERS ('e'<<8 ) | 110
> +#define DIGI_REALPORT_GETEVENTS ('e'<<8 ) | 111
> +
> +#define EV_OPU         0x0001          //!<Output paused by client
> +#define EV_OPS         0x0002          //!<Output paused by reqular sw flowctrl
> +#define EV_OPX         0x0004          //!<Output paused by extra sw flowctrl
> +#define EV_OPH         0x0008          //!<Output paused by hw flowctrl
> +#define EV_OPT         0x0800          //!<Output paused for RTS Toggle predelay
> +
> +#define EV_IPU         0x0010          //!<Input paused unconditionally by user
> +#define EV_IPS         0x0020          //!<Input paused by high/low water marks
> +//#define EV_IPH       0x0040          //!<Input paused w/ hardware
> +#define EV_IPA         0x0400          //!<Input paused by pattern alarm module
> +
> +#define EV_TXB         0x0040          //!<Transmit break pending
> +#define EV_TXI         0x0080          //!<Transmit immediate pending
> +#define EV_TXF         0x0100          //!<Transmit flowctrl char pending
> +#define EV_RXB         0x0200          //!<Break received
> +
> +#define EV_OPALL       0x080f          //!<Output pause flags
> +#define EV_IPALL       0x0430          //!<Input pause flags
> +
> +#endif /* DIGI_H */
> diff --git a/drivers/staging/dgnc/dpacompat.h b/drivers/staging/dgnc/dpacompat.h
> new file mode 100644
> index 0000000..44379eb
> --- /dev/null
> +++ b/drivers/staging/dgnc/dpacompat.h
> @@ -0,0 +1,115 @@
> +/*
> + * 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!!!
> + */
> +
> +
> +/*
> + * This structure holds data needed for the intelligent <--> nonintelligent
> + * DPA translation
> + */
> + struct ni_info {
> +       int board;
> +       int channel;
> +       int dtr;
> +       int rts;
> +       int cts;
> +       int dsr;
> +       int ri;
> +       int dcd;
> +       int curtx;
> +       int currx;
> +       unsigned short iflag;
> +       unsigned short oflag;
> +       unsigned short cflag;
> +       unsigned short lflag;
> +
> +       unsigned int mstat;
> +       unsigned char hflow;
> +
> +       unsigned char xmit_stopped;
> +       unsigned char recv_stopped;
> +
> +       unsigned int baud;
> +};
> +
> +#define RW_READ                1
> +#define RW_WRITE        2
> +#define DIGI_KME        ('e'<<8) | 98           /* Read/Write Host */
> +
> +#define SUBTYPE         0007
> +#define T_PCXI          0000
> +#define T_PCXEM         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 T_NEO   0000
> +#define T_NEO_EXPRESS  0001
> +#define T_CLASSIC 0002
> +
> +#define FAMILY          0070
> +#define T_COMXI         0000
> +#define        T_NI            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_AIXON      0x0400          /* Aux flow control in fep */
> +
> +/* Ioctls needed for dpa operation */
> +
> +#define DIGI_GETDD      ('d'<<8) | 248          /* get driver info      */
> +#define DIGI_GETBD      ('d'<<8) | 249          /* get board info       */
> +#define DIGI_GET_NI_INFO ('d'<<8) | 250                /* nonintelligent state snfo */
> +
> +/* Other special ioctls */
> +#define DIGI_TIMERIRQ ('d'<<8) | 251           /* Enable/disable RS_TIMER use */
> +#define DIGI_LOOPBACK ('d'<<8) | 252           /* Enable/disable UART internal loopback */
> --
> 1.8.1.2
>
_______________________________________________
devel mailing list
devel@xxxxxxxxxxxxxxxxxxxxxx
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel




[Index of Archives]     [Linux Driver Backports]     [DMA Engine]     [Linux GPIO]     [Linux SPI]     [Video for Linux]     [Linux USB Devel]     [Linux Coverity]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux