上一节中,我们主要了解了uart的一些重要的数据结构,和uart的发送数据的流程。下来我们会分析uart接受数据的流程,和发送比较起来,收流程更加复杂一点!关于发送流程,我们会从底层一步一步分析,直到tty core层。
- --------------------------------------------- /tty/serial/omap_serial.c -------------------------------------------------
static inline irqreturn_t serial_omap_irq(int irq, void *dev_id)
{
....................................
spin_lock_irqsave(&up->port.lock, flags);
lsr = serial_in(up, UART_LSR); //读取线路状态寄存器;
if (iir & UART_IIR_RLSI) {
if (!up->use_dma) { //若果使用非DMA
if (lsr & UART_LSR_DR) //如果数据准备好
receive_chars(up, &lsr); //接受字符
} else {
up->ier &= ~(UART_IER_RDI | UART_IER_RLSI); //
serial_out(up, UART_IER, up->ier);
if ((serial_omap_start_rxdma(up) != 0) &&
(lsr & UART_LSR_DR))
receive_chars(up, &lsr);
}
}
....................................
}
在receive_chars中,将会做很多事情:
static inline void receive_chars(struct uart_omap_port *up,
unsigned int *status)
{
struct tty_struct *tty = up->port.state->port.tty;
unsigned int flag, lsr = *status;
unsigned char ch = 0;
int max_count = 256;
do {
if (likely(lsr & UART_LSR_DR)) //如果数据准备好,则接受数据;
ch = serial_in(up, UART_RX);
flag = TTY_NORMAL; //设置标志为TTY_NORMAL
up->port.icount.rx++;
if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) { //如果产生错误?
/*
* For statistics only
*/
if (lsr & UART_LSR_BI) { //break signal set bit
lsr &= ~(UART_LSR_FE | UART_LSR_PE);
up->port.icount.brk++;
/*
* We do the SysRQ and SAK checking
* here because otherwise the break
* may get masked by ignore_status_mask
* or read_status_mask.
*/
if (uart_handle_break(&up->port)) //break信号处理
goto ignore_char;
} else if (lsr & UART_LSR_PE) { //如果是校验错误,设置相应计数器
up->port.icount.parity++;
} else if (lsr & UART_LSR_FE) { //如果是帧错误
up->port.icount.frame++;
}
if (lsr & UART_LSR_OE) //如果是溢出错误
up->port.icount.overrun++;
/*
* Mask off conditions which should be ignored.
*/
lsr &= up->port.read_status_mask;
#ifdef CONFIG_SERIAL_OMAP_CONSOLE
if (up->port.line == up->port.cons->index) {
/* Recover the break flag from console xmit */
lsr |= up->lsr_break_flag;
}
#endif
if (lsr & UART_LSR_BI) //设置错误标志
flag = TTY_BREAK;
else if (lsr & UART_LSR_PE)
flag = TTY_PARITY;
else if (lsr & UART_LSR_FE)
flag = TTY_FRAME;
}
if (uart_handle_sysrq_char(&up->port, ch))
goto ignore_char;
uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag); //将字符插入tty缓存中
ignore_char:
lsr = serial_in(up, UART_LSR);
} while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (max_count-- > 0));
spin_unlock(&up->port.lock);
tty_flip_buffer_push(tty); //讲数据刷新到tty core的buf中
spin_lock(&up->port.lock);
}
下面我们会一步一步分析标红的函数:
------------------------------- /tty/serial-core.c ----------------------------------------------------
static inline int uart_handle_break(struct uart_port *port)
{
struct uart_state *state = port->state;
#ifdef SUPPORT_SYSRQ
if (port->cons && port->cons->index == port->line) {
if (!port->sysrq) { //如果port->sysrq为0
port->sysrq = jiffies + HZ*5; // 设置时间为 5 s后;
return 1;
}
port->sysrq = 0; //如果不为0,设置为0,即再次发生中断时。
}
#endif
if (port->flags & UPF_SAK)
do_SAK(state->port.tty);
return 0;
}
主要用于设置多长时间, 后面我们会看到,当break信号发生中断时,该函数主要设置接受后面的普通字符所占的时长(我们设置为5s)。当设置好时长后,将会退出中断,等再次发生中断时,将会执行uart_handle_sysrq_char函数去处理这些在五秒内接受的“含有特殊意义”的字符。
static inline int
uart_handle_sysrq_char(struct uart_port *port, unsigned int ch)
{
#ifdef SUPPORT_SYSRQ
if (port->sysrq) {
if (ch && time_before(jiffies, port->sysrq)) {//如果当前时间在刚才设置的5s时间内
handle_sysrq(ch); //处理sysrq的特殊字符
port->sysrq = 0;
return 1;
}
port->sysrq = 0; //处理完后将其置0;
}
#endif
return 0;
}
由上面可以得知,系统在断点信号后,将会处理特殊字符。
static inline void
uart_insert_char(struct uart_port *port, unsigned int status,
unsigned int overrun, unsigned int ch, unsigned int flag)
{
struct tty_struct *tty = port->state->port.tty;
if ((status & port->ignore_status_mask & ~overrun) == 0) //如果不是overrun
tty_insert_flip_char(tty, ch, flag); //向tty插入字符;
/*
* Overrun is special. Since it's reported immediately,
* it doesn't affect the current character.
*/
if (status & ~port->ignore_status_mask & overrun) //产生overrun
tty_insert_flip_char(tty, 0, TTY_OVERRUN);
}
static inline int tty_insert_flip_char(struct tty_struct *tty,
unsigned char ch, char flag)
{
struct tty_buffer *tb = tty->buf.tail;
if (tb && tb->used < tb->size) {
tb->flag_buf_ptr[tb->used] = flag;
tb->char_buf_ptr[tb->used++] = ch;
return 1;
}
return tty_insert_flip_string_flags(tty, &ch, &flag, 1);
}
首先我们来看看tty->buf的数据结构;
struct tty_struct {
..........................
struct tty_bufhead buf; /* Locked internally */
........................
}; --->
struct tty_bufhead {
struct work_struct work;
spinlock_t lock;
struct tty_buffer *head; /*数据存储的队列头*/
struct tty_buffer *tail; /* 数据存储的队列尾 */
//<==Qing tail:the point to data buffer having been read;====>//
struct tty_buffer *free; /* 空的数据队列*/
int memory_used; /* Buffer space used excluding
free queue */
};--->
tail是数据链表的尾部,我们要插入数据,便是从尾部插入!
head是数据存储的队列头,要读取数据,便从头部开始;
free是空的数据队列,要删除已读的存储节点,可以考虑插入到free中!
struct tty_buffer {
struct tty_buffer *next; //buf的next指针
char *char_buf_ptr; //数据存储的指针,实际上指向data的首地址;
unsigned char *flag_buf_ptr; //存储flag的指针;
int used; //被使用的长度
int size; //buf的长度
int commit; //经常保存used的数据;
int read; //被读过的数据长度
/* Data points here */
unsigned long data[0];//数据的真实存储的起始位置
};
在这里值得注意的是unsigned long data[0];被称作“柔性数组”,是GCC的正确语法。 data数据成员不占空间,我们一般会这样使用它:
eg. kmalloc(sizeof(struct tty_buffer)+len);其中len表示给该结构体多分配的空间,一般是用来存储数据的buffer。
这样的好处是,只需要一次free,并不需要再次free掉len长度的空间。因为len长度的数据已经被释放!
理解这些数据结构中成员的含义,是理解下面流程的基本.
接下来我们接着分析:
static inline int tty_insert_flip_char(struct tty_struct *tty,
unsigned char ch, char flag)
{
struct tty_buffer *tb = tty->buf.tail; //获取数据存储队列的尾部!
if (tb && tb->used < tb->size) { //如果已使用的比总空间小,
tb->flag_buf_ptr[tb->used] = flag; //便可向该空间插入一个字符的数据;
tb->char_buf_ptr[tb->used++] = ch;
return 1;
}
//如果该存储节点的空间已满,则调用下面的函数扩充空间;
return tty_insert_flip_string_flags(tty, &ch, &flag, 1);
}------>
int tty_insert_flip_string_flags(struct tty_struct *tty,
const unsigned char *chars, const char *flags, size_t size)
{
int copied = 0;
do {
int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE);
int space = tty_buffer_request_room(tty, goal); //请求获取goal长度的存储空间
struct tty_buffer *tb = tty->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); //将数据copy到存储节点空间
memcpy(tb->flag_buf_ptr + tb->used, flags, space);
tb->used += space;
copied += space;
chars += space;
flags += 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)); //知道copy完size个字节!
return copied;
}
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 */
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) { //为其分配size+0xff的空间,并将该空间设置为tail。可以继续深追代码!
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;
}
请求完空间后,将返回到tty_insert_flip_string_flags函数,其字符填入该存储空间!这样就成功地完成了插入字符!这样我们便知道,中断送来的字符被存储在tty->buf的队列成员中,然而在上层,其在tty->read_buf中读取数据的!所以下面的一个动作很重要,uart驱动需要靠他将数据刷新到真正的tty->read_buf中!
void tty_flip_buffer_push(struct tty_struct *tty)
{
unsigned long flags;
spin_lock_irqsave(&tty->buf.lock, flags);
if (tty->buf.tail != NULL)
tty->buf.tail->commit = tty->buf.tail->used; //记录已读的数据
spin_unlock_irqrestore(&tty->buf.lock, flags);
if (tty->low_latency)
flush_to_ldisc(&tty->buf.work); //刷新数据到线路规程层!这是核心!
else
schedule_work(&tty->buf.work);
} ---->
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) {//存在下一个节点,这样的循环会把从head到tail的所有数据都刷新上去!
int count;
char *char_buf;
unsigned char *flag_buf;
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;
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; //刷新的已读数据量!这保证while循环的正常进行!
spin_unlock_irqrestore(&tty->buf.lock, flags);
disc->ops->receive_buf(tty, char_buf,
flag_buf, count); //调用线路规程中的receive_buf函数将该节点的数据刷新到readbuf中!
spin_lock_irqsave(&tty->buf.lock, flags);
} //end while
clear_bit(TTY_FLUSHING, &tty->flags);
}
/* 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_buffer_free的实现:
static void tty_buffer_free(struct tty_struct *tty, struct tty_buffer *b)
{
/* Dumb strategy for now - should keep some stats */
tty->buf.memory_used -= b->size;
WARN_ON(tty->buf.memory_used < 0);
if (b->size >= 512)
kfree(b); //如果SIZE >=512,则把它 kfree掉。
else {
b->next = tty->buf.free; //将其添加到free链表
tty->buf.free = b;
}
}
下面我们将分析receive_buf函数,
------------------------------- /tty/N_tty.c ----------------------------------------------------
static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count) //cp 便是字符,fp是flag
{
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);
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); //将字符copy到tty->read_buf
tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
tty->read_cnt += i;
spin_unlock_irqrestore(&tty->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 ((!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);
}
从这个流程来看,中断的字符被flush到tty->read_buf 中!我们再来看上层是如何将数据读取到用户空间的!
------------------------------- /tty/tty_io.c ----------------------------------------------------
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); //调用ldisc的read函数;
else
i = -EIO;
tty_ldisc_deref(ld);
if (i > 0)
inode->i_atime = current_fs_time(inode->i_sb);
return i;
}
------------------------------- /tty/N_tty.c ----------------------------------------------------
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:
BUG_ON(!tty->read_buf);
c = job_control(tty, file);
if (c < 0)
return c;
minimum = time = 0;
timeout = MAX_SCHEDULE_TIMEOUT;
if (!tty->icanon) {
time = (HZ / 10) * TIME_CHAR(tty);
minimum = MIN_CHAR(tty);
if (minimum) {
if (time)
tty->minimum_to_wake = 1;
else if (!waitqueue_active(&tty->read_wait) ||
(tty->minimum_to_wake > minimum))
tty->minimum_to_wake = minimum;
} else {
timeout = 0;
if (time) {
timeout = time;
time = 0;
}
tty->minimum_to_wake = minimum = 1;
}
}
/*
* Internal serialization of reads.
*/
if (file->f_flags & O_NONBLOCK) {
if (!mutex_trylock(&tty->atomic_read_lock))
return -EAGAIN;
} else {
if (mutex_lock_interruptible(&tty->atomic_read_lock))
return -ERESTARTSYS;
}
packet = tty->packet;
add_wait_queue(&tty->read_wait, &wait); //添加到等待队列
while (nr) {
/* First test for status change. */
if (packet && tty->link->ctrl_status) {
unsigned char cs;
if (b != buf)
break;
spin_lock_irqsave(&tty->link->ctrl_lock, flags);
cs = tty->link->ctrl_status;
tty->link->ctrl_status = 0;
spin_unlock_irqrestore(&tty->link->ctrl_lock, flags);
if (tty_put_user(tty, cs, b++)) {
retval = -EFAULT;
b--;
break;
}
nr--;
break;
}
/* This statement must be first before checking for input
so that any interrupt will set the state back to
TASK_RUNNING. */
set_current_state(TASK_INTERRUPTIBLE); //设置为可中断状态!
if (((minimum - (b - buf)) < tty->minimum_to_wake) &&
((minimum - (b - buf)) >= 1))
tty->minimum_to_wake = (minimum - (b - buf));
if (!input_available_p(tty, 0)) {
if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
retval = -EIO;
break;
}
if (tty_hung_up_p(file))
break;
if (!timeout)
break;
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
break;
}
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
/* FIXME: does n_tty_set_room need locking ? */
n_tty_set_room(tty);
timeout = schedule_timeout(timeout); //调度,此时不执行下面的程序,直到被唤醒!
BUG_ON(!tty->read_buf);
continue;
}
__set_current_state(TASK_RUNNING);
/* Deal with packet mode. */
if (packet && b == buf) {
if (tty_put_user(tty, TIOCPKT_DATA, b++)) {
retval = -EFAULT;
b--;
break;
}
nr--;
}
if (tty->icanon && !L_EXTPROC(tty)) {
/* N.B. avoid overrun if nr == 0 */
while (nr && tty->read_cnt) {
int eol;
eol = test_and_clear_bit(tty->read_tail,
tty->read_flags);
c = tty->read_buf[tty->read_tail]; //读取字符
spin_lock_irqsave(&tty->read_lock, flags);
tty->read_tail = ((tty->read_tail+1) &
(N_TTY_BUF_SIZE-1));
tty->read_cnt--;
if (eol) {
/* this test should be redundant:
* we shouldn't be reading data if
* canon_data is 0
*/
if (--tty->canon_data < 0)
tty->canon_data = 0;
}
spin_unlock_irqrestore(&tty->read_lock, flags);
if (!eol || (c != __DISABLED_CHAR)) {
if (tty_put_user(tty, c, b++)) {
retval = -EFAULT;
b--;
break;
}
nr--;
}
if (eol) {
tty_audit_push(tty);
break;
}
}
if (retval)
break;
} else {
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); //copy到用户空间!
if (uncopied) {
retval = -EFAULT;
break;
}
}
/* 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);
}
if (b - buf >= minimum)
break;
if (time)
timeout = time;
}
mutex_unlock(&tty->atomic_read_lock);
remove_wait_queue(&tty->read_wait, &wait);
if (!waitqueue_active(&tty->read_wait))
tty->minimum_to_wake = minimum;
__set_current_state(TASK_RUNNING);
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;
}
通过上面的分析,整个读流程就OK了,第一次写分析,有诸多不足之处,还请见谅!