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

📄 serial.c

📁 arm平台上的uclinux系统全部源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
			}			continue;		}	} while (end_mark != info);	if (multi->port_monitor)		printk("rs port monitor (normal) irq %d: 0x%x, 0x%x\n",		       info->irq, first_multi, inb(multi->port_monitor));#ifdef SERIAL_DEBUG_INTR	printk("end.\n");#endif}/* * This is the serial driver's interrupt routine for a single port */static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs){	int status;	int pass_counter = 0;	int first_multi = 0;	struct async_struct * info;	struct rs_multiport_struct *multi;	#ifdef SERIAL_DEBUG_INTR	printk("rs_interrupt_single(%d)...", irq);#endif		info = IRQ_ports[irq];	if (!info || !info->tty)		return;	multi = &rs_multiport[irq];	if (multi->port_monitor)		first_multi = inb(multi->port_monitor);	do {		status = serial_inp(info, UART_LSR) & info->read_status_mask;#ifdef SERIAL_DEBUG_INTR		printk("status = %x...", status);#endif		if (status & UART_LSR_DR)			receive_chars(info, &status);		check_modem_status(info);		if (status & UART_LSR_THRE)			transmit_chars(info, 0);		if (pass_counter++ > RS_ISR_PASS_LIMIT) {#if 0			printk("rs_single loop break.\n");#endif			break;		}	} while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT));	info->last_active = jiffies;	if (multi->port_monitor)		printk("rs port monitor (single) irq %d: 0x%x, 0x%x\n",		       info->irq, first_multi, inb(multi->port_monitor));#ifdef SERIAL_DEBUG_INTR	printk("end.\n");#endif}/* * This is the serial driver's for multiport boards */static void rs_interrupt_multi(int irq, void *dev_id, struct pt_regs * regs){	int status;	struct async_struct * info;	int pass_counter = 0;	int first_multi= 0;	struct rs_multiport_struct *multi;#ifdef SERIAL_DEBUG_INTR	printk("rs_interrupt_multi(%d)...", irq);#endif		info = IRQ_ports[irq];	if (!info)		return;	multi = &rs_multiport[irq];	if (!multi->port1) {		/* Should never happen */		printk("rs_interrupt_multi: NULL port1!\n");		return;	}	if (multi->port_monitor)		first_multi = inb(multi->port_monitor);		while (1) {		if (!info->tty ||		    (serial_in(info, UART_IIR) & UART_IIR_NO_INT))			goto next;		info->last_active = jiffies;		status = serial_inp(info, UART_LSR) & info->read_status_mask;#ifdef SERIAL_DEBUG_INTR		printk("status = %x...", status);#endif		if (status & UART_LSR_DR)			receive_chars(info, &status);		check_modem_status(info);		if (status & UART_LSR_THRE)			transmit_chars(info, 0);	next:		info = info->next_port;		if (info)			continue;		info = IRQ_ports[irq];		if (pass_counter++ > RS_ISR_PASS_LIMIT) {#if 1			printk("rs_multi loop break\n");#endif			break; 	/* Prevent infinite loops */		}		if (multi->port_monitor)			printk("rs port monitor irq %d: 0x%x, 0x%x\n",			       info->irq, first_multi,			       inb(multi->port_monitor));		if ((inb(multi->port1) & multi->mask1) != multi->match1)			continue;		if (!multi->port2)			break;		if ((inb(multi->port2) & multi->mask2) != multi->match2)			continue;		if (!multi->port3)			break;		if ((inb(multi->port3) & multi->mask3) != multi->match3)			continue;		if (!multi->port4)			break;		if ((inb(multi->port4) & multi->mask4) == multi->match4)			continue;		break;	} #ifdef SERIAL_DEBUG_INTR	printk("end.\n");#endif}/* * ------------------------------------------------------------------- * Here ends the serial interrupt routines. * ------------------------------------------------------------------- *//* * 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_serial_bh(void){	run_task_queue(&tq_serial);}static void do_softint(void *private_){	struct async_struct	*info = (struct async_struct *) private_;	struct tty_struct	*tty;		tty = info->tty;	if (!tty)		return;	if (clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&		    tty->ldisc.write_wakeup)			(tty->ldisc.write_wakeup)(tty);		wake_up_interruptible(&tty->write_wait);	}}/* * This routine is called from the scheduler tqueue when the interrupt * routine has signalled that a hangup has occurred.  The path of * hangup processing is: * * 	serial interrupt routine -> (scheduler tqueue) -> * 	do_serial_hangup() -> tty->hangup() -> rs_hangup() *  */static void do_serial_hangup(void *private_){	struct async_struct	*info = (struct async_struct *) private_;	struct tty_struct	*tty;		tty = info->tty;	if (!tty)		return;	tty_hangup(tty);}/* * This subroutine is called when the RS_TIMER goes off.  It is used * by the serial driver to handle ports that do not have an interrupt * (irq=0).  This doesn't work very well for 16450's, but gives barely * passable results for a 16550A.  (Although at the expense of much * CPU overhead). */static void rs_timer(void){	static unsigned long last_strobe = 0;	struct async_struct *info;	unsigned int	i;	if ((jiffies - last_strobe) >= RS_STROBE_TIME) {		for (i=0; i < 32; i++) {			info = IRQ_ports[i];			if (!info)				continue;			cli();			if (info->next_port) {				do {					serial_out(info, UART_IER, 0);					info->IER |= UART_IER_THRI;					serial_out(info, UART_IER, info->IER);					info = info->next_port;				} while (info);				if (rs_multiport[i].port1)					rs_interrupt_multi(i, NULL, NULL);				else					rs_interrupt(i, NULL, NULL);			} else				rs_interrupt_single(i, NULL, NULL);			sti();		}	}	last_strobe = jiffies;	timer_table[RS_TIMER].expires = jiffies + RS_STROBE_TIME;	timer_active |= 1 << RS_TIMER;	if (IRQ_ports[0]) {		cli();		rs_interrupt(0, NULL, NULL);		sti();		timer_table[RS_TIMER].expires = jiffies + IRQ_timeout[0] - 2;	}}/* * --------------------------------------------------------------- * 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;		for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {		if (!(mask & dontgrab) && !request_irq(i, rs_probe, SA_INTERRUPT, "serial probe", NULL)) {			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, NULL);	}}/* * 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.  Now only used for IRQ 0.... */static void figure_IRQ_timeout(int irq){	struct	async_struct	*info;	int	timeout = 60*HZ;	/* 60 seconds === a long time :-) */	info = IRQ_ports[irq];	if (!info) {		IRQ_timeout[irq] = 60*HZ;		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){	unsigned short ICP;	unsigned long flags;	int	retval;	void (*handler)(int, void *, struct pt_regs *);	unsigned long page;	page = get_free_page(GFP_KERNEL);	if (!page)		return -ENOMEM;			save_flags_cli(flags);	if (info->flags & ASYNC_INITIALIZED) {		free_page(page);		restore_flags(flags);		return 0;	}	if (!info->port || !info->type) {		if (info->tty)			set_bit(TTY_IO_ERROR, &info->tty->flags);		free_page(page);		restore_flags(flags);		return 0;	}	if (info->xmit_buf)		free_page(page);	else		info->xmit_buf = (unsigned char *) page;#ifdef SERIAL_DEBUG_OPEN	printk("starting up ttys%d (irq %d)...", info->line, info->irq);#endif	/*	 * Clear the FIFO buffers and disable them	 * (they will be reenabled in change_speed())	 */	if (info->type == PORT_16650) {		serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |					     UART_FCR_CLEAR_XMIT));		info->xmit_fifo_size = 1; /* disabled for now */	} else 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;	/*	 * At this point there's no way the LSR could still be 0xFF;	 * if it is, then bail out, because there's likely no UART	 * here.	 */	if (serial_inp(info, UART_LSR) == 0xff) {		restore_flags(flags);		if (suser()) {			if (info->tty)				set_bit(TTY_IO_ERROR, &info->tty->flags);			return 0;		} else			return -ENODEV;	}		/*	 * Allocate the IRQ if necessary	 */	if (info->irq && (!IRQ_ports[info->irq] ||			  !IRQ_ports[info->irq]->next_port)) {		if (IRQ_ports[info->irq]) {			free_irq(info->irq, NULL);			if (rs_multiport[info->irq].port1)				handler = rs_interrupt_multi;			else				handler = rs_interrupt;		} else 			handler = rs_interrupt_single;		retval = request_irq(info->irq, handler, IRQ_T(info),				     "serial", NULL);		if (retval) {			restore_flags(flags);			if (suser()) {				if (info->tty)					set_bit(TTY_IO_ERROR,						&info->tty->flags);				return 0;			} else				return retval;		}	}	/*	 * Clear the interrupt registers.	 */     /* (void) serial_inp(info, UART_LSR); */   /* (see above) */	(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) {		info->MCR = UART_MCR_DTR | UART_MCR_RTS;		info->MCR_noint = UART_MCR_DTR | UART_MCR_OUT1;	} else {		info->MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;		info->MCR_noint = UART_MCR_DTR | UART_MCR_RTS;	}#if defined(__alpha__) && !defined(CONFIG_PCI)	info->MCR |= UART_MCR_OUT1 | UART_MCR_OUT2;	info->MCR_noint |= UART_MCR_OUT1 | UART_MCR_OUT2;#endif	if (info->irq == 0)		info->MCR = info->MCR_noint;	serial_outp(info, UART_MCR, info->MCR);		/*	 * Finally, enable interrupts	 */	info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;	serial_outp(info, UART_IER, info->IER);	/* enable interrupts */		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);	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;	/*	 * 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...	 */	timer_table[RS_TIMER].expires = jiffies + 2*HZ/100;	timer_active |= 1 << RS_TIMER;	/*	 * and set the speed of the serial port	 */	change_speed(info);	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){	unsigned long	flags;	int		retval;	if (!(info->flags & ASYNC_INITIALIZED))		return;#ifdef SERIAL_DEBUG_OPEN	printk("Shutting down serial port %d (irq %d)....", info->line,	       info->irq);#endif	

⌨️ 快捷键说明

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