📄 mxser.c
字号:
/*****************************************************************************//* * 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 * date : 1999/07/22 * version : 1.1 * */#include <linux/config.h>#include <linux/module.h>#include <linux/version.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 <asm/system.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/segment.h>#include <asm/bitops.h>#include <asm/uaccess.h>#define MXSER_VERSION "1.1kern"#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 SERIAL_TYPE_CALLOUT 2#define WAKEUP_CHARS 256#define UART_MCR_AFE 0x20#define UART_LSR_SPECIAL 0x1E#define PORTNO(x) (MINOR((x)->device) - (x)->driver.minor_start)#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT)#ifndef MIN#define MIN(a,b) ((a) < (b) ? (a) : (b))#endif/* * 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#define C168_ASIC_ID 1#define C104_ASIC_ID 2#define CI104J_ASIC_ID 5enum { MXSER_BOARD_C168_ISA = 0, MXSER_BOARD_C104_ISA, MXSER_BOARD_CI104J, MXSER_BOARD_C168_PCI, MXSER_BOARD_C104_PCI,};static char *mxser_brdname[] ={ "C168 series", "C104 series", "CI-104J series", "C168H/PCI series", "C104H/PCI series",};static int mxser_numports[] ={ 8, 4, 4, 8, 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)typedef struct { unsigned short vendor_id; unsigned short device_id; unsigned short board_type;} mxser_pciinfo;static mxser_pciinfo mxser_pcibrds[] ={ {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_C168, MXSER_BOARD_C168_PCI}, {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_C104, MXSER_BOARD_C104_PCI},};static int ioaddr[MXSER_BOARDS];static int ttymajor = MXSERMAJOR;static int calloutmajor = MXSERCUMAJOR;static int verbose;/* Variables for insmod */MODULE_AUTHOR("William Chen");MODULE_DESCRIPTION("MOXA Smartio Family Multiport Board Device Driver");MODULE_PARM(ioaddr, "1-4i");MODULE_PARM(ttymajor, "i");MODULE_PARM(calloutmajor, "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 */ 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 tq_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 */};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, mxvar_cdriver;static int mxvar_refcount;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;/* * 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: */#ifdef MODULEint init_module(void);void cleanup_module(void);#endifstatic void mxser_getcfg(int board, struct mxser_hwconf *hwconf);int mxser_init(void);static int mxser_get_ISA_conf(int, struct mxser_hwconf *);static int mxser_get_PCI_conf(struct pci_dev *, 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 void mxser_interrupt(int, void *, struct pt_regs *);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 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 *);static int mxser_set_serial_info(struct mxser_struct *, struct serial_struct *);static int mxser_get_lsr_info(struct mxser_struct *, unsigned int *);static void mxser_send_break(struct mxser_struct *, int);static int mxser_get_modem_info(struct mxser_struct *, unsigned int *);static int mxser_set_modem_info(struct mxser_struct *, unsigned int, unsigned int *);/* * The MOXA C168/C104 serial driver boot-time initialization code! */#ifdef MODULEint init_module(void){ int ret; if (verbose) printk("Loading module mxser ...\n"); ret = mxser_init(); if (verbose) printk("Done.\n"); return (ret);}void cleanup_module(void){ int i, err = 0; if (verbose) printk("Unloading module mxser ...\n"); if ((err |= tty_unregister_driver(&mxvar_cdriver))) printk("Couldn't unregister MOXA Smartio family callout driver\n"); if ((err |= tty_unregister_driver(&mxvar_sdriver))) printk("Couldn't unregister MOXA Smartio family serial driver\n"); 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");}#endifint 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; info->tqueue.routine = mxser_do_softint; info->tqueue.data = info; info->callout_termios = mxvar_cdriver.init_termios; 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); } /* * 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 - 1]); 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;}static 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 - 1]; 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);}int mxser_init(void){ int i, m, retval, b; int n, index; int ret1, ret2; struct mxser_hwconf hwconf; printk("MOXA Smartio family driver version %s\n", MXSER_VERSION); /* Initialize the tty_driver structure */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -