与tty_write相关的数据成员
struct tty_struct {/*在tty_io层维护*/
unsigned char *write_buf;
int write_cnt;
}
1]数据从用户空间copy到内核空间
tty_write -> do_tty_write(ld->ops->write, tty, file, buf, count)
{
tty_write_lock(tty, file->f_flags & O_NDELAY);
/*
* We chunk up writes into a temporary buffer. This
* simplifies low-level drivers immensely, since they
* don't have locking issues and user mode accesses.
*
* But if TTY_NO_WRITE_SPLIT is set, we should use a
* big chunk-size..
*
* The default chunk-size is 2kB, because the NTTY
* layer has problems with bigger chunks. It will
* claim to be able to handle more characters than
* it actually does.
*
* FIXME: This can probably go away now except that 64K chunks
* are too likely to fail unless switched to vmalloc...
*/
chunk = 2048;
if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags))
chunk = 65536;
if (count < chunk)
chunk = count;
/*申请kernel memory for writing operation*/
/* write_buf/write_cnt is protected by the atomic_write_lock mutex */
if (tty->write_cnt < chunk) {
unsigned char *buf_chunk;
if (chunk < 1024)
chunk = 1024;
buf_chunk = kmalloc(chunk, GFP_KERNEL);
if (!buf_chunk) {
ret = -ENOMEM;
goto out;
}
kfree(tty->write_buf);
tty->write_cnt = chunk;
tty->write_buf = buf_chunk;
}
/*把来自用户空间的数据copy到内核空间*/
copy_from_user(tty->write_buf, buf, size);
/*调用行规write operation*/
write(tty, file, tty->write_buf, size);
tty_write_unlock(tty);
}
2]数据从内核write_buffer到行规/tty_driver
n_tty_write(struct tty_struct *tty, struct file *file,
const unsigned char *buf, size_t nr)
-> tty->ops->write(tty, b, nr);
-> {
smd_stream_write_avail(tty_info->ch);
smd_stream_write(tty_info->ch, buf, len);
}
tty 写操作流程
1. tty_io 层
1.1 tty_write
/**
* tty_write - write method for tty device file* @file: tty file pointer
* @buf: user data to write
* @count: bytes to write
* @ppos: unused
*
* Write data to a tty device via the line discipline.
*
* Locking:
* Locks the line discipline as required
* Writes to the tty driver are serialized by the atomic_write_lock
* and are then processed in chunks to the device. The line discipline
* write method will not be invoked in parallel for each device.
*/
static ssize_t tty_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct inode *inode = file->f_path.dentry->d_inode;
struct tty_struct *tty = file_tty(file);
struct tty_ldisc *ld;
ssize_t ret;
/*1. check the input arguments */
if (tty_paranoia_check(tty, inode, "tty_write"))return -EIO;
if (!tty || !tty->ops->write ||
(test_bit(TTY_IO_ERROR, &tty->flags)))
return -EIO;
/* Short term debug to catch buggy drivers */if (tty->ops->write_room == NULL)
printk(KERN_ERR "tty driver %s lacks a write_room method.\n",
tty->driver->name);
/*2. get the line discipline by tty */
ld = tty_ldisc_ref_wait(tty);/*3. call do_tty_write with ldops write operation */
if (!ld->ops->write)ret = -EIO;
else
ret = do_tty_write(ld->ops->write, tty, file, buf, count);
/*4. free a tty ldisc reference */
tty_ldisc_deref(ld);return ret;
}
1.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 char __user *buf,
size_t count)
{
ssize_t ret, written = 0;
unsigned int chunk;
/*1. get the atomic_write_lock */
ret = tty_write_lock(tty, file->f_flags & O_NDELAY);if (ret < 0)
return ret;
/*
* We chunk up writes into a temporary buffer. This
* simplifies low-level drivers immensely, since they
* don't have locking issues and user mode accesses.
*
* But if TTY_NO_WRITE_SPLIT is set, we should use a
* big chunk-size..
*
* The default chunk-size is 2kB, because the NTTY
* layer has problems with bigger chunks. It will
* claim to be able to handle more characters than
* it actually does.
*
* FIXME: This can probably go away now except that 64K chunks
* are too likely to fail unless switched to vmalloc...
*/
/*2. 分配 write_buf and update the member of write_cnt, write_buf*/
chunk = 2048;if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags))
chunk = 65536;
if (count < chunk)
chunk = count;
/* write_buf/write_cnt is protected by the atomic_write_lock mutex */
if (tty->write_cnt < chunk) {
unsigned char *buf_chunk;
if (chunk < 1024)
chunk = 1024;
buf_chunk = kmalloc(chunk, GFP_KERNEL);
if (!buf_chunk) {
ret = -ENOMEM;
goto out;
}
kfree(tty->write_buf);
tty->write_cnt = chunk;
tty->write_buf = buf_chunk;
}
/*3. Do the write .. */
for (;;) {size_t size = count;
if (size > chunk)
size = chunk;
ret = -EFAULT;
/*3.1 把来自用户空间的数据保存到分配的 write_buf内*/
if (copy_from_user(tty->write_buf, buf, size))break;
/*3.2 通过函数指针调用 n_tty_write*/
ret = write(tty, file, tty->write_buf, size);if (ret <= 0)
break;
written += ret;
buf += ret;
count -= ret;
if (!count)
break;
ret = -ERESTARTSYS;
/*signal pending,外界发送命令停止传送等情况*/
if (signal_pending(current))
break;
cond_resched();
}
/*4. 有数据写入,更新 inode的时间信息,估计就是最后一次写入时间*/
if (written) {struct inode *inode = file->f_path.dentry->d_inode;
inode->i_mtime = current_fs_time(inode->i_sb);
ret = written;
}
out:
/*5. 释放atomic_write_lock*/
tty_write_unlock(tty);return ret;
}
2. n_tty层
/**
* n_tty_write - write function for tty
* @tty: tty device
* @file: file object
* @buf: userspace buffer pointer
* @nr: size of I/O
*
* Write function of the terminal device. This is serialized with
* respect to other write callers but not to termios changes, reads
* and other such events. Since the receive code will echo characters,
* thus calling driver write methods, the output_lock is used in
* the output processing functions called here as well as in the
* echo processing function to protect the column state and space
* left in the buffer.
*
* This code must be sure never to sleep through a hangup.
*
* Locking: output_lock to protect column state and space left
* (note that the process_output*() functions take this
* lock themselves)
*/
static ssize_t n_tty_write(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;
/*不知这里的 waitqueue什么个意思?*/
add_wait_queue(&tty->write_wait, &wait);
while (1) {
set_current_state(TASK_INTERRUPTIBLE);
while (nr > 0) {
/*这里是多次写入的,如果期望写入的大小和返回值【实际写入的】不一致,继续写剩下的
*这有点流控的意思,不知道写操作的流控是怎样实现的?
*/
c = tty->ops->write(tty, b, nr);
if (c < 0) {
retval = c;
goto break_out;
}
if (!c)
break;
b += c;
nr -= c;
}
if (!nr)
break;
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
break;
}
schedule();
}
break_out:
__set_current_state(TASK_RUNNING);
remove_wait_queue(&tty->write_wait, &wait);
if (b - buf != nr && tty->fasync)
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
return (b - buf) ? b - buf : retval;
}
3. 硬件具体的层
static int smd_tty_write(struct tty_struct *tty,const unsigned char *buf, int len)
{
struct smd_tty_info *info = tty->driver_data;
int avail;
/* if we're writing to a packet channel we will
** never be able to write more data than there
** is currently space for
*/
avail = smd_write_avail(info->ch);
if (len > avail)
len = avail;
return smd_write(info->ch, buf, len);
}
4 总结
tty write操作分问3个层次 tty_io, n_tty, 设备具体的tty_driver.从中个层次间的调用关系也
看出,设备具体的要实现那些函数,或者那些函数是必须的。
int (*write_room)(struct tty_struct *tty); /*有多少写空间*/
int (*write)(struct tty_struct * tty, /*具体硬件相关的写操作*/
const unsigned char *buf, int count);
static ssize_t tty_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct inode *inode = file->f_path.dentry->d_inode;
struct tty_struct *tty = file_tty(file);
struct tty_ldisc *ld;
ssize_t ret;
if (tty_paranoia_check(tty, inode, "tty_write"))
return -EIO;
if (!tty || !tty->ops->write ||
(test_bit(TTY_IO_ERROR, &tty->flags)))
return -EIO;
/* Short term debug to catch buggy drivers */
if (tty->ops->write_room == NULL)
printk(KERN_ERR "tty driver %s lacks a write_room method.\n",
tty->driver->name);
ld = tty_ldisc_ref_wait(tty);
if (!ld->ops->write)
ret = -EIO;
else
ret = do_tty_write(ld->ops->write, tty, file, buf, count);
tty_ldisc_deref(ld);
return ret;
}
/*
* 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 char __user *buf,
size_t count)
{
ssize_t ret, written = 0;
unsigned int chunk;
ret = tty_write_lock(tty, file->f_flags & O_NDELAY);
if (ret < 0)
return ret;
/*
* We chunk up writes into a temporary buffer. This
* simplifies low-level drivers immensely, since they
* don't have locking issues and user mode accesses.
*
* But if TTY_NO_WRITE_SPLIT is set, we should use a
* big chunk-size..
*
* The default chunk-size is 2kB, because the NTTY
* layer has problems with bigger chunks. It will
* claim to be able to handle more characters than
* it actually does.
*
* FIXME: This can probably go away now except that 64K chunks
* are too likely to fail unless switched to vmalloc...
*/
chunk = 2048;
if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags))
chunk = 65536;
if (count < chunk)
chunk = count;
/* write_buf/write_cnt is protected by the atomic_write_lock mutex */
if (tty->write_cnt < chunk) {
unsigned char *buf_chunk;
if (chunk < 1024)
chunk = 1024;
buf_chunk = kmalloc(chunk, GFP_KERNEL);
if (!buf_chunk) {
ret = -ENOMEM;
goto out;
}
kfree(tty->write_buf);
tty->write_cnt = chunk;
tty->write_buf = buf_chunk;
}
/* Do the write ..
* 这个过程会被阻塞吗?
**/
for (;;) {
size_t size = count;
if (size > chunk)
size = chunk;
ret = -EFAULT;
if (copy_from_user(tty->write_buf, buf, size))
break;
ret = write(tty, file, tty->write_buf, size);
if (ret <= 0)
break;
written += ret;
buf += ret;
count -= ret;
if (!count)
break;
ret = -ERESTARTSYS;
if (signal_pending(current))
break;
cond_resched();
}
if (written) {
struct inode *inode = file->f_path.dentry->d_inode;
inode->i_mtime = current_fs_time(inode->i_sb);
ret = written;
}
out:
tty_write_unlock(tty);
return ret;
}
/**
* n_tty_write - write function for tty
* @tty: tty device
* @file: file object
* @buf: userspace buffer pointer
* @nr: size of I/O
**/
static ssize_t n_tty_write(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;
add_wait_queue(&tty->write_wait, &wait);
while (1) {
/* 设置状态TASK_INTERRUPTIBLE,应该并不会把进程设置为睡眠状态,直到schedule()
*
**/
set_current_state(TASK_INTERRUPTIBLE);
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
while (nr > 0) {
c = tty->ops->write(tty, b, nr);
if (c < 0) {
retval = c;
goto break_out;
}
/* 如果这里写入数据为0,当前进程将会sleep,什么时候醒啊?
* 这里看是存在隐患的。
**/
if (!c)
break;
b += c;
nr -= c;
}
if (!nr)
break;
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
break;
}
schedule();
}
break_out:
__set_current_state(TASK_RUNNING);
remove_wait_queue(&tty->write_wait, &wait);
if (b - buf != nr && tty->fasync)
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
return (b - buf) ? b - buf : retval;
}
static int smd_tty_write(struct tty_struct *tty,
const unsigned char *buf, int len)
{
struct smd_tty_info *tty_info = tty->driver_data;
int avail;
dbg("enter %s\n",__func__);
/* if we're writing to a packet channel we will
** never be able to write more data than there
** is currently space for
*/
avail = smd_stream_write_avail(tty_info->ch);
dbg("avail=%d\n", avail);
if (len > avail)
len = avail;
if (len) {
len = smd_stream_write(tty_info->ch, buf, len);
smd_send_intr(tty_info->a2b_int_tx);
}
return len;
}