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 + -
显示快捷键?