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

📄 serial.c

📁 基于16450/16550A的多串口驱动,估计很多人在找吧
💻 C
📖 第 1 页 / 共 5 页
字号:
#endif/* * This routine is used by the interrupt handler to schedule * processing in the software interrupt portion of the driver. */static _INLINE_ void rs_sched_event(struct async_struct *info,				  int event){	info->event |= 1 << event;	queue_task_irq_off(&info->tqueue, &tq_serial);	mark_bh(SERIAL_BH);}static _INLINE_ void receive_chars(struct async_struct *info,				 int *status){	struct tty_struct *tty = info->tty;	unsigned char ch;	int ignored = 0;#if defined(CONFIG_LEDMAN)	ledman_cmd(LEDMAN_CMD_SET, info->line ? LEDMAN_COM2_RX : LEDMAN_COM1_RX);#endif	do {		ch = serial_inp(info, UART_RX);		if (*status & info->ignore_status_mask) {			if (++ignored > 100)				break;			goto ignore_char;		}		if (tty->flip.count >= TTY_FLIPBUF_SIZE)			break;		tty->flip.count++;		if (*status & (UART_LSR_BI)) {#ifdef SERIAL_DEBUG_INTR			printk("handling break....");#endif			*tty->flip.flag_buf_ptr++ = TTY_BREAK;			if (info->flags & ASYNC_SAK)				do_SAK(tty);		} else if (*status & UART_LSR_PE)			*tty->flip.flag_buf_ptr++ = TTY_PARITY;		else if (*status & UART_LSR_FE)			*tty->flip.flag_buf_ptr++ = TTY_FRAME;		else if (*status & UART_LSR_OE) 			*tty->flip.flag_buf_ptr++ = TTY_OVERRUN;		else			*tty->flip.flag_buf_ptr++ = 0;		*tty->flip.char_buf_ptr++ = ch;	ignore_char:		*status = serial_inp(info, UART_LSR) & info->read_status_mask;	} while (*status & UART_LSR_DR);	queue_task_irq_off(&tty->flip.tqueue, &tq_timer);#ifdef SERIAL_DEBUG_INTR	printk("DR...");#endif}static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done){	int count;	#if defined(CONFIG_LEDMAN)	ledman_cmd(LEDMAN_CMD_SET, info->line ? LEDMAN_COM2_TX : LEDMAN_COM1_TX);#endif	if (info->x_char) {		serial_outp(info, UART_TX, info->x_char);		info->x_char = 0;		if (intr_done)			*intr_done = 0;		return;	}	if ((info->xmit_cnt <= 0) || info->tty->stopped ||	    info->tty->hw_stopped) {		info->IER &= ~UART_IER_THRI;		serial_out(info, UART_IER, info->IER);		return;	}		count = info->xmit_fifo_size;	do {		serial_out(info, UART_TX, info->xmit_buf[info->xmit_tail++]);		info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);		if (--info->xmit_cnt <= 0)			break;	} while (--count > 0);		if (info->xmit_cnt < WAKEUP_CHARS)		rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);#ifdef SERIAL_DEBUG_INTR	printk("THRE...");#endif	if (intr_done)		*intr_done = 0;	if (info->xmit_cnt <= 0) {		info->IER &= ~UART_IER_THRI;		serial_out(info, UART_IER, info->IER);	}}static _INLINE_ void check_modem_status(struct async_struct *info){	int	status;		status = serial_in(info, UART_MSR);	if (status & UART_MSR_ANY_DELTA) {		/* update input line counters */		if (status & UART_MSR_TERI)			info->icount.rng++;		if (status & UART_MSR_DDSR)			info->icount.dsr++;		if (status & UART_MSR_DDCD) {			info->icount.dcd++;#ifdef CONFIG_LEDMAN			ledman_cmd(					(status & UART_MSR_DCD) ? LEDMAN_CMD_ON : LEDMAN_CMD_OFF,					info->line ? LEDMAN_COM2_DCD : LEDMAN_COM1_DCD);#endif		}		if (status & UART_MSR_DCTS)			info->icount.cts++;		wake_up_interruptible(&info->delta_msr_wait);	}	if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) {#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))		printk("ttys%d CD now %s...", info->line,		       (status & UART_MSR_DCD) ? "on" : "off");#endif				if (status & UART_MSR_DCD)			wake_up_interruptible(&info->open_wait);		else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&			   (info->flags & ASYNC_CALLOUT_NOHUP))) {#ifdef SERIAL_DEBUG_OPEN			printk("scheduling hangup...");#endif			queue_task_irq_off(&info->tqueue_hangup,					   &tq_scheduler);		}	}	if (info->flags & ASYNC_CTS_FLOW) {		if (info->tty->hw_stopped) {			if (status & UART_MSR_CTS) {#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))				printk("CTS tx start...");#endif				info->tty->hw_stopped = 0;				info->IER |= UART_IER_THRI;				serial_out(info, UART_IER, info->IER);				rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);				return;			}		} else {			if (!(status & UART_MSR_CTS)) {#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))				printk("CTS tx stop...");#endif				info->tty->hw_stopped = 1;				info->IER &= ~UART_IER_THRI;				serial_out(info, UART_IER, info->IER);			}		}	}}/* * This is the serial driver's generic interrupt routine */static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs){	int status;	struct async_struct * info;	int pass_counter = 0;	struct async_struct *end_mark = 0;	int first_multi = 0;	struct rs_multiport_struct *multi;#ifdef SERIAL_DEBUG_INTR	printk("rs_interrupt(%d)...", irq);#endif		info = IRQ_ports[IRQMASK(irq)];	if (!info)		return;		multi = &rs_multiport[IRQMASK(irq)];	if (multi->port_monitor)		first_multi = inb(multi->port_monitor);	do {		if (!info->tty ||		    (serial_in(info, UART_IIR) & UART_IIR_NO_INT)) {			if (!end_mark)				end_mark = info;			goto next;		}		end_mark = 0;		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) {			info = IRQ_ports[IRQMASK(irq)];			if (pass_counter++ > RS_ISR_PASS_LIMIT) {#if 0				printk("rs loop break\n");#endif				break; 	/* Prevent infinite loops */			}			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[IRQMASK(irq)];	if (!info || !info->tty)		return;	multi = &rs_multiport[IRQMASK(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[IRQMASK(irq)];	if (!info)		return;	multi = &rs_multiport[IRQMASK(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[IRQMASK(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=1; i < 16; i++) {			info = IRQ_ports[IRQMASK(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[IRQMASK(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[IRQMASK(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.

⌨️ 快捷键说明

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