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

📄 serial_core.c

📁 宋宝华的《Linux设备驱动开发详解》第一版的源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
			tty->hw_stopped = 1;			state->port->ops->stop_tx(state->port);		}		spin_unlock_irqrestore(&state->port->lock, flags);	}#if 0	/*	 * No need to wake up processes in open wait, since they	 * sample the CLOCAL flag once, and don't recheck it.	 * XXX  It's not clear whether the current behavior is correct	 * or not.  Hence, this may change.....	 */	if (!(old_termios->c_cflag & CLOCAL) &&	    (tty->termios->c_cflag & CLOCAL))		wake_up_interruptible(&state->info->open_wait);#endif}/* * In 2.4.5, calls to this will be serialized via the BKL in *  linux/drivers/char/tty_io.c:tty_release() *  linux/drivers/char/tty_io.c:do_tty_handup() */static void uart_close(struct tty_struct *tty, struct file *filp){	struct uart_state *state = tty->driver_data;	struct uart_port *port;		BUG_ON(!kernel_locked());	if (!state || !state->port)		return;	port = state->port;	DPRINTK("uart_close(%d) called\n", port->line);	mutex_lock(&state->mutex);	if (tty_hung_up_p(filp))		goto done;	if ((tty->count == 1) && (state->count != 1)) {		/*		 * Uh, oh.  tty->count is 1, which means that the tty		 * structure will be freed.  state->count should always		 * be one in these conditions.  If it's greater than		 * one, we've got real problems, since it means the		 * serial port won't be shutdown.		 */		printk(KERN_ERR "uart_close: bad serial port count; tty->count is 1, "		       "state->count is %d\n", state->count);		state->count = 1;	}	if (--state->count < 0) {		printk(KERN_ERR "uart_close: bad serial port count for %s: %d\n",		       tty->name, state->count);		state->count = 0;	}	if (state->count)		goto done;	/*	 * Now we wait for the transmit buffer to clear; and we notify	 * the line discipline to only process XON/XOFF characters by	 * setting tty->closing.	 */	tty->closing = 1;	if (state->closing_wait != USF_CLOSING_WAIT_NONE)		tty_wait_until_sent(tty, msecs_to_jiffies(state->closing_wait));	/*	 * At this point, we stop accepting input.  To do this, we	 * disable the receive line status interrupts.	 */	if (state->info->flags & UIF_INITIALIZED) {		unsigned long flags;		spin_lock_irqsave(&port->lock, flags);		port->ops->stop_rx(port);		spin_unlock_irqrestore(&port->lock, flags);		/*		 * Before we drop DTR, make sure the UART transmitter		 * has completely drained; this is especially		 * important if there is a transmit FIFO!		 */		uart_wait_until_sent(tty, port->timeout);	}	uart_shutdown(state);	uart_flush_buffer(tty);	tty_ldisc_flush(tty);			tty->closing = 0;	state->info->tty = NULL;	if (state->info->blocked_open) {		if (state->close_delay)			msleep_interruptible(state->close_delay);	} else if (!uart_console(port)) {		uart_change_pm(state, 3);	}	/*	 * Wake up anyone trying to open this port.	 */	state->info->flags &= ~UIF_NORMAL_ACTIVE;	wake_up_interruptible(&state->info->open_wait); done:	mutex_unlock(&state->mutex);}static void uart_wait_until_sent(struct tty_struct *tty, int timeout){	struct uart_state *state = tty->driver_data;	struct uart_port *port = state->port;	unsigned long char_time, expire;	BUG_ON(!kernel_locked());	if (port->type == PORT_UNKNOWN || port->fifosize == 0)		return;	/*	 * Set the check interval to be 1/5 of the estimated time to	 * send a single character, and make it at least 1.  The check	 * interval should also be less than the timeout.	 *	 * Note: we have to use pretty tight timings here to satisfy	 * the NIST-PCTS.	 */	char_time = (port->timeout - HZ/50) / port->fifosize;	char_time = char_time / 5;	if (char_time == 0)		char_time = 1;	if (timeout && timeout < char_time)		char_time = timeout;	/*	 * If the transmitter hasn't cleared in twice the approximate	 * amount of time to send the entire FIFO, it probably won't	 * ever clear.  This assumes the UART isn't doing flow	 * control, which is currently the case.  Hence, if it ever	 * takes longer than port->timeout, this is probably due to a	 * UART bug of some kind.  So, we clamp the timeout parameter at	 * 2*port->timeout.	 */	if (timeout == 0 || timeout > 2 * port->timeout)		timeout = 2 * port->timeout;	expire = jiffies + timeout;	DPRINTK("uart_wait_until_sent(%d), jiffies=%lu, expire=%lu...\n",	        port->line, jiffies, expire);	/*	 * Check whether the transmitter is empty every 'char_time'.	 * 'timeout' / 'expire' give us the maximum amount of time	 * we wait.	 */	while (!port->ops->tx_empty(port)) {		msleep_interruptible(jiffies_to_msecs(char_time));		if (signal_pending(current))			break;		if (time_after(jiffies, expire))			break;	}	set_current_state(TASK_RUNNING); /* might not be needed */}/* * This is called with the BKL held in *  linux/drivers/char/tty_io.c:do_tty_hangup() * We're called from the eventd thread, so we can sleep for * a _short_ time only. */static void uart_hangup(struct tty_struct *tty){	struct uart_state *state = tty->driver_data;	BUG_ON(!kernel_locked());	DPRINTK("uart_hangup(%d)\n", state->port->line);	mutex_lock(&state->mutex);	if (state->info && state->info->flags & UIF_NORMAL_ACTIVE) {		uart_flush_buffer(tty);		uart_shutdown(state);		state->count = 0;		state->info->flags &= ~UIF_NORMAL_ACTIVE;		state->info->tty = NULL;		wake_up_interruptible(&state->info->open_wait);		wake_up_interruptible(&state->info->delta_msr_wait);	}	mutex_unlock(&state->mutex);}/* * Copy across the serial console cflag setting into the termios settings * for the initial open of the port.  This allows continuity between the * kernel settings, and the settings init adopts when it opens the port * for the first time. */static void uart_update_termios(struct uart_state *state){	struct tty_struct *tty = state->info->tty;	struct uart_port *port = state->port;	if (uart_console(port) && port->cons->cflag) {		tty->termios->c_cflag = port->cons->cflag;		port->cons->cflag = 0;	}	/*	 * If the device failed to grab its irq resources,	 * or some other error occurred, don't try to talk	 * to the port hardware.	 */	if (!(tty->flags & (1 << TTY_IO_ERROR))) {		/*		 * Make termios settings take effect.		 */		uart_change_speed(state, NULL);		/*		 * And finally enable the RTS and DTR signals.		 */		if (tty->termios->c_cflag & CBAUD)			uart_set_mctrl(port, TIOCM_DTR | TIOCM_RTS);	}}/* * Block the open until the port is ready.  We must be called with * the per-port semaphore held. */static intuart_block_til_ready(struct file *filp, struct uart_state *state){	DECLARE_WAITQUEUE(wait, current);	struct uart_info *info = state->info;	struct uart_port *port = state->port;	unsigned int mctrl;	info->blocked_open++;	state->count--;	add_wait_queue(&info->open_wait, &wait);	while (1) {		set_current_state(TASK_INTERRUPTIBLE);		/*		 * If we have been hung up, tell userspace/restart open.		 */		if (tty_hung_up_p(filp) || info->tty == NULL)			break;		/*		 * If the port has been closed, tell userspace/restart open.		 */		if (!(info->flags & UIF_INITIALIZED))			break;		/*		 * If non-blocking mode is set, or CLOCAL mode is set,		 * we don't want to wait for the modem status lines to		 * indicate that the port is ready.		 *		 * Also, if the port is not enabled/configured, we want		 * to allow the open to succeed here.  Note that we will		 * have set TTY_IO_ERROR for a non-existant port.		 */		if ((filp->f_flags & O_NONBLOCK) ||	            (info->tty->termios->c_cflag & CLOCAL) ||		    (info->tty->flags & (1 << TTY_IO_ERROR))) {			break;		}		/*		 * Set DTR to allow modem to know we're waiting.  Do		 * not set RTS here - we want to make sure we catch		 * the data from the modem.		 */		if (info->tty->termios->c_cflag & CBAUD)			uart_set_mctrl(port, TIOCM_DTR);		/*		 * and wait for the carrier to indicate that the		 * modem is ready for us.		 */		spin_lock_irq(&port->lock);		port->ops->enable_ms(port);		mctrl = port->ops->get_mctrl(port);		spin_unlock_irq(&port->lock);		if (mctrl & TIOCM_CAR)			break;		mutex_unlock(&state->mutex);		schedule();		mutex_lock(&state->mutex);		if (signal_pending(current))			break;	}	set_current_state(TASK_RUNNING);	remove_wait_queue(&info->open_wait, &wait);	state->count++;	info->blocked_open--;	if (signal_pending(current))		return -ERESTARTSYS;	if (!info->tty || tty_hung_up_p(filp))		return -EAGAIN;	return 0;}static struct uart_state *uart_get(struct uart_driver *drv, int line){	struct uart_state *state;	mutex_lock(&port_mutex);	state = drv->state + line;	if (mutex_lock_interruptible(&state->mutex)) {		state = ERR_PTR(-ERESTARTSYS);		goto out;	}	state->count++;	if (!state->port) {		state->count--;		mutex_unlock(&state->mutex);		state = ERR_PTR(-ENXIO);		goto out;	}	if (!state->info) {		state->info = kmalloc(sizeof(struct uart_info), GFP_KERNEL);		if (state->info) {			memset(state->info, 0, sizeof(struct uart_info));			init_waitqueue_head(&state->info->open_wait);			init_waitqueue_head(&state->info->delta_msr_wait);			/*			 * Link the info into the other structures.			 */			state->port->info = state->info;			tasklet_init(&state->info->tlet, uart_tasklet_action,				     (unsigned long)state);		} else {			state->count--;			mutex_unlock(&state->mutex);			state = ERR_PTR(-ENOMEM);		}	} out:	mutex_unlock(&port_mutex);	return state;}/* * In 2.4.5, calls to uart_open are serialised by the BKL in *   linux/fs/devices.c:chrdev_open() * Note that if this fails, then uart_close() _will_ be called. * * In time, we want to scrap the "opening nonpresent ports" * behaviour and implement an alternative way for setserial * to set base addresses/ports/types.  This will allow us to * get rid of a certain amount of extra tests. */static int uart_open(struct tty_struct *tty, struct file *filp){	struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state;	struct uart_state *state;	int retval, line = tty->index;	BUG_ON(!kernel_locked());	DPRINTK("uart_open(%d) called\n", line);	/*	 * tty->driver->num won't change, so we won't fail here with	 * tty->driver_data set to something non-NULL (and therefore	 * we won't get caught by uart_close()).	 */	retval = -ENODEV;	if (line >= tty->driver->num)		goto fail;	/*	 * We take the semaphore inside uart_get to guarantee that we won't	 * be re-entered while allocating the info structure, or while we	 * request any IRQs that the driver may need.  This also has the nice	 * side-effect that it delays the action of uart_hangup, so we can	 * guarantee that info->tty will always contain something reasonable.	 */	state = uart_get(drv, line);	if (IS_ERR(state)) {		retval = PTR_ERR(state);		goto fail;	}	/*	 * Once we set tty->driver_data here, we are guaranteed that	 * uart_close() will decrement the driver module use count.	 * Any failures from here onwards should not touch the count.	 */	tty->driver_data = state;	tty->low_latency = (state->port->flags & UPF_LOW_LATENCY) ? 1 : 0;	tty->alt_speed = 0;	state->info->tty = tty;	/*	 * If the port is in the middle of closing, bail out now.	 */	if (tty_hung_up_p(filp)) {		retval = -EAGAIN;		state->count--;		mutex_unlock(&state->mutex);		goto fail;	}	/*	 * Make sure the device is in D0 state.	 */	if (state->count == 1)		uart_change_pm(state, 0);	/*	 * Start up the serial port.	 */	retval = uart_startup(state, 0);	/*	 * If we succeeded, wait until the port is ready.	 */	if (retval == 0)		retval = uart_block_til_ready(filp, state);	mutex_unlock(&state->mutex);	/*	 * If this is the first open to succeed, adjust things to suit.	 */	if (retval == 0 && !(state->info->flags & UIF_NORMAL_ACTIVE)) {		state->info->flags |= UIF_NORMAL_ACTIVE;		uart_update_termios(state);	} fail:	return retval;}static const char *uart_type(struct uart_port *port){	const char *str = NULL;	if (port->ops->type)		str = port->ops->type(port);	if (!str)		str = "unknown";	return str;}#ifdef CONFIG_PROC_FSstatic int uart_line_info(char *buf, struct uart_driver *drv, int i){	struct uart_state *state = drv->state + i;	struct uart_port *port = state->port;	char stat_buf[32];	unsigned int status;	int ret;	if (!port)		return 0;	ret = sprintf(buf, "%d: uart:%s %s%08lX irq:%d",			port->line, uart_type(port),			port->iotype == UPIO_MEM ? "mmio:0x" : "port:",			port->iotype == UPIO_MEM ? port->mapbase :						(unsigned long) port->iobase,			port->irq);	if (port->type == PORT_UNKNOWN) {		strcat(buf, "\n");		return ret + 1;	}	if(capable(CAP_SYS_ADMIN))	{		spin_lock_irq(&port->lock);		status = port->ops->get_mctrl(port);		spin_unlock_irq(&port->lock);		ret += sprintf(buf + ret, " tx:%d rx:%d",				port->icount.tx, port->icount.rx);		if (port->icount.frame)			ret += sprintf(buf + ret, " fe:%d",				port->icount.frame);		if (port->icount.parity)			ret += sprintf(buf + ret, " pe:%d",				port->icount.parity);		if (port->icount.brk)			ret += sprintf(buf + ret, " brk:%d",				port->icount.brk);		if (port->icount.overrun)			ret += sprintf(buf + ret, " oe:%d",				port->icount.overrun);	#define INFOBIT(bit,str) \	if (port->mctrl & (bit)) \		strncat(stat_buf, (str), sizeof(stat_buf) - \			strlen(stat_buf) - 2)#define STATBIT(bit,str) \	if (status & (bit)) \		strncat(stat_buf, (str), sizeof(stat_buf) - \		       strlen(stat_buf) - 2)		stat_buf[0] = '\0';		stat_buf[1] = '\0';		INFOBIT(TIOCM_RTS, "|RTS");		STATBIT(TIOCM_CTS, "|CTS");		INFOBIT(TIOCM_DTR, "|DTR");		STATBIT(TIOCM_DSR, "|DSR");		STATBIT(TIOCM_CAR, "|CD");		STATBIT(TIOCM_RNG, "|RI");		if (stat_buf[0])			stat_buf[0] = ' ';		strcat(stat_buf, "\n");			ret += sprintf(buf + ret, stat_buf);	} else {		strcat(buf, "\n");		ret++;	}#undef STATBIT#undef INFOBIT	return ret;}static int uart_read_proc(char *page, char **start, off_t off,			  int count, int *eof, void *data){	struct tty_driver *ttydrv = data;	struct uart_driver *drv = ttydrv->driver_state;	int i, len = 0, l;	off_t begin = 0;	len += sprintf(page, "serinfo:1.0 driver%s%s revision:%s\n",			"", "", "");	for (i = 0; i < drv->nr && len < PAGE_SIZE - 96; i++) {		l = uart_line_info(page + len, drv, i);		len += l;		if (len + begin > off + count)			goto done;		if (len + begin < off) {			begin += len;			len = 0;		}	}	*eof = 1; done:	if (off >= len + begin)		return 0;	*start = page + (off - begin);	return (count < begin + len - off) ? count : (begin + len - off);}#endif#ifdef CONFIG_SERIAL_CORE_CONSOLE/* *	Check whether an invalid uart number has been specified, and *	if so, search for the first available port that does have *	console support. */struct uart_port * __inituart_get_console(struct uart_port *ports, int nr, struct console *co){	int idx = co->index;	if (idx < 0 || idx >= nr || (ports[idx].iobase == 0 &&

⌨️ 快捷键说明

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