对tty节点的操作tty_open

       接上一篇,我们已经注册了一个tty设备驱动并且在/dev生成相应的设备节点,我们对这个节点的操作open,read,write等对应的就是

static const struct file_operations tty_fops = {
	.llseek		= no_llseek,
	.read		= tty_read,
	.write		= tty_write,
	.poll		= tty_poll,
	.unlocked_ioctl	= tty_ioctl,
	.compat_ioctl	= tty_compat_ioctl,
	.open		= tty_open,
	.release	= tty_release,
	.fasync		= tty_fasync,
};

下面我们来一个个的分析:

 

tty_open

static int tty_open(struct inode *inode, struct file *filp)
{
    dev_t device = inode->i_rdev;
	noctty = filp->f_flags & O_NOCTTY;

	/* This is protected by the tty_mutex */
	tty = tty_open_current_tty(device, filp);
	if (IS_ERR(tty)) {
		goto err_unlock;
	} else if (!tty) {
		driver = tty_lookup_driver(device, filp, &noctty, &index);
		/* check whether we're reopening an existing tty */
		tty = tty_driver_lookup_tty(driver, inode, index);
	}

	if (tty) {	
		retval = tty_reopen(tty);
	} else	/* Returns with the tty_lock held for now */
		tty = tty_init_dev(driver, index);
		
	if (tty->ops->open)
		retval = tty->ops->open(tty, filp);
	else
		retval = -ENODEV;
	filp->f_flags = saved_flags;

	return 0;
}

删除很多不太重要的代码,剩下主干代码。这里最重要的功能是tty->ops->open(tty, filp); 调用tty的ops。我们要想办法知道这个ops的实现。同时tty = tty_init_dev(driver, index);这一句也是十分的重要,这里面对tty的初始化,启用线路规划功能,里面包含的信息十分的多。

static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp)
{
	struct tty_struct *tty;

	if (device != MKDEV(TTYAUX_MAJOR, 0))
		return NULL;

	tty = get_current_tty();
	if (!tty)
		return ERR_PTR(-ENXIO);

	filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
	/* noctty = 1; */
	tty_kref_put(tty);
	/* FIXME: we put a reference and return a TTY! */
	/* This is only safe because the caller holds tty_mutex */
	return tty;
}

cdc的tty主设备好和TTYAUX_MAJOR不同首先就返回了。其他的代码是正对/dev/tty这个节点的。

static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp,
		int *noctty, int *index)
    driver = get_tty_driver(device, index);
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;
}

注册驱动时候我们把tty_driver放入到tty_driver的链表中,现在我们把它取出来,判断的依据是设备号区间。

static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver,
		struct inode *inode, int idx)
{
	if (driver->ops->lookup)
		return driver->ops->lookup(driver, inode, idx);

	return driver->ttys[idx];
}

这里有driver->ops->lookup存在就执行,不存在就取driver->ttys对应的一个,cdc没有lookup所以取对应的ttys,在一起给出的是一个tty_struct 的指针,ttys是在alloc_tty_driver中分配内存的,但是单个只能的值还是NULL。

上面两个就是得到tty_driver,index,和ttys中对应的tty_struct ,以后应该再给这个ttys数组复值。

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

    tty = alloc_tty_struct();

    initialize_tty_struct(tty, driver, idx);

    tty_lock(tty);
    retval = tty_driver_install_tty(driver, tty);

    if (!tty->port)
        tty->port = driver->ports[idx];

    tty->port->itty = tty;


    retval = tty_ldisc_setup(tty, tty->link);

    return tty;
}

这个函数是对tty的初始化,设置线路规划。

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;
	mutex_init(&tty->legacy_mutex);
	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_write_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);
	tty->dev = tty_get_device(tty);
}

初始化tty,初始化线路规划,tty的ops赋值为driver的ops。

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 = ld;
}
static struct tty_ldisc *tty_ldisc_get(int disc)
{
	struct tty_ldisc *ld;
	struct tty_ldisc_ops *ldops;

	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);
	init_waitqueue_head(&ld->wq_idle);

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

	raw_spin_lock_irqsave(&tty_ldisc_lock, flags);
	ret = ERR_PTR(-EINVAL);
	ldops = tty_ldiscs[disc];
	if (ldops) {
		ret = ERR_PTR(-EAGAIN);
		if (try_module_get(ldops->owner)) {
			ldops->refcount++;
			ret = ldops;
		}
	}
	raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
	return ret;
}

线路规划的设置就是tty的ldisc的ops指向tty_ldiscs数组中关于tty设备的线路规划ops。这个tty设备线路的ops的注册是在console_init中。

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
};

继续分析tty_init_dev

static int tty_driver_install_tty(struct tty_driver *driver,
						struct tty_struct *tty)
{
	return driver->ops->install ? driver->ops->install(driver, tty) :
		tty_standard_install(driver, tty);
}
int tty_standard_install(struct tty_driver *driver, struct tty_struct *tty)
{
	int ret = tty_init_termios(tty);
	if (ret)
		return ret;

	tty_driver_kref_get(driver);
	tty->count++;
	driver->ttys[tty->index] = tty;
	return 0;
}

这里我们采用tty_standard_install 继续向下看就是初始化termios,同时让tty_driver的ttys对应的一项指向tty。tty_init_termios暂时不分析

int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
    struct tty_ldisc *ld = tty->ldisc;
	tty_ldisc_open(tty, ld);
        ld->ops->open(tty)
	tty_ldisc_enable(tty);
        wake_up(&tty_ldisc_wait);
static int n_tty_open(struct tty_struct *tty)
{
	struct n_tty_data *ldata;

	ldata = kzalloc(sizeof(*ldata), GFP_KERNEL);
	if (!ldata)
		goto err;

	ldata->overrun_time = jiffies;
	mutex_init(&ldata->atomic_read_lock);
	mutex_init(&ldata->output_lock);
	mutex_init(&ldata->echo_lock);
	raw_spin_lock_init(&ldata->read_lock);

	/* These are ugly. Currently a malloc failure here can panic */
	ldata->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
	ldata->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
	if (!ldata->read_buf || !ldata->echo_buf)
		goto err_free_bufs;

	tty->disc_data = ldata;
	reset_buffer_flags(tty->disc_data);
	ldata->column = 0;
	tty->minimum_to_wake = 1;
	tty->closing = 0;
	/* indicate buffer work may resume */
	clear_bit(TTY_LDISC_HALTED, &tty->flags);
	n_tty_set_termios(tty, NULL);
	tty_unthrottle(tty);

	return 0;
}

这里是把这个tty的线路规划给open起来了,同时相关的成员变量也初始化了。到这里tty_init_dev就分析玩了。

对于tty_open中有一句retval = tty->ops->open(tty, filp); 上面分析中tty->ops是等于driver->ops的,所以等于调用driver->ops->open(tty, filp)。

总结一下:

通过这个tty_open我们可以知道几个重要的信息如下:

1、 tty->ops=driver->ops 这里我们知道怎么从节点操作到注册的cdc驱动中,

2、tty->ldisc->ops=tty_ldisc_N_TTY 知道线路规划是怎么样

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值