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