分类: LINUX
2013-11-15 15:59:40
//下面对tty_open函数进行分析,open函数的具体操作就是初始化tty_struct结构并作为赋值filp->private_data,为后续的操作做准备
/**
* tty_open - open a tty device
* @inode: inode of device file
* @filp: file pointer to tty
*
* tty_open and tty_release keep up the tty count that contains the
* number of opens done on a tty. We cannot use the inode-count, as
* different inodes might point to the same tty.
*
* Open-counting is needed for pty masters, as well as for keeping
* track of serial lines: DTR is dropped when the last close happens.
* (This is not done solely through tty->count, now. - Ted 1/27/92)
*
* The termios state of a pty is reset on first open so that
* settings don't persist across reuse.
*
* Locking: tty_mutex protects tty, get_tty_driver and init_dev work.
* tty->count should protect the rest.
* ->siglock protects ->signal/->sighand
*/
static int __tty_open(struct inode *inode, struct file *filp)
{
struct tty_struct *tty;
int noctty, retval;
struct tty_driver *driver;
int index;
dev_t device = inode->i_rdev;//得到设备的设备号
unsigned short saved_flags = filp->f_flags;
nonseekable_open(inode, filp);//主要是设置file->f_mode的一些标志使文件不可定位
retry_open:
noctty = filp->f_flags & O_NOCTTY;//是否终端绑定到指定的进程
index = -1;
retval = 0;
mutex_lock(&tty_mutex);
//这里表示的是设备号为(5,0)及/dev/tty是进程控制终端的别名,因此是一个要找到真正的设备
if (device == MKDEV(TTYAUX_MAJOR, 0)) {
tty = get_current_tty();
if (!tty) {//进程没有控制终端则直接返回
mutex_unlock(&tty_mutex);
return -ENXIO;
}
driver = tty->driver;
index = tty->index;//设备在驱动中的driver->ttys中的位置
filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
/* noctty = 1; */
goto got_driver;
}
#ifdef CONFIG_VT
//这里表示的是设备号为(4,0)及/dev/tty0 是当前进程使用虚拟终端的别名,因此是一个要找到真正的设备
if (device == MKDEV(TTY_MAJOR, 0)) {
extern struct tty_driver *console_driver;//控制台的驱动
driver = console_driver;
index = fg_console;//表示当前控制台索引
noctty = 1;
goto got_driver;
}
#endif
//这里表示的是设备号为(5,1)及/dev/console 是指系统控制台,因此是一个要找到真正的设备
if (device == MKDEV(TTYAUX_MAJOR, 1)) {
driver = console_device(&index);
if (driver) {
/* Don't let /dev/console block */
filp->f_flags |= O_NONBLOCK;
noctty = 1;
goto got_driver;
}
mutex_unlock(&tty_mutex);
return -ENODEV;
}
//依据设备的设备号来确定设备在具体驱动中的索引。
driver = get_tty_driver(device, &index);
if (!driver) {
mutex_unlock(&tty_mutex);
return -ENODEV;
}
/*到此位置我们的目的是确定设备驱动和设备在驱动中的位置以及设备是否和进程绑定*/
got_driver:
//init_dev 初始化一个tty_struct 结构 具体在下面分析
retval = init_dev(driver, index, &tty);
mutex_unlock(&tty_mutex);
if (retval)
return retval;
filp->private_data = tty;//把tty_struct结构作为file->private_data这也是open函数的主要目的
file_move(filp, &tty->tty_files);
check_tty_count(tty, "tty_open");//统计tty设备打开的次数
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) {
if (tty->ops->open)
retval = tty->ops->open(tty, filp);//调用tty_operations中的回调函数tty驱动中我们要实现的%%%%%
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
release_dev(filp);//init_dev的反操作
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;
}
/* BKL pushdown: scary code avoidance wrapper */
static int tty_open(struct inode *inode, struct file *filp)
{
int ret;
lock_kernel();
ret = __tty_open(inode, filp);
unlock_kernel();
return ret;
}
/**
* init_dev - initialise a tty device
* @driver: tty driver we are opening a device on
* @idx: device index
* @tty: returned tty structure
*
* Prepare a tty device. This may not be a "new" clean device but
* could also be an active device. The pty drivers require special
* handling because of this.
*
* Locking:
* The function is called under the tty_mutex, which
* protects us from the tty struct or driver itself going away.
*
* On exit the tty device has the line discipline attached and
* a reference count of 1. If a pair was created for pty/tty use
* and the other was a pty master then it too has a reference count of 1.
*
* WSH 06/09/97: Rewritten to remove races and properly clean up after a
* failed open. The new code protects the open with a mutex, so it's
* really quite straightforward. The mutex locking can probably be
* relaxed for the (most common) case of reopening a tty.
*/
static int init_dev(struct tty_driver *driver, int idx,
struct tty_struct **ret_tty)
{
struct tty_struct *tty, *o_tty;
struct ktermios *tp, **tp_loc, *o_tp, **o_tp_loc;
struct ktermios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc;
int retval = 0;
/* check whether we're reopening an existing tty */
if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {//伪终端初始化,从设备。
tty = devpts_get_tty(idx);
/*
* If we don't have a tty here on a slave open, it's because
* the master already started the close process and there's
* no relation between devpts file and tty anymore.
*/
if (!tty && driver->subtype == PTY_TYPE_SLAVE) {
retval = -EIO;
goto end_init;
}
/*
* It's safe from now on because init_dev() is called with
* tty_mutex held and release_dev() won't change tty->count
* or tty->flags without having to grab tty_mutex
*/
if (tty && driver->subtype == PTY_TYPE_MASTER)
tty = tty->link;
} else {
tty = driver->ttys[idx];
}
if (tty) goto fast_track;//fast_track表示设备不是第一次打开
/*
* 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)) {
retval = -ENODEV;
goto end_init;
}
o_tty = NULL;
tp = o_tp = NULL;
ltp = o_ltp = NULL;
tty = alloc_tty_struct();
if (!tty)
goto fail_no_mem;
initialize_tty_struct(tty);
tty->driver = driver;
tty->ops = driver->ops;
tty->index = idx;
tty_line_name(driver, idx, tty->name);
if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {
tp_loc = &tty->termios;
ltp_loc = &tty->termios_locked;
} else {
tp_loc = &driver->termios[idx];
ltp_loc = &driver->termios_locked[idx];
}
if (!*tp_loc) {
tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL);
if (!tp)
goto free_mem_out;
*tp = driver->init_termios;
}
if (!*ltp_loc) {
ltp = kzalloc(sizeof(struct ktermios), GFP_KERNEL);
if (!ltp)
goto free_mem_out;
}
if (driver->type == TTY_DRIVER_TYPE_PTY) {
o_tty = alloc_tty_struct();
if (!o_tty)
goto free_mem_out;
initialize_tty_struct(o_tty);
o_tty->driver = driver->other;
o_tty->ops = driver->ops;
o_tty->index = idx;
tty_line_name(driver->other, idx, o_tty->name);
if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {
o_tp_loc = &o_tty->termios;
o_ltp_loc = &o_tty->termios_locked;
} else {
o_tp_loc = &driver->other->termios[idx];
o_ltp_loc = &driver->other->termios_locked[idx];
}
if (!*o_tp_loc) {
o_tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL);
if (!o_tp)
goto free_mem_out;
*o_tp = driver->other->init_termios;
}
if (!*o_ltp_loc) {
o_ltp = kzalloc(sizeof(struct ktermios), GFP_KERNEL);
if (!o_ltp)
goto free_mem_out;
}
/*
* Everything allocated ... set up the o_tty structure.
*/
if (!(driver->other->flags & TTY_DRIVER_DEVPTS_MEM))
driver->other->ttys[idx] = o_tty;
if (!*o_tp_loc)
*o_tp_loc = o_tp;
if (!*o_ltp_loc)
*o_ltp_loc = o_ltp;
o_tty->termios = *o_tp_loc;
o_tty->termios_locked = *o_ltp_loc;
driver->other->refcount++;
if (driver->subtype == PTY_TYPE_MASTER)
o_tty->count++;
/* Establish the links in both directions */
tty->link = o_tty;
o_tty->link = tty;
}
/*
* All structures have been allocated, so now we install them.
* Failures after this point use release_tty to clean up, so
* there's no need to null out the local pointers.
*/
if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM))
driver->ttys[idx] = tty;
if (!*tp_loc)
*tp_loc = tp;
if (!*ltp_loc)
*ltp_loc = ltp;
tty->termios = *tp_loc;
tty->termios_locked = *ltp_loc;
/* Compatibility until drivers always set this */
tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios);
tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios);
driver->refcount++;
tty->count++;
/*
* 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, o_tty);//打开线路规程
if (retval)
goto release_mem_out;
goto success;
/*
* This fast open can be used if the tty is already open.
* No memory is allocated, and the only failures are from
* attempting to open a closing tty or attempting multiple
* opens on a pty master.
*/
fast_track:
if (test_bit(TTY_CLOSING, &tty->flags)) {
retval = -EIO;
goto end_init;
}
if (driver->type == TTY_DRIVER_TYPE_PTY &&
driver->subtype == PTY_TYPE_MASTER) {
/*
* special case for PTY masters: only one open permitted,
* and the slave side open count is incremented as well.
*/
if (tty->count) {
retval = -EIO;
goto end_init;
}
tty->link->count++;
}
tty->count++;
tty->driver = driver; /* N.B. why do this every time?? */
/* FIXME */
if (!test_bit(TTY_LDISC, &tty->flags))
printk(KERN_ERR "init_dev but no ldisc/n");
success:
*ret_tty = tty;
/* All paths come through here to release the mutex */
end_init:
return retval;
/* Release locally allocated memory ... nothing placed in slots */
free_mem_out:
kfree(o_tp);
if (o_tty)
free_tty_struct(o_tty);
kfree(ltp);
kfree(tp);
free_tty_struct(tty);
fail_no_mem:
module_put(driver->owner);
retval = -ENOMEM;
goto end_init;
/* call the tty release_tty routine to clean out this slot */
release_mem_out:
if (printk_ratelimit())
printk(KERN_INFO "init_dev: ldisc open failed, "
"clearing slot %d/n", idx);
release_tty(tty, idx);
goto end_init;