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

📄 serial.c

📁 arm平台上的uclinux系统全部源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
	save_flags_cli(flags); /* 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[info->irq] = info->next_port;	figure_IRQ_timeout(info->irq);		/*	 * Free 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);			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);	}	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);		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 short port;	int	quot = 0;	unsigned cflag,cval,fcr;	int	i;	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;		cli();		serial_out(info, UART_MCR, info->MCR);		sti();	} else {		info->MCR &= ~UART_MCR_DTR;		info->MCR_noint &= ~UART_MCR_DTR;		cli();		serial_out(info, UART_MCR, info->MCR);		sti();		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;		}	}	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();}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;	save_flags_cli (flags);	if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {		restore_flags(flags);		return;	}	info->xmit_buf[info->xmit_head++] = ch;	info->xmit_head &= SERIAL_XMIT_SIZE-1;	info->xmit_cnt++;	restore_flags(flags);}static void rs_flush_chars(struct tty_struct *tty){	struct async_struct *info = (struct async_struct *)tty->driver_data;	unsigned long flags;					if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))		return;	if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||	    !info->xmit_buf)		return;	save_flags_cli (flags);	info->IER |= UART_IER_THRI;	serial_out(info, UART_IER, info->IER);	restore_flags(flags);}static int rs_write(struct tty_struct * tty, int from_user,		    const unsigned char *buf, int count){	int	c, total = 0;	struct async_struct *info = (struct async_struct *)tty->driver_data;	unsigned long flags;					if (serial_paranoia_check(info, tty->device, "rs_write"))		return 0;	if (!tty || !info->xmit_buf || !tmp_buf)		return 0;	    	if (from_user)		down(&tmp_buf_sem);	save_flags(flags);	while (1) {		cli();				c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,				   SERIAL_XMIT_SIZE - info->xmit_head));		if (c <= 0)			break;		if (from_user) {			memcpy_fromfs(tmp_buf, buf, c);			c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,				       SERIAL_XMIT_SIZE - info->xmit_head));			memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);		} else			memcpy(info->xmit_buf + info->xmit_head, buf, c);		info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);		info->xmit_cnt += c;		restore_flags(flags);		buf += c;		count -= c;		total += c;	}	if (from_user)		up(&tmp_buf_sem);	if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped &&	    !(info->IER & UART_IER_THRI)) {		info->IER |= UART_IER_THRI;		serial_out(info, UART_IER, info->IER);	}	restore_flags(flags);	return total;}static int rs_write_room(struct tty_struct *tty){	struct async_struct *info = (struct async_struct *)tty->driver_data;	int	ret;					if (serial_paranoia_check(info, tty->device, "rs_write_room"))		return 0;	ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;	if (ret < 0)		ret = 0;	return ret;}static int rs_chars_in_buffer(struct tty_struct *tty){	struct async_struct *info = (struct async_struct *)tty->driver_data;					if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer"))		return 0;	return info->xmit_cnt;}static void rs_flush_buffer(struct tty_struct *tty){	struct async_struct *info = (struct async_struct *)tty->driver_data;					if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))		return;	cli();	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;	sti();	wake_up_interruptible(&tty->write_wait);	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&	    tty->ldisc.write_wakeup)		(tty->ldisc.write_wakeup)(tty);}/* * ------------------------------------------------------------ * rs_throttle() *  * This routine is called by the upper-layer tty layer to signal that * incoming characters should be throttled. * ------------------------------------------------------------ */static void rs_throttle(struct tty_struct * tty){	struct async_struct *info = (struct async_struct *)tty->driver_data;#ifdef SERIAL_DEBUG_THROTTLE	char	buf[64];		printk("throttle %s: %d....\n", _tty_name(tty, buf),	       tty->ldisc.chars_in_buffer(tty));#endif	if (serial_paranoia_check(info, tty->device, "rs_throttle"))		return;		if (I_IXOFF(tty))		info->x_char = STOP_CHAR(tty);	info->MCR &= ~UART_MCR_RTS;	info->MCR_noint &= ~UART_MCR_RTS;	cli();	serial_out(info, UART_MCR, info->MCR);	sti();}static void rs_unthrottle(struct tty_struct * tty){	struct async_struct *info = (struct async_struct *)tty->driver_data;#ifdef SERIAL_DEBUG_THROTTLE	char	buf[64];		printk("unthrottle %s: %d....\n", _tty_name(tty, buf),	       tty->ldisc.chars_in_buffer(tty));#endif	if (serial_paranoia_check(info, tty->device, "rs_unthrottle"))		return;		if (I_IXOFF(tty)) {		if (info->x_char)			info->x_char = 0;		else			info->x_char = START_CHAR(tty);	}	info->MCR |= UART_MCR_RTS;	info->MCR_noint |= UART_MCR_RTS;	cli();	serial_out(info, UART_MCR, info->MCR);	sti();}/* * ------------------------------------------------------------ * rs_ioctl() and friends * ------------------------------------------------------------ */static int get_serial_info(struct async_struct * info,			   struct serial_struct * retinfo){	struct serial_struct tmp;  	if (!retinfo)		return -EFAULT;	memset(&tmp, 0, sizeof(tmp));	tmp.type = info->type;	tmp.line = info->line;	tmp.port = info->port;	tmp.irq = info->irq;	tmp.flags = info->flags;	tmp.baud_base = info->baud_base;	tmp.close_delay = info->close_delay;	tmp.closing_wait = info->closing_wait;	tmp.custom_divisor = info->custom_divisor;	tmp.hub6 = info->hub6;	memcpy_tofs(retinfo,&tmp,sizeof(*retinfo));	return 0;}static int set_serial_info(struct async_struct * info,			   struct serial_struct * new_info){	struct serial_struct new_serial;	struct async_struct old_info;	unsigned int		i,change_irq,change_port;	int 			retval = 0;	if (!new_info)		return -EFAULT;	memcpy_fromfs(&new_serial,new_info,sizeof(new_serial));	old_info = *info;	change_irq = new_serial.irq != info->irq;	change_port = (new_serial.port != info->port) || (new_serial.hub6 != info->hub6);	if (!suser()) {		if (change_irq || change_port ||		    (new_serial.baud_base != info->baud_base) ||		    (new_serial.type != info->type) ||		    (new_serial.close_delay != info->close_delay) ||		    ((new_serial.flags & ~ASYNC_USR_MASK) !=		     (info->flags & ~ASYNC_USR_MASK)))			return -EPERM;		info->flags = ((info->flags & ~ASYNC_USR_MASK) |			       (new_serial.flags & ASYNC_USR_MASK));		info->custom_divisor = new_serial.custom_divisor;		goto check_and_exit;	}#if 0	if (new_serial.irq == 2)		new_serial.irq = 9;	if ((new_serial.irq > 15) || (new_serial.port > 0xffff) ||	    (new_serial.type < PORT_UNKNOWN) || (new_serial.type > PORT_MAX)) {		return -EINVAL;	}#else	if ((new_serial.irq > 31) || (new_serial.type < PORT_UNKNOWN) ||		(new_serial.type > PORT_MAX)) {		return -EINVAL;	}#endif	/* Make sure address is not already in use */	if (new_serial.type) {		for (i = 0 ; i < NR_PORTS; i++)			if ((info != &rs_table[i]) &&			    (rs_table[i].port == new_serial.port) &&			    rs_table[i].type)				return -EADDRINUSE;	}	if ((change_port || change_irq) && (info->count > 1))		return -EBUSY;	/*	 * OK, past this point, all the error checking has been done.	 * At this point, we start making changes.....	 */	info->baud_base = new_serial.baud_base;	info->flags = ((info->flags & ~ASYNC_FLAGS) |			(new_serial.flags & ASYNC_FLAGS));	info->custom_divisor = new_serial.custom_divisor;	info->type = new_serial.type;	info->close_delay = new_serial.close_delay * HZ/100;	info->closing_wait = new_serial.closing_wait * HZ/100;	release_region(info->port,8);	if (change_port || change_irq) {		/*		 * We need to shutdown the serial port at the old		 * port/irq combination.		 */		shutdown(info);		info->irq = new_serial.irq;		info->port = new_serial.port;

⌨️ 快捷键说明

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