接上一篇,我们已经注册了一个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 知道线路规划是怎么样