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

📄 core.c

📁 IXP425 平台下嵌入式LINUX的串口的驱动程序
💻 C
📖 第 1 页 / 共 4 页
字号:
{	struct serial_struct new_serial;	struct uart_state *state = info->state;	struct uart_port *port = info->port;	unsigned long new_port;	unsigned int change_irq, change_port, old_flags;	unsigned int old_custom_divisor;	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_cannonicalize(new_serial.irq);	/*	 * 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.	 */	down(&port_sem);	down(&state->count_sem);	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;	old_custom_divisor = state->custom_divisor;	if (!capable(CAP_SYS_ADMIN)) {		retval = -EPERM;		if (change_irq || change_port ||		    (new_serial.baud_base != port->uartclk / 16) ||		    (new_serial.close_delay != state->close_delay) ||		    (new_serial.closing_wait != state->closing_wait) ||		    (new_serial.xmit_fifo_size != port->fifosize) ||		    ((new_serial.flags & ~ASYNC_USR_MASK) !=		     (port->flags & ~ASYNC_USR_MASK)))			goto exit;		port->flags = ((port->flags & ~ASYNC_USR_MASK) |			       (new_serial.flags & ASYNC_USR_MASK));		info->flags = ((info->flags & ~ASYNC_USR_MASK) |			       (new_serial.flags & ASYNC_USR_MASK));		state->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 (state->count > 1)			goto exit;		/*		 * We need to shutdown the serial port at the old		 * port/type/irq combination.		 */		uart_shutdown(info);	}	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);		/*		 * 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 & ~ASYNC_FLAGS) |			(new_serial.flags & ASYNC_FLAGS));	info->flags = ((port->flags & ~ASYNC_INTERNAL_FLAGS) |		       (info->flags & ASYNC_INTERNAL_FLAGS));	state->custom_divisor = new_serial.custom_divisor;	state->close_delay = new_serial.close_delay * HZ / 100;	state->closing_wait = new_serial.closing_wait * HZ / 100;	info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;	port->fifosize = new_serial.xmit_fifo_size;check_and_exit:	retval = 0;	if (port->type == PORT_UNKNOWN)		goto exit;	if (info->flags & ASYNC_INITIALIZED) {		if (((old_flags & info->flags) & ASYNC_SPD_MASK) ||		    old_custom_divisor != state->custom_divisor) {			uart_update_altspeed(info);			uart_change_speed(info, NULL);		}	} else		retval = uart_startup(info);exit:	up(&state->count_sem);	up(&port_sem);	return retval;}/* * uart_get_lsr_info - get line status register info */static int uart_get_lsr_info(struct uart_info *info, unsigned int *value){	u_int result;	unsigned long flags;	spin_lock_irqsave(&info->lock, flags);	result = info->ops->tx_empty(info->port);	spin_unlock_irqrestore(&info->lock, flags);	/*	 * 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 (info->port->x_char ||	    ((CIRC_CNT(info->xmit.head, info->xmit.tail,		       UART_XMIT_SIZE) > 0) &&	     !info->tty->stopped && !info->tty->hw_stopped))		result &= ~TIOCSER_TEMT;		return put_user(result, value);}static int uart_get_modem_info(struct uart_info *info, unsigned int *value){	unsigned int result = info->mctrl;	result |= info->ops->get_mctrl(info->port);	return put_user(result, value);}static int uart_set_modem_info(struct uart_info *info, unsigned int cmd,			  unsigned int *value){	unsigned int arg, old;	int ret = 0;	if (get_user(arg, value))		return -EFAULT;	spin_lock_irq(&info->lock);	old = info->mctrl;	switch (cmd) {	case TIOCMBIS:	info->mctrl |= arg;	break;	case TIOCMBIC:	info->mctrl &= ~arg;	break;	case TIOCMSET:	info->mctrl = arg;	break;	default:	ret = -EINVAL;		break;	}	if (old != info->mctrl)		info->ops->set_mctrl(info->port, info->mctrl);	spin_unlock_irq(&info->lock);	return ret;}static void uart_break_ctl(struct tty_struct *tty, int break_state){	struct uart_info *info = tty->driver_data;	unsigned long flags;	if (info->port->type != PORT_UNKNOWN) {		spin_lock_irqsave(&info->lock, flags);		info->ops->break_ctl(info->port, break_state);		spin_unlock_irqrestore(&info->lock, flags);	}}static int uart_do_autoconfig(struct uart_info *info){	struct uart_port *port = info->port;	int flags, ret;	if (!capable(CAP_SYS_ADMIN))		return -EPERM;	/*	 * Take the 'count' lock.  This prevents count	 * from incrementing, and hence any extra opens	 * of the port while we're auto-configging.	 */	down(&info->state->count_sem);	ret = -EBUSY;	if (info->state->count == 1) {		uart_shutdown(info);		/*		 * 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 & ASYNC_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(info);	}	up(&info->state->count_sem);	return ret;}/* * Called from userspace.  We can use spin_lock_irq() here. */static int uart_ioctl(struct tty_struct *tty, struct file *file,			   unsigned int cmd, unsigned long arg){	struct uart_info *info = tty->driver_data;	struct uart_icount cprev, cnow;	struct serial_icounter_struct icount;	int ret = -ENOIOCTLCMD;	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&	    (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {		if (tty->flags & (1 << TTY_IO_ERROR))			return -EIO;	}	switch (cmd) {		case TIOCMGET:			ret = uart_get_modem_info(info, (unsigned int *)arg);			break;		case TIOCMBIS:		case TIOCMBIC:		case TIOCMSET:			ret = uart_set_modem_info(info, cmd,						  (unsigned int *)arg);			break;		case TIOCGSERIAL:			ret = uart_get_info(info, (struct serial_struct *)arg);			break;		case TIOCSSERIAL:			ret = uart_set_info(info, (struct serial_struct *)arg);			break;		case TIOCSERCONFIG:			ret = uart_do_autoconfig(info);			break;		case TIOCSERGETLSR: /* Get line status register */			ret = uart_get_lsr_info(info, (unsigned int *)arg);			break;		/*		 * 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		 */		case TIOCMIWAIT:			spin_lock_irq(&info->lock);			/* note the counters on entry */			cprev = info->port->icount;			/* Force modem status interrupts on */			info->ops->enable_ms(info->port);			spin_unlock_irq(&info->lock);			while (1) {				interruptible_sleep_on(&info->delta_msr_wait);				/* see if a signal did it */				if (signal_pending(current)) {					ret = -ERESTARTSYS;					break;				}				spin_lock_irq(&info->lock);				cnow = info->port->icount; /* atomic copy */				spin_unlock_irq(&info->lock);				if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&				    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) {				    	ret = -EIO; /* no change => error */				    	break;				}				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;				}				cprev = cnow;			}			break;		/*		 * 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.		 */		case TIOCGICOUNT:			spin_lock_irq(&info->lock);			cnow = info->port->icount;			spin_unlock_irq(&info->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;			ret = copy_to_user((void *)arg, &icount, sizeof(icount))					? -EFAULT : 0;			break;		case TIOCSERGWILD: /* obsolete */		case TIOCSERSWILD: /* obsolete */			ret = 0;			break;		default:			if (info->ops->ioctl)				ret = info->ops->ioctl(info->port, cmd, arg);			break;	}	return ret;}static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios){	struct uart_info *info = tty->driver_data;	unsigned long flags;	unsigned int cflag = tty->termios->c_cflag;	if ((cflag ^ old_termios->c_cflag) == 0 &&	    RELEVENT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0)		return;	uart_change_speed(info, old_termios);	spin_lock_irqsave(&info->lock, flags);	/* Handle transition to B0 status */	if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) {		info->mctrl &= ~(TIOCM_RTS | TIOCM_DTR);		info->ops->set_mctrl(info->port, info->mctrl);	}	/* Handle transition away from B0 status */	if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {		info->mctrl |= TIOCM_DTR;		if (!(cflag & CRTSCTS) ||		    !test_bit(TTY_THROTTLED, &tty->flags))			info->mctrl |= TIOCM_RTS;		info->ops->set_mctrl(info->port, info->mctrl);	}	/* Handle turning off CRTSCTS */	if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) {		tty->hw_stopped = 0;		__uart_start(tty);	}	spin_unlock_irqrestore(&info->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(&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_driver *drv = (struct uart_driver *)tty->driver.driver_state;	struct uart_info *info = tty->driver_data;	struct uart_state *state;	unsigned long flags;	if (!info)		return;	state = info->state;#ifdef DEBUG	printk("uart_close() called\n");#endif	/*	 * This is safe, as long as the BKL exists in	 * do_tty_hangup(), and we're protected by the BKL.	 */	if (tty_hung_up_p(filp))		goto done;	down(&state->count_sem);	spin_lock_irqsave(&info->lock, flags);	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("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("rs_close: bad serial port count for %s%d: %d\n",		       tty->driver.name, info->port->line, state->count);		state->count = 0;	}	if (state->count) {		spin_unlock_irqrestore(&info->lock, flags);		up(&state->count_sem);		goto done;	}	info->flags |= ASYNC_CLOSING;	spin_unlock_irqrestore(&info->lock, flags);	up(&state->count_sem);	/*	 * Save the termios structure, since this port may have	 * separate termios for callout and dialin.	 */	if (info->flags & ASYNC_NORMAL_ACTIVE)		info->state->normal_termios = *tty->termios;	if (info->flags & ASYNC_CALLOUT_ACTIVE)		info->state->callout_termios = *tty->termios;	/*	 * Now we wait for the transmit buffer to clear; and we notify	 * the line discipline to only process XON/XOFF characters.	 */	tty->closing = 1;	if (info->state->closing_wait != ASYNC_CLOSING_WAIT_NONE)		tty_wait_until_sent(tty, info->state->closing_wait);	/*	 * At this point, we stop accepting input.  To do this, we	 * disable the receive line status interrupts.	 */	if (info->flags & ASYNC_INITIALIZED) {		info->ops->stop_rx(info->port);		/*		 * 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, info->timeout);	}	uart_shutdown(info);	if (tty->driver.flush_buffer)		tty->driver.flush_buffer(tty);	if (tty->ldisc.flush_buffer)		tty->ldisc.flush_buffer(tty);	tty->closing = 0;	info->event = 0;	info->tty = NULL;	if (info->blocked_open) {		if (info->state->close_delay) {			set_current_state(TASK_INTERRUPTIBLE);			schedule_timeout(info->state->close_delay);			set_current_state(TASK_RUNNING);

⌨️ 快捷键说明

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