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

📄 serial.c

📁 基于16450/16550A的多串口驱动,估计很多人在找吧
💻 C
📖 第 1 页 / 共 5 页
字号:
 * --------------------------------------------------------------- */#ifndef CONFIG_COLDFIRE/* * 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);	}}#endif /* CONFIG_COLDFIRE *//* * 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[IRQMASK(irq)];	if (!info) {		IRQ_timeout[IRQMASK(irq)] = 60*HZ;		return;	}	while (info) {		if (info->timeout < timeout)			timeout = info->timeout;		info = info->next_port;	}	if (!irq)		timeout = timeout / 2;	IRQ_timeout[IRQMASK(irq)] = timeout ? timeout : 1;}static int startup(struct async_struct * info){	unsigned int 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(flags); cli();	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;	}	#ifndef CONFIG_COLDFIRE	/*	 * Allocate the IRQ if necessary	 */	if (info->irq && (!IRQ_ports[IRQMASK(info->irq)] ||			  !IRQ_ports[IRQMASK(info->irq)]->next_port)) {		if (IRQ_ports[IRQMASK(info->irq)]) {			free_irq(info->irq, NULL);			if (rs_multiport[IRQMASK(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;		}	}#endif	/*	 * 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[IRQMASK(info->irq)];	if (info->next_port)		info->next_port->prev_port = info;	IRQ_ports[IRQMASK(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		save_flags(flags); cli(); /* Disable interrupts */	/*	 * clear delta_msr_wait queue to avoid mem leaks: we may free the irq	 * here so the queue might never be waken up	 */	wake_up_interruptible(&info->delta_msr_wait);		/*	 * 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[IRQMASK(info->irq)] = info->next_port;	figure_IRQ_timeout(info->irq);	#ifndef CONFIG_COLDFIRE	/*	 * Free the IRQ, if necessary	 */	if (info->irq && (!IRQ_ports[IRQMASK(info->irq)] ||			  !IRQ_ports[IRQMASK(info->irq)]->next_port)) {		if (IRQ_ports[IRQMASK(info->irq)]) {			free_irq(info->irq, NULL);			retval = request_irq(info->irq, rs_interrupt_single,					     IRQ_T(info), "serial", NULL);						if (retval)				printk("serial shutdown: request_irq: error %d"				       "  Couldn't reacquire IRQ.\n", retval);		} else			free_irq(info->irq, NULL);	}#endif /* CONFIG_COLDFIRE */	if (info->xmit_buf) {		free_page((unsigned long) info->xmit_buf);		info->xmit_buf = 0;	}	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)) {		info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);		info->MCR_noint &= ~(UART_MCR_DTR|UART_MCR_RTS);	}	serial_outp(info, UART_MCR, info->MCR_noint);	/* 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);#ifdef CONFIG_LEDMAN	ledman_cmd(LEDMAN_CMD_OFF, info->line ? LEDMAN_COM2_DCD : LEDMAN_COM1_DCD);#endif		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(struct async_struct *info){	unsigned int port;	int	quot = 0;	unsigned cflag,cval,fcr;	int	i;	unsigned long flags;	if (!info->tty || !info->tty->termios)		return;	cflag = info->tty->termios->c_cflag;	if (!(port = info->port))		return;	i = cflag & CBAUD;	if (i & CBAUDEX) {		i &= ~CBAUDEX;		if (i < 1 || i > 2) 			info->tty->termios->c_cflag &= ~CBAUDEX;		else			i += 15;	}	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;	}	if (quot) {		info->MCR |= UART_MCR_DTR;		info->MCR_noint |= UART_MCR_DTR;		save_flags(flags); cli();		serial_out(info, UART_MCR, info->MCR);		restore_flags(flags);	} else {		info->MCR &= ~UART_MCR_DTR;		info->MCR_noint &= ~UART_MCR_DTR;		save_flags(flags); cli();		serial_out(info, UART_MCR, info->MCR);		restore_flags(flags);		return;	}	/* byte size and parity */	switch (cflag & CSIZE) {	      case CS5: cval = 0x00; break;	      case CS6: cval = 0x01; break;	      case CS7: cval = 0x02; break;	      case CS8: cval = 0x03; break;	      default:  cval = 0x00; break;	/* too keep GCC shut... */	}	if (cflag & CSTOPB) {		cval |= 0x04;	}	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 if (info->type == PORT_16650) {		/*		 * On the 16650, we disable the FIFOs altogether		 * because of a design bug in how the implement		 * things.  We could support it by completely changing		 * how we handle the interrupt driver, but not today....		 *		 * N.B.  Because there's no way to set a FIFO trigger		 * at 1 char, we'd probably disable at speed below		 * 2400 baud anyway...		 */		fcr = 0;	} else		fcr = 0;		/* CTS flow control flag and modem status interrupts */	info->IER &= ~UART_IER_MSI;	if (cflag & CRTSCTS) {		info->flags |= ASYNC_CTS_FLOW;		info->IER |= UART_IER_MSI;	} else		info->flags &= ~ASYNC_CTS_FLOW;	if (cflag & CLOCAL)		info->flags &= ~ASYNC_CHECK_CD;	else {		info->flags |= ASYNC_CHECK_CD;		info->IER |= UART_IER_MSI;	}	serial_out(info, UART_IER, info->IER);	/*	 * Set up parity check flag	 */#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))	info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;	if (I_INPCK(info->tty))		info->read_status_mask |= UART_LSR_FE | UART_LSR_PE;	if (I_BRKINT(info->tty) || I_PARMRK(info->tty))		info->read_status_mask |= UART_LSR_BI;		info->ignore_status_mask = 0;#if 0	/* This should be safe, but for some broken bits of hardware... */	if (I_IGNPAR(info->tty)) {		info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;		info->read_status_mask |= UART_LSR_PE | UART_LSR_FE;	}#endif	if (I_IGNBRK(info->tty)) {		info->ignore_status_mask |= UART_LSR_BI;		info->read_status_mask |= UART_LSR_BI;		/*		 * If we're ignore parity and break indicators, ignore 		 * overruns too.  (For real raw support).		 */		if (I_IGNPAR(info->tty)) {			info->ignore_status_mask |= UART_LSR_OE | \				UART_LSR_PE | UART_LSR_FE;			info->read_status_mask |= UART_LSR_OE | \				UART_LSR_PE | UART_LSR_FE;		}	}	save_flags(flags); 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 */	restore_flags(flags);}static void rs_put_char(struct tty_struct *tty, unsigned char ch){	struct async_struct *info = (struct async_struct *)tty->driver_data;	unsigned long flags;	if (serial_paranoia_check(info, tty->device, "rs_put_char"))		return;	if (!tty || !info->xmit_buf)		return;

⌨️ 快捷键说明

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