上一节中,我们主要了解了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,
}
------------------------------- /tty/N_tty.c ----------------------------------------------------