diff options
-rw-r--r-- | drivers/char/Kconfig | 15 | ||||
-rw-r--r-- | drivers/char/Makefile | 1 | ||||
-rw-r--r-- | drivers/char/mxser_new.c | 3183 | ||||
-rw-r--r-- | drivers/char/mxser_new.h | 450 |
4 files changed, 3649 insertions, 0 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 24f922f1278..b10f4d8fdc7 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -201,6 +201,21 @@ config MOXA_SMARTIO The module will be called mxser. If you want to do that, say M here. +config MOXA_SMARTIO_NEW + tristate "Moxa SmartIO support v. 2.0 (EXPERIMENTAL)" + depends on SERIAL_NONSTANDARD + help + Say Y here if you have a Moxa SmartIO multiport serial card and/or + want to help develop a new version of this driver. + + This is upgraded (1.9.1) driver from original Moxa drivers with + changes finally resulting in PCI probing. + + Use at your own risk. + + This driver can also be built as a module. The module will be called + mxser_new. If you want to do that, say M here. + config ISI tristate "Multi-Tech multiport card support (EXPERIMENTAL)" depends on SERIAL_NONSTANDARD diff --git a/drivers/char/Makefile b/drivers/char/Makefile index b1fcdab9094..fc110637ced 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_MOXA_INTELLIO) += moxa.o obj-$(CONFIG_A2232) += ser_a2232.o generic_serial.o obj-$(CONFIG_ATARI_DSP56K) += dsp56k.o obj-$(CONFIG_MOXA_SMARTIO) += mxser.o +obj-$(CONFIG_MOXA_SMARTIO_NEW) += mxser_new.o obj-$(CONFIG_COMPUTONE) += ip2/ obj-$(CONFIG_RISCOM8) += riscom8.o obj-$(CONFIG_ISI) += isicom.o diff --git a/drivers/char/mxser_new.c b/drivers/char/mxser_new.c new file mode 100644 index 00000000000..a3aff0bd8e2 --- /dev/null +++ b/drivers/char/mxser_new.c @@ -0,0 +1,3183 @@ +/* + * mxser.c -- MOXA Smartio/Industio family multiport serial driver. + * + * Copyright (C) 1999-2001 Moxa Technologies (support@moxa.com.tw). + * + * This code is loosely based on the Linux serial driver, written by + * Linus Torvalds, Theodore T'so and others. + * + * 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 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; 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. + * + * Original release 10/26/00 + * + * 02/06/01 Support MOXA Industio family boards. + * 02/06/01 Support TIOCGICOUNT. + * 02/06/01 Fix the problem for connecting to serial mouse. + * 02/06/01 Fix the problem for H/W flow control. + * 02/06/01 Fix the compling warning when CONFIG_PCI + * don't be defined. + * + * Fed through a cleanup, indent and remove of non 2.6 code by Alan Cox + * <alan@redhat.com>. The original 1.8 code is available on www.moxa.com. + * - Fixed x86_64 cleanness + * - Fixed sleep with spinlock held in mxser_send_break + */ + + +#include <linux/module.h> +#include <linux/autoconf.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial.h> +#include <linux/serial_reg.h> +#include <linux/major.h> +#include <linux/string.h> +#include <linux/fcntl.h> +#include <linux/ptrace.h> +#include <linux/gfp.h> +#include <linux/ioport.h> +#include <linux/mm.h> +#include <linux/smp_lock.h> +#include <linux/delay.h> +#include <linux/pci.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/bitops.h> +#include <asm/uaccess.h> + +#include "mxser.h" + +#define MXSER_VERSION "1.8" +#define MXSERMAJOR 174 +#define MXSERCUMAJOR 175 + +#define MXSER_EVENT_TXLOW 1 +#define MXSER_EVENT_HANGUP 2 + +#define MXSER_BOARDS 4 /* Max. boards */ +#define MXSER_PORTS 32 /* Max. ports */ +#define MXSER_PORTS_PER_BOARD 8 /* Max. ports per board */ +#define MXSER_ISR_PASS_LIMIT 256 + +#define MXSER_ERR_IOADDR -1 +#define MXSER_ERR_IRQ -2 +#define MXSER_ERR_IRQ_CONFLIT -3 +#define MXSER_ERR_VECTOR -4 + +#define SERIAL_TYPE_NORMAL 1 +#define SERIAL_TYPE_CALLOUT 2 + +#define WAKEUP_CHARS 256 + +#define UART_MCR_AFE 0x20 +#define UART_LSR_SPECIAL 0x1E + +#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK|\ + IXON|IXOFF)) + +#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? IRQF_SHARED : IRQF_DISABLED) + +#define C168_ASIC_ID 1 +#define C104_ASIC_ID 2 +#define C102_ASIC_ID 0xB +#define CI132_ASIC_ID 4 +#define CI134_ASIC_ID 3 +#define CI104J_ASIC_ID 5 + +enum { + MXSER_BOARD_C168_ISA = 1, + MXSER_BOARD_C104_ISA, + MXSER_BOARD_CI104J, + MXSER_BOARD_C168_PCI, + MXSER_BOARD_C104_PCI, + MXSER_BOARD_C102_ISA, + MXSER_BOARD_CI132, + MXSER_BOARD_CI134, + MXSER_BOARD_CP132, + MXSER_BOARD_CP114, + MXSER_BOARD_CT114, + MXSER_BOARD_CP102, + MXSER_BOARD_CP104U, + MXSER_BOARD_CP168U, + MXSER_BOARD_CP132U, + MXSER_BOARD_CP134U, + MXSER_BOARD_CP104JU, + MXSER_BOARD_RC7000, + MXSER_BOARD_CP118U, + MXSER_BOARD_CP102UL, + MXSER_BOARD_CP102U, +}; + +static char *mxser_brdname[] = { + "C168 series", + "C104 series", + "CI-104J series", + "C168H/PCI series", + "C104H/PCI series", + "C102 series", + "CI-132 series", + "CI-134 series", + "CP-132 series", + "CP-114 series", + "CT-114 series", + "CP-102 series", + "CP-104U series", + "CP-168U series", + "CP-132U series", + "CP-134U series", + "CP-104JU series", + "Moxa UC7000 Serial", + "CP-118U series", + "CP-102UL series", + "CP-102U series", +}; + +static int mxser_numports[] = { + 8, /* C168-ISA */ + 4, /* C104-ISA */ + 4, /* CI104J */ + 8, /* C168-PCI */ + 4, /* C104-PCI */ + 2, /* C102-ISA */ + 2, /* CI132 */ + 4, /* CI134 */ + 2, /* CP132 */ + 4, /* CP114 */ + 4, /* CT114 */ + 2, /* CP102 */ + 4, /* CP104U */ + 8, /* CP168U */ + 2, /* CP132U */ + 4, /* CP134U */ + 4, /* CP104JU */ + 8, /* RC7000 */ + 8, /* CP118U */ + 2, /* CP102UL */ + 2, /* CP102U */ +}; + +#define UART_TYPE_NUM 2 + +static const unsigned int Gmoxa_uart_id[UART_TYPE_NUM] = { + MOXA_MUST_MU150_HWID, + MOXA_MUST_MU860_HWID +}; + +/* This is only for PCI */ +#define UART_INFO_NUM 3 +struct mxpciuart_info { + int type; + int tx_fifo; + int rx_fifo; + int xmit_fifo_size; + int rx_high_water; + int rx_trigger; + int rx_low_water; + long max_baud; +}; + +static const struct mxpciuart_info Gpci_uart_info[UART_INFO_NUM] = { + {MOXA_OTHER_UART, 16, 16, 16, 14, 14, 1, 921600L}, + {MOXA_MUST_MU150_HWID, 64, 64, 64, 48, 48, 16, 230400L}, + {MOXA_MUST_MU860_HWID, 128, 128, 128, 96, 96, 32, 921600L} +}; + + +#ifdef CONFIG_PCI + +static struct pci_device_id mxser_pcibrds[] = { + {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_C168, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_C168_PCI}, + {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_C104, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_C104_PCI}, + {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP132}, + {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP114, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP114}, + {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CT114, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CT114}, + {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP102}, + {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP104U, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP104U}, + {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP168U, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP168U}, + {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP132U, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP132U}, + {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP134U, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP134U}, + {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP104JU, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP104JU}, + {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_RC7000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_RC7000}, + {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP118U, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP118U}, + {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP102UL, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP102UL}, + {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP102U, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP102U}, + {0} +}; + +MODULE_DEVICE_TABLE(pci, mxser_pcibrds); + + +#endif + +typedef struct _moxa_pci_info { + unsigned short busNum; + unsigned short devNum; + struct pci_dev *pdev; /* add by Victor Yu. 06-23-2003 */ +} moxa_pci_info; + +static int ioaddr[MXSER_BOARDS] = { 0, 0, 0, 0 }; +static int ttymajor = MXSERMAJOR; +static int calloutmajor = MXSERCUMAJOR; +static int verbose = 0; + +/* Variables for insmod */ + +MODULE_AUTHOR("Casper Yang"); +MODULE_DESCRIPTION("MOXA Smartio/Industio Family Multiport Board Device Driver"); +module_param_array(ioaddr, int, NULL, 0); +module_param(ttymajor, int, 0); +module_param(calloutmajor, int, 0); +module_param(verbose, bool, 0); +MODULE_LICENSE("GPL"); + +struct mxser_log { + int tick; + unsigned long rxcnt[MXSER_PORTS]; + unsigned long txcnt[MXSER_PORTS]; +}; + + +struct mxser_mon { + unsigned long rxcnt; + unsigned long txcnt; + unsigned long up_rxcnt; + unsigned long up_txcnt; + int modem_status; + unsigned char hold_reason; +}; + +struct mxser_mon_ext { + unsigned long rx_cnt[32]; + unsigned long tx_cnt[32]; + unsigned long up_rxcnt[32]; + unsigned long up_txcnt[32]; + int modem_status[32]; + + long baudrate[32]; + int databits[32]; + int stopbits[32]; + int parity[32]; + int flowctrl[32]; + int fifo[32]; + int iftype[32]; +}; + +struct mxser_hwconf { + int board_type; + int ports; + int irq; + int vector; + int vector_mask; + int uart_type; + int ioaddr[MXSER_PORTS_PER_BOARD]; + int baud_base[MXSER_PORTS_PER_BOARD]; + moxa_pci_info pciInfo; + int IsMoxaMustChipFlag; /* add by Victor Yu. 08-30-2002 */ + int MaxCanSetBaudRate[MXSER_PORTS_PER_BOARD]; /* add by Victor Yu. 09-04-2002 */ + int opmode_ioaddr[MXSER_PORTS_PER_BOARD]; /* add by Victor Yu. 01-05-2004 */ +}; + +struct mxser_struct { + int port; + int base; /* port base address */ + int irq; /* port using irq no. */ + int vector; /* port irq vector */ + int vectormask; /* port vector mask */ + int rx_high_water; + int rx_trigger; /* Rx fifo trigger level */ + int rx_low_water; + int baud_base; /* max. speed */ + int flags; /* defined in tty.h */ + int type; /* UART type */ + struct tty_struct *tty; + int read_status_mask; + int ignore_status_mask; + int xmit_fifo_size; + int custom_divisor; + int x_char; /* xon/xoff character */ + int close_delay; + unsigned short closing_wait; + int IER; /* Interrupt Enable Register */ + int MCR; /* Modem control register */ + unsigned long event; + int count; /* # of fd on device */ + int blocked_open; /* # of blocked opens */ + long session; /* Session of opening process */ + long pgrp; /* pgrp of opening process */ + unsigned char *xmit_buf; + int xmit_head; + int xmit_tail; + int xmit_cnt; + struct work_struct tqueue; + struct termios normal_termios; + struct termios callout_termios; + wait_queue_head_t open_wait; + wait_queue_head_t close_wait; + wait_queue_head_t delta_msr_wait; + struct async_icount icount; /* kernel counters for the 4 input interrupts */ + int timeout; + int IsMoxaMustChipFlag; /* add by Victor Yu. 08-30-2002 */ + int MaxCanSetBaudRate; /* add by Victor Yu. 09-04-2002 */ + int opmode_ioaddr; /* add by Victor Yu. 01-05-2004 */ + unsigned char stop_rx; + unsigned char ldisc_stop_rx; + long realbaud; + struct mxser_mon mon_data; + unsigned char err_shadow; + spinlock_t slock; +}; + +struct mxser_mstatus { + tcflag_t cflag; + int cts; + int dsr; + int ri; + int dcd; +}; + +static struct mxser_mstatus GMStatus[MXSER_PORTS]; + +static int mxserBoardCAP[MXSER_BOARDS] = { + 0, 0, 0, 0 + /* 0x180, 0x280, 0x200, 0x320 */ +}; + +static struct tty_driver *mxvar_sdriver; +static struct mxser_struct mxvar_table[MXSER_PORTS]; +static struct tty_struct *mxvar_tty[MXSER_PORTS + 1]; +static struct termios *mxvar_termios[MXSER_PORTS + 1]; +static struct termios *mxvar_termios_locked[MXSER_PORTS + 1]; +static struct mxser_log mxvar_log; +static int mxvar_diagflag; +static unsigned char mxser_msr[MXSER_PORTS + 1]; +static struct mxser_mon_ext mon_data_ext; +static int mxser_set_baud_method[MXSER_PORTS + 1]; +static spinlock_t gm_lock; + +/* + * This is used to figure out the divisor speeds and the timeouts + */ + +static struct mxser_hwconf mxsercfg[MXSER_BOARDS]; + +/* + * static functions: + */ + +static void mxser_getcfg(int board, struct mxser_hwconf *hwconf); +static int mxser_init(void); + +/* static void mxser_poll(unsigned long); */ +static int mxser_get_ISA_conf(int, struct mxser_hwconf *); +static int mxser_get_PCI_conf(int, int, int, struct mxser_hwconf *); +static void mxser_do_softint(void *); +static int mxser_open(struct tty_struct *, struct file *); +static void mxser_close(struct tty_struct *, struct file *); +static int mxser_write(struct tty_struct *, const unsigned char *, int); +static int mxser_write_room(struct tty_struct *); +static void mxser_flush_buffer(struct tty_struct *); +static int mxser_chars_in_buffer(struct tty_struct *); +static void mxser_flush_chars(struct tty_struct *); +static void mxser_put_char(struct tty_struct *, unsigned char); +static int mxser_ioctl(struct tty_struct *, struct file *, uint, ulong); +static int mxser_ioctl_special(unsigned int, void __user *); +static void mxser_throttle(struct tty_struct *); +static void mxser_unthrottle(struct tty_struct *); +static void mxser_set_termios(struct tty_struct *, struct termios *); +static void mxser_stop(struct tty_struct *); +static void mxser_start(struct tty_struct *); +static void mxser_hangup(struct tty_struct *); +static void mxser_rs_break(struct tty_struct *, int); +static irqreturn_t mxser_interrupt(int, void *, struct pt_regs *); +static void mxser_receive_chars(struct mxser_struct *, int *); +static void mxser_transmit_chars(struct mxser_struct *); +static void mxser_check_modem_status(struct mxser_struct *, int); +static int mxser_block_til_ready(struct tty_struct *, struct file *, struct mxser_struct *); +static int mxser_startup(struct mxser_struct *); +static void mxser_shutdown(struct mxser_struct *); +static int mxser_change_speed(struct mxser_struct *, struct termios *old_termios); +static int mxser_get_serial_info(struct mxser_struct *, struct serial_struct __user *); +static int mxser_set_serial_info(struct mxser_struct *, struct serial_struct __user *); +static int mxser_get_lsr_info(struct mxser_struct *, unsigned int __user *); +static void mxser_send_break(struct mxser_struct *, int); +static int mxser_tiocmget(struct tty_struct *, struct file *); +static int mxser_tiocmset(struct tty_struct *, struct file *, unsigned int, unsigned int); +static int mxser_set_baud(struct mxser_struct *info, long newspd); +static void mxser_wait_until_sent(struct tty_struct *tty, int timeout); + +static void mxser_startrx(struct tty_struct *tty); +static void mxser_stoprx(struct tty_struct *tty); + + +static int CheckIsMoxaMust(int io) +{ + u8 oldmcr, hwid; + int i; + + outb(0, io + UART_LCR); + DISABLE_MOXA_MUST_ENCHANCE_MODE(io); + oldmcr = inb(io + UART_MCR); + outb(0, io + UART_MCR); + SET_MOXA_MUST_XON1_VALUE(io, 0x11); + if ((hwid = inb(io + UART_MCR)) != 0) { + outb(oldmcr, io + UART_MCR); + return MOXA_OTHER_UART; + } + + GET_MOXA_MUST_HARDWARE_ID(io, &hwid); + for (i = 0; i < UART_TYPE_NUM; i++) { + if (hwid == Gmoxa_uart_id[i]) + return (int)hwid; + } + return MOXA_OTHER_UART; +} + +/* above is modified by Victor Yu. 08-15-2002 */ + +static const struct tty_operations mxser_ops = { + .open = mxser_open, + .close = mxser_close, + .write = mxser_write, + .put_char = mxser_put_char, + .flush_chars = mxser_flush_chars, + .write_room = mxser_write_room, + .chars_in_buffer = mxser_chars_in_buffer, + .flush_buffer = mxser_flush_buffer, + .ioctl = mxser_ioctl, + .throttle = mxser_throttle, + .unthrottle = mxser_unthrottle, + .set_termios = mxser_set_termios, + .stop = mxser_stop, + .start = mxser_start, + .hangup = mxser_hangup, + .break_ctl = mxser_rs_break, + .wait_until_sent = mxser_wait_until_sent, + .tiocmget = mxser_tiocmget, + .tiocmset = mxser_tiocmset, +}; + +/* + * The MOXA Smartio/Industio serial driver boot-time initialization code! + */ + +static int __init mxser_module_init(void) +{ + int ret; + + if (verbose) + printk(KERN_DEBUG "Loading module mxser ...\n"); + ret = mxser_init(); + if (verbose) + printk(KERN_DEBUG "Done.\n"); + return ret; +} + +static void __exit mxser_module_exit(void) +{ + int i, err; + + if (verbose) + printk(KERN_DEBUG "Unloading module mxser ...\n"); + + err = tty_unregister_driver(mxvar_sdriver); + if (!err) + put_tty_driver(mxvar_sdriver); + else + printk(KERN_ERR "Couldn't unregister MOXA Smartio/Industio family serial driver\n"); + + for (i = 0; i < MXSER_BOARDS; i++) { + struct pci_dev *pdev; + + if (mxsercfg[i].board_type == -1) + continue; + else { + pdev = mxsercfg[i].pciInfo.pdev; + free_irq(mxsercfg[i].irq, &mxvar_table[i * MXSER_PORTS_PER_BOARD]); + if (pdev != NULL) { /* PCI */ + release_region(pci_resource_start(pdev, 2), pci_resource_len(pdev, 2)); + release_region(pci_resource_start(pdev, 3), pci_resource_len(pdev, 3)); + pci_dev_put(pdev); + } else { + release_region(mxsercfg[i].ioaddr[0], 8 * mxsercfg[i].ports); + release_region(mxsercfg[i].vector, 1); + } + } + } + if (verbose) + printk(KERN_DEBUG "Done.\n"); +} + +static void process_txrx_fifo(struct mxser_struct *info) +{ + int i; + + if ((info->type == PORT_16450) || (info->type == PORT_8250)) { + info->rx_trigger = 1; + info->rx_high_water = 1; + info->rx_low_water = 1; + info->xmit_fifo_size = 1; + } else { + for (i = 0; i < UART_INFO_NUM; i++) { + if (info->IsMoxaMustChipFlag == Gpci_uart_info[i].type) { + info->rx_trigger = Gpci_uart_info[i].rx_trigger; + info->rx_low_water = Gpci_uart_info[i].rx_low_water; + info->rx_high_water = Gpci_uart_info[i].rx_high_water; + info->xmit_fifo_size = Gpci_uart_info[i].xmit_fifo_size; + break; + } + } + } +} + +static int mxser_initbrd(int board, struct mxser_hwconf *hwconf) +{ + struct mxser_struct *info; + int retval; + int i, n; + + n = board * MXSER_PORTS_PER_BOARD; + info = &mxvar_table[n]; + /*if (verbose) */ { + printk(KERN_DEBUG " ttyM%d - ttyM%d ", + n, n + hwconf->ports - 1); + printk(" max. baud rate = %d bps.\n", + hwconf->MaxCanSetBaudRate[0]); + } + + for (i = 0; i < hwconf->ports; i++, n++, info++) { + info->port = n; + info->base = hwconf->ioaddr[i]; + info->irq = hwconf->irq; + info->vector = hwconf->vector; + info->vectormask = hwconf->vector_mask; + info->opmode_ioaddr = hwconf->opmode_ioaddr[i]; /* add by Victor Yu. 01-05-2004 */ + info->stop_rx = 0; + info->ldisc_stop_rx = 0; + + info->IsMoxaMustChipFlag = hwconf->IsMoxaMustChipFlag; + /* Enhance mode enabled here */ + if (info->IsMoxaMustChipFlag != MOXA_OTHER_UART) { + ENABLE_MOXA_MUST_ENCHANCE_MODE(info->base); + } + + info->flags = ASYNC_SHARE_IRQ; + info->type = hwconf->uart_type; + info->baud_base = hwconf->baud_base[i]; + + info->MaxCanSetBaudRate = hwconf->MaxCanSetBaudRate[i]; + + process_txrx_fifo(info); + + + info->custom_divisor = hwconf->baud_base[i] * 16; + info->close_delay = 5 * HZ / 10; + info->closing_wait = 30 * HZ; + INIT_WORK(&info->tqueue, mxser_do_softint, info); + info->normal_termios = mxvar_sdriver->init_termios; + init_waitqueue_head(&info->open_wait); + init_waitqueue_head(&info->close_wait); + init_waitqueue_head(&info->delta_msr_wait); + memset(&info->mon_data, 0, sizeof(struct mxser_mon)); + info->err_shadow = 0; + spin_lock_init(&info->slock); + } + /* + * Allocate the IRQ if necessary + */ + + + /* before set INT ISR, disable all int */ + for (i = 0; i < hwconf->ports; i++) { + outb(inb(hwconf->ioaddr[i] + UART_IER) & 0xf0, + hwconf->ioaddr[i] + UART_IER); + } + + n = board * MXSER_PORTS_PER_BOARD; + info = &mxvar_table[n]; + + retval = request_irq(hwconf->irq, mxser_interrupt, IRQ_T(info), + "mxser", info); + if (retval) { + printk(KERN_ERR "Board %d: %s", + board, mxser_brdname[hwconf->board_type - 1]); + printk(" Request irq failed, IRQ (%d) may conflict with" + " another device.\n", info->irq); + return retval; + } + return 0; +} + +static void mxser_getcfg(int board, struct mxser_hwconf *hwconf) +{ + mxsercfg[board] = *hwconf; +} + +#ifdef CONFIG_PCI +static int mxser_get_PCI_conf(int busnum, int devnum, int board_type, struct mxser_hwconf *hwconf) +{ + int i, j; + /* unsigned int val; */ + unsigned int ioaddress; + struct pci_dev *pdev = hwconf->pciInfo.pdev; + + /* io address */ + hwconf->board_type = board_type; + hwconf->ports = mxser_numports[board_type - 1]; + ioaddress = pci_resource_start(pdev, 2); + request_region(pci_resource_start(pdev, 2), pci_resource_len(pdev, 2), + "mxser(IO)"); + + for (i = 0; i < hwconf->ports; i++) + hwconf->ioaddr[i] = ioaddress + 8 * i; + + /* vector */ + ioaddress = pci_resource_start(pdev, 3); + request_region(pci_resource_start(pdev, 3), pci_resource_len(pdev, 3), + "mxser(vector)"); + hwconf->vector = ioaddress; + + /* irq */ + hwconf->irq = hwconf->pciInfo.pdev->irq; + + hwconf->IsMoxaMustChipFlag = CheckIsMoxaMust(hwconf->ioaddr[0]); + hwconf->uart_type = PORT_16550A; + hwconf->vector_mask = 0; + + + for (i = 0; i < hwconf->ports; i++) { + for (j = 0; j < UART_INFO_NUM; j++) { + if (Gpci_uart_info[j].type == hwconf->IsMoxaMustChipFlag) { + hwconf->MaxCanSetBaudRate[i] = Gpci_uart_info[j].max_baud; + + /* exception....CP-102 */ + if (board_type == MXSER_BOARD_CP102) + hwconf->MaxCanSetBaudRate[i] = 921600; + break; + } + } + } + + if (hwconf->IsMoxaMustChipFlag == MOXA_MUST_MU860_HWID) { + for (i = 0; i < hwconf->ports; i++) { + if (i < 4) + hwconf->opmode_ioaddr[i] = ioaddress + 4; + else + hwconf->opmode_ioaddr[i] = ioaddress + 0x0c; + } + outb(0, ioaddress + 4); /* default set to RS232 mode */ + outb(0, ioaddress + 0x0c); /* default set to RS232 mode */ + } + + for (i = 0; i < hwconf->ports; i++) { + hwconf->vector_mask |= (1 << i); + hwconf->baud_base[i] = 921600; + } + return 0; +} +#endif + +static int mxser_init(void) +{ + int i, m, retval, b, n; + struct pci_dev *pdev = NULL; + int index; + unsigned char busnum, devnum; + struct mxser_hwconf hwconf; + + mxvar_sdriver = alloc_tty_driver(MXSER_PORTS + 1); + if (!mxvar_sdriver) + return -ENOMEM; + spin_lock_init(&gm_lock); + + for (i = 0; i < MXSER_BOARDS; i++) { + mxsercfg[i].board_type = -1; + } + + printk(KERN_INFO "MOXA Smartio/Industio family driver version %s\n", + MXSER_VERSION); + + /* Initialize the tty_driver structure */ + memset(mxvar_sdriver, 0, sizeof(struct tty_driver)); + mxvar_sdriver->magic = TTY_DRIVER_MAGIC; + mxvar_sdriver->name = "ttyM"; + mxvar_sdriver->major = ttymajor; + mxvar_sdriver->minor_start = 0; + mxvar_sdriver->num = MXSER_PORTS + 1; + mxvar_sdriver->type = TTY_DRIVER_TYPE_SERIAL; + mxvar_sdriver->subtype = SERIAL_TYPE_NORMAL; + mxvar_sdriver->init_termios = tty_std_termios; + mxvar_sdriver->init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL; + mxvar_sdriver->flags = TTY_DRIVER_REAL_RAW; + tty_set_operations(mxvar_sdriver, &mxser_ops); + mxvar_sdriver->ttys = mxvar_tty; + mxvar_sdriver->termios = mxvar_termios; + mxvar_sdriver->termios_locked = mxvar_termios_locked; + + mxvar_diagflag = 0; + memset(mxvar_table, 0, MXSER_PORTS * sizeof(struct mxser_struct)); + memset(&mxvar_log, 0, sizeof(struct mxser_log)); + + memset(&mxser_msr, 0, sizeof(unsigned char) * (MXSER_PORTS + 1)); + memset(&mon_data_ext, 0, sizeof(struct mxser_mon_ext)); + memset(&mxser_set_baud_method, 0, sizeof(int) * (MXSER_PORTS + 1)); + memset(&hwconf, 0, sizeof(struct mxser_hwconf)); + + m = 0; + /* Start finding ISA boards here */ + for (b = 0; b < MXSER_BOARDS && m < MXSER_BOARDS; b++) { + int cap; + + if (!(cap = mxserBoardCAP[b])) + continue; + + retval = mxser_get_ISA_conf(cap, &hwconf); + + if (retval != 0) + printk(KERN_INFO "Found MOXA %s board (CAP=0x%x)\n", + mxser_brdname[hwconf.board_type - 1], ioaddr[b]); + + if (retval <= 0) { + if (retval == MXSER_ERR_IRQ) + printk(KERN_ERR "Invalid interrupt number, " + "board not configured\n"); + else if (retval == MXSER_ERR_IRQ_CONFLIT) + printk(KERN_ERR "Invalid interrupt number, " + "board not configured\n"); + else if (retval == MXSER_ERR_VECTOR) + printk(KERN_ERR "Invalid interrupt vector, " + "board not configured\n"); + else if (retval == MXSER_ERR_IOADDR) + printk(KERN_ERR "Invalid I/O address, " + "board not configured\n"); + + continue; + } + + hwconf.pciInfo.busNum = 0; + hwconf.pciInfo.devNum = 0; + hwconf.pciInfo.pdev = NULL; + + mxser_getcfg(m, &hwconf); + /* + * init mxsercfg first, + * or mxsercfg data is not correct on ISR. + */ + /* mxser_initbrd will hook ISR. */ + if (mxser_initbrd(m, &hwconf) < 0) + continue; + + m++; + } + + /* Start finding ISA boards from module arg */ + for (b = 0; b < MXSER_BOARDS && m < MXSER_BOARDS; b++) { + int cap; + + if (!(cap = ioaddr[b])) + continue; + + retval = mxser_get_ISA_conf(cap, &hwconf); + + if (retval != 0) + printk(KERN_INFO "Found MOXA %s board (CAP=0x%x)\n", + mxser_brdname[hwconf.board_type - 1], ioaddr[b]); + + if (retval <= 0) { + if (retval == MXSER_ERR_IRQ) + printk(KERN_ERR "Invalid interrupt number, " + "board not configured\n"); + else if (retval == MXSER_ERR_IRQ_CONFLIT) + printk(KERN_ERR "Invalid interrupt number, " + "board not configured\n"); + else if (retval == MXSER_ERR_VECTOR) + printk(KERN_ERR "Invalid interrupt vector, " + "board not configured\n"); + else if (retval == MXSER_ERR_IOADDR) + printk(KERN_ERR "Invalid I/O address, " + "board not configured\n"); + + continue; + } + + hwconf.pciInfo.busNum = 0; + hwconf.pciInfo.devNum = 0; + hwconf.pciInfo.pdev = NULL; + + mxser_getcfg(m, &hwconf); + /* + * init mxsercfg first, + * or mxsercfg data is not correct on ISR. + */ + /* mxser_initbrd will hook ISR. */ + if (mxser_initbrd(m, &hwconf) < 0) + continue; + + m++; + } + + /* start finding PCI board here */ +#ifdef CONFIG_PCI + n = ARRAY_SIZE(mxser_pcibrds) - 1; + index = 0; + b = 0; + while (b < n) { + pdev = pci_get_device(mxser_pcibrds[b].vendor, + mxser_pcibrds[b].device, pdev); + if (pdev == NULL) { + b++; + continue; + } + hwconf.pciInfo.busNum = busnum = pdev->bus->number; + hwconf.pciInfo.devNum = devnum = PCI_SLOT(pdev->devfn) << 3; + hwconf.pciInfo.pdev = pdev; + printk(KERN_INFO "Found MOXA %s board(BusNo=%d,DevNo=%d)\n", + mxser_brdname[(int) (mxser_pcibrds[b].driver_data) - 1], + busnum, devnum >> 3); + index++; + if (m >= MXSER_BOARDS) + printk(KERN_ERR + "Too many Smartio/Industio family boards find " + "(maximum %d), board not configured\n", + MXSER_BOARDS); + else { + if (pci_enable_device(pdev)) { + printk(KERN_ERR "Moxa SmartI/O PCI enable " + "fail !\n"); + continue; + } + retval = mxser_get_PCI_conf(busnum, devnum, + (int)mxser_pcibrds[b].driver_data, + &hwconf); + if (retval < 0) { + if (retval == MXSER_ERR_IRQ) + printk(KERN_ERR + "Invalid interrupt number, " + "board not configured\n"); + else if (retval == MXSER_ERR_IRQ_CONFLIT) + printk(KERN_ERR + "Invalid interrupt number, " + "board not configured\n"); + else if (retval == MXSER_ERR_VECTOR) + printk(KERN_ERR + "Invalid interrupt vector, " + "board not configured\n"); + else if (retval == MXSER_ERR_IOADDR) + printk(KERN_ERR + "Invalid I/O address, " + "board not configured\n"); + continue; + } + mxser_getcfg(m, &hwconf); + /* init mxsercfg first, + * or mxsercfg data is not correct on ISR. + */ + /* mxser_initbrd will hook ISR. */ + if (mxser_initbrd(m, &hwconf) < 0) + continue; + m++; + /* Keep an extra reference if we succeeded. It will + be returned at unload time */ + pci_dev_get(pdev); + } + } +#endif + + retval = tty_register_driver(mxvar_sdriver); + if (retval) { + printk(KERN_ERR "Couldn't install MOXA Smartio/Industio family" + " driver !\n"); + put_tty_driver(mxvar_sdriver); + + for (i = 0; i < MXSER_BOARDS; i++) { + if (mxsercfg[i].board_type == -1) + continue; + else { + free_irq(mxsercfg[i].irq, &mxvar_table[i * MXSER_PORTS_PER_BOARD]); + /* todo: release io, vector */ + } + } + return retval; + } + + return 0; +} + +static void mxser_do_softint(void *private_) +{ + struct mxser_struct *info = private_; + struct tty_struct *tty; + + tty = info->tty; + + if (tty) { + if (test_and_clear_bit(MXSER_EVENT_TXLOW, &info->event)) + tty_wakeup(tty); + if (test_and_clear_bit(MXSER_EVENT_HANGUP, &info->event)) + tty_hangup(tty); + } +} + +static unsigned char mxser_get_msr(int baseaddr, int mode, int port, struct mxser_struct *info) +{ + unsigned char status = 0; + + status = inb(baseaddr + UART_MSR); + + mxser_msr[port] &= 0x0F; + mxser_msr[port] |= status; + status = mxser_msr[port]; + if (mode) + mxser_msr[port] = 0; + + return status; +} + +/* + * This routine is called whenever a serial port is opened. It + * enables interrupts for a serial port, linking in its async structure into + * the IRQ chain. It also performs the serial-specific + * initialization for the tty structure. + */ +static int mxser_open(struct tty_struct *tty, struct file *filp) +{ + struct mxser_struct *info; + int retval, line; + + /* initialize driver_data in case something fails */ + tty->driver_data = NULL; + + line = tty->index; + if (line == MXSER_PORTS) + return 0; + if (line < 0 || line > MXSER_PORTS) + return -ENODEV; + info = mxvar_table + line; + if (!info->base) + return -ENODEV; + + tty->driver_data = info; + info->tty = tty; + /* + * Start up serial port + */ + retval = mxser_startup(info); + if (retval) + return retval; + + retval = mxser_block_til_ready(tty, filp, info); + if (retval) + return retval; + + info->count++; + + if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver->subtype == SERIAL_TYPE_NORMAL) + *tty->termios = info->normal_termios; + else + *tty->termios = info->callout_termios; + mxser_change_speed(info, NULL); + } + + info->session = current->signal->session; + info->pgrp = process_group(current); + + /* + status = mxser_get_msr(info->base, 0, info->port); + mxser_check_modem_status(info, status); + */ + +/* unmark here for very high baud rate (ex. 921600 bps) used */ + tty->low_latency = 1; + return 0; +} + +/* + * This routine is called when the serial port gets closed. First, we + * wait for the last remaining data to be sent. Then, we unlink its + * async structure from the interrupt chain if necessary, and we free + * that IRQ if nothing is left in the chain. + */ +static void mxser_close(struct tty_struct *tty, struct file *filp) +{ + struct mxser_struct *info = tty->driver_data; + + unsigned long timeout; + unsigned long flags; + struct tty_ldisc *ld; + + if (tty->index == MXSER_PORTS) + return; + if (!info) + return; + + spin_lock_irqsave(&info->slock, flags); + + if (tty_hung_up_p(filp)) { + spin_unlock_irqrestore(&info->slock, flags); + return; + } + if ((tty->count == 1) && (info->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. Info->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. + */ + printk(KERN_ERR "mxser_close: bad serial port count; " + "tty->count is 1, info->count is %d\n", info->count); + info->count = 1; + } + if (--info->count < 0) { + printk(KERN_ERR "mxser_close: bad serial port count for " + "ttys%d: %d\n", info->port, info->count); + info->count = 0; + } + if (info->count) { + spin_unlock_irqrestore(&info->slock, flags); + return; + } + info->flags |= ASYNC_CLOSING; + spin_unlock_irqrestore(&info->slock, flags); + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (info->flags & ASYNC_NORMAL_ACTIVE) + info->normal_termios = *tty->termios; + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->closing_wait); + /* + * At this point we stop accepting input. To do this, we + * disable the receive line status interrupts, and tell the + * interrupt driver to stop checking the data ready bit in the + * line status register. + */ + info->IER &= ~UART_IER_RLSI; + if (info->IsMoxaMustChipFlag) + info->IER &= ~MOXA_MUST_RECV_ISR; +/* by William + info->read_status_mask &= ~UART_LSR_DR; +*/ + if (info->flags & ASYNC_INITIALIZED) { + outb(info->IER, info->base + UART_IER); + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + timeout = jiffies + HZ; + while (!(inb(info->base + |