jsm_tty.c

来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 943 行 · 第 1/2 页

C
943
字号
	int i;	struct jsm_channel *ch;	if (!brd)		return -ENXIO;	jsm_printk(INIT, INFO, &brd->pci_dev, "start\n");	/*	 * Initialize board structure elements.	 */	brd->nasync = brd->maxports;	/* Set up channel variables */	for (i = 0; i < brd->nasync; i++) {		if (!brd->channels[i])			continue;		ch = brd->channels[i];		uart_remove_one_port(&jsm_uart_driver, &brd->channels[i]->uart_port);	}	jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n");	return 0;}void jsm_input(struct jsm_channel *ch){	struct jsm_board *bd;	struct tty_struct *tp;	struct tty_ldisc *ld;	u32 rmask;	u16 head;	u16 tail;	int data_len;	unsigned long lock_flags;	int flip_len = 0;	int len = 0;	int n = 0;	int s = 0;	int i = 0;	jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start\n");	if (!ch)		return;	tp = ch->uart_port.info->tty;	bd = ch->ch_bd;	if(!bd)		return;	spin_lock_irqsave(&ch->ch_lock, lock_flags);	/*	 *Figure the number of characters in the buffer.	 *Exit immediately if none.	 */	rmask = RQUEUEMASK;	head = ch->ch_r_head & rmask;	tail = ch->ch_r_tail & rmask;	data_len = (head - tail) & rmask;	if (data_len == 0) {		spin_unlock_irqrestore(&ch->ch_lock, lock_flags);		return;	}	jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start\n");	/*	 *If the device is not open, or CREAD is off, flush	 *input data and return immediately.	 */	if (!tp ||		!(tp->termios->c_cflag & CREAD) ) {		jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,			"input. dropping %d bytes on port %d...\n", data_len, ch->ch_portnum);		ch->ch_r_head = tail;		/* Force queue flow control to be released, if needed */		jsm_check_queue_flow_control(ch);		spin_unlock_irqrestore(&ch->ch_lock, lock_flags);		return;	}	/*	 * If we are throttled, simply don't read any data.	 */	if (ch->ch_flags & CH_STOPI) {		spin_unlock_irqrestore(&ch->ch_lock, lock_flags);		jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,			"Port %d throttled, not reading any data. head: %x tail: %x\n",			ch->ch_portnum, head, tail);		return;	}	jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start 2\n");	/*	 * If the rxbuf is empty and we are not throttled, put as much	 * as we can directly into the linux TTY buffer.	 *	 */	flip_len = TTY_FLIPBUF_SIZE;	len = min(data_len, flip_len);	len = min(len, (N_TTY_BUF_SIZE - 1) - tp->read_cnt);	ld = tty_ldisc_ref(tp);	/*	 * If the DONT_FLIP flag is on, don't flush our buffer, and act	 * like the ld doesn't have any space to put the data right now.	 */	if (test_bit(TTY_DONT_FLIP, &tp->flags))		len = 0;	/*	 * If we were unable to get a reference to the ld,	 * don't flush our buffer, and act like the ld doesn't	 * have any space to put the data right now.	 */	if (!ld) {		len = 0;	} else {		/*		 * If ld doesn't have a pointer to a receive_buf function,		 * flush the data, then act like the ld doesn't have any		 * space to put the data right now.		 */		if (!ld->receive_buf) {				ch->ch_r_head = ch->ch_r_tail;				len = 0;		}	}	if (len <= 0) {		spin_unlock_irqrestore(&ch->ch_lock, lock_flags);		jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "jsm_input 1\n");		if (ld)			tty_ldisc_deref(ld);		return;	}	len = tty_buffer_request_room(tp, len);	n = len;	/*	 * n now contains the most amount of data we can copy,	 * bounded either by the flip buffer size or the amount	 * of data the card actually has pending...	 */	while (n) {		s = ((head >= tail) ? head : RQUEUESIZE) - tail;		s = min(s, n);		if (s <= 0)			break;			/*			 * If conditions are such that ld needs to see all			 * UART errors, we will have to walk each character			 * and error byte and send them to the buffer one at			 * a time.			 */		if (I_PARMRK(tp) || I_BRKINT(tp) || I_INPCK(tp)) {			for (i = 0; i < s; i++) {				/*				 * Give the Linux ld the flags in the				 * format it likes.				 */				if (*(ch->ch_equeue +tail +i) & UART_LSR_BI)					tty_insert_flip_char(tp, *(ch->ch_rqueue +tail +i),  TTY_BREAK);				else if (*(ch->ch_equeue +tail +i) & UART_LSR_PE)					tty_insert_flip_char(tp, *(ch->ch_rqueue +tail +i), TTY_PARITY);				else if (*(ch->ch_equeue +tail +i) & UART_LSR_FE)					tty_insert_flip_char(tp, *(ch->ch_rqueue +tail +i), TTY_FRAME);				else				tty_insert_flip_char(tp, *(ch->ch_rqueue +tail +i), TTY_NORMAL);			}		} else {			tty_insert_flip_string(tp, ch->ch_rqueue + tail, s) ;		}		tail += s;		n -= s;		/* Flip queue if needed */		tail &= rmask;	}	ch->ch_r_tail = tail & rmask;	ch->ch_e_tail = tail & rmask;	jsm_check_queue_flow_control(ch);	spin_unlock_irqrestore(&ch->ch_lock, lock_flags);	/* Tell the tty layer its okay to "eat" the data now */	tty_flip_buffer_push(tp);	if (ld)		tty_ldisc_deref(ld);	jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, "finish\n");}static void jsm_carrier(struct jsm_channel *ch){	struct jsm_board *bd;	int virt_carrier = 0;	int phys_carrier = 0;	jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev, "start\n");	if (!ch)		return;	bd = ch->ch_bd;	if (!bd)		return;	if (ch->ch_mistat & UART_MSR_DCD) {		jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev,			"mistat: %x D_CD: %x\n", ch->ch_mistat, ch->ch_mistat & UART_MSR_DCD);		phys_carrier = 1;	}	if (ch->ch_c_cflag & CLOCAL)		virt_carrier = 1;	jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev,		"DCD: physical: %d virt: %d\n", phys_carrier, virt_carrier);	/*	 * Test for a VIRTUAL carrier transition to HIGH.	 */	if (((ch->ch_flags & CH_FCAR) == 0) && (virt_carrier == 1)) {		/*		 * When carrier rises, wake any threads waiting		 * for carrier in the open routine.		 */		jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev,			"carrier: virt DCD rose\n");		if (waitqueue_active(&(ch->ch_flags_wait)))			wake_up_interruptible(&ch->ch_flags_wait);	}	/*	 * Test for a PHYSICAL carrier transition to HIGH.	 */	if (((ch->ch_flags & CH_CD) == 0) && (phys_carrier == 1)) {		/*		 * When carrier rises, wake any threads waiting		 * for carrier in the open routine.		 */		jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev,			"carrier: physical DCD rose\n");		if (waitqueue_active(&(ch->ch_flags_wait)))			wake_up_interruptible(&ch->ch_flags_wait);	}	/*	 *  Test for a PHYSICAL transition to low, so long as we aren't	 *  currently ignoring physical transitions (which is what "virtual	 *  carrier" indicates).	 *	 *  The transition of the virtual carrier to low really doesn't	 *  matter... it really only means "ignore carrier state", not	 *  "make pretend that carrier is there".	 */	if ((virt_carrier == 0) && ((ch->ch_flags & CH_CD) != 0)			&& (phys_carrier == 0)) {		/*		 *	When carrier drops:		 *		 *	Drop carrier on all open units.		 *		 *	Flush queues, waking up any task waiting in the		 *	line discipline.		 *		 *	Send a hangup to the control terminal.		 *		 *	Enable all select calls.		 */		if (waitqueue_active(&(ch->ch_flags_wait)))			wake_up_interruptible(&ch->ch_flags_wait);	}	/*	 *  Make sure that our cached values reflect the current reality.	 */	if (virt_carrier == 1)		ch->ch_flags |= CH_FCAR;	else		ch->ch_flags &= ~CH_FCAR;	if (phys_carrier == 1)		ch->ch_flags |= CH_CD;	else		ch->ch_flags &= ~CH_CD;}void jsm_check_queue_flow_control(struct jsm_channel *ch){	struct board_ops *bd_ops = ch->ch_bd->bd_ops;	int qleft = 0;	/* Store how much space we have left in the queue */	if ((qleft = ch->ch_r_tail - ch->ch_r_head - 1) < 0)		qleft += RQUEUEMASK + 1;	/*	 * Check to see if we should enforce flow control on our queue because	 * the ld (or user) isn't reading data out of our queue fast enuf.	 *	 * NOTE: This is done based on what the current flow control of the	 * port is set for.	 *	 * 1) HWFLOW (RTS) - Turn off the UART's Receive interrupt.	 *	This will cause the UART's FIFO to back up, and force	 *	the RTS signal to be dropped.	 * 2) SWFLOW (IXOFF) - Keep trying to send a stop character to	 *	the other side, in hopes it will stop sending data to us.	 * 3) NONE - Nothing we can do.  We will simply drop any extra data	 *	that gets sent into us when the queue fills up.	 */	if (qleft < 256) {		/* HWFLOW */		if (ch->ch_c_cflag & CRTSCTS) {			if(!(ch->ch_flags & CH_RECEIVER_OFF)) {				bd_ops->disable_receiver(ch);				ch->ch_flags |= (CH_RECEIVER_OFF);				jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,					"Internal queue hit hilevel mark (%d)! Turning off interrupts.\n",					qleft);			}		}		/* SWFLOW */		else if (ch->ch_c_iflag & IXOFF) {			if (ch->ch_stops_sent <= MAX_STOPS_SENT) {				bd_ops->send_stop_character(ch);				ch->ch_stops_sent++;				jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,					"Sending stop char! Times sent: %x\n", ch->ch_stops_sent);			}		}	}	/*	 * Check to see if we should unenforce flow control because	 * ld (or user) finally read enuf data out of our queue.	 *	 * NOTE: This is done based on what the current flow control of the	 * port is set for.	 *	 * 1) HWFLOW (RTS) - Turn back on the UART's Receive interrupt.	 *	This will cause the UART's FIFO to raise RTS back up,	 *	which will allow the other side to start sending data again.	 * 2) SWFLOW (IXOFF) - Send a start character to	 *	the other side, so it will start sending data to us again.	 * 3) NONE - Do nothing. Since we didn't do anything to turn off the	 *	other side, we don't need to do anything now.	 */	if (qleft > (RQUEUESIZE / 2)) {		/* HWFLOW */		if (ch->ch_c_cflag & CRTSCTS) {			if (ch->ch_flags & CH_RECEIVER_OFF) {				bd_ops->enable_receiver(ch);				ch->ch_flags &= ~(CH_RECEIVER_OFF);				jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,					"Internal queue hit lowlevel mark (%d)! Turning on interrupts.\n",					qleft);			}		}		/* SWFLOW */		else if (ch->ch_c_iflag & IXOFF && ch->ch_stops_sent) {			ch->ch_stops_sent = 0;			bd_ops->send_start_character(ch);			jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "Sending start char!\n");		}	}}/* * jsm_tty_write() * * Take data from the user or kernel and send it out to the FEP. * In here exists all the Transparent Print magic as well. */int jsm_tty_write(struct uart_port *port){	int bufcount = 0, n = 0;	int data_count = 0,data_count1 =0;	u16 head;	u16 tail;	u16 tmask;	u32 remain;	int temp_tail = port->info->xmit.tail;	struct jsm_channel *channel = (struct jsm_channel *)port;	tmask = WQUEUEMASK;	head = (channel->ch_w_head) & tmask;	tail = (channel->ch_w_tail) & tmask;	if ((bufcount = tail - head - 1) < 0)		bufcount += WQUEUESIZE;	n = bufcount;	n = min(n, 56);	remain = WQUEUESIZE - head;	data_count = 0;	if (n >= remain) {		n -= remain;		while ((port->info->xmit.head != temp_tail) &&		(data_count < remain)) {			channel->ch_wqueue[head++] =			port->info->xmit.buf[temp_tail];			temp_tail++;			temp_tail &= (UART_XMIT_SIZE - 1);			data_count++;		}		if (data_count == remain) head = 0;	}	data_count1 = 0;	if (n > 0) {		remain = n;		while ((port->info->xmit.head != temp_tail) &&			(data_count1 < remain)) {			channel->ch_wqueue[head++] =				port->info->xmit.buf[temp_tail];			temp_tail++;			temp_tail &= (UART_XMIT_SIZE - 1);			data_count1++;		}	}	port->info->xmit.tail = temp_tail;	data_count += data_count1;	if (data_count) {		head &= tmask;		channel->ch_w_head = head;	}	if (data_count) {		channel->ch_bd->bd_ops->copy_data_from_queue_to_uart(channel);	}	return data_count;}

⌨️ 快捷键说明

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