isicom.c

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

C
1,898
字号
			outsw(base, port->xmit_buf+port->xmit_tail,word_count);			port->xmit_tail = (port->xmit_tail				+ (word_count << 1)) & (SERIAL_XMIT_SIZE - 1);			txcount -= (word_count << 1);			port->xmit_cnt -= (word_count << 1);			if (cnt & 0x0001) {				residue = YES;				wrd = port->xmit_buf[port->xmit_tail];				port->xmit_tail = (port->xmit_tail + 1)					& (SERIAL_XMIT_SIZE - 1);				port->xmit_cnt--;				txcount--;			}		}		InterruptTheCard(base);		if (port->xmit_cnt <= 0)			port->status &= ~ISI_TXOK;		if (port->xmit_cnt <= WAKEUP_CHARS)			tty_wakeup(tty);	}unlock:	spin_unlock_irqrestore(&isi_card[card].card_lock, flags);	/*	schedule another tx for hopefully in about 10ms	*/sched_again:	mod_timer(&tx, jiffies + msecs_to_jiffies(10));}/* *	Main interrupt handler routine */static irqreturn_t isicom_interrupt(int irq, void *dev_id){	struct isi_board *card = dev_id;	struct isi_port *port;	struct tty_struct *tty;	unsigned long base;	u16 header, word_count, count, channel;	short byte_count;	unsigned char *rp;	if (!card || !(card->status & FIRMWARE_LOADED))		return IRQ_NONE;	base = card->base;	/* did the card interrupt us? */	if (!(inw(base + 0x0e) & 0x02))		return IRQ_NONE;	spin_lock(&card->card_lock);	/*	 * disable any interrupts from the PCI card and lower the	 * interrupt line	 */	outw(0x8000, base+0x04);	ClearInterrupt(base);	inw(base);		/* get the dummy word out */	header = inw(base);	channel = (header & 0x7800) >> card->shift_count;	byte_count = header & 0xff;	if (channel + 1 > card->port_count) {		printk(KERN_WARNING "ISICOM: isicom_interrupt(0x%lx): "			"%d(channel) > port_count.\n", base, channel+1);		outw(0x0000, base+0x04); /* enable interrupts */		spin_unlock(&card->card_lock);		return IRQ_HANDLED;	}	port = card->ports + channel;	if (!(port->flags & ASYNC_INITIALIZED)) {		outw(0x0000, base+0x04); /* enable interrupts */		spin_unlock(&card->card_lock);		return IRQ_HANDLED;	}	tty = port->tty;	if (tty == NULL) {		word_count = byte_count >> 1;		while(byte_count > 1) {			inw(base);			byte_count -= 2;		}		if (byte_count & 0x01)			inw(base);		outw(0x0000, base+0x04); /* enable interrupts */		spin_unlock(&card->card_lock);		return IRQ_HANDLED;	}	if (header & 0x8000) {		/* Status Packet */		header = inw(base);		switch(header & 0xff) {		case 0:	/* Change in EIA signals */			if (port->flags & ASYNC_CHECK_CD) {				if (port->status & ISI_DCD) {					if (!(header & ISI_DCD)) {					/* Carrier has been lost  */						pr_dbg("interrupt: DCD->low.\n"							);						port->status &= ~ISI_DCD;						tty_hangup(tty);					}				} else if (header & ISI_DCD) {				/* Carrier has been detected */					pr_dbg("interrupt: DCD->high.\n");					port->status |= ISI_DCD;					wake_up_interruptible(&port->open_wait);				}			} else {				if (header & ISI_DCD)					port->status |= ISI_DCD;				else					port->status &= ~ISI_DCD;			}			if (port->flags & ASYNC_CTS_FLOW) {				if (port->tty->hw_stopped) {					if (header & ISI_CTS) {						port->tty->hw_stopped = 0;						/* start tx ing */						port->status |= (ISI_TXOK							| ISI_CTS);						tty_wakeup(tty);					}				} else if (!(header & ISI_CTS)) {					port->tty->hw_stopped = 1;					/* stop tx ing */					port->status &= ~(ISI_TXOK | ISI_CTS);				}			} else {				if (header & ISI_CTS)					port->status |= ISI_CTS;				else					port->status &= ~ISI_CTS;			}			if (header & ISI_DSR)				port->status |= ISI_DSR;			else				port->status &= ~ISI_DSR;			if (header & ISI_RI)				port->status |= ISI_RI;			else				port->status &= ~ISI_RI;			break;		case 1:	/* Received Break !!! */			tty_insert_flip_char(tty, 0, TTY_BREAK);			if (port->flags & ASYNC_SAK)				do_SAK(tty);			tty_flip_buffer_push(tty);			break;		case 2:	/* Statistics		 */			pr_dbg("isicom_interrupt: stats!!!.\n");			break;		default:			pr_dbg("Intr: Unknown code in status packet.\n");			break;		}	} else {				/* Data   Packet */		count = tty_prepare_flip_string(tty, &rp, byte_count & ~1);		pr_dbg("Intr: Can rx %d of %d bytes.\n", count, byte_count);		word_count = count >> 1;		insw(base, rp, word_count);		byte_count -= (word_count << 1);		if (count & 0x0001) {			tty_insert_flip_char(tty,  inw(base) & 0xff,				TTY_NORMAL);			byte_count -= 2;		}		if (byte_count > 0) {			pr_dbg("Intr(0x%lx:%d): Flip buffer overflow! dropping "				"bytes...\n", base, channel + 1);			while(byte_count > 0) { /* drain out unread xtra data */				inw(base);				byte_count -= 2;			}		}		tty_flip_buffer_push(tty);	}	outw(0x0000, base+0x04); /* enable interrupts */	spin_unlock(&card->card_lock);	return IRQ_HANDLED;}static void isicom_config_port(struct isi_port *port){	struct isi_board *card = port->card;	struct tty_struct *tty;	unsigned long baud;	unsigned long base = card->base;	u16 channel_setup, channel = port->channel,		shift_count = card->shift_count;	unsigned char flow_ctrl;	if (!(tty = port->tty) || !tty->termios)		return;	baud = C_BAUD(tty);	if (baud & CBAUDEX) {		baud &= ~CBAUDEX;		/*  if CBAUDEX bit is on and the baud is set to either 50 or 75		 *  then the card is programmed for 57.6Kbps or 115Kbps		 *  respectively.		 */		/* 1,2,3,4 => 57.6, 115.2, 230, 460 kbps resp. */		if (baud < 1 || baud > 4)			port->tty->termios->c_cflag &= ~CBAUDEX;		else			baud += 15;	}	if (baud == 15) {		/*  the ASYNC_SPD_HI and ASYNC_SPD_VHI options are set		 *  by the set_serial_info ioctl ... this is done by		 *  the 'setserial' utility.		 */		if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)			baud++; /*  57.6 Kbps */		if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)			baud +=2; /*  115  Kbps */		if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)			baud += 3; /* 230 kbps*/		if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)			baud += 4; /* 460 kbps*/	}	if (linuxb_to_isib[baud] == -1) {		/* hang up */		drop_dtr(port);		return;	}	else		raise_dtr(port);	if (WaitTillCardIsFree(base) == 0) {		outw(0x8000 | (channel << shift_count) |0x03, base);		outw(linuxb_to_isib[baud] << 8 | 0x03, base);		channel_setup = 0;		switch(C_CSIZE(tty)) {		case CS5:			channel_setup |= ISICOM_CS5;			break;		case CS6:			channel_setup |= ISICOM_CS6;			break;		case CS7:			channel_setup |= ISICOM_CS7;			break;		case CS8:			channel_setup |= ISICOM_CS8;			break;		}		if (C_CSTOPB(tty))			channel_setup |= ISICOM_2SB;		if (C_PARENB(tty)) {			channel_setup |= ISICOM_EVPAR;			if (C_PARODD(tty))				channel_setup |= ISICOM_ODPAR;		}		outw(channel_setup, base);		InterruptTheCard(base);	}	if (C_CLOCAL(tty))		port->flags &= ~ASYNC_CHECK_CD;	else		port->flags |= ASYNC_CHECK_CD;	/* flow control settings ...*/	flow_ctrl = 0;	port->flags &= ~ASYNC_CTS_FLOW;	if (C_CRTSCTS(tty)) {		port->flags |= ASYNC_CTS_FLOW;		flow_ctrl |= ISICOM_CTSRTS;	}	if (I_IXON(tty))		flow_ctrl |= ISICOM_RESPOND_XONXOFF;	if (I_IXOFF(tty))		flow_ctrl |= ISICOM_INITIATE_XONXOFF;	if (WaitTillCardIsFree(base) == 0) {		outw(0x8000 | (channel << shift_count) |0x04, base);		outw(flow_ctrl << 8 | 0x05, base);		outw((STOP_CHAR(tty)) << 8 | (START_CHAR(tty)), base);		InterruptTheCard(base);	}	/*	rx enabled -> enable port for rx on the card	*/	if (C_CREAD(tty)) {		card->port_status |= (1 << channel);		outw(card->port_status, base + 0x02);	}}/* open et all */static inline void isicom_setup_board(struct isi_board *bp){	int channel;	struct isi_port *port;	unsigned long flags;	spin_lock_irqsave(&bp->card_lock, flags);	if (bp->status & BOARD_ACTIVE) {		spin_unlock_irqrestore(&bp->card_lock, flags);		return;	}	port = bp->ports;	bp->status |= BOARD_ACTIVE;	for (channel = 0; channel < bp->port_count; channel++, port++)		drop_dtr_rts(port);	spin_unlock_irqrestore(&bp->card_lock, flags);}static int isicom_setup_port(struct isi_port *port){	struct isi_board *card = port->card;	unsigned long flags;	if (port->flags & ASYNC_INITIALIZED) {		return 0;	}	if (!port->xmit_buf) {		unsigned long page;		if (!(page = get_zeroed_page(GFP_KERNEL)))			return -ENOMEM;		if (port->xmit_buf) {			free_page(page);			return -ERESTARTSYS;		}		port->xmit_buf = (unsigned char *) page;	}	spin_lock_irqsave(&card->card_lock, flags);	if (port->tty)		clear_bit(TTY_IO_ERROR, &port->tty->flags);	if (port->count == 1)		card->count++;	port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;	/*	discard any residual data	*/	if (WaitTillCardIsFree(card->base) == 0) {		outw(0x8000 | (port->channel << card->shift_count) | 0x02,				card->base);		outw(((ISICOM_KILLTX | ISICOM_KILLRX) << 8) | 0x06, card->base);		InterruptTheCard(card->base);	}	isicom_config_port(port);	port->flags |= ASYNC_INITIALIZED;	spin_unlock_irqrestore(&card->card_lock, flags);	return 0;}static int block_til_ready(struct tty_struct *tty, struct file *filp,	struct isi_port *port){	struct isi_board *card = port->card;	int do_clocal = 0, retval;	unsigned long flags;	DECLARE_WAITQUEUE(wait, current);	/* block if port is in the process of being closed */	if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {		pr_dbg("block_til_ready: close in progress.\n");		interruptible_sleep_on(&port->close_wait);		if (port->flags & ASYNC_HUP_NOTIFY)			return -EAGAIN;		else			return -ERESTARTSYS;	}	/* if non-blocking mode is set ... */	if ((filp->f_flags & O_NONBLOCK) ||			(tty->flags & (1 << TTY_IO_ERROR))) {		pr_dbg("block_til_ready: non-block mode.\n");		port->flags |= ASYNC_NORMAL_ACTIVE;		return 0;	}	if (C_CLOCAL(tty))		do_clocal = 1;	/* block waiting for DCD to be asserted, and while						callout dev is busy */	retval = 0;	add_wait_queue(&port->open_wait, &wait);	spin_lock_irqsave(&card->card_lock, flags);	if (!tty_hung_up_p(filp))		port->count--;	port->blocked_open++;	spin_unlock_irqrestore(&card->card_lock, flags);	while (1) {		raise_dtr_rts(port);		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 || (port->status & ISI_DCD))) {			break;		}		if (signal_pending(current)) {			retval = -ERESTARTSYS;			break;		}		schedule();	}	set_current_state(TASK_RUNNING);	remove_wait_queue(&port->open_wait, &wait);	spin_lock_irqsave(&card->card_lock, flags);	if (!tty_hung_up_p(filp))		port->count++;	port->blocked_open--;	spin_unlock_irqrestore(&card->card_lock, flags);	if (retval)		return retval;	port->flags |= ASYNC_NORMAL_ACTIVE;	return 0;}static int isicom_open(struct tty_struct *tty, struct file *filp){	struct isi_port *port;	struct isi_board *card;	unsigned int board;	int error, line;	line = tty->index;	if (line < 0 || line > PORT_COUNT-1)		return -ENODEV;	board = BOARD(line);	card = &isi_card[board];	if (!(card->status & FIRMWARE_LOADED))		return -ENODEV;	/*  open on a port greater than the port count for the card !!! */	if (line > ((board * 16) + card->port_count - 1))		return -ENODEV;	port = &isi_ports[line];	if (isicom_paranoia_check(port, tty->name, "isicom_open"))		return -ENODEV;	isicom_setup_board(card);	port->count++;	tty->driver_data = port;

⌨️ 快捷键说明

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