mxser.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,381 行 · 第 1/4 页
C
2,381 行
/*****************************************************************************//* * mxser.c -- MOXA Smartio family multiport serial driver. * * Copyright (C) 1999-2000 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. *//* * MOXA Smartio Family Serial Driver * * Copyright (C) 1999,2000 Moxa Technologies Co., LTD. * * for : LINUX 2.0.X, 2.2.X, 2.4.X * date : 2001/05/01 * version : 1.2 * * Fixes for C104H/PCI by Tim Hockin <thockin@sun.com> * Added support for: C102, CI-132, CI-134, CP-132, CP-114, CT-114 cards * by Damian Wrobel <dwrobel@ertel.com.pl> * * Added support for serial card CP104 * by James Nelson Provident Solutions <linux-info@provident-solutions.com> */#include <linux/config.h>#include <linux/module.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/ioport.h>#include <linux/mm.h>#include <linux/smp_lock.h>#include <linux/pci.h>#include <linux/init.h>#include <asm/system.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/bitops.h>#include <asm/uaccess.h>#define MXSER_VERSION "1.2.1"#define MXSERMAJOR 174#define MXSERCUMAJOR 175#define MXSER_EVENT_TXLOW 1#define MXSER_EVENT_HANGUP 2#define SERIAL_DO_RESTART#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 WAKEUP_CHARS 256#define UART_MCR_AFE 0x20#define UART_LSR_SPECIAL 0x1E#define PORTNO(x) ((x)->index)#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT)/* * Define the Moxa PCI vendor and device IDs. */#ifndef PCI_VENDOR_ID_MOXA#define PCI_VENDOR_ID_MOXA 0x1393#endif#ifndef PCI_DEVICE_ID_C168#define PCI_DEVICE_ID_C168 0x1680#endif#ifndef PCI_DEVICE_ID_C104#define PCI_DEVICE_ID_C104 0x1040#endif#ifndef PCI_DEVICE_ID_CP104#define PCI_DEVICE_ID_CP104 0x1041#endif#ifndef PCI_DEVICE_ID_CP132#define PCI_DEVICE_ID_CP132 0x1320#endif#ifndef PCI_DEVICE_ID_CP114#define PCI_DEVICE_ID_CP114 0x1141#endif#ifndef PCI_DEVICE_ID_CT114#define PCI_DEVICE_ID_CT114 0x1140#endif#define C168_ASIC_ID 1#define C104_ASIC_ID 2#define CI134_ASIC_ID 3#define CI132_ASIC_ID 4#define CI104J_ASIC_ID 5#define C102_ASIC_ID 0xBenum { MXSER_BOARD_C168_ISA = 0, MXSER_BOARD_C104_ISA, MXSER_BOARD_CI104J, MXSER_BOARD_C168_PCI, MXSER_BOARD_C104_PCI, MXSER_BOARD_CP104_PCI, MXSER_BOARD_C102_ISA, MXSER_BOARD_CI132, MXSER_BOARD_CI134, MXSER_BOARD_CP132_PCI, MXSER_BOARD_CP114_PCI, MXSER_BOARD_CT114_PCI};static char *mxser_brdname[] ={ "C168 series", "C104 series", "CI-104J series", "C168H/PCI series", "C104H/PCI series", "CP104/PCI series", "C102 series", "CI-132 series", "CI-134 series", "CP-132 series", "CP-114 series", "CT-114 series"};static int mxser_numports[] ={ 8, 4, 4, 8, 4, 4, 2, 2, 4, 2, 4, 4};/* * MOXA ioctls */#define MOXA 0x400#define MOXA_GETDATACOUNT (MOXA + 23)#define MOXA_GET_CONF (MOXA + 35)#define MOXA_DIAGNOSE (MOXA + 50)#define MOXA_CHKPORTENABLE (MOXA + 60)#define MOXA_HighSpeedOn (MOXA + 61)#define MOXA_GET_MAJOR (MOXA + 63)#define MOXA_GET_CUMAJOR (MOXA + 64)#define MOXA_GETMSTATUS (MOXA + 65)#ifdef CONFIG_PCIstatic struct pci_device_id mxser_pcibrds[] = { { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_C168, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_C168_PCI }, { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_C104, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_C104_PCI }, { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_CP104, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP104_PCI }, { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_CP132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP132_PCI }, { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_CP114, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP114_PCI }, { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_CT114, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CT114_PCI }, { 0 }};MODULE_DEVICE_TABLE(pci, mxser_pcibrds);#endif /* CONFIG_PCI */static int ioaddr[MXSER_BOARDS];static int ttymajor = MXSERMAJOR;static int verbose;/* Variables for insmod */MODULE_AUTHOR("William Chen");MODULE_DESCRIPTION("MOXA Smartio Family Multiport Board Device Driver");MODULE_LICENSE("GPL");MODULE_PARM(ioaddr, "1-4i");MODULE_PARM(ttymajor, "i");MODULE_PARM(verbose, "i");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]; struct pci_dev *pdev;};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_trigger; /* Rx fifo trigger level */ 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 */ unsigned char *xmit_buf; int xmit_head; int xmit_tail; int xmit_cnt; struct work_struct tqueue; int cflag; 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 */};struct mxser_log { int tick; int rxcnt[MXSER_PORTS]; int txcnt[MXSER_PORTS];};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 mxser_log mxvar_log;static int mxvar_diagflag;/* * mxvar_tmp_buf is used as a temporary buffer by serial_write. We need * to lock it in case the memcpy_fromfs blocks while swapping in a page, * and some other program tries to do a serial write at the same time. * Since the lock will only come under contention when the system is * swapping and available memory is low, it makes sense to share one * buffer across all the serial ports, since it significantly saves * memory if large numbers of serial ports are open. */static unsigned char *mxvar_tmp_buf;static struct semaphore mxvar_tmp_buf_sem;/* * This is used to figure out the divisor speeds and the timeouts */static int mxvar_baud_table[] ={ 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600, 0};struct mxser_hwconf mxsercfg[MXSER_BOARDS];/* * static functions: */static void mxser_getcfg(int board, struct mxser_hwconf *hwconf);static int mxser_get_ISA_conf(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 *, int, 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, unsigned long);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 inline void mxser_receive_chars(struct mxser_struct *, int *);static inline void mxser_transmit_chars(struct mxser_struct *);static inline void mxser_check_modem_status(struct mxser_struct *, int);static irqreturn_t mxser_interrupt(int, void *, struct pt_regs *);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);/* * The MOXA C168/C104 serial driver boot-time initialization code! */static void __exit mxser_module_exit(void){ int i, err = 0; if (verbose) printk("Unloading module mxser ...\n"); if ((err |= tty_unregister_driver(mxvar_sdriver))) printk("Couldn't unregister MOXA Smartio family serial 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]); } } if (verbose) printk("Done.\n");}int mxser_initbrd(int board, struct mxser_hwconf *hwconf){ struct mxser_struct *info; unsigned long flags; int retval; int i, n; init_MUTEX(&mxvar_tmp_buf_sem); n = board * MXSER_PORTS_PER_BOARD; info = &mxvar_table[n]; for (i = 0; i < hwconf->ports; i++, n++, info++) { if (verbose) { printk(" ttyM%d/cum%d at 0x%04x ", n, n, hwconf->ioaddr[i]); if (hwconf->baud_base[i] == 115200) printk(" max. baud rate up to 115200 bps.\n"); else printk(" max. baud rate up to 921600 bps.\n"); } info->port = n; info->base = hwconf->ioaddr[i]; info->irq = hwconf->irq; info->vector = hwconf->vector; info->vectormask = hwconf->vector_mask; info->rx_trigger = 14; info->baud_base = hwconf->baud_base[i]; info->flags = ASYNC_SHARE_IRQ; info->type = hwconf->uart_type; if ((info->type == PORT_16450) || (info->type == PORT_8250)) info->xmit_fifo_size = 1; else info->xmit_fifo_size = 16; 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->cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; init_waitqueue_head(&info->open_wait); init_waitqueue_head(&info->close_wait); init_waitqueue_head(&info->delta_msr_wait); } /* * Allocate the IRQ if necessary */ save_flags(flags); n = board * MXSER_PORTS_PER_BOARD; info = &mxvar_table[n]; cli(); retval = request_irq(hwconf->irq, mxser_interrupt, IRQ_T(info), "mxser", info); if (retval) { restore_flags(flags); printk("Board %d: %s", board, mxser_brdname[hwconf->board_type]); printk(" Request irq fail,IRQ (%d) may be conflit with another device.\n", info->irq); return (retval); } restore_flags(flags); return 0;}static void mxser_getcfg(int board, struct mxser_hwconf *hwconf){ mxsercfg[board] = *hwconf;}#ifdef CONFIG_PCIstatic int mxser_get_PCI_conf(struct pci_dev *pdev, int board_type, struct mxser_hwconf *hwconf){ int i; unsigned int ioaddress; hwconf->board_type = board_type; hwconf->ports = mxser_numports[board_type]; ioaddress = pci_resource_start (pdev, 2); for (i = 0; i < hwconf->ports; i++) hwconf->ioaddr[i] = ioaddress + 8 * i; ioaddress = pci_resource_start (pdev, 3); hwconf->vector = ioaddress; hwconf->irq = pdev->irq; hwconf->uart_type = PORT_16550A; hwconf->vector_mask = 0; for (i = 0; i < hwconf->ports; i++) { hwconf->vector_mask |= (1 << i); hwconf->baud_base[i] = 921600; } return (0);}#endif /* CONFIG_PCI */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,};static int __init mxser_module_init(void){ int i, m, retval, b; struct mxser_hwconf hwconf; mxvar_sdriver = alloc_tty_driver(MXSER_PORTS + 1); if (!mxvar_sdriver) return -ENOMEM; printk("MOXA Smartio family driver version %s\n", MXSER_VERSION); /* Initialize the tty_driver structure */ mxvar_sdriver->owner = THIS_MODULE; mxvar_sdriver->name = "ttyM"; mxvar_sdriver->devfs_name = "tts/M"; mxvar_sdriver->major = ttymajor; mxvar_sdriver->minor_start = 0; 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); printk("Tty devices major number = %d\n", ttymajor); mxvar_diagflag = 0; memset(mxvar_table, 0, MXSER_PORTS * sizeof(struct mxser_struct)); memset(&mxvar_log, 0, sizeof(struct mxser_log)); 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("Found MOXA %s board (CAP=0x%x)\n", mxser_brdname[hwconf.board_type], ioaddr[b]); if (retval <= 0) { if (retval == MXSER_ERR_IRQ) printk("Invalid interrupt number,board not configured\n"); else if (retval == MXSER_ERR_IRQ_CONFLIT) printk("Invalid interrupt number,board not configured\n"); else if (retval == MXSER_ERR_VECTOR) printk("Invalid interrupt vector,board not configured\n"); else if (retval == MXSER_ERR_IOADDR) printk("Invalid I/O address,board not configured\n"); continue; } hwconf.pdev = NULL; if (mxser_initbrd(m, &hwconf) < 0) continue; mxser_getcfg(m, &hwconf); 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("Found MOXA %s board (CAP=0x%x)\n", mxser_brdname[hwconf.board_type], ioaddr[b]); if (retval <= 0) { if (retval == MXSER_ERR_IRQ) printk("Invalid interrupt number,board not configured\n"); else if (retval == MXSER_ERR_IRQ_CONFLIT) printk("Invalid interrupt number,board not configured\n"); else if (retval == MXSER_ERR_VECTOR) printk("Invalid interrupt vector,board not configured\n"); else if (retval == MXSER_ERR_IOADDR) printk("Invalid I/O address,board not configured\n");
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?