终端设备驱动之tty_write函数

来源:互联网 发布:希腊神话电影 知乎 编辑:程序博客网 时间:2024/05/22 04:25

1、tty_write函数源码:

static ssize_t tty_write(struct file * file, const char * buf, size_t count,

loff_t *ppos)
{
int is_console;
struct tty_struct * tty;
struct inode *inode = file->f_dentry->d_inode;


/* Can't seek (pwrite) on ttys.  */
if (ppos != &file->f_pos)
return -ESPIPE;


/*
*      For now, we redirect writes from /dev/console as
*      well as /dev/tty0.
*/
inode = file->f_dentry->d_inode;
is_console = (inode->i_rdev == SYSCONS_DEV ||
     inode->i_rdev == CONSOLE_DEV);


if (is_console) {
struct file *p = NULL;


spin_lock(&redirect_lock);
if (redirect) {
get_file(redirect);
p = redirect;
}
spin_unlock(&redirect_lock);


if (p) {
ssize_t res = p->f_op->write(p, buf, count, &p->f_pos);
fput(p);
return res;
}
}


tty = (struct tty_struct *)file->private_data;得到tty_struct结构体
if (tty_paranoia_check(tty, inode->i_rdev, "tty_write"))
return -EIO;
if (!tty || !tty->driver.write || (test_bit(TTY_IO_ERROR, &tty->flags)))
return -EIO;
#if 0
if (!is_console && L_TOSTOP(tty) && (tty->pgrp > 0) &&
   (current->tty == tty) && (tty->pgrp != current->pgrp)) {
if (is_orphaned_pgrp(current->pgrp))
return -EIO;
if (!is_ignored(SIGTTOU)) {
(void) kill_pg(current->pgrp, SIGTTOU, 1);
return -ERESTARTSYS;
}
}
#endif
if (!tty->ldisc.write)
return -EIO;
return do_tty_write(tty->ldisc.write, tty, file,
   (const unsigned char *)buf, count);

}

调用do_tty_write函数,在此函数中调用线路规程中的write函数,此函数源码如下:

2、do_tty_write函数源码:

/*
 * Split writes up in sane blocksizes to avoid
 * denial-of-service type attacks
 */
static inline ssize_t do_tty_write(
ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),
struct tty_struct *tty,
struct file *file,
const unsigned char *buf,
size_t count)
{
ssize_t ret = 0, written = 0;

if (file->f_flags & O_NONBLOCK) {
if (down_trylock(&tty->atomic_write))
return -EAGAIN;
}
else {
if (down_interruptible(&tty->atomic_write))
return -ERESTARTSYS;
}
if ( test_bit(TTY_NO_WRITE_SPLIT, &tty->flags) ) {
lock_kernel();
written = write(tty, file, buf, count);
unlock_kernel();


} else {
for (;;) {
unsigned long size = MAX(PAGE_SIZE*2,16384);
if (size > count)
size = count;
lock_kernel();
ret = write(tty, file, buf, size);
unlock_kernel();

此函数是作为参数传进来的,现在进行调用。调用的实际是:


struct tty_ldisc tty_ldisc_N_TTY = {
TTY_LDISC_MAGIC,/* magic */
"n_tty",/* name */
0,/* num */
0,/* flags */
n_tty_open,/* open */
n_tty_close,/* close */
n_tty_flush_buffer,/* flush_buffer */
n_tty_chars_in_buffer,/* chars_in_buffer */
read_chan,/* read */
write_chan,/* write */
n_tty_ioctl,/* ioctl */
n_tty_set_termios,/* set_termios */
normal_poll,/* poll */
n_tty_receive_buf,/* receive_buf */
n_tty_receive_room,/* receive_room */
n_tty_write_wakeup/* write_wakeup */
};

源码如下:


static ssize_t
write_chan(struct tty_struct * tty, struct file * file,
 const unsigned char * buf, size_t nr)
{
const unsigned char *b = buf;
DECLARE_WAITQUEUE(wait, current);
int c;
ssize_t retval = 0;


/* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
if (L_TOSTOP(tty) && 
   file->f_dentry->d_inode->i_rdev != CONSOLE_DEV &&
   file->f_dentry->d_inode->i_rdev != SYSCONS_DEV) {
retval = tty_check_change(tty);
if (retval)
return retval;
}


add_wait_queue(&tty->write_wait, &wait); 等待队列
while (1) {
set_current_state(TASK_INTERRUPTIBLE);
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) {
retval = -EIO;
break;
}
if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) {
while (nr > 0) {
ssize_t num = opost_block(tty, b, nr);
if (num < 0) {
if (num == -EAGAIN)
break;
retval = num;
goto break_out;
}
b += num;
nr -= num;
if (nr == 0)
break;
get_user(c, b);得到用户空间要写入的数据。
if (opost(c, tty) < 0)
break;
b++; nr--;
}
if (tty->driver.flush_chars)
tty->driver.flush_chars(tty);
} else {
c = tty->driver.write(tty, 1, b, nr);

调用具体tty_driver结构体中的flush_chars函数或者write函数。
if (c < 0) {
retval = c;
goto break_out;
}
b += c;
nr -= c;
}
if (!nr)
break;
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
break;
}
schedule();
}
break_out:
current->state = TASK_RUNNING;
remove_wait_queue(&tty->write_wait, &wait);
return (b - buf) ? b - buf : retval;
}

无法写入时,要睡眠等待。
if (ret <= 0)
break;
written += ret;
buf += ret;
count -= ret;
if (!count)
break;
ret = -ERESTARTSYS;
if (signal_pending(current))
break;
if (current->need_resched)
schedule();
}
}
if (written) {
file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
ret = written;
}
up(&tty->atomic_write);
return ret;
}

原创粉丝点击