amiserial.c

来自「linux 内核源代码」· C语言 代码 · 共 2,129 行 · 第 1/4 页

C
2,129
字号
/* * --------------------------------------------------------------- * 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. * --------------------------------------------------------------- */static int startup(struct async_struct * info){	unsigned long flags;	int	retval=0;	unsigned long page;	page = get_zeroed_page(GFP_KERNEL);	if (!page)		return -ENOMEM;	local_irq_save(flags);	if (info->flags & ASYNC_INITIALIZED) {		free_page(page);		goto errout;	}	if (info->xmit.buf)		free_page(page);	else		info->xmit.buf = (unsigned char *) page;#ifdef SERIAL_DEBUG_OPEN	printk("starting up ttys%d ...", info->line);#endif	/* Clear anything in the input buffer */	custom.intreq = IF_RBF;	mb();	retval = request_irq(IRQ_AMIGA_VERTB, ser_vbl_int, 0, "serial status", info);	if (retval) {	  if (serial_isroot()) {	    if (info->tty)	      set_bit(TTY_IO_ERROR,		      &info->tty->flags);	    retval = 0;	  }	  goto errout;	}	/* enable both Rx and Tx interrupts */	custom.intena = IF_SETCLR | IF_RBF | IF_TBE;	mb();	info->IER = UART_IER_MSI;	/* remember current state of the DCD and CTS bits */	current_ctl_bits = ciab.pra & (SER_DCD | SER_CTS | SER_DSR);	IRQ_ports = info;	info->MCR = 0;	if (info->tty->termios->c_cflag & CBAUD)	  info->MCR = SER_DTR | SER_RTS;	rtsdtr_ctrl(info->MCR);	if (info->tty)		clear_bit(TTY_IO_ERROR, &info->tty->flags);	info->xmit.head = info->xmit.tail = 0;	/*	 * Set up the tty->alt_speed kludge	 */	if (info->tty) {		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)			info->tty->alt_speed = 57600;		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)			info->tty->alt_speed = 115200;		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)			info->tty->alt_speed = 230400;		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)			info->tty->alt_speed = 460800;	}	/*	 * and set the speed of the serial port	 */	change_speed(info, NULL);	info->flags |= ASYNC_INITIALIZED;	local_irq_restore(flags);	return 0;errout:	local_irq_restore(flags);	return retval;}/* * 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;	struct serial_state *state;	if (!(info->flags & ASYNC_INITIALIZED))		return;	state = info->state;#ifdef SERIAL_DEBUG_OPEN	printk("Shutting down serial port %d ....\n", info->line);#endif	local_irq_save(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);	IRQ_ports = NULL;	/*	 * Free the IRQ, if necessary	 */	free_irq(IRQ_AMIGA_VERTB, info);	if (info->xmit.buf) {		free_page((unsigned long) info->xmit.buf);		info->xmit.buf = NULL;	}	info->IER = 0;	custom.intena = IF_RBF | IF_TBE;	mb();	/* disable break condition */	custom.adkcon = AC_UARTBRK;	mb();	if (!info->tty || (info->tty->termios->c_cflag & HUPCL))		info->MCR &= ~(SER_DTR|SER_RTS);	rtsdtr_ctrl(info->MCR);	if (info->tty)		set_bit(TTY_IO_ERROR, &info->tty->flags);	info->flags &= ~ASYNC_INITIALIZED;	local_irq_restore(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,			 struct ktermios *old_termios){	int	quot = 0, baud_base, baud;	unsigned cflag, cval = 0;	int	bits;	unsigned long	flags;	if (!info->tty || !info->tty->termios)		return;	cflag = info->tty->termios->c_cflag;	/* Byte size is always 8 bits plus parity bit if requested */	cval = 3; bits = 10;	if (cflag & CSTOPB) {		cval |= 0x04;		bits++;	}	if (cflag & PARENB) {		cval |= UART_LCR_PARITY;		bits++;	}	if (!(cflag & PARODD))		cval |= UART_LCR_EPAR;#ifdef CMSPAR	if (cflag & CMSPAR)		cval |= UART_LCR_SPAR;#endif	/* Determine divisor based on baud rate */	baud = tty_get_baud_rate(info->tty);	if (!baud)		baud = 9600;	/* B0 transition handled in rs_set_termios */	baud_base = info->state->baud_base;	if (baud == 38400 &&	    ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))		quot = info->state->custom_divisor;	else {		if (baud == 134)			/* Special case since 134 is really 134.5 */			quot = (2*baud_base / 269);		else if (baud)			quot = baud_base / baud;	}	/* If the quotient is zero refuse the change */	if (!quot && old_termios) {		/* FIXME: Will need updating for new tty in the end */		info->tty->termios->c_cflag &= ~CBAUD;		info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD);		baud = tty_get_baud_rate(info->tty);		if (!baud)			baud = 9600;		if (baud == 38400 &&		    ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))			quot = info->state->custom_divisor;		else {			if (baud == 134)				/* Special case since 134 is really 134.5 */				quot = (2*baud_base / 269);			else if (baud)				quot = baud_base / baud;		}	}	/* As a last resort, if the quotient is zero, default to 9600 bps */	if (!quot)		quot = baud_base / 9600;	info->quot = quot;	info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base);	info->timeout += HZ/50;		/* Add .02 seconds of slop */	/* CTS flow control flag and modem status interrupts */	info->IER &= ~UART_IER_MSI;	if (info->flags & ASYNC_HARDPPS_CD)		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;	}	/* TBD:	 * Does clearing IER_MSI imply that we should disbale the VBL interrupt ?	 */	/*	 * Set up parity check flag	 */	info->read_status_mask = UART_LSR_OE | 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;	/*	 * Characters to ignore	 */	info->ignore_status_mask = 0;	if (I_IGNPAR(info->tty))		info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;	if (I_IGNBRK(info->tty)) {		info->ignore_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;	}	/*	 * !!! ignore all characters if CREAD is not set	 */	if ((cflag & CREAD) == 0)		info->ignore_status_mask |= UART_LSR_DR;	local_irq_save(flags);	{	  short serper;	/* Set up the baud rate */	  serper = quot - 1;	/* Enable or disable parity bit */	if(cval & UART_LCR_PARITY)	  serper |= (SERPER_PARENB);	custom.serper = serper;	mb();	}	info->LCR = cval;				/* Save LCR */	local_irq_restore(flags);}static void rs_put_char(struct tty_struct *tty, unsigned char ch){	struct async_struct *info;	unsigned long flags;	if (!tty)		return;	info = tty->driver_data;	if (serial_paranoia_check(info, tty->name, "rs_put_char"))		return;	if (!info->xmit.buf)		return;	local_irq_save(flags);	if (CIRC_SPACE(info->xmit.head,		       info->xmit.tail,		       SERIAL_XMIT_SIZE) == 0) {		local_irq_restore(flags);		return;	}	info->xmit.buf[info->xmit.head++] = ch;	info->xmit.head &= SERIAL_XMIT_SIZE-1;	local_irq_restore(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->name, "rs_flush_chars"))		return;	if (info->xmit.head == info->xmit.tail	    || tty->stopped	    || tty->hw_stopped	    || !info->xmit.buf)		return;	local_irq_save(flags);	info->IER |= UART_IER_THRI;	custom.intena = IF_SETCLR | IF_TBE;	mb();	/* set a pending Tx Interrupt, transmitter should restart now */	custom.intreq = IF_SETCLR | IF_TBE;	mb();	local_irq_restore(flags);}static int rs_write(struct tty_struct * tty, const unsigned char *buf, int count){	int	c, ret = 0;	struct async_struct *info;	unsigned long flags;	if (!tty)		return 0;	info = tty->driver_data;	if (serial_paranoia_check(info, tty->name, "rs_write"))		return 0;	if (!info->xmit.buf)		return 0;	local_irq_save(flags);	while (1) {		c = CIRC_SPACE_TO_END(info->xmit.head,				      info->xmit.tail,				      SERIAL_XMIT_SIZE);		if (count < c)			c = count;		if (c <= 0) {			break;		}		memcpy(info->xmit.buf + info->xmit.head, buf, c);		info->xmit.head = ((info->xmit.head + c) &				   (SERIAL_XMIT_SIZE-1));		buf += c;		count -= c;		ret += c;	}	local_irq_restore(flags);	if (info->xmit.head != info->xmit.tail	    && !tty->stopped	    && !tty->hw_stopped	    && !(info->IER & UART_IER_THRI)) {		info->IER |= UART_IER_THRI;		local_irq_disable();		custom.intena = IF_SETCLR | IF_TBE;		mb();		/* set a pending Tx Interrupt, transmitter should restart now */		custom.intreq = IF_SETCLR | IF_TBE;		mb();		local_irq_restore(flags);	}	return ret;}static int rs_write_room(struct tty_struct *tty){	struct async_struct *info = (struct async_struct *)tty->driver_data;	if (serial_paranoia_check(info, tty->name, "rs_write_room"))		return 0;	return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);}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->name, "rs_chars_in_buffer"))		return 0;	return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);}static void rs_flush_buffer(struct tty_struct *tty){	struct async_struct *info = (struct async_struct *)tty->driver_data;	unsigned long flags;	if (serial_paranoia_check(info, tty->name, "rs_flush_buffer"))		return;	local_irq_save(flags);	info->xmit.head = info->xmit.tail = 0;	local_irq_restore(flags);	tty_wakeup(tty);}/* * This function is used to send a high-priority XON/XOFF character to * the device */static void rs_send_xchar(struct tty_struct *tty, char ch){	struct async_struct *info = (struct async_struct *)tty->driver_data;        unsigned long flags;	if (serial_paranoia_check(info, tty->name, "rs_send_char"))		return;	info->x_char = ch;	if (ch) {		/* Make sure transmit interrupts are on */	        /* Check this ! */	        local_irq_save(flags);		if(!(custom.intenar & IF_TBE)) {		    custom.intena = IF_SETCLR | IF_TBE;		    mb();		    /* set a pending Tx Interrupt, transmitter should restart now */		    custom.intreq = IF_SETCLR | IF_TBE;		    mb();		}		local_irq_restore(flags);		info->IER |= UART_IER_THRI;	}}/* * ------------------------------------------------------------ * 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;	unsigned long flags;#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->name, "rs_throttle"))		return;	if (I_IXOFF(tty))		rs_send_xchar(tty, STOP_CHAR(tty));	if (tty->termios->c_cflag & CRTSCTS)		info->MCR &= ~SER_RTS;	local_irq_save(flags);	rtsdtr_ctrl(info->MCR);	local_irq_restore(flags);}static void rs_unthrottle(struct tty_struct * tty){	struct async_struct *info = (struct async_struct *)tty->driver_data;	unsigned long flags;#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->name, "rs_unthrottle"))		return;	if (I_IXOFF(tty)) {		if (info->x_char)			info->x_char = 0;		else			rs_send_xchar(tty, START_CHAR(tty));	}	if (tty->termios->c_cflag & CRTSCTS)		info->MCR |= SER_RTS;	local_irq_save(flags);	rtsdtr_ctrl(info->MCR);	local_irq_restore(flags);}/* * ------------------------------------------------------------ * rs_ioctl() and friends * ------------------------------------------------------------ */

⌨️ 快捷键说明

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