isicom.c

来自「linux 内核源代码」· C语言 代码 · 共 1,898 行 · 第 1/4 页

C
1,898
字号
/* *	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. * *	Original driver code supplied by Multi-Tech * *	Changes *	1/9/98	alan@redhat.com		Merge to 2.0.x kernel tree *					Obtain and use official major/minors *					Loader switched to a misc device *					(fixed range check bug as a side effect) *					Printk clean up *	9/12/98	alan@redhat.com		Rough port to 2.1.x * *	10/6/99 sameer			Merged the ISA and PCI drivers to *					a new unified driver. * *	3/9/99	sameer			Added support for ISI4616 cards. * *	16/9/99	sameer			We do not force RTS low anymore. *					This is to prevent the firmware *					from getting confused. * *	26/10/99 sameer			Cosmetic changes:The driver now *					dumps the Port Count information *					along with I/O address and IRQ. * *	13/12/99 sameer			Fixed the problem with IRQ sharing. * *	10/5/00  sameer			Fixed isicom_shutdown_board() *					to not lower DTR on all the ports *					when the last port on the card is *					closed. * *	10/5/00  sameer			Signal mask setup command added *					to  isicom_setup_port and *					isicom_shutdown_port. * *	24/5/00  sameer			The driver is now SMP aware. * * *	27/11/00 Vinayak P Risbud	Fixed the Driver Crash Problem * * *	03/01/01  anil .s		Added support for resetting the *					internal modems on ISI cards. * *	08/02/01  anil .s		Upgraded the driver for kernel *					2.4.x * *	11/04/01  Kevin			Fixed firmware load problem with *					ISIHP-4X card * *	30/04/01  anil .s		Fixed the remote login through *					ISI port problem. Now the link *					does not go down before password *					prompt. * *	03/05/01  anil .s		Fixed the problem with IRQ sharing *					among ISI-PCI cards. * *	03/05/01  anil .s		Added support to display the version *					info during insmod as well as module *					listing by lsmod. * *	10/05/01  anil .s		Done the modifications to the source *					file and Install script so that the *					same installation can be used for *					2.2.x and 2.4.x kernel. * *	06/06/01  anil .s		Now we drop both dtr and rts during *					shutdown_port as well as raise them *					during isicom_config_port. * *	09/06/01 acme@conectiva.com.br	use capable, not suser, do *					restore_flags on failure in *					isicom_send_break, verify put_user *					result * *	11/02/03  ranjeeth		Added support for 230 Kbps and 460 Kbps *					Baud index extended to 21 * *	20/03/03  ranjeeth		Made to work for Linux Advanced server. *					Taken care of license warning. * *	10/12/03  Ravindra		Made to work for Fedora Core 1 of *					Red Hat Distribution * *	06/01/05  Alan Cox 		Merged the ISI and base kernel strands *					into a single 2.6 driver * *	*********************************************************** * *	To use this driver you also need the support package. You *	can find this in RPM format on *		ftp://ftp.linux.org.uk/pub/linux/alan * *	You can find the original tools for this direct from Multitech *		ftp://ftp.multitech.com/ISI-Cards/ * *	Having installed the cards the module options (/etc/modprobe.conf) * *	options isicom   io=card1,card2,card3,card4 irq=card1,card2,card3,card4 * *	Omit those entries for boards you don't have installed. * *	TODO *		Merge testing *		64-bit verification */#include <linux/module.h>#include <linux/firmware.h>#include <linux/kernel.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/termios.h>#include <linux/fs.h>#include <linux/sched.h>#include <linux/serial.h>#include <linux/mm.h>#include <linux/interrupt.h>#include <linux/timer.h>#include <linux/delay.h>#include <linux/ioport.h>#include <asm/uaccess.h>#include <asm/io.h>#include <asm/system.h>#include <linux/pci.h>#include <linux/isicom.h>#define InterruptTheCard(base) outw(0, (base) + 0xc)#define ClearInterrupt(base) inw((base) + 0x0a)#define pr_dbg(str...) pr_debug("ISICOM: " str)#ifdef DEBUG#define isicom_paranoia_check(a, b, c) __isicom_paranoia_check((a), (b), (c))#else#define isicom_paranoia_check(a, b, c) 0#endifstatic int isicom_probe(struct pci_dev *, const struct pci_device_id *);static void __devexit isicom_remove(struct pci_dev *);static struct pci_device_id isicom_pci_tbl[] = {	{ PCI_DEVICE(VENDOR_ID, 0x2028) },	{ PCI_DEVICE(VENDOR_ID, 0x2051) },	{ PCI_DEVICE(VENDOR_ID, 0x2052) },	{ PCI_DEVICE(VENDOR_ID, 0x2053) },	{ PCI_DEVICE(VENDOR_ID, 0x2054) },	{ PCI_DEVICE(VENDOR_ID, 0x2055) },	{ PCI_DEVICE(VENDOR_ID, 0x2056) },	{ PCI_DEVICE(VENDOR_ID, 0x2057) },	{ PCI_DEVICE(VENDOR_ID, 0x2058) },	{ 0 }};MODULE_DEVICE_TABLE(pci, isicom_pci_tbl);static struct pci_driver isicom_driver = {	.name		= "isicom",	.id_table	= isicom_pci_tbl,	.probe		= isicom_probe,	.remove		= __devexit_p(isicom_remove)};static int prev_card = 3;	/*	start servicing isi_card[0]	*/static struct tty_driver *isicom_normal;static void isicom_tx(unsigned long _data);static void isicom_start(struct tty_struct *tty);static DEFINE_TIMER(tx, isicom_tx, 0, 0);/*   baud index mappings from linux defns to isi */static signed char linuxb_to_isib[] = {	-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 16, 17, 18, 19, 20, 21};struct	isi_board {	unsigned long		base;	int			irq;	unsigned char		port_count;	unsigned short		status;	unsigned short		port_status; /* each bit for each port */	unsigned short		shift_count;	struct isi_port		* ports;	signed char		count;	spinlock_t		card_lock; /* Card wide lock 11/5/00 -sameer */	unsigned long		flags;	unsigned int		index;};struct	isi_port {	unsigned short		magic;	unsigned int		flags;	int			count;	int			blocked_open;	int			close_delay;	u16			channel;	u16			status;	u16			closing_wait;	struct isi_board	* card;	struct tty_struct 	* tty;	wait_queue_head_t	close_wait;	wait_queue_head_t	open_wait;	unsigned char		* xmit_buf;	int			xmit_head;	int			xmit_tail;	int			xmit_cnt;};static struct isi_board isi_card[BOARD_COUNT];static struct isi_port  isi_ports[PORT_COUNT];/* *	Locking functions for card level locking. We need to own both *	the kernel lock for the card and have the card in a position that *	it wants to talk. */static inline int WaitTillCardIsFree(unsigned long base){	unsigned int count = 0;	unsigned int a = in_atomic(); /* do we run under spinlock? */	while (!(inw(base + 0xe) & 0x1) && count++ < 100)		if (a)			mdelay(1);		else			msleep(1);	return !(inw(base + 0xe) & 0x1);}static int lock_card(struct isi_board *card){	unsigned long base = card->base;	unsigned int retries, a;	for (retries = 0; retries < 10; retries++) {		spin_lock_irqsave(&card->card_lock, card->flags);		for (a = 0; a < 10; a++) {			if (inw(base + 0xe) & 0x1)				return 1;			udelay(10);		}		spin_unlock_irqrestore(&card->card_lock, card->flags);		msleep(10);	}	printk(KERN_WARNING "ISICOM: Failed to lock Card (0x%lx)\n",		card->base);	return 0;	/* Failed to acquire the card! */}static void unlock_card(struct isi_board *card){	spin_unlock_irqrestore(&card->card_lock, card->flags);}/* *  ISI Card specific ops ... *//* card->lock HAS to be held */static void raise_dtr(struct isi_port *port){	struct isi_board *card = port->card;	unsigned long base = card->base;	u16 channel = port->channel;	if (WaitTillCardIsFree(base))		return;	outw(0x8000 | (channel << card->shift_count) | 0x02, base);	outw(0x0504, base);	InterruptTheCard(base);	port->status |= ISI_DTR;}/* card->lock HAS to be held */static inline void drop_dtr(struct isi_port *port){	struct isi_board *card = port->card;	unsigned long base = card->base;	u16 channel = port->channel;	if (WaitTillCardIsFree(base))		return;	outw(0x8000 | (channel << card->shift_count) | 0x02, base);	outw(0x0404, base);	InterruptTheCard(base);	port->status &= ~ISI_DTR;}/* card->lock HAS to be held */static inline void raise_rts(struct isi_port *port){	struct isi_board *card = port->card;	unsigned long base = card->base;	u16 channel = port->channel;	if (WaitTillCardIsFree(base))		return;	outw(0x8000 | (channel << card->shift_count) | 0x02, base);	outw(0x0a04, base);	InterruptTheCard(base);	port->status |= ISI_RTS;}/* card->lock HAS to be held */static inline void drop_rts(struct isi_port *port){	struct isi_board *card = port->card;	unsigned long base = card->base;	u16 channel = port->channel;	if (WaitTillCardIsFree(base))		return;	outw(0x8000 | (channel << card->shift_count) | 0x02, base);	outw(0x0804, base);	InterruptTheCard(base);	port->status &= ~ISI_RTS;}/* card->lock MUST NOT be held */static inline void raise_dtr_rts(struct isi_port *port){	struct isi_board *card = port->card;	unsigned long base = card->base;	u16 channel = port->channel;	if (!lock_card(card))		return;	outw(0x8000 | (channel << card->shift_count) | 0x02, base);	outw(0x0f04, base);	InterruptTheCard(base);	port->status |= (ISI_DTR | ISI_RTS);	unlock_card(card);}/* card->lock HAS to be held */static void drop_dtr_rts(struct isi_port *port){	struct isi_board *card = port->card;	unsigned long base = card->base;	u16 channel = port->channel;	if (WaitTillCardIsFree(base))		return;	outw(0x8000 | (channel << card->shift_count) | 0x02, base);	outw(0x0c04, base);	InterruptTheCard(base);	port->status &= ~(ISI_RTS | ISI_DTR);}/* *	ISICOM Driver specific routines ... * */static inline int __isicom_paranoia_check(struct isi_port const *port,	char *name, const char *routine){	if (!port) {		printk(KERN_WARNING "ISICOM: Warning: bad isicom magic for "			"dev %s in %s.\n", name, routine);		return 1;	}	if (port->magic != ISICOM_MAGIC) {		printk(KERN_WARNING "ISICOM: Warning: NULL isicom port for "			"dev %s in %s.\n", name, routine);		return 1;	}	return 0;}/* *	Transmitter. * *	We shovel data into the card buffers on a regular basis. The card *	will do the rest of the work for us. */static void isicom_tx(unsigned long _data){	unsigned long flags, base;	unsigned int retries;	short count = (BOARD_COUNT-1), card;	short txcount, wrd, residue, word_count, cnt;	struct isi_port *port;	struct tty_struct *tty;	/*	find next active board	*/	card = (prev_card + 1) & 0x0003;	while(count-- > 0) {		if (isi_card[card].status & BOARD_ACTIVE)			break;		card = (card + 1) & 0x0003;	}	if (!(isi_card[card].status & BOARD_ACTIVE))		goto sched_again;	prev_card = card;	count = isi_card[card].port_count;	port = isi_card[card].ports;	base = isi_card[card].base;	spin_lock_irqsave(&isi_card[card].card_lock, flags);	for (retries = 0; retries < 100; retries++) {		if (inw(base + 0xe) & 0x1)			break;		udelay(2);	}	if (retries >= 100)		goto unlock;	for (;count > 0;count--, port++) {		/* port not active or tx disabled to force flow control */		if (!(port->flags & ASYNC_INITIALIZED) ||				!(port->status & ISI_TXOK))			continue;		tty = port->tty;		if (tty == NULL)			continue;		txcount = min_t(short, TX_SIZE, port->xmit_cnt);		if (txcount <= 0 || tty->stopped || tty->hw_stopped)			continue;		if (!(inw(base + 0x02) & (1 << port->channel)))			continue;		pr_dbg("txing %d bytes, port%d.\n", txcount,			port->channel + 1);		outw((port->channel << isi_card[card].shift_count) | txcount,			base);		residue = NO;		wrd = 0;		while (1) {			cnt = min_t(int, txcount, (SERIAL_XMIT_SIZE					- port->xmit_tail));			if (residue == YES) {				residue = NO;				if (cnt > 0) {					wrd |= (port->xmit_buf[port->xmit_tail]									<< 8);					port->xmit_tail = (port->xmit_tail + 1)						& (SERIAL_XMIT_SIZE - 1);					port->xmit_cnt--;					txcount--;					cnt--;					outw(wrd, base);				} else {					outw(wrd, base);					break;				}			}			if (cnt <= 0) break;			word_count = cnt >> 1;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?