2817 lines · 70.7 KB · C
RawDownload
/* *          mxser.c  -- MOXA Smartio/Industio family multiport serial driver. * *      Copyright (C) 1999-2006  Moxa Technologies (support@moxa.com). *	Copyright (C) 2006-2008  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@lxorguk.ukuu.org.uk>. The original 1.8 code is available on *	www.moxa.com. *	- Fixed x86_64 cleanness */ #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/delay.h>#include <linux/pci.h>#include <linux/bitops.h>#include <linux/slab.h>#include <linux/ratelimit.h> #include <asm/io.h>#include <asm/irq.h>#include <asm/uaccess.h> #include "mxser.h" #define	MXSER_VERSION	"2.0.5"		/* 1.14 */#define	MXSERMAJOR	 174 #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 /*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_POS104UL	0x1044#define PCI_DEVICE_ID_CB108	0x1080#define PCI_DEVICE_ID_CP102UF	0x1023#define PCI_DEVICE_ID_CP112UL	0x1120#define PCI_DEVICE_ID_CB114	0x1142#define PCI_DEVICE_ID_CP114UL	0x1143#define PCI_DEVICE_ID_CB134I	0x1341#define PCI_DEVICE_ID_CP138U	0x1380  #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 {	char *name;	unsigned int nports;	unsigned int flags;}; static const struct mxser_cardinfo mxser_cards[] = {/* 0*/	{ "C168 series",	8, },	{ "C104 series",	4, },	{ "CI-104J series",	4, },	{ "C168H/PCI series",	8, },	{ "C104H/PCI series",	4, },/* 5*/	{ "C102 series",	4, MXSER_HAS2 },	/* C102-ISA */	{ "CI-132 series",	4, MXSER_HAS2 },	{ "CI-134 series",	4, },	{ "CP-132 series",	2, },	{ "CP-114 series",	4, },/*10*/	{ "CT-114 series",	4, },	{ "CP-102 series",	2, MXSER_HIGHBAUD },	{ "CP-104U series",	4, },	{ "CP-168U series",	8, },	{ "CP-132U series",	2, },/*15*/	{ "CP-134U series",	4, },	{ "CP-104JU series",	4, },	{ "Moxa UC7000 Serial",	8, },		/* RC7000 */	{ "CP-118U series",	8, },	{ "CP-102UL series",	2, },/*20*/	{ "CP-102U series",	2, },	{ "CP-118EL series",	8, },	{ "CP-168EL series",	8, },	{ "CP-104EL series",	4, },	{ "CB-108 series",	8, },/*25*/	{ "CB-114 series",	4, },	{ "CB-134I series",	4, },	{ "CP-138U series",	8, },	{ "POS-104UL series",	4, },	{ "CP-114UL series",	4, },/*30*/	{ "CP-102UF series",	2, },	{ "CP-112UL series",	2, },}; /* 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 },	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP114UL),	.driver_data = 29 },	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP102UF),	.driver_data = 30 },	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP112UL),	.driver_data = 31 },	{ }};MODULE_DEVICE_TABLE(pci, mxser_pcibrds); static unsigned long ioaddr[MXSER_BOARDS];static int ttymajor = MXSERMAJOR; /* Variables for insmod */ MODULE_AUTHOR("Casper Yang");MODULE_DESCRIPTION("MOXA Smartio/Industio Family Multiport Board Device Driver");module_param_array(ioaddr, ulong, NULL, 0);MODULE_PARM_DESC(ioaddr, "ISA io addresses to look for a moxa board");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 tty_port port;	struct mxser_board *board; 	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 */	int type;		/* UART type */ 	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;	unsigned char err_shadow; 	struct async_icount icount; /* kernel counters for 4 input interrupts */	int timeout; 	int read_status_mask;	int ignore_status_mask;	int xmit_fifo_size;	int xmit_head;	int xmit_tail;	int xmit_cnt; 	struct ktermios normal_termios; 	struct mxser_mon mon_data; 	spinlock_t slock;}; 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_board mxser_boards[MXSER_BOARDS];static struct tty_driver *mxvar_sdriver;static struct mxser_log mxvar_log;static int mxser_set_baud_method[MXSER_PORTS + 1]; static void mxser_enable_must_enchance_mode(unsigned long baseio){	u8 oldlcr;	u8 efr; 	oldlcr = inb(baseio + UART_LCR);	outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); 	efr = inb(baseio + MOXA_MUST_EFR_REGISTER);	efr |= MOXA_MUST_EFR_EFRB_ENABLE; 	outb(efr, baseio + MOXA_MUST_EFR_REGISTER);	outb(oldlcr, baseio + UART_LCR);} #ifdef	CONFIG_PCIstatic void mxser_disable_must_enchance_mode(unsigned long baseio){	u8 oldlcr;	u8 efr; 	oldlcr = inb(baseio + UART_LCR);	outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); 	efr = inb(baseio + MOXA_MUST_EFR_REGISTER);	efr &= ~MOXA_MUST_EFR_EFRB_ENABLE; 	outb(efr, baseio + MOXA_MUST_EFR_REGISTER);	outb(oldlcr, baseio + UART_LCR);}#endif static void mxser_set_must_xon1_value(unsigned long baseio, u8 value){	u8 oldlcr;	u8 efr; 	oldlcr = inb(baseio + UART_LCR);	outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); 	efr = inb(baseio + MOXA_MUST_EFR_REGISTER);	efr &= ~MOXA_MUST_EFR_BANK_MASK;	efr |= MOXA_MUST_EFR_BANK0; 	outb(efr, baseio + MOXA_MUST_EFR_REGISTER);	outb(value, baseio + MOXA_MUST_XON1_REGISTER);	outb(oldlcr, baseio + UART_LCR);} static void mxser_set_must_xoff1_value(unsigned long baseio, u8 value){	u8 oldlcr;	u8 efr; 	oldlcr = inb(baseio + UART_LCR);	outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); 	efr = inb(baseio + MOXA_MUST_EFR_REGISTER);	efr &= ~MOXA_MUST_EFR_BANK_MASK;	efr |= MOXA_MUST_EFR_BANK0; 	outb(efr, baseio + MOXA_MUST_EFR_REGISTER);	outb(value, baseio + MOXA_MUST_XOFF1_REGISTER);	outb(oldlcr, baseio + UART_LCR);} static void mxser_set_must_fifo_value(struct mxser_port *info){	u8 oldlcr;	u8 efr; 	oldlcr = inb(info->ioaddr + UART_LCR);	outb(MOXA_MUST_ENTER_ENCHANCE, info->ioaddr + UART_LCR); 	efr = inb(info->ioaddr + MOXA_MUST_EFR_REGISTER);	efr &= ~MOXA_MUST_EFR_BANK_MASK;	efr |= MOXA_MUST_EFR_BANK1; 	outb(efr, info->ioaddr + MOXA_MUST_EFR_REGISTER);	outb((u8)info->rx_high_water, info->ioaddr + MOXA_MUST_RBRTH_REGISTER);	outb((u8)info->rx_trigger, info->ioaddr + MOXA_MUST_RBRTI_REGISTER);	outb((u8)info->rx_low_water, info->ioaddr + MOXA_MUST_RBRTL_REGISTER);	outb(oldlcr, info->ioaddr + UART_LCR);} static void mxser_set_must_enum_value(unsigned long baseio, u8 value){	u8 oldlcr;	u8 efr; 	oldlcr = inb(baseio + UART_LCR);	outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); 	efr = inb(baseio + MOXA_MUST_EFR_REGISTER);	efr &= ~MOXA_MUST_EFR_BANK_MASK;	efr |= MOXA_MUST_EFR_BANK2; 	outb(efr, baseio + MOXA_MUST_EFR_REGISTER);	outb(value, baseio + MOXA_MUST_ENUM_REGISTER);	outb(oldlcr, baseio + UART_LCR);} #ifdef CONFIG_PCIstatic void mxser_get_must_hardware_id(unsigned long baseio, u8 *pId){	u8 oldlcr;	u8 efr; 	oldlcr = inb(baseio + UART_LCR);	outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); 	efr = inb(baseio + MOXA_MUST_EFR_REGISTER);	efr &= ~MOXA_MUST_EFR_BANK_MASK;	efr |= MOXA_MUST_EFR_BANK2; 	outb(efr, baseio + MOXA_MUST_EFR_REGISTER);	*pId = inb(baseio + MOXA_MUST_HWID_REGISTER);	outb(oldlcr, baseio + UART_LCR);}#endif static void SET_MOXA_MUST_NO_SOFTWARE_FLOW_CONTROL(unsigned long baseio){	u8 oldlcr;	u8 efr; 	oldlcr = inb(baseio + UART_LCR);	outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); 	efr = inb(baseio + MOXA_MUST_EFR_REGISTER);	efr &= ~MOXA_MUST_EFR_SF_MASK; 	outb(efr, baseio + MOXA_MUST_EFR_REGISTER);	outb(oldlcr, baseio + UART_LCR);} static void mxser_enable_must_tx_software_flow_control(unsigned long baseio){	u8 oldlcr;	u8 efr; 	oldlcr = inb(baseio + UART_LCR);	outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); 	efr = inb(baseio + MOXA_MUST_EFR_REGISTER);	efr &= ~MOXA_MUST_EFR_SF_TX_MASK;	efr |= MOXA_MUST_EFR_SF_TX1; 	outb(efr, baseio + MOXA_MUST_EFR_REGISTER);	outb(oldlcr, baseio + UART_LCR);} static void mxser_disable_must_tx_software_flow_control(unsigned long baseio){	u8 oldlcr;	u8 efr; 	oldlcr = inb(baseio + UART_LCR);	outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); 	efr = inb(baseio + MOXA_MUST_EFR_REGISTER);	efr &= ~MOXA_MUST_EFR_SF_TX_MASK; 	outb(efr, baseio + MOXA_MUST_EFR_REGISTER);	outb(oldlcr, baseio + UART_LCR);} static void mxser_enable_must_rx_software_flow_control(unsigned long baseio){	u8 oldlcr;	u8 efr; 	oldlcr = inb(baseio + UART_LCR);	outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); 	efr = inb(baseio + MOXA_MUST_EFR_REGISTER);	efr &= ~MOXA_MUST_EFR_SF_RX_MASK;	efr |= MOXA_MUST_EFR_SF_RX1; 	outb(efr, baseio + MOXA_MUST_EFR_REGISTER);	outb(oldlcr, baseio + UART_LCR);} static void mxser_disable_must_rx_software_flow_control(unsigned long baseio){	u8 oldlcr;	u8 efr; 	oldlcr = inb(baseio + UART_LCR);	outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); 	efr = inb(baseio + MOXA_MUST_EFR_REGISTER);	efr &= ~MOXA_MUST_EFR_SF_RX_MASK; 	outb(efr, baseio + MOXA_MUST_EFR_REGISTER);	outb(oldlcr, baseio + UART_LCR);} #ifdef CONFIG_PCIstatic int CheckIsMoxaMust(unsigned long io){	u8 oldmcr, hwid;	int i; 	outb(0, io + UART_LCR);	mxser_disable_must_enchance_mode(io);	oldmcr = inb(io + UART_MCR);	outb(0, io + UART_MCR);	mxser_set_must_xon1_value(io, 0x11);	if ((hwid = inb(io + UART_MCR)) != 0) {

Lines 1–500 of 2817.