跳到主要内容

10_UART驱动情景分析_read

UART驱动情景分析_read

参考资料

  • 参考代码:

    硬件相关:
    drivers/tty/serial/imx.c
    drivers/tty/serial/stm32-usart.c

    串口核心层:
    drivers/tty/serial/serial_core.c

    TTY层:
    drivers/tty/tty_io.c

1. 情景分析大纲

  • 注册过程分析
  • open过程分析
  • read过程分析
  • write过程分析

2. 源码框架回顾

image-20210722145518292

3. 使用哪个行规程

3.1 行规程注册

文件:drivers/tty/n_tty.c

void __init n_tty_init(void)
{
tty_register_ldisc(N_TTY, &n_tty_ops);
}

以后可以通过标号N_TTY找到这个行规程。

3.2 open设备时确定行规程

tty_open
tty_open_by_driver
tty_init_dev
tty = alloc_tty_struct(driver, idx);
tty_ldisc_init(tty);
struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY);
tty->ldisc = ld;

4. read过程分析

流程为:

  • APP读

    • 使用行规程来读
    • 无数据则休眠
  • UART接收到数据,产生中断

    • 中断程序从硬件上读入数据
  • 发给行规程

    • 行规程处理后存入buffer
    • 行规程唤醒APP
  • APP被唤醒后,从行规程buffer中读入数据,返回

4.1 tty_read

文件:drivers/tty/tty_io.c

4.2 ldisk read

文件:drivers/tty/n_tty.c

函数:n_tty_read

image-20210724095007517

copy_from_read_buf
const unsigned char *from = read_buf_addr(ldata, tail);
// return &ldata->read_buf[i & (N_TTY_BUF_SIZE - 1)];
retval = copy_to_user(*b, from, n);

4.3 数据源头: 中断

4.3.1 IMX6ULL

文件:drivers/tty/serial/imx.c

函数:imx_rxint

imx_rxint
// 读取硬件状态
// 得到数据
// 在对应的uart_port中更新统计信息, 比如sport->port.icount.rx++;

// 把数据存入tty_port里的tty_buffer
tty_insert_flip_char(port, rx, flg)

// 通知行规程来处理
tty_flip_buffer_push(port);
tty_schedule_flip(port);
queue_work(system_unbound_wq, &buf->work); // 使用工作队列来处理
// 对应flush_to_ldisc函数
4.3.2 STM32MP157

文件:drivers/tty/serial/stm32-usart.c

函数:stm32_usart_threaded_interrupt

stm32_usart_threaded_interrupt
stm32_usart_receive_chars(port, true);
// 通过DMA方式得到数据
stm32_usart_receive_chars_dma(port);
stm32_usart_push_buffer_dma(port, dma_size);
// 把数据存入tty_port里的tty_buffer
dma_count = tty_insert_flip_string(ttyport, dma_start, dma_size);
// 更新统计信息
port->icount.rx += dma_count;

// 通知行规程来处理
tty_flip_buffer_push(tport);
tty_schedule_flip(port);
queue_work(system_unbound_wq, &buf->work); // 使用工作队列来处理
// 对应flush_to_ldisc函数