mxser_new.c
来自「linux 内核源代码」· C语言 代码 · 共 2,540 行 · 第 1/5 页
C
2,540 行
/* * mxser.c -- MOXA Smartio/Industio family multiport serial driver. * * Copyright (C) 1999-2006 Moxa Technologies (support@moxa.com.tw). * Copyright (C) 2006-2007 Jiri Slaby <jirislaby@gmail.com> * * This code is loosely based on the 1.8 moxa driver which is based on * 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. * * 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/delay.h>#include <linux/pci.h>#include <linux/bitops.h>#include <asm/system.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/uaccess.h>#include "mxser_new.h"#define MXSER_VERSION "2.0.2" /* 1.10 */#define MXSERMAJOR 174#define MXSERCUMAJOR 175#define MXSER_BOARDS 4 /* Max. boards */#define MXSER_PORTS_PER_BOARD 8 /* Max. ports per board */#define MXSER_PORTS (MXSER_BOARDS * MXSER_PORTS_PER_BOARD)#define MXSER_ISR_PASS_LIMIT 100#define MXSER_ERR_IOADDR -1#define MXSER_ERR_IRQ -2#define MXSER_ERR_IRQ_CONFLIT -3#define MXSER_ERR_VECTOR -4/*CheckIsMoxaMust return value*/#define MOXA_OTHER_UART 0x00#define MOXA_MUST_MU150_HWID 0x01#define MOXA_MUST_MU860_HWID 0x02#define WAKEUP_CHARS 256#define UART_MCR_AFE 0x20#define UART_LSR_SPECIAL 0x1E#define PCI_DEVICE_ID_CB108 0x1080#define PCI_DEVICE_ID_CB114 0x1142#define PCI_DEVICE_ID_CB134I 0x1341#define PCI_DEVICE_ID_CP138U 0x1380#define PCI_DEVICE_ID_POS104UL 0x1044#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#define MXSER_HIGHBAUD 1#define MXSER_HAS2 2/* This is only for PCI */static const struct { 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;} Gpci_uart_info[] = { {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}};#define UART_INFO_NUM ARRAY_SIZE(Gpci_uart_info)struct mxser_cardinfo { unsigned int nports; char *name; unsigned int flags;};static const struct mxser_cardinfo mxser_cards[] = {/* 0*/ { 8, "C168 series", }, { 4, "C104 series", }, { 4, "CI-104J series", }, { 8, "C168H/PCI series", }, { 4, "C104H/PCI series", },/* 5*/ { 4, "C102 series", MXSER_HAS2 }, /* C102-ISA */ { 4, "CI-132 series", MXSER_HAS2 }, { 4, "CI-134 series", }, { 2, "CP-132 series", }, { 4, "CP-114 series", },/*10*/ { 4, "CT-114 series", }, { 2, "CP-102 series", MXSER_HIGHBAUD }, { 4, "CP-104U series", }, { 8, "CP-168U series", }, { 2, "CP-132U series", },/*15*/ { 4, "CP-134U series", }, { 4, "CP-104JU series", }, { 8, "Moxa UC7000 Serial", }, /* RC7000 */ { 8, "CP-118U series", }, { 2, "CP-102UL series", },/*20*/ { 2, "CP-102U series", }, { 8, "CP-118EL series", }, { 8, "CP-168EL series", }, { 4, "CP-104EL series", }, { 8, "CB-108 series", },/*25*/ { 4, "CB-114 series", }, { 4, "CB-134I series", }, { 8, "CP-138U series", }, { 4, "POS-104UL series", }};/* driver_data correspond to the lines in the structure above see also ISA probe function before you change something */static struct pci_device_id mxser_pcibrds[] = { { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_C168), .driver_data = 3 }, { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_C104), .driver_data = 4 }, { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132), .driver_data = 8 }, { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP114), .driver_data = 9 }, { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CT114), .driver_data = 10 }, { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102), .driver_data = 11 }, { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104U), .driver_data = 12 }, { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP168U), .driver_data = 13 }, { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132U), .driver_data = 14 }, { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP134U), .driver_data = 15 }, { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104JU),.driver_data = 16 }, { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_RC7000), .driver_data = 17 }, { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118U), .driver_data = 18 }, { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102UL),.driver_data = 19 }, { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102U), .driver_data = 20 }, { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118EL),.driver_data = 21 }, { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP168EL),.driver_data = 22 }, { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104EL),.driver_data = 23 }, { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CB108), .driver_data = 24 }, { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CB114), .driver_data = 25 }, { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CB134I), .driver_data = 26 }, { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP138U), .driver_data = 27 }, { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_POS104UL), .driver_data = 28 }, { }};MODULE_DEVICE_TABLE(pci, mxser_pcibrds);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};static unsigned int mxvar_baud_table1[] = { 0, B50, B75, B110, B134, B150, B200, B300, B600, B1200, B1800, B2400, B4800, B9600, B19200, B38400, B57600, B115200, B230400, B460800, B921600};#define BAUD_TABLE_NO ARRAY_SIZE(mxvar_baud_table)#define B_SPEC B2000000static int ioaddr[MXSER_BOARDS] = { 0, 0, 0, 0 };static int ttymajor = MXSERMAJOR;static int calloutmajor = MXSERCUMAJOR;/* 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_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_board;struct mxser_port { struct mxser_board *board; struct tty_struct *tty; unsigned long ioaddr; unsigned long opmode_ioaddr; int max_baud; int rx_high_water; int rx_trigger; /* Rx fifo trigger level */ int rx_low_water; int baud_base; /* max. speed */ long realbaud; int type; /* UART type */ int flags; /* defined in tty.h */ int speed; int x_char; /* xon/xoff character */ int IER; /* Interrupt Enable Register */ int MCR; /* Modem control register */ unsigned char stop_rx; unsigned char ldisc_stop_rx; int custom_divisor; int close_delay; unsigned short closing_wait; unsigned char err_shadow; unsigned long event; int count; /* # of fd on device */ int blocked_open; /* # of blocked opens */ struct async_icount icount; /* kernel counters for 4 input interrupts */ int timeout; int read_status_mask; int ignore_status_mask; int xmit_fifo_size; unsigned char *xmit_buf; int xmit_head; int xmit_tail; int xmit_cnt; struct ktermios normal_termios; struct mxser_mon mon_data; spinlock_t slock; wait_queue_head_t open_wait; wait_queue_head_t delta_msr_wait;};struct mxser_board { unsigned int idx; int irq; const struct mxser_cardinfo *info; unsigned long vector; unsigned long vector_mask; int chip_flag; int uart_type; struct mxser_port ports[MXSER_PORTS_PER_BOARD];};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 mxser_board mxser_boards[MXSER_BOARDS];static struct tty_driver *mxvar_sdriver;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];#ifdef CONFIG_PCIstatic int __devinit 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 = 1; i < UART_INFO_NUM; i++) { /* 0 = OTHER_UART */ if (hwid == Gpci_uart_info[i].type) return (int)hwid; } return MOXA_OTHER_UART;}#endifstatic void process_txrx_fifo(struct mxser_port *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->board->chip_flag == 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 unsigned char mxser_get_msr(int baseaddr, int mode, int port){ 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;}static int mxser_block_til_ready(struct tty_struct *tty, struct file *filp, struct mxser_port *port){ DECLARE_WAITQUEUE(wait, current); int retval; int do_clocal = 0; unsigned long flags; /* * If non-blocking mode is set, or the port is not enabled, * then make the check up front and then exit. */ if ((filp->f_flags & O_NONBLOCK) || test_bit(TTY_IO_ERROR, &tty->flags)) { port->flags |= ASYNC_NORMAL_ACTIVE; return 0; } if (tty->termios->c_cflag & CLOCAL) do_clocal = 1; /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in * this loop, port->count is dropped by one, so that * mxser_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ retval = 0; add_wait_queue(&port->open_wait, &wait); spin_lock_irqsave(&port->slock, flags); if (!tty_hung_up_p(filp)) port->count--; spin_unlock_irqrestore(&port->slock, flags); port->blocked_open++; while (1) { spin_lock_irqsave(&port->slock, flags); outb(inb(port->ioaddr + UART_MCR) | UART_MCR_DTR | UART_MCR_RTS, port->ioaddr + UART_MCR); spin_unlock_irqrestore(&port->slock, flags); set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) { if (port->flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; break; } if (!(port->flags & ASYNC_CLOSING) && (do_clocal || (inb(port->ioaddr + UART_MSR) & UART_MSR_DCD))) break; if (signal_pending(current)) { retval = -ERESTARTSYS; break; } schedule(); } set_current_state(TASK_RUNNING); remove_wait_queue(&port->open_wait, &wait); if (!tty_hung_up_p(filp)) port->count++; port->blocked_open--; if (retval) return retval; port->flags |= ASYNC_NORMAL_ACTIVE; return 0;}static int mxser_set_baud(struct mxser_port *info, long newspd){ unsigned int i; int quot = 0; unsigned char cval; int ret = 0; if (!info->tty || !info->tty->termios) return ret; if (!(info->ioaddr)) return ret; if (newspd > info->max_baud) return 0; info->realbaud = newspd; for (i = 0; i < BAUD_TABLE_NO; i++) if (newspd == mxvar_baud_table[i]) break; if (i == BAUD_TABLE_NO) { quot = info->baud_base / info->speed; if (info->speed <= 0 || info->speed > info->max_baud) quot = 0; } else { if (newspd == 134) { quot = (2 * info->baud_base / 269); } else if (newspd) { quot = info->baud_base / newspd; if (quot == 0) quot = 1; } else { quot = 0; } } info->timeout = ((info->xmit_fifo_size * HZ * 10 * quot) / info->baud_base); info->timeout += HZ / 50; /* Add .02 seconds of slop */ if (quot) { info->MCR |= UART_MCR_DTR; outb(info->MCR, info->ioaddr + UART_MCR); } else { info->MCR &= ~UART_MCR_DTR; outb(info->MCR, info->ioaddr + UART_MCR); return ret; } cval = inb(info->ioaddr + UART_LCR); outb(cval | UART_LCR_DLAB, info->ioaddr + UART_LCR); /* set DLAB */ outb(quot & 0xff, info->ioaddr + UART_DLL); /* LS of divisor */ outb(quot >> 8, info->ioaddr + UART_DLM); /* MS of divisor */ outb(cval, info->ioaddr + UART_LCR); /* reset DLAB */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?