tty driver(5)
来源:互联网 发布:java数据结构编程 编辑:程序博客网 时间:2024/06/05 02:51
与读操作相关的数据变量有:
struct tty_struct {/*这些变量都是在行规函数中处理的*/
unsigned int receive_room;/*read_buf中还有多少剩余空间*/
char *read_buf;/*read_buf的开始地址*/
int read_head;/*read_buf写的最后位置*/
int read_tail;/*用户空间从这里开始读出*/
int read_cnt; /*read_buf中有多少数据,需要用户读出*/
unsigned long read_flags[128];
}
/*有关 receive room
* 这两个行规的 receive room差别还是很大的 65536 & N_TTY_BUF_SIZE【4096】
**/
ppp_asynctty_open(struct tty_struct *tty) -> tty->receive_room = 65536;
static void n_tty_set_room(struct tty_struct *tty)
{
int left = N_TTY_BUF_SIZE - tty->read_cnt - 1;
tty->receive_room = left;
}
/*哪里分配的read memory buffer?
*当tty_open的时候,在具体行规的open里申请的memory
**/
n_tty_open -> tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
/*初始化 read_head,tail and cnt*/
reset_buffer_flags(struct tty_struct *tty) -> tty->read_head = tty->read_tail = tty->read_cnt = 0;
1] 数据从tty_buffer 到 read_buf
flush_to_ldisc -> disc->ops->receive_buf(tty, char_buf,flag_buf, count);
static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count)
{
const unsigned char *p;
char *f, flags = TTY_NORMAL;
int i;
char buf[64];
unsigned long cpuflags;
if (!tty->read_buf)
return;
if (tty->real_raw) {
spin_lock_irqsave(&tty->read_lock, cpuflags);
i = min(N_TTY_BUF_SIZE - tty->read_cnt,
N_TTY_BUF_SIZE - tty->read_head);
i = min(count, i);
/*
*把来自tty_buffer的数据copy到(tty->read_buf + tty->read_head)开始的地方
*更新read_head,[从这里看read_head表示数据写到read_buf那里了]
*更新read_cnt[表示read_buf里有多少数据]
**/
memcpy(tty->read_buf + tty->read_head, cp, i);
tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
tty->read_cnt += i;
spin_unlock_irqrestore(&tty->read_lock, cpuflags);
}
n_tty_set_room(tty);/*更新 receive_room*/
/*唤醒read_wait上的进程*/
if (waitqueue_active(&tty->read_wait))
wake_up_interruptible(&tty->read_wait);
/*
* Check the remaining room for the input canonicalization
* mode. We don't want to throttle the driver if we're in
* canonical mode and don't have a newline yet!
*/
if (tty->receive_room < TTY_THRESHOLD_THROTTLE)
tty_throttle(tty);
}
2] 从read_buf到用户空间buffer
tty_read -> n_tty_read{
copy_from_read_buf(tty, &b, &nr);
/* If there is enough space in the read buffer now, let the
* low-level driver know. We use n_tty_chars_in_buffer() to
* check the buffer, as it now knows about canonical mode.
* Otherwise, if the driver is throttled and the line is
* longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode,
* we won't get any more characters.
*/
if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) {
n_tty_set_room(tty);
check_unthrottle(tty);
}
n_tty_set_room(tty);
}
/**
* copy_from_read_buf - copy read data directly
* @tty: terminal device
* @b: user data
* @nr: size of data
*
* Helper function to speed up n_tty_read. It is only called when
* ICANON is off; it copies characters straight from the tty queue to
* user space directly. It can be profitably called twice;
* once to drain the space from the tail pointer to the (physical) end of the buffer
* and once to drain the space from the (physical) beginning of the buffer to head pointer.
**/
static int copy_from_read_buf(struct tty_struct *tty, unsigned char __user **b, size_t *nr)
{
copy_to_user(*b, &tty->read_buf[tty->read_tail], n);
tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1);
tty->read_cnt -= n;
}
/*unthrottle的判断标准*/
n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE;
static ssize_t n_tty_chars_in_buffer(struct tty_struct *tty)
{
unsigned long flags;
ssize_t n = 0;
spin_lock_irqsave(&tty->read_lock, flags);
if (!tty->icanon) {
n = tty->read_cnt;
}
spin_unlock_irqrestore(&tty->read_lock, flags);
return n;
}
tty driver 读操作,这里缺张图:描述memory copy的过程,各个 memory copy 在那个函数中实现的。
/***********************************************************************************/
(一)数据从中断处理程序到tty_buffer
/***********************************************************************************/有关tty buffer的数据结构:
struct tty_buffer {
struct tty_buffer *next;
/*char_bur_ptr and flag_buf_ptr的数据类型为什么不一样?*/
char *char_buf_ptr;
unsigned char *flag_buf_ptr;
int used;
int size;
int commit;
int read;
/* Data points here */
unsigned long data[0];
};
/*tty_struct操作的数据结构是tty_bufhead,不是tty_buffer,需要用tty_buffer 初始化tty_bufhead*/
struct tty_bufhead {
struct work_struct work;
spinlock_t lock;
struct tty_buffer *head; /* Queue head */
struct tty_buffer *tail; /* Active buffer */
struct tty_buffer *free; /* Free queue head */
int memory_used; /* Buffer space used excluding free queue */
};
/* Allocate a new tty buffer to hold the desired number of characters.
* Return NULL if out of memory or the allocation would exceed the
* per device queue
*/
static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size)
{
struct tty_buffer *p;
if (tty->buf.memory_used + size > 65536)
return NULL;
/*分配的memory包含 struct tty_buffer and data regions: char and flag*/
p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC);
if (p == NULL)
return NULL;
/*member size描述 char data size*/
p->size = size;
/*通过该指针形成一个link?*/
p->next = NULL;
/* commit, read, used 描述什么?*/
p->commit = 0;
p->read = 0;
p->used = 0;
/*data[0]就是这个用途?否则的话要 + sizeof()*/
p->char_buf_ptr = (char *)(p->data);
p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size;
/*Buffer space used,但不是占用的 memory,使用的memory有个2倍等*/
tty->buf.memory_used += size;
return p;
}
/* Make at least size bytes of linear space available for the tty
* buffer. If we fail return the size we managed to find.
*/
int tty_buffer_request_room(struct tty_struct *tty, size_t size)
{
struct tty_buffer *b, *n;
int left;
unsigned long flags;
spin_lock_irqsave(&tty->buf.lock, flags);
/* OPTIMISATION: We could keep a per tty "zero" sized buffer to
remove this conditional if its worth it. This would be invisible
to the callers
* 什么时候初始化的tty->buf.tail,何时分配的memory?
void tty_buffer_init(struct tty_struct *tty)
{
spin_lock_init(&tty->buf.lock);
tty->buf.head = NULL;
tty->buf.tail = NULL;
tty->buf.free = NULL;
tty->buf.memory_used = 0;
INIT_WORK(&tty->buf.work, flush_to_ldisc);
}
指针变量的初始化是在函数tty_buffer_init,而memory的分配就是在该函数中通过
tty_buffer_find得到的。
*/
/*1] 如果最后一个即tail.tty_buf存在,则判断还有多少空间,
* 如果存在但剩余的空间不够,则要构建tty_buff link,and commit <- used
* 如果不存在tail.tty_buf,则要调用函数tty_buffer_find找到或创建一个tty_buffer
*2] 只要当前的 tty->buf.tail,不满足size要求,tty->buf.tail会被更新,如果是第一个
* tty_buffer, tty->buf.head 也会被更新
*/
if ((b = tty->buf.tail) != NULL)
left = b->size - b->used;
else
left = 0;
if (left < size) {
/* This is the slow path - looking for new buffers to use */
if ((n = tty_buffer_find(tty, size)) != NULL) {
if (b != NULL) {
b->next = n;
b->commit = b->used;
} else
tty->buf.head = n;
tty->buf.tail = n;
} else
size = left;
}
spin_unlock_irqrestore(&tty->buf.lock, flags);
return size;
}
这里假设第一次read data,也就是tty->buf.tail = NULL;tty->buf.free = NULL;
此时:tty_buffer_find -> tty_buffer_alloc.
int tty_buffer_request_room(struct tty_struct *tty, size_t size)
{
struct tty_buffer *b, *n;
int left;
n = tty_buffer_find(tty, size);
tty->buf.head = n;
tty->buf.tail = n;
return size;
}
/* Prepare a block of space in the buffer for data. Returns the length
* available and buffer pointer to the space which is now allocated and
* accounted for as ready for normal characters. This is used for drivers
* that need their own block copy routines into the buffer.
*/
int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars,
size_t size)
{
/*tty_buffer_request_room得到一个tty_buffer*/
int space = tty_buffer_request_room(tty, size);
if (likely(space)) {
struct tty_buffer *tb = tty->buf.tail;
/*存放char data的位置*/
*chars = tb->char_buf_ptr + tb->used;
/*初始化flag region*/
memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space);
/*还没往这个char data开始的位置赋值, used已经更新*/
tb->used += space;
}
return space;
}
/*通过tty_prepare_flip_string得到char data的存储位置后,就要给他赋值*/
/*---------------------------------------------------------------------------*/
static void smd_ch_irq_tasklet_handler(unsigned long data)
{
-----
for (;;) {
/*1]中断处理函数read到数据;
*2]申请tty_buffer去保存该数据;
*3]把中断得到数据copy到tty_buffer中;
*4]调用tty_flip_buffer_push
*/
avail = smd_stream_read_avail(tty_info->ch);
if (avail == 0)
break;
avail = tty_prepare_flip_string(tty, &ptr, avail);
smd_stream_read(tty_info->ch,ptr,avail);
smd_send_intr(tty_info->a2b_int_rx);
tty_flip_buffer_push(tty);
}
tty_kref_put(tty);
}
/***********************************************************************************/
(二)数据从tty_buffer拷贝到 tty read_buf
/***********************************************************************************//*以上内容是如何把中断得到数据保存到tty_buffer中, 下一步是tty_flip_buffer_push*/
void tty_flip_buffer_push(struct tty_struct *tty)
{
unsigned long flags;
spin_lock_irqsave(&tty->buf.lock, flags);
if (tty->buf.tail != NULL)
/*update the commit to used*/
tty->buf.tail->commit = tty->buf.tail->used;
spin_unlock_irqrestore(&tty->buf.lock, flags);
/*schedule_work的work function 就是flush_to_ldisc*/
if (tty->low_latency)
flush_to_ldisc(&tty->buf.work);
else
schedule_work(&tty->buf.work);
}
/*flush_to_ldisc的任务只是把tty->buf.head指向的list的所有数据调用函数n_tty receive_buf传送出去
*
*/
static void flush_to_ldisc(struct work_struct *work)
{
struct tty_struct *tty =
container_of(work, struct tty_struct, buf.work);
unsigned long flags;
struct tty_ldisc *disc;
disc = tty_ldisc_ref(tty);
if (disc == NULL) /* !TTY_LDISC */
return;
spin_lock_irqsave(&tty->buf.lock, flags);
if (!test_and_set_bit(TTY_FLUSHING, &tty->flags)) {
struct tty_buffer *head;
while ((head = tty->buf.head) != NULL) {
int count;
char *char_buf;
unsigned char *flag_buf;
/*1] 判断read, commit位置看是否有数据需要读
* 如果没有,判断下一个 tty_buffer, 并释放当前没有读数据的tty_buffer*/
count = head->commit - head->read;
if (!count) {
if (head->next == NULL)
break;
tty->buf.head = head->next;
tty_buffer_free(tty, head);
continue;
}
/* Ldisc or user is trying to flush the buffers
we are feeding to the ldisc, stop feeding the
line discipline as we want to empty the queue */
if (test_bit(TTY_FLUSHPENDING, &tty->flags))
break;
if (!tty->receive_room)
break;
/*2] tty->receive_room是如何更新的?*/
if (count > tty->receive_room)
count = tty->receive_room;
/*3] 更新pointer and call into line discipline operations*/
char_buf = head->char_buf_ptr + head->read;
flag_buf = head->flag_buf_ptr + head->read;
head->read += count;
spin_unlock_irqrestore(&tty->buf.lock, flags);
disc->ops->receive_buf(tty, char_buf,flag_buf, count);
spin_lock_irqsave(&tty->buf.lock, flags);
}
clear_bit(TTY_FLUSHING, &tty->flags);
}
/*4] ?????????????????*/
/* We may have a deferred request to flush the input buffer,
if so pull the chain under the lock and empty the queue */
if (test_bit(TTY_FLUSHPENDING, &tty->flags)) {
__tty_buffer_flush(tty);
clear_bit(TTY_FLUSHPENDING, &tty->flags);
wake_up(&tty->read_wait);
}
spin_unlock_irqrestore(&tty->buf.lock, flags);
tty_ldisc_deref(disc);
}
/*把tty_buff中的数据copy到 read_buf中*/
static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count)
{
const unsigned char *p;
char *f, flags = TTY_NORMAL;
int i;
char buf[64];
unsigned long cpuflags;
if (!tty->read_buf)
return;
if (tty->real_raw) {
spin_lock_irqsave(&tty->read_lock, cpuflags);
/*这里的read_cnt,read_head什么意思?
*reset_buffer_flags -> tty->read_head = tty->read_tail = tty->read_cnt = 0;
*/
i = min(N_TTY_BUF_SIZE - tty->read_cnt,
N_TTY_BUF_SIZE - tty->read_head);
i = min(count, i);
/*这里的 read_buf哪里赋的值?
* n_tty_open -> {tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);}
*/
/*1]copy the data from tty_buffer char data to read_buf
*2]update the controld variable
*3]updata tty->receive_room;
*4]
*/
memcpy(tty->read_buf + tty->read_head, cp, i);
tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
tty->read_cnt += i;
cp += i;
count -= i;
i = min(N_TTY_BUF_SIZE - tty->read_cnt,
N_TTY_BUF_SIZE - tty->read_head);
i = min(count, i);
memcpy(tty->read_buf + tty->read_head, cp, i);
tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
tty->read_cnt += i;
spin_unlock_irqrestore(&tty->read_lock, cpuflags);
}
n_tty_set_room(tty);
if ((!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) ||
L_EXTPROC(tty)) {
kill_fasync(&tty->fasync, SIGIO, POLL_IN);
if (waitqueue_active(&tty->read_wait))
wake_up_interruptible(&tty->read_wait);
}
/*
* Check the remaining room for the input canonicalization
* mode. We don't want to throttle the driver if we're in
* canonical mode and don't have a newline yet!
*/
if (tty->receive_room < TTY_THRESHOLD_THROTTLE)
tty_throttle(tty);
}
static void n_tty_set_room(struct tty_struct *tty)
{
/* tty->read_cnt is not read locked ? */
int left = N_TTY_BUF_SIZE - tty->read_cnt - 1;
int old_left;
/*
* If we are doing input canonicalization, and there are no
* pending newlines, let characters through without limit, so
* that erase characters will be handled. Other excess
* characters will be beeped.
*/
if (left <= 0)
left = tty->icanon && !tty->canon_data;
/*保存原来的 receive_room, and update it with new*/
old_left = tty->receive_room;
tty->receive_room = left;
/* Did this open up the receive buffer? We may need to flip
* 这里的意思是old_left必须为 0,也就是tty->receive_room为0
* tty_set_ldisc -> tty->receive_room = 0;
* n_tty_set_room -> tty->receive_room = left;也就是该函数
* 直到old_left == 0,调用schedule_work(&tty->buf.work);
*/
if (left && !old_left)
schedule_work(&tty->buf.work);
}
到这里数据从tty_buffer拷贝到tty的read_buff memory.
/***********************************************************************************/
(三)如何把数据从read_buf拷贝到 user space.
/***********************************************************************************/static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
unsigned char __user *buf, size_t nr)
{
unsigned char __user *b = buf;
DECLARE_WAITQUEUE(wait, current);
int c;
int minimum, time;
ssize_t retval = 0;
ssize_t size;
long timeout;
unsigned long flags;
int packet;
do_it_again:
minimum = time = 0;
timeout = MAX_SCHEDULE_TIMEOUT;
if (!tty->icanon) {
tty->minimum_to_wake = minimum = 1;
}
add_wait_queue(&tty->read_wait, &wait);
while (nr) {
int uncopied;
/* The copy function takes the read lock and handles
locking internally for this case */
uncopied = copy_from_read_buf(tty, &b, &nr);
uncopied += copy_from_read_buf(tty, &b, &nr);
if (uncopied) {
retval = -EFAULT;
break;
}
}
size = b - buf;
if (size) {
retval = size;
if (nr)
clear_bit(TTY_PUSH, &tty->flags);
} else if (test_and_clear_bit(TTY_PUSH, &tty->flags))
goto do_it_again;
n_tty_set_room(tty);
return retval;
}
/**
* copy_from_read_buf - copy read data directly
* @tty: terminal device
* @b: user data
* @nr: size of data
*
* Helper function to speed up n_tty_read. It is only called when
* ICANON is off; it copies characters straight from the tty queue to
* user space directly. It can be profitably called twice; once to
* drain the space from the tail pointer to the (physical) end of the
* buffer, and once to drain the space from the (physical) beginning of
* the buffer to head pointer.
*
* Called under the tty->atomic_read_lock sem
*
*/
static int copy_from_read_buf(struct tty_struct *tty,
unsigned char __user **b,
size_t *nr)
{
int retval;
size_t n;
unsigned long flags;
retval = 0;
spin_lock_irqsave(&tty->read_lock, flags);
n = min(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail);
n = min(*nr, n);
spin_unlock_irqrestore(&tty->read_lock, flags);
if (n) {
retval = copy_to_user(*b, &tty->read_buf[tty->read_tail], n);
n -= retval;
tty_audit_add_data(tty, &tty->read_buf[tty->read_tail], n);
spin_lock_irqsave(&tty->read_lock, flags);
tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1);
tty->read_cnt -= n;
/* Turn single EOF into zero-length read */
if (L_EXTPROC(tty) && tty->icanon && n == 1) {
if (!tty->read_cnt && (*b)[n-1] == EOF_CHAR(tty))
n--;
}
spin_unlock_irqrestore(&tty->read_lock, flags);
*b += n;
*nr -= n;
}
return retval;
}
/* char *read_buf;
* int read_head; used to describe the write read buffer starting address
* int read_tail; used to describe the read read buffer starting address
* int read_cnt; used to describe the data size in the read buffer
*该函数中提及的tty read的member: read_cnt, read_tail
*从copy_to_user的输入参数可以看出read_tail是被读数据的开始地址;
*read_cnt是需要被读出的数据大小
*1] reset_buffer_flags -> tty->read_head = tty->read_tail = tty->read_cnt = 0;
*2] n_tty_receive_buf: tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
* tty->read_cnt += i;
*3] copy_from_read_buf:tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1);
* tty->read_cnt -= n;
*/
/***********************************************************************************/
(四)和设备文件的接口.
/***********************************************************************************//**
* tty_read - read method for tty device files
* @file: pointer to tty file
* @buf: user buffer
* @count: size of user buffer
* @ppos: unused
*
* Perform the read system call function on this terminal device. Checks
* for hung up devices before calling the line discipline method.
*
* Locking:
* Locks the line discipline internally while needed. Multiple
* read calls may be outstanding in parallel.
*/
static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
int i;
struct inode *inode = file->f_path.dentry->d_inode;
struct tty_struct *tty = file_tty(file);
struct tty_ldisc *ld;
if (tty_paranoia_check(tty, inode, "tty_read"))
return -EIO;
if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))
return -EIO;
/* We want to wait for the line discipline to sort out in this
situation */
ld = tty_ldisc_ref_wait(tty);
if (ld->ops->read)
i = (ld->ops->read)(tty, file, buf, count);
else
i = -EIO;
tty_ldisc_deref(ld);
if (i > 0)
inode->i_atime = current_fs_time(inode->i_sb);
return i;
}
- tty driver(5)
- Linux tty driver
- tty driver(1)
- tty driver(2)
- tty driver(3)
- tty driver(4)
- tty driver总结
- linux tty driver
- tty driver 赏析
- A soft watchdog of tty driver
- Linux TTY framework(4)_TTY driver
- linux tty driver 相关结构体 整理(tty driver 自己写出来之前保持更新)
- TTY
- tty
- tty
- tty
- TTY
- tty
- PHP mysql 事务处理实例
- 在线文档查看方案- 易度云查看
- float型与0的比较
- 字符串转换为整数(atoi)
- catwalk carousel
- tty driver(5)
- 网络流dinic算法
- 关于XSS(跨站脚本攻击)和CSRF(跨站请求伪造)
- 846 - Steps
- EJB异步提交和存取过程的使用
- 哈哈
- android中的数据库操作
- 页面加载完成执行方法
- CodeForces 288C - Polo the Penguin and XOR operation