diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/char/mxser.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/char/mxser.c')
-rw-r--r-- | drivers/char/mxser.c | 3170 |
1 files changed, 3170 insertions, 0 deletions
diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c new file mode 100644 index 00000000000..7a245068e3e --- /dev/null +++ b/drivers/char/mxser.c @@ -0,0 +1,3170 @@ +/* + * 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/config.h> +#include <linux/module.h> +#include <linux/version.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/segment.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) ? SA_SHIRQ : SA_INTERRUPT) + +#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_PARM(ioaddr, "1-4i"); +MODULE_PARM(ttymajor, "i"); +MODULE_PARM(calloutmajor, "i"); +MODULE_PARM(verbose, "i"); +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 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, + .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 = 0; + + if (verbose) + printk(KERN_DEBUG "Unloading module mxser ...\n"); + + if ((err |= tty_unregister_driver(mxvar_sdriver))) + 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)); + } 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 fail,IRQ (%d) may be conflit 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; + int ret1; + 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_sdriver->open = mxser_open; + mxvar_sdriver->close = mxser_close; + mxvar_sdriver->write = mxser_write; + mxvar_sdriver->put_char = mxser_put_char; + mxvar_sdriver->flush_chars = mxser_flush_chars; + mxvar_sdriver->write_room = mxser_write_room; + mxvar_sdriver->chars_in_buffer = mxser_chars_in_buffer; + mxvar_sdriver->flush_buffer = mxser_flush_buffer; + mxvar_sdriver->ioctl = mxser_ioctl; + mxvar_sdriver->throttle = mxser_throttle; + mxvar_sdriver->unthrottle = mxser_unthrottle; + mxvar_sdriver->set_termios = mxser_set_termios; + mxvar_sdriver->stop = mxser_stop; + mxvar_sdriver->start = mxser_start; + mxvar_sdriver->hangup = mxser_hangup; + mxvar_sdriver->break_ctl = mxser_rs_break; + mxvar_sdriver->wait_until_sent = mxser_wait_until_sent; + + 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 = (sizeof(mxser_pcibrds) / sizeof(mxser_pcibrds[0])) - 1; + index = 0; + b = 0; + while (b < n) { + pdev = pci_find_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++; + } + } +#endif + + ret1 = 0; + if (!(ret1 = tty_register_driver(mxvar_sdriver))) { + return 0; + } else + printk(KERN_ERR "Couldn't install MOXA Smartio/Industio family driver !\n"); + + + if (ret1) { + 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 -1; + } + + return (0); +} + +static void mxser_do_softint(void *private_) +{ + struct mxser_struct *info = (struct mxser_struct *) 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; + + 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); + clear_bit(TTY_DONT_FLIP, &tty->flags); + + //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 = (struct mxser_struct *) tty->driver_data; + + unsigned long timeout; + unsigned long flags; + struct tty_ldisc *ld; + + if (tty->index == MXSER_PORTS) + return; + if (!info) + BUG(); + + 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 + UART_LSR) & UART_LSR_TEMT)) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(5); + if (time_after(jiffies, timeout)) + break; + } + } + mxser_shutdown(info); + + if (tty->driver->flush_buffer) + tty->driver->flush_buffer(tty); + + ld = tty_ldisc_ref(tty); + if (ld) { + if(ld->flush_buffer) + ld->flush_buffer(tty); + tty_ldisc_deref(ld); + } + + tty->closing = 0; + info->event = 0; + info->tty = NULL; + if (info->blocked_open) { + if (info->close_delay) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(info->close_delay); + } + wake_up_interruptible(&info->open_wait); + } + + info->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); + wake_up_interruptible(&info->close_wait); + +} + +static int mxser_write(struct tty_struct *tty, const unsigned char *buf, int count) +{ + int c, total = 0; + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + unsigned long flags; + + if (!tty || !info->xmit_buf) + return (0); + + while (1) { + c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); |