Uart Controller --- 架构uart controller driver

/*  Register 
    uart Line Status Register 
 */
#define UART_REGISTER_LSR 
driver 的第一要义是要 深刻理解 该hardware所拥有的行为,
比如Altera cyclone V 的 Can controller 的 mo interrupt,印象好深刻

好了,这些hardware的行为背后是一大堆复杂的电路呈现出来的register,
下面会有对 触发器的详细介绍,一定要让你彻底明白什么是 D trigger

https://github.com/leesagacious/lee-TEE/tree/master/core/driver

在Linux kernel中 bitmap使用很是广泛, Register 是hardware中真正的 “位图”
这里写图片描述

1 : 看一下tee kernel中的实现

/*
    也许会问 不是有 Rx interrupt 吗 ?
*/
static bool serial8250_uart_have_rx_data(.....)
{
    /*
        #define LSR_DR      0x01  DATA Ready 

        就是读取LSR register 判断该register的bit[0].
    */
    return (read32(base + UART_LSR) & LSR_DR);
}

2 : 下面是linux kernel 中的实现

/*
    刷缓冲区中的数据到线路规程中.
    flush data from the buffer chain to the line discipline
    1 : 幕后推手是谁 ?
         software interrupt, 好,为什么是software interrupt,
         而不是其他
*/
static void flush_to_ldisc(struct work_struct *work)
{
    /**
        tty_port 它也维护了一个缓冲区,类比uart_port也维护了一个缓冲区,
        (每个port的发送数据的 struct circ_buf 发送缓冲区)
        只是这两个buffer分别在不同的层中,他们是间接的联系在一起的. 
        层多了,显然影响效率,那就使用缓存了, 哈哈

        其实,整个写入的流程是:
        tty core -> line discipline -> tty driver ->uart driver
        -> circ_buf 发送缓冲区中的
        好,上面说了 推动数据最终流动到circ_buf中的是 software interrupt
        下面详细说这个circ_buf是怎么分配的 为什么要分配一个page大小, 这和
        UART_MAX_SIZE 有关系吗

        你仔细看这个 contianer_of() 实现的很妙, 当然了用户空间也可
        使用container_of() ,
        只是您必须加上头文件 #include <stddef.h>

        好了,来看看这个tty_port 是什么了,
        1. uart_state,tty_struct, tty_driver 都维护了它, 果然集千万宠爱于一身,
        2. 在uart_register_driver()注册uart_driver的时候被初始化
           uart_state的时候被初始化了

        https://blog.csdn.net/leesagacious/article/details/54670735

        int uart_register_driver(...)
        {
            .....
            支持多少个物理port, 就给你几个圈圈转转
            好了,你想想物理port与tty_port之间的对应关系吧,一对一 ?
            for (i = 0; i < drv->nr; i++) {
                struct uart_state *state = drv->state + i;
                struct tty_port *port = &state->port;

                初始化tty_port 吧. 无非是初始化 mutex , buf ,wait_queue 之类的
                tty_port_init(port);
                port->ops = &uart_port_ops;
                .....
            }
        }
        tty_port 在整个uart体系中只是一个配角.
        重要性低于tty_driver,tty_struct,以及 各个 ops.
        好了,用到了在说吧.

        这里拿出来tty_port是为了什么呢 ? 
        很显然 : 

        1 : 是为了获取它维护的缓冲区
        2 :获取维护的tty_struct,
            因为tty_struct维护了线路规程, 
            该函数的目的就是 将tty_port中维护的缓冲区的数据刷入到线路规程中
            该函数一上来就开始准备这些,
            最终一个一个位写入到uart register的时候
            速度是恒定的,发送一个位 耗时 1/115200 (s) ,显然速度很慢
            在这里临时抱抱佛脚,也没关系的. 是吧? 哈哈
        3 : tty_port维护了一个32位的位图,iflags, 正在刷入线路规程的时候
            缓冲区是锁定的, 看下面的图示 
     */
    struct tty_port *port = container_of(work, struct tty_port, buf.work);
    /*
        port维护的这个buf 很有意义. 下面用到了再说吧
        这里先放过他.
    */
    struct tty_bufhead *buf = &port->buf;
    /**
        这个鬼也来凑热闹,... 
        上面说了,获取它就是为了获取它维护的线路规程.
        看.. serial_in()从 register中读取字符开始,到这里刷入line discipline之前各路大神都请来了,不容易呀.
    */
    struct tty_struct *tty;
    /*
        该变量你应该知道的. 哈哈,
    */
    unsigned long   flags;
    /*
        线路规程, 下面会给它赋值,
        看到了吧, 在这里开始进行"串线", 可增加独有的线路规程,
        然后将数据刷入到自定义的线路规程中
        当然了,如果不想这样做, 还有其他更好的方法.
        请移步 ; 
        https://blog.csdn.net/leesagacious/article/details/78237306

        底层统一的数据, 从这里要开始分道扬镳了. 而不是像signal那样速途同归.
        https://blog.csdn.net/leesagacious/article/details/53678666
    */
    struct tty_ldisc *disc;
    /*
        获取tty_struct. 
        什么时候开始建立关联关系的 ?

        当用户空间调用 open()的时候, tty_open() 会被调用
        于是 :
        tty_open()
        {
            ....
            初始化一个 tty device.
            tty = tty_init_dev(driver, index);
            {
                struct tty_struct *tty;
                tty = alloc_tty_struct(); // 分配
                ....
                tty->port->itty = tty;   // 赋值
            }
        }
    */
    tty = port->itty;
    if (tty == NULL)
        return;

    /*
        怎么获取的 ?
        请移步 : https://blog.csdn.net/leesagacious/article/details/77170670
    */  
    disc = tty_ldisc_ref(tty);
    if (disc == NULL)   /*  !TTY_LDISC */
        return;

    /*
        这个一定要有的.
        https://blog.csdn.net/leesagacious/article/details/64980255
    */
    spin_lock_irqsave(&buf->lock, flags);
    /*
        还好,这不是"独角戏" 该来的大神都请来了, 登台演戏吧, 
    */
    if (!test_and_set_bit(TTYP_FLUSHING, &port->iflags)) {  

    }
}

这里写图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值