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

📄 serial_core.c

📁 本书是一本介绍Linux设备驱动开发理论、框架与实例的书
💻 C
📖 第 1 页 / 共 4 页
字号:
static void uart_unthrottle(struct tty_struct *tty){	struct uart_state *state = tty->driver_data;	struct uart_port *port = state->port;	if (I_IXOFF(tty)) {		if (port->x_char)			port->x_char = 0;		else			uart_send_xchar(tty, START_CHAR(tty));	}	if (tty->termios->c_cflag & CRTSCTS)		uart_set_mctrl(port, TIOCM_RTS);}static int uart_get_info(struct uart_state *state,			 struct serial_struct __user *retinfo){	struct uart_port *port = state->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 = (long) 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 / 10;	tmp.closing_wait    = state->closing_wait == USF_CLOSING_WAIT_NONE ?				ASYNC_CLOSING_WAIT_NONE :			        state->closing_wait / 10;	tmp.custom_divisor  = port->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_state *state,			 struct serial_struct __user *newinfo){	struct serial_struct new_serial;	struct uart_port *port = state->port;	unsigned long new_port;	unsigned int change_irq, change_port, closing_wait;	unsigned int old_custom_divisor, close_delay;	upf_t old_flags, new_flags;	int retval = 0;	if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))		return -EFAULT;	new_port = new_serial.port;	if (HIGH_BITS_OFFSET)		new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET;	new_serial.irq = irq_canonicalize(new_serial.irq);	close_delay = new_serial.close_delay * 10;	closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ?			USF_CLOSING_WAIT_NONE : new_serial.closing_wait * 10;	/*	 * This semaphore protects state->count.  It is also	 * very useful to prevent opens.  Also, take the	 * port configuration semaphore to make sure that a	 * module insertion/removal doesn't change anything	 * under us.	 */	mutex_lock(&state->mutex);	change_irq  = new_serial.irq != port->irq;	/*	 * Since changing the 'type' of the port changes its resource	 * allocations, we should treat type changes the same as	 * IO port changes.	 */	change_port = new_port != port->iobase ||		      (unsigned long)new_serial.iomem_base != port->mapbase ||		      new_serial.hub6 != port->hub6 ||		      new_serial.io_type != port->iotype ||		      new_serial.iomem_reg_shift != port->regshift ||		      new_serial.type != port->type;	old_flags = port->flags;	new_flags = new_serial.flags;	old_custom_divisor = port->custom_divisor;	if (!capable(CAP_SYS_ADMIN)) {		retval = -EPERM;		if (change_irq || change_port ||		    (new_serial.baud_base != port->uartclk / 16) ||		    (close_delay != state->close_delay) ||		    (closing_wait != state->closing_wait) ||		    (new_serial.xmit_fifo_size != port->fifosize) ||		    (((new_flags ^ old_flags) & ~UPF_USR_MASK) != 0))			goto exit;		port->flags = ((port->flags & ~UPF_USR_MASK) |			       (new_flags & UPF_USR_MASK));		port->custom_divisor = new_serial.custom_divisor;		goto check_and_exit;	}	/*	 * Ask the low level driver to verify the settings.	 */	if (port->ops->verify_port)		retval = port->ops->verify_port(port, &new_serial);	if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) ||	    (new_serial.baud_base < 9600))		retval = -EINVAL;	if (retval)		goto exit;	if (change_port || change_irq) {		retval = -EBUSY;		/*		 * Make sure that we are the sole user of this port.		 */		if (uart_users(state) > 1)			goto exit;		/*		 * We need to shutdown the serial port at the old		 * port/type/irq combination.		 */		uart_shutdown(state);	}	if (change_port) {		unsigned long old_iobase, old_mapbase;		unsigned int old_type, old_iotype, old_hub6, old_shift;		old_iobase = port->iobase;		old_mapbase = port->mapbase;		old_type = port->type;		old_hub6 = port->hub6;		old_iotype = port->iotype;		old_shift = port->regshift;		/*		 * Free and release old regions		 */		if (old_type != PORT_UNKNOWN)			port->ops->release_port(port);		port->iobase = new_port;		port->type = new_serial.type;		port->hub6 = new_serial.hub6;		port->iotype = new_serial.io_type;		port->regshift = new_serial.iomem_reg_shift;		port->mapbase = (unsigned long)new_serial.iomem_base;		/*		 * Claim and map the new regions		 */		if (port->type != PORT_UNKNOWN) {			retval = port->ops->request_port(port);		} else {			/* Always success - Jean II */			retval = 0;		}		/*		 * If we fail to request resources for the		 * new port, try to restore the old settings.		 */		if (retval && old_type != PORT_UNKNOWN) {			port->iobase = old_iobase;			port->type = old_type;			port->hub6 = old_hub6;			port->iotype = old_iotype;			port->regshift = old_shift;			port->mapbase = old_mapbase;			retval = port->ops->request_port(port);			/*			 * If we failed to restore the old settings,			 * we fail like this.			 */			if (retval)				port->type = PORT_UNKNOWN;			/*			 * We failed anyway.			 */			retval = -EBUSY;		}	}	port->irq              = new_serial.irq;	port->uartclk          = new_serial.baud_base * 16;	port->flags            = (port->flags & ~UPF_CHANGE_MASK) |				 (new_flags & UPF_CHANGE_MASK);	port->custom_divisor   = new_serial.custom_divisor;	state->close_delay     = close_delay;	state->closing_wait    = closing_wait;	port->fifosize         = new_serial.xmit_fifo_size;	if (state->info->tty)		state->info->tty->low_latency =			(port->flags & UPF_LOW_LATENCY) ? 1 : 0; check_and_exit:	retval = 0;	if (port->type == PORT_UNKNOWN)		goto exit;	if (state->info->flags & UIF_INITIALIZED) {		if (((old_flags ^ port->flags) & UPF_SPD_MASK) ||		    old_custom_divisor != port->custom_divisor) {			/*			 * If they're setting up a custom divisor or speed,			 * instead of clearing it, then bitch about it. No			 * need to rate-limit; it's CAP_SYS_ADMIN only.			 */			if (port->flags & UPF_SPD_MASK) {				char buf[64];				printk(KERN_NOTICE				       "%s sets custom speed on %s. This "				       "is deprecated.\n", current->comm,				       tty_name(state->info->tty, buf));			}			uart_change_speed(state, NULL);		}	} else		retval = uart_startup(state, 1); exit:	mutex_unlock(&state->mutex);	return retval;}/* * uart_get_lsr_info - get line status register info. * Note: uart_ioctl protects us against hangups. */static int uart_get_lsr_info(struct uart_state *state,			     unsigned int __user *value){	struct uart_port *port = state->port;	unsigned int result;	result = port->ops->tx_empty(port);	/*	 * If we're about to load something into the transmit	 * register, we'll pretend the transmitter isn't empty to	 * avoid a race condition (depending on when the transmit	 * interrupt happens).	 */	if (port->x_char ||	    ((uart_circ_chars_pending(&state->info->xmit) > 0) &&	     !state->info->tty->stopped && !state->info->tty->hw_stopped))		result &= ~TIOCSER_TEMT;		return put_user(result, value);}static int uart_tiocmget(struct tty_struct *tty, struct file *file){	struct uart_state *state = tty->driver_data;	struct uart_port *port = state->port;	int result = -EIO;	mutex_lock(&state->mutex);	if ((!file || !tty_hung_up_p(file)) &&	    !(tty->flags & (1 << TTY_IO_ERROR))) {		result = port->mctrl;		spin_lock_irq(&port->lock);		result |= port->ops->get_mctrl(port);		spin_unlock_irq(&port->lock);	}	mutex_unlock(&state->mutex);	return result;}static intuart_tiocmset(struct tty_struct *tty, struct file *file,	      unsigned int set, unsigned int clear){	struct uart_state *state = tty->driver_data;	struct uart_port *port = state->port;	int ret = -EIO;	mutex_lock(&state->mutex);	if ((!file || !tty_hung_up_p(file)) &&	    !(tty->flags & (1 << TTY_IO_ERROR))) {		uart_update_mctrl(port, set, clear);		ret = 0;	}	mutex_unlock(&state->mutex);	return ret;}static void uart_break_ctl(struct tty_struct *tty, int break_state){	struct uart_state *state = tty->driver_data;	struct uart_port *port = state->port;	BUG_ON(!kernel_locked());	mutex_lock(&state->mutex);	if (port->type != PORT_UNKNOWN)		port->ops->break_ctl(port, break_state);	mutex_unlock(&state->mutex);}static int uart_do_autoconfig(struct uart_state *state){	struct uart_port *port = state->port;	int flags, ret;	if (!capable(CAP_SYS_ADMIN))		return -EPERM;	/*	 * Take the per-port semaphore.  This prevents count from	 * changing, and hence any extra opens of the port while	 * we're auto-configuring.	 */	if (mutex_lock_interruptible(&state->mutex))		return -ERESTARTSYS;	ret = -EBUSY;	if (uart_users(state) == 1) {		uart_shutdown(state);		/*		 * If we already have a port type configured,		 * we must release its resources.		 */		if (port->type != PORT_UNKNOWN)			port->ops->release_port(port);		flags = UART_CONFIG_TYPE;		if (port->flags & UPF_AUTO_IRQ)			flags |= UART_CONFIG_IRQ;		/*		 * This will claim the ports resources if		 * a port is found.		 */		port->ops->config_port(port, flags);		ret = uart_startup(state, 1);	}	mutex_unlock(&state->mutex);	return ret;}/* * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change * - mask passed in arg for lines of interest *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) * Caller should use TIOCGICOUNT to see which one it was */static intuart_wait_modem_status(struct uart_state *state, unsigned long arg){	struct uart_port *port = state->port;	DECLARE_WAITQUEUE(wait, current);	struct uart_icount cprev, cnow;	int ret;	/*	 * note the counters on entry	 */	spin_lock_irq(&port->lock);	memcpy(&cprev, &port->icount, sizeof(struct uart_icount));	/*	 * Force modem status interrupts on	 */	port->ops->enable_ms(port);	spin_unlock_irq(&port->lock);	add_wait_queue(&state->info->delta_msr_wait, &wait);	for (;;) {		spin_lock_irq(&port->lock);		memcpy(&cnow, &port->icount, sizeof(struct uart_icount));		spin_unlock_irq(&port->lock);		set_current_state(TASK_INTERRUPTIBLE);		if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||		    ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||		    ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) ||		    ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) {		    	ret = 0;		    	break;		}		schedule();		/* see if a signal did it */		if (signal_pending(current)) {			ret = -ERESTARTSYS;			break;		}		cprev = cnow;	}	current->state = TASK_RUNNING;	remove_wait_queue(&state->info->delta_msr_wait, &wait);	return ret;}/* * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) * Return: write counters to the user passed counter struct * NB: both 1->0 and 0->1 transitions are counted except for *     RI where only 0->1 is counted. */static int uart_get_count(struct uart_state *state,			  struct serial_icounter_struct __user *icnt){	struct serial_icounter_struct icount;	struct uart_icount cnow;	struct uart_port *port = state->port;	spin_lock_irq(&port->lock);	memcpy(&cnow, &port->icount, sizeof(struct uart_icount));	spin_unlock_irq(&port->lock);	icount.cts         = cnow.cts;	icount.dsr         = cnow.dsr;	icount.rng         = cnow.rng;	icount.dcd         = cnow.dcd;	icount.rx          = cnow.rx;	icount.tx          = cnow.tx;	icount.frame       = cnow.frame;	icount.overrun     = cnow.overrun;	icount.parity      = cnow.parity;	icount.brk         = cnow.brk;	icount.buf_overrun = cnow.buf_overrun;	return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0;}/* * Called via sys_ioctl under the BKL.  We can use spin_lock_irq() here. */static intuart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd,	   unsigned long arg){	struct uart_state *state = tty->driver_data;	void __user *uarg = (void __user *)arg;	int ret = -ENOIOCTLCMD;	BUG_ON(!kernel_locked());	/*	 * These ioctls don't rely on the hardware to be present.	 */	switch (cmd) {	case TIOCGSERIAL:		ret = uart_get_info(state, uarg);		break;	case TIOCSSERIAL:		ret = uart_set_info(state, uarg);		break;	case TIOCSERCONFIG:		ret = uart_do_autoconfig(state);		break;	case TIOCSERGWILD: /* obsolete */	case TIOCSERSWILD: /* obsolete */		ret = 0;		break;	}	if (ret != -ENOIOCTLCMD)		goto out;	if (tty->flags & (1 << TTY_IO_ERROR)) {		ret = -EIO;		goto out;	}	/*	 * The following should only be used when hardware is present.	 */	switch (cmd) {	case TIOCMIWAIT:		ret = uart_wait_modem_status(state, arg);		break;	case TIOCGICOUNT:		ret = uart_get_count(state, uarg);		break;	}	if (ret != -ENOIOCTLCMD)		goto out;	mutex_lock(&state->mutex);	if (tty_hung_up_p(filp)) {		ret = -EIO;		goto out_up;	}	/*	 * All these rely on hardware being present and need to be	 * protected against the tty being hung up.	 */	switch (cmd) {	case TIOCSERGETLSR: /* Get line status register */		ret = uart_get_lsr_info(state, uarg);		break;	default: {		struct uart_port *port = state->port;		if (port->ops->ioctl)			ret = port->ops->ioctl(port, cmd, arg);		break;	}	} out_up:	mutex_unlock(&state->mutex); out:	return ret;}static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios){	struct uart_state *state = tty->driver_data;	unsigned long flags;	unsigned int cflag = tty->termios->c_cflag;	BUG_ON(!kernel_locked());	/*	 * These are the bits that are used to setup various	 * flags in the low level driver.	 */#define RELEVANT_IFLAG(iflag)	((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))	if ((cflag ^ old_termios->c_cflag) == 0 &&	    RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0)		return;	uart_change_speed(state, old_termios);	/* Handle transition to B0 status */	if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))		uart_clear_mctrl(state->port, TIOCM_RTS | TIOCM_DTR);	/* Handle transition away from B0 status */	if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {		unsigned int mask = TIOCM_DTR;		if (!(cflag & CRTSCTS) ||		    !test_bit(TTY_THROTTLED, &tty->flags))			mask |= TIOCM_RTS;		uart_set_mctrl(state->port, mask);	}	/* Handle turning off CRTSCTS */	if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) {		spin_lock_irqsave(&state->port->lock, flags);		tty->hw_stopped = 0;		__uart_start(tty);		spin_unlock_irqrestore(&state->port->lock, flags);	}	/* Handle turning on CRTSCTS */	if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) {		spin_lock_irqsave(&state->port->lock, flags);		if (!(state->port->ops->get_mctrl(state->port) & TIOCM_CTS)) {

⌨️ 快捷键说明

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