⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 serial.c

📁 内核是系统的心脏
💻 C
📖 第 1 页 / 共 4 页
字号:
			if (recheck_count > 16)
				printk("recheck_count = %d\n", recheck_count);
#endif
		}
#ifdef ISR_HACK
		serial_outp(info, UART_IER, 0);
		serial_out(info, UART_IER, info->IER);
#endif
		
		info = info->next_port;
		if (!info && !done) {
			info = IRQ_ports[irq];
			done = 1;
			if (pass_number++ > 64)
				break; 		/* Prevent infinite loops */
		}
	}
	if ((info = IRQ_ports[irq]) != NULL) {
#ifdef 0
		do {
			serial_outp(info, UART_IER, 0);
			serial_out(info, UART_IER, info->IER);
			info = info->next_port;
		} while (info);
#endif			
		if (irq && !done_work)
			IRQ_timer[irq] = jiffies + 1500;
		else
			IRQ_timer[irq] = jiffies + IRQ_timeout[irq];
		IRQ_active |= 1 << irq;
	}
	figure_RS_timer();
}

/*
 * -------------------------------------------------------------------
 * Here ends the serial interrupt routines.
 * -------------------------------------------------------------------
 */

/*
 * This routine is called when we receive a break on a serial line.
 * It is executed out of the software interrupt routine.
 */
static inline void handle_rs_break(struct async_struct *info)
{
	if (info->flags & ASYNC_SAK)
		do_SAK(info->tty);
		
	if (!I_IGNBRK(info->tty) && I_BRKINT(info->tty)) {
		flush_input(info->tty);
		flush_output(info->tty);
		if (info->tty->pgrp > 0)
			kill_pg(info->tty->pgrp, SIGINT,1);
	}
}

/*
 * This routine is used to handle the "bottom half" processing for the
 * serial driver, known also the "software interrupt" processing.
 * This processing is done at the kernel interrupt level, after the
 * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This
 * is where time-consuming activities which can not be done in the
 * interrupt driver proper are done; the interrupt driver schedules
 * them using rs_sched_event(), and they get done here.
 */
static void do_softint(void *unused)
{
	int			i;
	struct async_struct	*info;
	
	for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
		if (clear_bit(i, rs_event)) {
			if (!info->tty)	
				continue;
			if (clear_bit(RS_EVENT_READ_PROCESS, &info->event)) {
				TTY_READ_FLUSH(info->tty);
			}
			if (clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
				wake_up_interruptible(&info->tty->write_q.proc_list);
			}
			if (clear_bit(RS_EVENT_HANGUP, &info->event)) {
				tty_hangup(info->tty);
				wake_up_interruptible(&info->open_wait);
				info->flags &= ~(ASYNC_NORMAL_ACTIVE|
						 ASYNC_CALLOUT_ACTIVE);
			}
			if (clear_bit(RS_EVENT_BREAK, &info->event))
				handle_rs_break(info);
			if (clear_bit(RS_EVENT_OPEN_WAKEUP, &info->event)) {
				wake_up_interruptible(&info->open_wait);
			}
		}
	}
}

/*
 * This subroutine is called when the RS_TIMER goes off.  It is used
 * by the serial driver to run the rs_interrupt routine at certain
 * intervals, either because a serial interrupt might have been lost,
 * or because (in the case of IRQ=0) the serial port does not have an
 * interrupt, and is being checked only via the timer interrupts.
 */
static void rs_timer(void)
{
	int	i, mask;
	int	timeout = 0;

	for (i = 0, mask = 1; mask <= IRQ_active; i++, mask <<= 1) {
		if ((mask & IRQ_active) && (IRQ_timer[i] <= jiffies)) {
			IRQ_active &= ~mask;
			cli();
#ifdef SERIAL_DEBUG_TIMER
			printk("rs_timer: rs_interrupt(%d)...", i);
#endif
			rs_interrupt(i);
			sti();
		}
		if (mask & IRQ_active) {
			if (!timeout || (IRQ_timer[i] < timeout))
				timeout = IRQ_timer[i];
		}
	}
	if (timeout) {
		timer_table[RS_TIMER].expires = timeout;
		timer_active |= 1 << RS_TIMER;
	}
}

/*
 * ---------------------------------------------------------------
 * Low level utility subroutines for the serial driver:  routines to
 * figure out the appropriate timeout for an interrupt chain, routines
 * to initialize and startup a serial port, and routines to shutdown a
 * serial port.  Useful stuff like that.
 * ---------------------------------------------------------------
 */

/*
 * Grab all interrupts in preparation for doing an automatic irq
 * detection.  dontgrab is a mask of irq's _not_ to grab.  Returns a
 * mask of irq's which were grabbed and should therefore be freed
 * using free_all_interrupts().
 */
static int grab_all_interrupts(int dontgrab)
{
	int 			irq_lines = 0;
	int			i, mask;
	struct sigaction 	sa;
	
	sa.sa_handler = rs_probe;
	sa.sa_flags = (SA_INTERRUPT);
	sa.sa_mask = 0;
	sa.sa_restorer = NULL;
	
	for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
		if (!(mask & dontgrab) && !irqaction(i, &sa)) {
			irq_lines |= mask;
		}
	}
	return irq_lines;
}

/*
 * Release all interrupts grabbed by grab_all_interrupts
 */
static void free_all_interrupts(int irq_lines)
{
	int	i;
	
	for (i = 0; i < 16; i++) {
		if (irq_lines & (1 << i))
			free_irq(i);
	}
}

/*
 * This routine figures out the correct timeout for a particular IRQ.
 * It uses the smallest timeout of all of the serial ports in a
 * particular interrupt chain.
 */
static void figure_IRQ_timeout(int irq)
{
	struct	async_struct	*info;
	int	timeout = 6000;	/* 60 seconds === a long time :-) */

	info = IRQ_ports[irq];
	if (!info) {
		IRQ_timeout[irq] = 6000;
		return;
	}
	while (info) {
		if (info->timeout < timeout)
			timeout = info->timeout;
		info = info->next_port;
	}
	if (!irq)
		timeout = timeout / 2;
	IRQ_timeout[irq] = timeout ? timeout : 1;
}

static int startup(struct async_struct * info, int get_irq)
{
	unsigned short ICP;
	unsigned long flags;
	struct sigaction	sa;
	int			retval;

	if (info->flags & ASYNC_INITIALIZED)
		return 0;

	if (!info->port || !info->type) {
		if (info->tty)
			set_bit(TTY_IO_ERROR, &info->tty->flags);
		return 0;
	}

	save_flags(flags); cli();

#ifdef SERIAL_DEBUG_OPEN
	printk("starting up ttys%d (irq %d)...", info->line, info->irq);
#endif

	/*
	 * Allocate the IRQ if necessary
	 */
	if (get_irq && info->irq && !IRQ_ports[info->irq]) {
		sa.sa_handler = rs_interrupt;
		sa.sa_flags = (SA_INTERRUPT);
		sa.sa_mask = 0;
		sa.sa_restorer = NULL;
		retval = irqaction(info->irq,&sa);
		if (retval) {
			restore_flags(flags);
			return retval;
		}
	}

	/*
	 * Clear the FIFO buffers and disable them
	 * (they will be reenabled in change_speed())
	 */
	if (info->type == PORT_16550A) {
		serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
					     UART_FCR_CLEAR_XMIT));
		info->xmit_fifo_size = 16;
	} else
		info->xmit_fifo_size = 1;

	/*
	 * Clear the interrupt registers.
	 */
	(void)serial_inp(info, UART_LSR);
	(void)serial_inp(info, UART_RX);
	(void)serial_inp(info, UART_IIR);
	(void)serial_inp(info, UART_MSR);

	/*
	 * Now, initialize the UART 
	 */
	serial_outp(info, UART_LCR, UART_LCR_WLEN8);	/* reset DLAB */
	if (info->flags & ASYNC_FOURPORT) 
		serial_outp(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS);
	else
		serial_outp(info, UART_MCR,
			    UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
	
	/*
	 * Finally, enable interrupts
	 */
#ifdef ISR_HACK
	info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
	serial_outp(info, UART_IER, info->IER);	/* enable interrupts */
#else
	info->IER = (UART_IER_MSI | UART_IER_RLSI |
		     UART_IER_THRI | UART_IER_RDI);
	serial_outp(info, UART_IER, info->IER);	/* enable all intrs */
#endif
	if (info->flags & ASYNC_FOURPORT) {
		/* Enable interrupts on the AST Fourport board */
		ICP = (info->port & 0xFE0) | 0x01F;
		outb_p(0x80, ICP);
		(void) inb_p(ICP);
	}

	/*
	 * And clear the interrupt registers again for luck.
	 */
	(void)serial_inp(info, UART_LSR);
	(void)serial_inp(info, UART_RX);
	(void)serial_inp(info, UART_IIR);
	(void)serial_inp(info, UART_MSR);

	if (info->tty)
		clear_bit(TTY_IO_ERROR, &info->tty->flags);
	/*
	 * Set up parity check flag
	 */
	if (info->tty && info->tty->termios && I_INPCK(info->tty))
		info->read_status_mask = (UART_LSR_OE | UART_LSR_BI |
					  UART_LSR_FE | UART_LSR_PE);
	else
		info->read_status_mask = (UART_LSR_OE | UART_LSR_BI |
					  UART_LSR_FE);

	/*
	 * Insert serial port into IRQ chain.
	 */
	info->prev_port = 0;
	info->next_port = IRQ_ports[info->irq];
	if (info->next_port)
		info->next_port->prev_port = info;
	IRQ_ports[info->irq] = info;
	figure_IRQ_timeout(info->irq);

	/*
	 * Set up serial timers...
	 */
	IRQ_active |= 1 << info->irq;
	figure_RS_timer();

	/*
	 * and set the speed of the serial port
	 */
	change_speed(info->line);

	info->flags |= ASYNC_INITIALIZED;
	restore_flags(flags);
	return 0;
}

/*
 * This routine will shutdown a serial port; interrupts are disabled, and
 * DTR is dropped if the hangup on close termio flag is on.
 */
static void shutdown(struct async_struct * info, int do_free_irq)
{
	unsigned long flags;

	if (!(info->flags & ASYNC_INITIALIZED))
		return;

#ifdef SERIAL_DEBUG_OPEN
	printk("Shutting down serial port %d (irq %d)....", info->line,
	       info->irq);
#endif
	
	save_flags(flags); cli(); /* Disable interrupts */
	
	/*
	 * First unlink the serial port from the IRQ chain...
	 */
	if (info->next_port)
		info->next_port->prev_port = info->prev_port;
	if (info->prev_port)
		info->prev_port->next_port = info->next_port;
	else
		IRQ_ports[info->irq] = info->next_port;
	figure_IRQ_timeout(info->irq);
	
	/*
	 * Free the IRQ, if necessary
	 */
	if (do_free_irq && info->irq && !IRQ_ports[info->irq])
		free_irq(info->irq);
	
	info->IER = 0;
	serial_outp(info, UART_IER, 0x00);	/* disable all intrs */
	if (info->flags & ASYNC_FOURPORT) {
		/* reset interrupts on the AST Fourport board */
		(void) inb((info->port & 0xFE0) | 0x01F);
	}
	if (info->tty && !(info->tty->termios->c_cflag & HUPCL))
		serial_outp(info, UART_MCR, UART_MCR_DTR);
	else
		/* reset DTR,RTS,OUT_2 */		
		serial_outp(info, UART_MCR, 0x00);

	/* disable FIFO's */	
	serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
				     UART_FCR_CLEAR_XMIT));
	(void)serial_in(info, UART_RX);    /* read data port to reset things */
	
	if (info->tty)
		set_bit(TTY_IO_ERROR, &info->tty->flags);
	
	info->flags &= ~ASYNC_INITIALIZED;
	restore_flags(flags);
}

/*
 * This routine is called to set the UART divisor registers to match
 * the specified baud rate for a serial port.
 */
static void change_speed(unsigned int line)
{
	struct async_struct * info;
	unsigned short port;
	int	quot = 0;
	unsigned cflag,cval,mcr,fcr;
	int	i;

	if (line >= NR_PORTS)
		return;
	info = rs_table + line;
	if (!info->tty || !info->tty->termios)
		return;
	cflag = info->tty->termios->c_cflag;
	if (!(port = info->port))
		return;
	i = cflag & CBAUD;
	if (i == 15) {
		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
			i += 1;
		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
			i += 2;
		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
			quot = info->custom_divisor;
	}
	if (quot) {
		info->timeout = ((info->xmit_fifo_size*HZ*15*quot) /
				 info->baud_base) + 2;
	} else if (baud_table[i] == 134) {
		quot = (2*info->baud_base / 269);
		info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2;
	} else if (baud_table[i]) {
		quot = info->baud_base / baud_table[i];
		info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2;
	} else {
		quot = 0;
		info->timeout = 0;
	}
	cli();
	mcr = serial_in(info, UART_MCR);
	if (quot) {
		serial_out(info, UART_MCR, mcr | UART_MCR_DTR);
	} else {
		serial_out(info, UART_MCR, mcr & ~UART_MCR_DTR);
		sti();
		return;
	}
	sti();
	/* byte size and parity */
	cval = cflag & (CSIZE | CSTOPB);
	cval >>= 4;
	if (cflag & PARENB)
		cval |= UART_LCR_PARITY;
	if (!(cflag & PARODD))
		cval |= UART_LCR_EPAR;
	if (info->type == PORT_16550A) {
		if ((info->baud_base / quot) < 2400)
			fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
		else
			fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
	} else
		fcr = 0;
	
	cli();
	serial_outp(info, UART_LCR, cval | UART_LCR_DLAB);	/* set DLAB */
	serial_outp(info, UART_DLL, quot & 0xff);	/* LS of divisor */
	serial_outp(info, UART_DLM, quot >> 8);		/* MS of divisor */
	serial_outp(info, UART_LCR, cval);		/* reset DLAB */
	serial_outp(info, UART_FCR, fcr); 	/* set fcr */
	sti();
}

/*
 * ------------------------------------------------------------
 * rs_write() and friends
 * ------------------------------------------------------------
 */

/*
 * This routine is used by rs_write to restart transmitter interrupts,
 * which are disabled after we have a transmitter interrupt which went
 * unacknowledged because we had run out of data to transmit.
 * 
 * Note: this subroutine must be called with the interrupts *off*
 */
static inline void restart_port(struct async_struct *info)
{
	struct tty_queue * queue;
	int head, tail, count;
	
	if (!info)
		return;

	if (serial_inp(info, UART_LSR) & UART_LSR_THRE) {
		if (info->x_char) {
			serial_outp(info, UART_TX, info->x_char);
			info->x_char = 0;
		} else {
			queue = &info->tty->write_q;
			head = queue->head;
			tail = queue->tail;
			count = info->xmit_fifo_size;
			while (count--) {
				if (tail == head)
					break;
				serial_outp(info, UART_TX, queue->buf[tail++]);
				tail &= TTY_BUF_SIZE-1;
			}
			queue->tail = tail;
		}
	}
}	

/*
 * This routine gets called when tty_write has put something into
 * the write_queue.  
 */
void rs_write(struct tty_struct * tty)
{
	struct async_struct *info;

	if (!tty || tty->stopped || tty->hw_stopped)
		return;
	info = rs_table + DEV_TO_SL(tty->line);
	cli();

⌨️ 快捷键说明

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