linux UART驱动 三

本文详细解析了UART设备打开过程中的关键步骤和技术细节,包括tty_open函数内部实现、tty_driver和tty_struct之间的交互机制,以及如何初始化和配置串口设备。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

当我们打开一个串口(UART)设备的时候,实际上调用的是 tty_open 函数

static int tty_open(struct inode *inode, struct file *filp)
{
	int ret;

	lock_kernel();
	ret = __tty_open(inode, filp);
	unlock_kernel();
	return ret;
}

static int __tty_open(struct inode *inode, struct file *filp)
{
	struct tty_struct *tty = NULL;
	int noctty, retval;
	struct tty_driver *driver;
	int index;
	dev_t device = inode->i_rdev;
	unsigned saved_flags = filp->f_flags;

	nonseekable_open(inode, filp);

retry_open:
	noctty = filp->f_flags & O_NOCTTY;
	index  = -1;
	retval = 0;

	mutex_lock(&tty_mutex);

	if (device == MKDEV(TTYAUX_MAJOR, 0)) {
		tty = get_current_tty();
		if (!tty) {
			mutex_unlock(&tty_mutex);
			return -ENXIO;
		}
		driver = tty_driver_kref_get(tty->driver);
		index = tty->index;
		filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
		/* noctty = 1; */
		/* FIXME: Should we take a driver reference ? */
		tty_kref_put(tty);
		goto got_driver;
	}
#ifdef CONFIG_VT
	if (device == MKDEV(TTY_MAJOR, 0)) {
		extern struct tty_driver *console_driver;
		driver = tty_driver_kref_get(console_driver);
		index = fg_console;
		noctty = 1;
		goto got_driver;
	}
#endif
	if (device == MKDEV(TTYAUX_MAJOR, 1)) {
		struct tty_driver *console_driver = console_device(&index);
		if (console_driver) {
			driver = tty_driver_kref_get(console_driver);
			if (driver) {
				/* Don't let /dev/console block */
				filp->f_flags |= O_NONBLOCK;
				noctty = 1;
				goto got_driver;
			}
		}
		mutex_unlock(&tty_mutex);
		return -ENODEV;
	}
	
/*
以s3c2440 uart设备为例,
static struct uart_driver s3c24xx_uart_drv = {
	.owner		= THIS_MODULE,
	.dev_name	= "s3c2410_serial",
	.nr		= CONFIG_SERIAL_SAMSUNG_UARTS,
	.cons		= S3C24XX_SERIAL_CONSOLE,
	.driver_name	= S3C24XX_SERIAL_NAME,
	.major		= S3C24XX_SERIAL_MAJOR,   //204
	.minor		= S3C24XX_SERIAL_MINOR,   //64
};
所以前面的条件都不成立,从这里开始
*/
//描述A
	driver = get_tty_driver(device, &index);
	if (!driver) {
		mutex_unlock(&tty_mutex);
		return -ENODEV;
	}
got_driver:
	if (!tty) {
		/* check whether we're reopening an existing tty */
//描述B
		tty = tty_driver_lookup_tty(driver, inode, index);

		if (IS_ERR(tty)) {
			mutex_unlock(&tty_mutex);
			return PTR_ERR(tty);
		}
	}

	if (tty) {
		retval = tty_reopen(tty);
		if (retval)
			tty = ERR_PTR(retval);
	} else
//描述C
		tty = tty_init_dev(driver, index, 0);

	mutex_unlock(&tty_mutex);
	tty_driver_kref_put(driver);
	if (IS_ERR(tty))
		return PTR_ERR(tty);

	filp->private_data = tty;
	file_move(filp, &tty->tty_files);
	check_tty_count(tty, "tty_open");
	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
	    tty->driver->subtype == PTY_TYPE_MASTER)
		noctty = 1;
#ifdef TTY_DEBUG_HANGUP
	printk(KERN_DEBUG "opening %s...", tty->name);
#endif
	if (!retval) {
//描述D
		if (tty->ops->open)
			retval = tty->ops->open(tty, filp);
		else
			retval = -ENODEV;
	}
	filp->f_flags = saved_flags;

	if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) &&
						!capable(CAP_SYS_ADMIN))
		retval = -EBUSY;

	if (retval) {
#ifdef TTY_DEBUG_HANGUP
		printk(KERN_DEBUG "error %d in opening %s...", retval,
		       tty->name);
#endif
		tty_release_dev(filp);
		if (retval != -ERESTARTSYS)
			return retval;
		if (signal_pending(current))
			return retval;
		schedule();
		/*
		 * Need to reset f_op in case a hangup happened.
		 */
		if (filp->f_op == &hung_up_tty_fops)
			filp->f_op = &tty_fops;
		goto retry_open;
	}

	mutex_lock(&tty_mutex);
	spin_lock_irq(¤t->sighand->siglock);
	if (!noctty &&
	    current->signal->leader &&
	    !current->signal->tty &&
	    tty->session == NULL)
		__proc_set_tty(current, tty);
	spin_unlock_irq(¤t->sighand->siglock);
	mutex_unlock(&tty_mutex);
	return 0;
}

描述A:

static struct tty_driver *get_tty_driver(dev_t device, int *index)
{
	struct tty_driver *p;


	list_for_each_entry(p, &tty_drivers, tty_drivers) {
		dev_t base = MKDEV(p->major, p->minor_start);
		if (device < base || device >= base + p->num)
			continue;
		*index = device - base;
		return tty_driver_kref_get(p);
	}
	return NULL;
}	

前面在函数 uart_register_driver -> tty_register_driver 中,将 tty_driver 挂载到
tty_drivers链表中,这里则是通过遍历 来查找曾经注册过的 tty_driver


描述B:
static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver,
		struct inode *inode, int idx)
{
	struct tty_struct *tty;


	if (driver->ops->lookup)
		return driver->ops->lookup(driver, inode, idx);


	tty = driver->ttys[idx];
	return tty;
}

前面在函数 uart_register_driver 中将 driver->ops 设置为 uart_ops
static const struct tty_operations uart_ops = {
	.open		= uart_open,
	.close		= uart_close,
	.write		= uart_write,
	.put_char	= uart_put_char,
	.flush_chars	= uart_flush_chars,
	.write_room	= uart_write_room,
	.chars_in_buffer= uart_chars_in_buffer,
	.flush_buffer	= uart_flush_buffer,
	.ioctl		= uart_ioctl,
	.throttle	= uart_throttle,
	.unthrottle	= uart_unthrottle,
	.send_xchar	= uart_send_xchar,
	.set_termios	= uart_set_termios,
	.set_ldisc	= uart_set_ldisc,
	.stop		= uart_stop,
	.start		= uart_start,
	.hangup		= uart_hangup,
	.break_ctl	= uart_break_ctl,
	.wait_until_sent= uart_wait_until_sent,
#ifdef CONFIG_PROC_FS
	.proc_fops	= &uart_proc_fops,
#endif
	.tiocmget	= uart_tiocmget,
	.tiocmset	= uart_tiocmset,
#ifdef CONFIG_CONSOLE_POLL
	.poll_init	= uart_poll_init,
	.poll_get_char	= uart_poll_get_char,
	.poll_put_char	= uart_poll_put_char,
#endif
};

在这里 driver->ops->lookup 为空,所以执行下一条语句
tty = driver->ttys[idx];
而 driver->ttys 是在 uart_register_driver -> tty_register_driver 中初始化的,
	void **p = NULL;
	if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {
		p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL);
		if (!p)
			return -ENOMEM;
	}
	if (p) {
		driver->ttys = (struct tty_struct **)p;
		driver->termios = (struct ktermios **)(p + driver->num);
	} 
所以 tty = driver->ttys[idx] = NULL;


描述C:
tty = tty_init_dev(driver, index, 0);

struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
								int first_ok)
{
	struct tty_struct *tty;
	int retval;


	/* Check if pty master is being opened multiple times */
	if (driver->subtype == PTY_TYPE_MASTER &&
		(driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok)
		return ERR_PTR(-EIO);


	/*
	 * First time open is complex, especially for PTY devices.
	 * This code guarantees that either everything succeeds and the
	 * TTY is ready for operation, or else the table slots are vacated
	 * and the allocated memory released.  (Except that the termios
	 * and locked termios may be retained.)
	 */


	if (!try_module_get(driver->owner))
		return ERR_PTR(-ENODEV);


/*
struct tty_struct *alloc_tty_struct(void)
{
	return kzalloc(sizeof(struct tty_struct), GFP_KERNEL);
}
分配内存
*/
	tty = alloc_tty_struct();
	if (!tty)
		goto fail_no_mem;
//描述1
	initialize_tty_struct(tty, driver, idx);
	
//描述2
	retval = tty_driver_install_tty(driver, tty);
	if (retval < 0) {
		free_tty_struct(tty);
		module_put(driver->owner);
		return ERR_PTR(retval);
	}


	/*
	 * Structures all installed ... call the ldisc open routines.
	 * If we fail here just call release_tty to clean up.  No need
	 * to decrement the use counts, as release_tty doesn't care.
	 */


//描述3
	retval = tty_ldisc_setup(tty, tty->link);
	if (retval)
		goto release_mem_out;
	return tty;


fail_no_mem:
	module_put(driver->owner);
	return ERR_PTR(-ENOMEM);


	/* call the tty release_tty routine to clean out this slot */
release_mem_out:
	if (printk_ratelimit())
		printk(KERN_INFO "tty_init_dev: ldisc open failed, "
				 "clearing slot %d\n", idx);
	release_tty(tty, idx);
	return ERR_PTR(retval);
}
描述1
initialize_tty_struct(tty, driver, idx);
void initialize_tty_struct(struct tty_struct *tty,
		struct tty_driver *driver, int idx)
{
	memset(tty, 0, sizeof(struct tty_struct));
	kref_init(&tty->kref);
	tty->magic = TTY_MAGIC;
	tty_ldisc_init(tty);
	tty->session = NULL;
	tty->pgrp = NULL;
	tty->overrun_time = jiffies;
	tty->buf.head = tty->buf.tail = NULL;
	tty_buffer_init(tty);
	mutex_init(&tty->termios_mutex);
	mutex_init(&tty->ldisc_mutex);
	init_waitqueue_head(&tty->write_wait);
	init_waitqueue_head(&tty->read_wait);
	INIT_WORK(&tty->hangup_work, do_tty_hangup);
	mutex_init(&tty->atomic_read_lock);
	mutex_init(&tty->atomic_write_lock);
	mutex_init(&tty->output_lock);
	mutex_init(&tty->echo_lock);
	spin_lock_init(&tty->read_lock);
	spin_lock_init(&tty->ctrl_lock);
	INIT_LIST_HEAD(&tty->tty_files);
	INIT_WORK(&tty->SAK_work, do_SAK_work);


	tty->driver = driver;
	tty->ops = driver->ops;
	tty->index = idx;
	tty_line_name(driver, idx, tty->name);
}
该函数完成对 struct tty_struct 结构中成员的初始化工作。需要注意的有几点:
1> tty_ldisc_init(tty); 该函数是对 struct tty_struct 结构中成员 struct tty_ldisc
的初始化
void tty_ldisc_init(struct tty_struct *tty)
{
	struct tty_ldisc *ld = tty_ldisc_get(N_TTY);
	if (IS_ERR(ld))
		panic("n_tty: init_tty");
	tty_ldisc_assign(tty, ld);
}


static struct tty_ldisc *tty_ldisc_get(int disc)
{
	struct tty_ldisc *ld;
	struct tty_ldisc_ops *ldops;


	if (disc < N_TTY || disc >= NR_LDISCS)
		return ERR_PTR(-EINVAL);


	/*
	 * 获取操作函数集
	 */
	ldops = get_ldops(disc);
	if (IS_ERR(ldops)) {
		request_module("tty-ldisc-%d", disc);
		ldops = get_ldops(disc);
		if (IS_ERR(ldops))
			return ERR_CAST(ldops);
	}
	
	/*
	 * 分配内存
	 */
	ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL);
	if (ld == NULL) {
		put_ldops(ldops);
		return ERR_PTR(-ENOMEM);
	}


	ld->ops = ldops;
	atomic_set(&ld->users, 1);
	return ld;
}


static struct tty_ldisc_ops *get_ldops(int disc)
{
	unsigned long flags;
	struct tty_ldisc_ops *ldops, *ret;


	spin_lock_irqsave(&tty_ldisc_lock, flags);
	ret = ERR_PTR(-EINVAL);
	
	/*
	 * tty_ldiscs是一个全局变量,它的值是什么呢?
	 */
	ldops = tty_ldiscs[disc];
	if (ldops) {
		ret = ERR_PTR(-EAGAIN);
		if (try_module_get(ldops->owner)) {
			ldops->refcount++;
			ret = ldops;
		}
	}
	spin_unlock_irqrestore(&tty_ldisc_lock, flags);
	return ret;
}
看一下 tty_ldiscs 变量的定义
static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS];
看一下对它的赋值
console_init();
	->tty_ldisc_begin();
		->tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
			int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc)
			{
				unsigned long flags;
				int ret = 0;
			
				if (disc < N_TTY || disc >= NR_LDISCS)
					return -EINVAL;
			
				spin_lock_irqsave(&tty_ldisc_lock, flags);
				/*
				 * 将新的值注册进tty_ldiscs中
				 */
				tty_ldiscs[disc] = new_ldisc;
				new_ldisc->num = disc;
				new_ldisc->refcount = 0;
				spin_unlock_irqrestore(&tty_ldisc_lock, flags);
			
				return ret;
			}
tty_ldisc_N_TTY 是一个全局变量
	struct tty_ldisc_ops tty_ldisc_N_TTY = {
		.magic           = TTY_LDISC_MAGIC,
		.name            = "n_tty",
		.open            = n_tty_open,
		.close           = n_tty_close,
		.flush_buffer    = n_tty_flush_buffer,
		.chars_in_buffer = n_tty_chars_in_buffer,
		.read            = n_tty_read,
		.write           = n_tty_write,
		.ioctl           = n_tty_ioctl,
		.set_termios     = n_tty_set_termios,
		.poll            = n_tty_poll,
		.receive_buf     = n_tty_receive_buf,
		.write_wakeup    = n_tty_write_wakeup
	};

2> 注意下面的两条语句
	tty->driver = driver;
	tty->ops = driver->ops;

initialize_tty_struct 函数分析完了。


描述2:
retval = tty_driver_install_tty(driver, tty);
static int tty_driver_install_tty(struct tty_driver *driver,
						struct tty_struct *tty)
{
	int idx = tty->index;


	if (driver->ops->install)
		return driver->ops->install(driver, tty);


	if (tty_init_termios(tty) == 0) {
		tty_driver_kref_get(driver);
		tty->count++;
		
		/*
		 *这里将tty安装到driver中
		 */
		driver->ttys[idx] = tty;
		return 0;
	}
	return -ENOMEM;
}

描述3:
retval = tty_ldisc_setup(tty, tty->link/*NULL*/);
int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
{
	struct tty_ldisc *ld = tty->ldisc;
	int retval;


	retval = tty_ldisc_open(tty, ld);
	if (retval)
		return retval;


	if (o_tty) {
		retval = tty_ldisc_open(o_tty, o_tty->ldisc);
		if (retval) {
			tty_ldisc_close(tty, ld);
			return retval;
		}
		tty_ldisc_enable(o_tty);
	}
	tty_ldisc_enable(tty);
	return 0;
}


static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)
{
	WARN_ON(test_and_set_bit(TTY_LDISC_OPEN, &tty->flags));
	if (ld->ops->open)
		return ld->ops->open(tty);
	return 0;
}
ld->ops->open 即 tty_ldisc_N_TTY->n_tty_open 函数


static int n_tty_open(struct tty_struct *tty)
{
	if (!tty)
		return -EINVAL;


	/* These are ugly. Currently a malloc failure here can panic */
	if (!tty->read_buf) {
		/* 为 read_buf 分配内存空间*/
		tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
		if (!tty->read_buf)
			return -ENOMEM;
	}
	if (!tty->echo_buf) {
		/* 为 echo_buf 分配内存空间*/
		tty->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);


		if (!tty->echo_buf)
			return -ENOMEM;
	}
	reset_buffer_flags(tty);
	tty->column = 0;
	
	/* 为uart设备设置 termios */
	n_tty_set_termios(tty, NULL);
	tty->minimum_to_wake = 1;
	tty->closing = 0;
	return 0;
}

tty_init_dev 函数分析完了。


描述D:
	if (tty->ops->open)
		retval = tty->ops->open(tty, filp);
tty->ops->open 即 全局变量[uart_ops]里的open成员即:uart_open 函数
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;
	struct tty_port *port;
	int retval, line = tty->index;


	BUG_ON(!kernel_locked());
	pr_debug("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 state 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 state->port.tty will always contain something
	 * reasonable.
	 */
	state = uart_get(drv, line);
	if (IS_ERR(state)) {
		retval = PTR_ERR(state);
		goto fail;
	}
	port = &state->port;


	/*
	 * 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;
	state->uart_port->state = state;
	tty->low_latency = (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0;
	tty->alt_speed = 0;
	tty_port_tty_set(port, tty);


	/*
	 * If the port is in the middle of closing, bail out now.
	 */
	if (tty_hung_up_p(filp)) {
		retval = -EAGAIN;
		port->count--;
		mutex_unlock(&port->mutex);
		goto fail;
	}


	/*
	 * Make sure the device is in D0 state.
	 */
	if (port->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(&port->mutex);


	/*
	 * If this is the first open to succeed, adjust things to suit.
	 */
	if (retval == 0 && !(port->flags & ASYNC_NORMAL_ACTIVE)) {
		set_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);


		uart_update_termios(state);
	}


fail:
	return retval;
}
这个函数其实并不复杂,只需要理清这几个变量之间的关系即可:
drv、state、port、tty
它们之间的关系请参阅后面的图片来看。
主要关注一下
retval = uart_startup(state, 0);
static int uart_startup(struct uart_state *state, int init_hw)
{
	struct uart_port *uport = state->uart_port;
	struct tty_port *port = &state->port;
	unsigned long page;
	int retval = 0;


	if (port->flags & ASYNC_INITIALIZED)
		return 0;


	/*
	 * Set the TTY IO error marker - we will only clear this
	 * once we have successfully opened the port.  Also set
	 * up the tty->alt_speed kludge
	 */
	set_bit(TTY_IO_ERROR, &port->tty->flags);


	if (uport->type == PORT_UNKNOWN)
		return 0;


	/*
	 * Initialise and allocate the transmit and temporary
	 * buffer.
	 */
	if (!state->xmit.buf) {
		/* This is protected by the per port mutex */
		/*
		 * 为 xmit.buf 分配内存空间
		 */
		page = get_zeroed_page(GFP_KERNEL);
		if (!page)
			return -ENOMEM;


		state->xmit.buf = (unsigned char *) page;
		uart_circ_clear(&state->xmit);
	}


	/*
	 * 调用 uport->ops->startup 函数,即 s3c24xx_serial_startup 函数
	 */
	retval = uport->ops->startup(uport);
	if (retval == 0) {
		if (init_hw) {
			/*
			 * Initialise the hardware port settings.
			 */
			uart_change_speed(state, NULL);


			/*
			 * Setup the RTS and DTR signals once the
			 * port is open and ready to respond.
			 */
			if (port->tty->termios->c_cflag & CBAUD)
				uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR);
		}


		if (port->flags & ASYNC_CTS_FLOW) {
			spin_lock_irq(&uport->lock);
			if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS))
				port->tty->hw_stopped = 1;
			spin_unlock_irq(&uport->lock);
		}


		set_bit(ASYNCB_INITIALIZED, &port->flags);


		clear_bit(TTY_IO_ERROR, &port->tty->flags);
	}


	if (retval && capable(CAP_SYS_ADMIN))
		retval = 0;


	return retval;
}

终于到我们自己写的函数了
static int s3c24xx_serial_startup(struct uart_port *port)
{
	struct s3c24xx_uart_port *ourport = to_ourport(port);
	int ret;


	dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n",
	    port->mapbase, port->membase);


	rx_enabled(port) = 1;


	/*
	 * 注册接收中断处理函数
	 */
	ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0,
			  s3c24xx_serial_portname(port), ourport);


	if (ret != 0) {
		printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq);
		return ret;
	}


	ourport->rx_claimed = 1;


	dbg("requesting tx irq...\n");


	tx_enabled(port) = 1;


	/*
	 * 注册发送中断处理函数
	 */
	ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0,
			  s3c24xx_serial_portname(port), ourport);


	if (ret) {
		printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq);
		goto err;
	}


	ourport->tx_claimed = 1;


	dbg("s3c24xx_serial_startup ok\n");


	/* the port reset code should have done the correct
	 * register setup for the port controls */


	return ret;


 err:
	s3c24xx_serial_shutdown(port);
	return ret;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值