一、tty_read
对于tty_read这个函数,很多标志我也没有弄得太清楚,但是问题不大,以后有机会在看。我觉得重点是看数据怎么从cdc驱动到通过线路规划到tty,再从tty到用户空间。标志位等东西都是为这一个数据的流程服务的。
static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
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);
}
第4行,tty_ldisc_ref_wait获取到ld,这个是open中设置好的线路规划操作函数集。
第6行,利用函数集中的read夺取数据,这个read对应的是n_tty_read。
第9行,唤醒其他的等待获取ld的tty_ldisc_ref_wait
static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
unsigned char __user *buf, size_t nr)
{
while (nr) {
if (!input_available_p(tty, 0)) {}
if (ldata->icanon && !L_EXTPROC(tty)) {
while (nr && ldata->read_cnt) {
c = ldata->read_buf[ldata->read_tail];
ldata->read_tail = ((ldata->read_tail+1) &
(N_TTY_BUF_SIZE-1));
ldata->read_cnt--;
if (tty_put_user(tty, c, b++))
nr--;
}
} else {
uncopied = copy_from_read_buf(tty, &b, &nr);
uncopied += copy_from_read_buf(tty, &b, &nr);
}
}
return retval;
}
1、input_available_p这个函数中调用了tty_flush_to_ldisc(tty)这个是利用队列将cdc中的数据存储到线路规划的缓存中,后面在具体分析。
2、接下来很长一段代码都是从线路规划的read_buf中读取数据,并送到用户空间,分为两种方式,一是一个一个的读取缓存中的数据,一个是整个的读取出来。
看到这里我们只能在代码中寻找哪里给read_buf赋值了,我们再看看cdc的中断,看看数据怎么从cdc驱动中送到线路规划中
acm_read_bulk_callback
acm_process_read_urb(acm, urb)
tty_insert_flip_string(&acm->port, urb->transfer_buffer,urb->actual_length)
tty_insert_flip_string_fixed_flag(port, chars, TTY_NORMAL, size)
tty_flip_buffer_push(&acm->port);
这里是cdc的回掉,这里有一部分数据的流动过程。
int tty_insert_flip_string_fixed_flag(struct tty_port *port,
const unsigned char *chars, char flag, size_t size)
{
int copied = 0;
do {
int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE);
int space = tty_buffer_request_room(port, goal);
struct tty_buffer *tb = port->buf.tail;
/* If there is no space then tb may be NULL */
if (unlikely(space == 0)) {
break;
}
memcpy(tb->char_buf_ptr + tb->used, chars, space);
memset(tb->flag_buf_ptr + tb->used, flag, space);
tb->used += space;
copied += space;
chars += space;
/* There is a small chance that we need to split the data over
several buffers. If this is the case we must loop */
} while (unlikely(size > copied));
return copied;
}
从这个函数是把cdc回掉中的数据存储到对应port的buf.tail的buf中。
void tty_flip_buffer_push(struct tty_port *port)
{
struct tty_bufhead *buf = &port->buf;
unsigned long flags;
spin_lock_irqsave(&buf->lock, flags);
if (buf->tail != NULL)
buf->tail->commit = buf->tail->used;
spin_unlock_irqrestore(&buf->lock, flags);
if (port->low_latency)
flush_to_ldisc(&buf->work);
else
schedule_work(&buf->work);
}
这里可以直接调用flush_to_ldisc处理数据后者使用队列来处理数据。buf->work的处理函数就是flush_to_ldisc。
static void flush_to_ldisc(struct work_struct *work)
{
struct tty_port *port = container_of(work, struct tty_port, buf.work);
struct tty_bufhead *buf = &port->buf;
struct tty_struct *tty;
unsigned long flags;
struct tty_ldisc *disc;
tty = port->itty;
if (tty == NULL)
return;
disc = tty_ldisc_ref(tty);
if (disc == NULL) /* !TTY_LDISC */
return;
spin_lock_irqsave(&buf->lock, flags);
if (!test_and_set_bit(TTYP_FLUSHING, &port->iflags)) {
struct tty_buffer *head;
while ((head = buf->head) != NULL) {
int count;
char *char_buf;
unsigned char *flag_buf;
count = head->commit - head->read;
if (!count) {
if (head->next == NULL)
break;
buf->head = head->next;
tty_buffer_free(port, head);
continue;
}
if (!tty->receive_room)
break;
if (count > tty->receive_room)
count = tty->receive_room;
char_buf = head->char_buf_ptr + head->read;
flag_buf = head->flag_buf_ptr + head->read;
head->read += count;
spin_unlock_irqrestore(&buf->lock, flags);
disc->ops->receive_buf(tty, char_buf,
flag_buf, count);
spin_lock_irqsave(&buf->lock, flags);
/* Ldisc or user is trying to flush the buffers.
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(TTYP_FLUSHPENDING, &port->iflags)) {
__tty_buffer_flush(port);
clear_bit(TTYP_FLUSHPENDING, &port->iflags);
wake_up(&tty->read_wait);
break;
}
}
clear_bit(TTYP_FLUSHING, &port->iflags);
}
spin_unlock_irqrestore(&buf->lock, flags);
tty_ldisc_deref(disc);
}
这个就是调用 disc->ops->receive_buf来处理port->buf中的数据
static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count)
{
struct n_tty_data *ldata = tty->disc_data;
const unsigned char *p;
char *f, flags = TTY_NORMAL;
int i;
char buf[64];
unsigned long cpuflags;
if (ldata->real_raw) {
raw_spin_lock_irqsave(&ldata->read_lock, cpuflags);
i = min(N_TTY_BUF_SIZE - ldata->read_cnt,
N_TTY_BUF_SIZE - ldata->read_head);
i = min(count, i);
memcpy(ldata->read_buf + ldata->read_head, cp, i);
ldata->read_head = (ldata->read_head + i) & (N_TTY_BUF_SIZE-1);
ldata->read_cnt += i;
cp += i;
count -= i;
i = min(N_TTY_BUF_SIZE - ldata->read_cnt,
N_TTY_BUF_SIZE - ldata->read_head);
i = min(count, i);
memcpy(ldata->read_buf + ldata->read_head, cp, i);
ldata->read_head = (ldata->read_head + i) & (N_TTY_BUF_SIZE-1);
ldata->read_cnt += i;
raw_spin_unlock_irqrestore(&ldata->read_lock, cpuflags);
} else {
for (i = count, p = cp, f = fp; i; i--, p++) {
if (f)
flags = *f++;
switch (flags) {
case TTY_NORMAL:
n_tty_receive_char(tty, *p);
break;
case TTY_BREAK:
n_tty_receive_break(tty);
break;
case TTY_PARITY:
case TTY_FRAME:
n_tty_receive_parity_error(tty, *p);
break;
case TTY_OVERRUN:
n_tty_receive_overrun(tty);
break;
default:
printk(KERN_ERR "%s: unknown flag %d\n",
tty_name(tty, buf), flags);
break;
}
}
if (tty->ops->flush_chars)
tty->ops->flush_chars(tty);
}
n_tty_set_room(tty);
if ((!ldata->icanon && (ldata->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!
*/
while (1) {
tty_set_flow_change(tty, TTY_THROTTLE_SAFE);
if (tty->receive_room >= TTY_THRESHOLD_THROTTLE)
break;
if (!tty_throttle_safe(tty))
break;
}
__tty_set_flow_change(tty, 0);
}
我的记得open的时候说这个数据是不处理的,所以走上面直接复制的分支。
所以我们可以看到数据的整个流程是这样的,cdc的callback把数据存储到port的buf中,再到线路规划的read_buf,在送到用户空间,跟这个线索,其他的部分可以碰到具体问题时在分析。
二、tty_write
tty_write
do_tty_write(ld->ops->write, tty, file, buf, count);
write(tty, file, tty->write_buf, size)//这个write就是上面的ld->ops->write, tty
n_tty_write
tty->ops->write(tty, b, nr);
acm_tty_write
write比较简单基本就这样
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)