概述
/* 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)) {
}
}
最后
以上就是霸气镜子为你收集整理的Uart Controller --- 架构uart controller driver的全部内容,希望文章能够帮你解决Uart Controller --- 架构uart controller driver所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复