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

📄 core.c

📁 IXP425 平台下嵌入式LINUX的串口的驱动程序
💻 C
📖 第 1 页 / 共 4 页
字号:
/* *  linux/drivers/char/serial_core.c * *  Driver core for serial ports * *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. * *  Copyright 1999 ARM Limited *  Copyright (C) 2000-2001 Deep Blue Solutions Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA * *  $Id: core.c,v 1.20.2.5 2002/03/13 15:22:26 rmk Exp $ * */#include <linux/config.h>#include <linux/module.h>#include <linux/errno.h>#include <linux/signal.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/major.h>#include <linux/string.h>#include <linux/fcntl.h>#include <linux/ptrace.h>#include <linux/ioport.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/circ_buf.h>#include <linux/console.h>#include <linux/sysrq.h>#include <linux/pm.h>#include <linux/serial_core.h>#include <asm/system.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/uaccess.h>#include <asm/bitops.h>#undef	DEBUG#ifndef CONFIG_PM#define pm_access(pm)		do { } while (0)#define pm_unregister(pm)	do { } while (0)#endif/* * tmp_buf is used as a temporary buffer by serial_write.  We need to * lock it in case the copy_from_user blocks while swapping in a page, * and some other program tries to do a serial write at the same time. * Since the lock will only come under contention when the system is * swapping and available memory is low, it makes sense to share one * buffer across all the serial ports, since it significantly saves * memory if large numbers of serial ports are open. */static u_char *tmp_buf;static DECLARE_MUTEX(tmp_buf_sem);/* * This is used to lock changes in serial line configuration. */static DECLARE_MUTEX(port_sem);#define HIGH_BITS_OFFSET	((sizeof(long)-sizeof(int))*8)static void uart_change_speed(struct uart_info *info, struct termios *old_termios);static void uart_wait_until_sent(struct tty_struct *tty, int timeout);/* * This routine is used by the interrupt handler to schedule processing in * the software interrupt portion of the driver.  It is expected that * interrupts will be disabled (and so the tasklet will be prevented * from running (CHECK)). */void uart_event(struct uart_info *info, int event){	info->event |= 1 << event;	tasklet_schedule(&info->tlet);}static void uart_stop(struct tty_struct *tty){	struct uart_info *info = tty->driver_data;	unsigned long flags;	spin_lock_irqsave(&info->lock, flags);	info->ops->stop_tx(info->port, 1);	spin_unlock_irqrestore(&info->lock, flags);}static void __uart_start(struct tty_struct *tty){	struct uart_info *info = tty->driver_data;	if (info->xmit.head != info->xmit.tail && info->xmit.buf &&	    !tty->stopped && !tty->hw_stopped)		info->ops->start_tx(info->port, 1, 1);}static void uart_start(struct tty_struct *tty){	struct uart_info *info = tty->driver_data;	unsigned long flags;	pm_access(info->state->pm);	spin_lock_irqsave(&info->lock, flags);	__uart_start(tty);	spin_unlock_irqrestore(&info->lock, flags);}static void uart_tasklet_action(unsigned long data){	struct uart_info *info = (struct uart_info *)data;	struct tty_struct *tty;	tty = info->tty;	if (!tty || !test_and_clear_bit(EVT_WRITE_WAKEUP, &info->event))		return;	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&	    tty->ldisc.write_wakeup)		(tty->ldisc.write_wakeup)(tty);	wake_up_interruptible(&tty->write_wait);}static inline void uart_update_altspeed(struct uart_info *info){	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;}static int uart_startup(struct uart_info *info){	unsigned long flags;	unsigned long page;	int retval = 0;	page = get_zeroed_page(GFP_KERNEL);	if (!page)		return -ENOMEM;	save_flags(flags); cli();	if (info->flags & ASYNC_INITIALIZED) {		free_page(page);		goto errout;	}	if (info->port->type == PORT_UNKNOWN) {		if (info->tty)			set_bit(TTY_IO_ERROR, &info->tty->flags);		free_page(page);		goto errout;	}	if (info->xmit.buf)		free_page(page);	else		info->xmit.buf = (unsigned char *) page;	info->mctrl = 0;	retval = info->ops->startup(info->port, info);	if (retval) {		if (capable(CAP_SYS_ADMIN)) {			if (info->tty)				set_bit(TTY_IO_ERROR, &info->tty->flags);			retval = 0;		}		goto errout;	}	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)		uart_update_altspeed(info);	/*	 * and set the speed of the serial port	 */	uart_change_speed(info, NULL);	/*	 * Setup the RTS and DTR signals once the port	 * is open and ready to respond.	 */	if (info->tty->termios->c_cflag & CBAUD)		info->mctrl |= TIOCM_RTS | TIOCM_DTR;	info->ops->set_mctrl(info->port, info->mctrl);	info->flags |= ASYNC_INITIALIZED;	retval = 0;errout:	restore_flags(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 uart_shutdown(struct uart_info *info){	unsigned long flags;	if (!(info->flags & ASYNC_INITIALIZED))		return;	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 woken up	 */	wake_up_interruptible(&info->delta_msr_wait);	/*	 * Free the IRQ and disable the port	 */	info->ops->shutdown(info->port, info);	if (info->xmit.buf) {		unsigned long pg = (unsigned long) info->xmit.buf;		info->xmit.buf = NULL;		free_page(pg);	}	if (!info->tty || (info->tty->termios->c_cflag & HUPCL))		info->mctrl &= ~(TIOCM_DTR|TIOCM_RTS);	info->ops->set_mctrl(info->port, info->mctrl);	/* kill off our tasklet */	tasklet_kill(&info->tlet);	if (info->tty)		set_bit(TTY_IO_ERROR, &info->tty->flags);	info->flags &= ~ASYNC_INITIALIZED;	restore_flags(flags);}static inline u_int uart_calculate_quot(struct uart_info *info, u_int baud){	u_int quot;	/* Special case: B0 rate */	if (!baud)		baud = 9600;	/* Old HI/VHI/custom speed handling */	if (baud == 38400 &&	    ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))		quot = info->state->custom_divisor;	else		quot = info->port->uartclk / (16 * baud);	return quot;}static void uart_change_speed(struct uart_info *info, struct termios *old_termios){	struct uart_port *port = info->port;	u_int quot, baud, cflag, bits, try;	/*	 * If we have no tty, termios, or the port does not exist,	 * then we can't set the parameters for this port.	 */	if (!info->tty || !info->tty->termios ||	    info->port->type == PORT_UNKNOWN)		return;	cflag = info->tty->termios->c_cflag;	/* byte size and parity */	switch (cflag & CSIZE) {	case CS5: bits = 7;  break;	case CS6: bits = 8;  break;	case CS7: bits = 9;  break;	default:  bits = 10; break; // CS8	}	if (cflag & CSTOPB)		bits++;	if (cflag & PARENB)		bits++;	for (try = 0; try < 2; try ++) {		/* Determine divisor based on baud rate */		baud = tty_get_baud_rate(info->tty);		quot = uart_calculate_quot(info, baud);		if (quot)			break;		/*		 * Oops, the quotient was zero.  Try again with		 * the old baud rate if possible.		 */		info->tty->termios->c_cflag &= ~CBAUD;		if (old_termios) {			info->tty->termios->c_cflag |=				 (old_termios->c_cflag & CBAUD);			old_termios = NULL;			continue;		}		/*		 * As a last resort, if the quotient is zero,		 * default to 9600 bps		 */		info->tty->termios->c_cflag |= B9600;	}	info->timeout = (port->fifosize * HZ * bits * quot) /			 (port->uartclk / 16);	info->timeout += HZ/50;		/* Add .02 seconds of slop */	if (cflag & CRTSCTS)		info->flags |= ASYNC_CTS_FLOW;	else		info->flags &= ~ASYNC_CTS_FLOW;	if (cflag & CLOCAL)		info->flags &= ~ASYNC_CHECK_CD;	else		info->flags |= ASYNC_CHECK_CD;	/*	 * Set up parity check flag	 */#define RELEVENT_IFLAG(iflag)	((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))	pm_access(info->state->pm);	info->ops->change_speed(port, cflag, info->tty->termios->c_iflag, quot);}static void uart_put_char(struct tty_struct *tty, u_char ch){	struct uart_info *info = tty->driver_data;	unsigned long flags;	if (!tty || !info->xmit.buf)		return;	spin_lock_irqsave(&info->lock, flags);	if (CIRC_SPACE(info->xmit.head, info->xmit.tail, UART_XMIT_SIZE) != 0) {		info->xmit.buf[info->xmit.head] = ch;		info->xmit.head = (info->xmit.head + 1) & (UART_XMIT_SIZE - 1);	}	spin_unlock_irqrestore(&info->lock, flags);}static void uart_flush_chars(struct tty_struct *tty){	uart_start(tty);}static int uart_write(struct tty_struct *tty, int from_user,			  const u_char * buf, int count){	struct uart_info *info = tty->driver_data;	unsigned long flags;	int c, ret = 0;	if (!tty || !info->xmit.buf || !tmp_buf)		return 0;	if (from_user) {		down(&tmp_buf_sem);		while (1) {			int c1;			c = CIRC_SPACE_TO_END(info->xmit.head,					      info->xmit.tail,					      UART_XMIT_SIZE);			if (count < c)				c = count;			if (c <= 0)				break;			c -= copy_from_user(tmp_buf, buf, c);			if (!c) {				if (!ret)					ret = -EFAULT;				break;			}			spin_lock_irqsave(&info->lock, flags);			c1 = CIRC_SPACE_TO_END(info->xmit.head,					       info->xmit.tail,					       UART_XMIT_SIZE);			if (c1 < c)				c = c1;			memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c);			info->xmit.head = (info->xmit.head + c) &					  (UART_XMIT_SIZE - 1);			spin_unlock_irqrestore(&info->lock, flags);			buf += c;			count -= c;			ret += c;		}		up(&tmp_buf_sem);	} else {		spin_lock_irqsave(&info->lock, flags);		while (1) {			c = CIRC_SPACE_TO_END(info->xmit.head,					      info->xmit.tail,					      UART_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) &					  (UART_XMIT_SIZE - 1);			buf += c;			count -= c;			ret += c;		}		spin_unlock_irqrestore(&info->lock, flags);	}	uart_start(tty);	return ret;}static int uart_write_room(struct tty_struct *tty){	struct uart_info *info = tty->driver_data;	return CIRC_SPACE(info->xmit.head, info->xmit.tail, UART_XMIT_SIZE);}static int uart_chars_in_buffer(struct tty_struct *tty){	struct uart_info *info = tty->driver_data;	return CIRC_CNT(info->xmit.head, info->xmit.tail, UART_XMIT_SIZE);}static void uart_flush_buffer(struct tty_struct *tty){	struct uart_info *info = tty->driver_data;	unsigned long flags;#ifdef DEBUG	printk("uart_flush_buffer(%d) called\n",	       MINOR(tty->device) - tty->driver.minor_start);#endif	spin_lock_irqsave(&info->lock, flags);	info->xmit.head = info->xmit.tail = 0;	spin_unlock_irqrestore(&info->lock, flags);	wake_up_interruptible(&tty->write_wait);	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&	    tty->ldisc.write_wakeup)		(tty->ldisc.write_wakeup)(tty);}/* * This function is used to send a high-priority XON/XOFF character to * the device */static void uart_send_xchar(struct tty_struct *tty, char ch){	struct uart_info *info = tty->driver_data;	info->port->x_char = ch;	if (ch)		info->ops->start_tx(info->port, 1, 0);}static void uart_throttle(struct tty_struct *tty){	struct uart_info *info = tty->driver_data;	unsigned long flags;	if (I_IXOFF(tty))		uart_send_xchar(tty, STOP_CHAR(tty));	if (tty->termios->c_cflag & CRTSCTS) {		spin_lock_irqsave(&info->lock, flags);		info->mctrl &= ~TIOCM_RTS;		info->ops->set_mctrl(info->port, info->mctrl);		spin_unlock_irqrestore(&info->lock, flags);	}}static void uart_unthrottle(struct tty_struct *tty){	struct uart_info *info = (struct uart_info *) tty->driver_data;	unsigned long flags;	if (I_IXOFF(tty)) {		if (info->port->x_char)			info->port->x_char = 0;		else			uart_send_xchar(tty, START_CHAR(tty));	}	if (tty->termios->c_cflag & CRTSCTS) {		spin_lock_irqsave(&info->lock, flags);		info->mctrl |= TIOCM_RTS;		info->ops->set_mctrl(info->port, info->mctrl);		spin_unlock_irqrestore(&info->lock, flags);	}}static int uart_get_info(struct uart_info *info, struct serial_struct *retinfo){	struct uart_state *state = info->state;	struct uart_port *port = info->port;	struct serial_struct tmp;	memset(&tmp, 0, sizeof(tmp));	tmp.type	   = port->type;	tmp.line	   = port->line;	tmp.port	   = port->iobase;	if (HIGH_BITS_OFFSET)		tmp.port_high = port->iobase >> HIGH_BITS_OFFSET;	tmp.irq		   = port->irq;	tmp.flags	   = port->flags;	tmp.xmit_fifo_size = port->fifosize;	tmp.baud_base	   = port->uartclk / 16;	tmp.close_delay	   = state->close_delay;	tmp.closing_wait   = state->closing_wait;	tmp.custom_divisor = state->custom_divisor;	tmp.hub6	   = port->hub6;	tmp.io_type        = port->iotype;	tmp.iomem_reg_shift= port->regshift;	tmp.iomem_base     = (void *)port->mapbase;	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))		return -EFAULT;	return 0;}static int uart_set_info(struct uart_info *info,			 struct serial_struct *newinfo)

⌨️ 快捷键说明

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