1. 通过设备文件节点调到注册的 tty_fops
有关设备结点的文件系统调用,可以看char device:
tty_register_driver -> cdev_init(&driver->cdev, &tty_fops);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,
};
2. tty_open 的具体实现
当open tty设备结点时,tty_open会被调用。
static int tty_open(struct inode *inode, struct file *filp)
{
struct tty_struct *tty;
int noctty, retval;
struct tty_driver *driver = NULL;
int index;
dev_t device = inode->i_rdev;
unsigned saved_flags = filp->f_flags;
/*分配tty_file_private结构体内存,并赋值到file->private_data*/
tty_alloc_file(filp);
/*device != MKDEV(TTYAUX_MAJOR【5】, 0)
*tty_register_driver中动态生成的dev,不是5,所以return NULL
*/
tty = tty_open_current_tty(device, filp);
if(!tty){
driver = tty_lookup_driver(device, filp, &noctty, &index);
tty = tty_driver_lookup_tty(driver, inode, index);
}
if(!tty)
tty = tty_init_dev(driver, index);
tty_add_file(tty, filp);
if (tty->ops->open)
retval = tty->ops->open(tty, filp);
return 0;
}
2.1 tty_lookup_driver
/**
* tty_lookup_driver - lookup a tty driver for a given device file
* @device: device number
* @filp: file pointer to tty
* @noctty: set if the device should not become a controlling tty
* @index: index for the device in the @return driver
* @return: driver for this inode (with increased refcount)
*/
static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp,
int *noctty, int *index)
{
struct tty_driver *driver;
driver = get_tty_driver(device, index);
return driver;
}
2.1.2 get_tty_driver
/** 通过 dev_t 就找到了对应的tty_driver
* get_tty_driver - find device of a tty
* @dev_t: device identifier
* @index: returns the index of the tty
*
* This routine returns a tty driver structure, given a device number
* and also passes back the index number.
*/
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;
}
2.2 tty_driver_lookup_tty
/*** tty_driver_lookup_tty() - find an existing tty, if any
* @driver: the driver for the tty
* @idx: the minor number
*
* Return the tty, if found or ERR_PTR() otherwise.
*/
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];
}
2.3 tty_init_dev
/**
* tty_init_dev - initialise a tty device
* @driver: tty driver we are opening a device on
* @idx: device index
* @ret_tty: returned tty structure
*/
struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
{
struct tty_struct *tty;
int retval;
/*allocated and initialized tty_struct to 0*/
tty = alloc_tty_struct();
if (!tty) {
retval = -ENOMEM;
goto err_module_put;
}
initialize_tty_struct(tty, driver, idx);
retval = tty_driver_install_tty(driver, tty);
if (retval < 0)
goto err_deinit_tty;
/*
* 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.
*/
retval = tty_ldisc_setup(tty, tty->link);
if (retval)
goto err_release_tty;
return tty;
}
2.3.1 initialize_tty_struct
/**
* initialize_tty_struct
* @tty: tty to initialize
*
* This subroutine initializes a tty structure that has been newly
* allocated.
*/
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->session = NULL;
tty->pgrp = NULL;
tty->overrun_time = jiffies;
/*有关tty_buffer*/
tty_buffer_init(tty);
/*有关mutex*/
mutex_init(&tty->termios_mutex);
mutex_init(&tty->ldisc_mutex);
mutex_init(&tty->atomic_read_lock);
mutex_init(&tty->atomic_write_lock);
mutex_init(&tty->output_lock);
mutex_init(&tty->echo_lock);
/*有关waitqueue_head*/
init_waitqueue_head(&tty->write_wait);
init_waitqueue_head(&tty->read_wait);
/*有关work*/
INIT_WORK(&tty->hangup_work, do_tty_hangup);
INIT_WORK(&tty->SAK_work, do_SAK_work);
/*有关spin lock*/
spin_lock_init(&tty->read_lock);
spin_lock_init(&tty->ctrl_lock);
INIT_LIST_HEAD(&tty->tty_files);
/*有关行规*/
tty_ldisc_init(tty);
tty_line_name(driver, idx, tty->name);
tty->driver = driver;
tty->ops = driver->ops;
tty->index = idx;
tty->dev = tty_get_device(tty);
}
2.3.1.1有关行规
/*** tty_ldisc_init - ldisc setup for new tty
* @tty: tty being allocated
*
* Set up the line discipline objects for a newly allocated tty. Note that
* the tty structure is not completely set up when this call is made.
*/
void tty_ldisc_init(struct tty_struct *tty)
{
struct tty_ldisc *ld = tty_ldisc_get(N_TTY);
tty_ldisc_assign(tty, ld);
}
void tty_ldisc_init(struct tty_struct *tty)
{
struct tty_ldisc *ld = tty_ldisc_get(N_TTY);
tty_ldisc_assign(tty, ld);
}
static struct tty_ldisc *tty_ldisc_get(int disc)
{
struct tty_ldisc *ld;
struct tty_ldisc_ops *ldops;
/*从全局变量tty_ldiscs中根据disc得到tty_ldisc_ops *
* 但是指向的内容是什么?
*/
ldops = get_ldops(disc);
/*create tty_ldisc*/
ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL);
ld->ops = ldops;
return ld;
}
以下调用序列tty_ldiscs[N_TTY] = tty_ldisc_N_TTY;
start_kernel -> console_init -> tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
void tty_ldisc_begin(void)
{
/* Setup the default TTY line discipline. */
(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
}
2.3.2 tty_driver_install_tty
/**
* tty_driver_install_tty() - install a tty entry in the driver
* @driver: the driver for the tty
* @tty: the tty
*/
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;
}
2.3.3 tty_ldisc_setup
/* tty_ldisc_setup - open line discipline
* @tty: tty being shut down
* @o_tty: pair tty for pty/tty pairs
*/
int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
{
struct tty_ldisc *ld = tty->ldisc;
int retval;
/*调用ldisc:open 函数*/
retval = tty_ldisc_open(tty, ld);
/*设置tty flags*/
tty_ldisc_enable(tty);
return 0;
}
static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)
{
ld->ops->open(tty);
}
static int n_tty_open(struct tty_struct *tty)
{
/* These are ugly. Currently a malloc failure here can panic */
if (!tty->read_buf) {
tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
if (!tty->read_buf)
return -ENOMEM;
}
if (!tty->echo_buf) {
tty->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
}
reset_buffer_flags(tty);
tty_unthrottle(tty);
tty->column = 0;
n_tty_set_termios(tty, NULL);
tty->minimum_to_wake = 1;
tty->closing = 0;
return 0;
}
2.4 call into tty_operations open
tty->ops->open(tty, filp)
static int smd_tty_open(struct tty_struct *tty, struct file *f)
{
int n=tty->index;
struct smd_tty_info *tty_info = &driver_info->smd_tty[n];
return tty_port_open(&tty_info->port, tty, f);
}
2.4.1 tty_port_open
int tty_port_open(struct tty_port *port, struct tty_struct *tty,
struct file *filp)
{
tty_port_tty_set(port, tty);/*set the tty member of tty_port to this tty*/
port->ops->activate(port, tty);/*call into the user specific activate function*/
return tty_port_block_til_ready(port, tty, filp);/*blocked until port ready*/
}
3.总结
tty_open的主要功能是通过 dev_t查找到注册的 tty_driver, 然后创建 tty_struct,创建 tty_ldisc, 当然还创建了read buffer 等。
tty相关的operations有file, tty, lisc, and tty_port 4种分别是在什么情况下调用的?
不管怎样file的open先被调用,然后是lisc的open,然后是tty driver的,最后是tty_port的。从初始化的过程和内容看核心的数据结构应该是tty_struct.