uart无论在ARM,Android/Linux,还是MCU裸机中都应用非常广泛,TTL/RS232/RS485都是uart实现,只是电平差别.
uart以其良好的兼容性/低成本/传输距离等优点,广泛应用.
本文记录自己对uart的一些理解.
硬件拓扑:
SOC都封装了一个uart控制器,一个uart控制器(以8250为例)引出多个uart口.一个uart口接一个具体的硬件设备.
同时SPI设备/USB设备/i2c设备,也可以成为uart控制器,分出uart口.
uart是tty 子系统中三大块之一(终端terminal,控制台console,串口uart).
tty框架如下,
对于普通串口,应用层也是以系统调用open(/dev/ttySx,…)/write/read方式直接操作ttySx方式
调用过程参考https://blog.csdn.net/Jalyn_Fang/article/details/55798671
因此,用户控制主要还是通过设备文件同tty_core交互,tty_core跟进用户空间操作的类型选择合适的线路规定或者直接与serial core交互(例如ioctl会直接交给serial_core,read/write会交给线路规程).
线路规程,可以理解 为应用程序和设备驱动之间的一个适配层,主要为了描述这个操作串口的线程的输入输出规范,主要用来进行输入/输出数据的预处理.处理之后,简单理解就是将输入的字符转化成一行一行的数据,将数据交给serial_core,最后serial_core会调用底层的操作.主要实现文件n_tty.c
为了解uart,必须了解它的更高以及框架tty框架.
TTY是各类终端设备的简称,为了简化TTY的使用,Linux kernel抽象出了tty framework,对上,以字符设备(tty_io.c)的形式(tty_core),为用户提供标准应用api,对下,提供编写终端程序(如serial_core)的统一框架.
tty framework 框架如下
tty框架以及其子设备,由于出现的时间比较早,与经典的设备驱动模型并不非常符合,其弱化了device的概念,而增强了driver的结构和功能.这一点要注意.
有对tty framework的基本了解,我们再来看看Linux对uart的处理.
以RK3288-Android7.1SDK为例,Linux kernel版本4.4.143
RK3288 其硬件uart控制器,采用的是synopsys DesignWare 8250 IP.
kernel的分析从8250_core.c开始.
Linux kernel将具体的串口硬件抽象为uart_port.某些具体的SOC,也会在这个基础上重新封装一层.
8250作为虚拟的plaform_device.没有在dts中声明,8250_core.c的module_init中以显示指定serial8250字段的形式调用platform_device_alloc申请platform_device.然后直接显示注册platform_device_add().然后再注册驱动platform_driver_register,进而probe起作用,完成后续的任务.
Linux driver将uart控制8250抽象为struct uart_8250_port.该结构里面包含struct uart_port;这个变量.
serial8250_init中,
1.显式对8250下的5个uart_port分别初始化,指定uart_ops回调函数集.
2.调用uart_register_driver,注册8250 uart_driver.在这个uart_register_driver里面,会定义tty_driver,作为uart_driver的一个成员.并初始化tty_operation,这个tty_operation直接和tty framework最顶层的字符设备接口相联系,被其直接调用.
再一个,会初始化每一个uart_port的uart_state(uart_state对应与串口接的具体的硬件设备),uart_state有一个tty_port,对应于/dev/ttySx,然后会为这个tty_port申请buf缓冲区.然后通过tty_register_driver(),将申请的tty_driver注册到tty_core
对于uart,有tty_operation,uart_ops两个函数集非常重要,tty_operation负责实现tty字符设备api的具体实现,uart_ops负责本soc关于uart寄存器级别的实现.
但是,8250_core.c里面serial8250_init相关的uart_port还是一个虚拟的概念,还没有与具体的dts中的uart设备关联起来的,那么这个是怎么关联的呢?
先看uart再dts中的定义,
uart0: serial@ff180000 {
compatible = "rockchip,rk3288-uart", "snps,dw-apb-uart";
reg = <0x0 0xff180000 0x0 0x100>;
interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>;
reg-shift = <2>;
reg-io-width = <4>;
clocks = <&cru SCLK_UART0>, <&cru PCLK_UART0>;
clock-names = "baudclk", "apb_pclk";
pinctrl-names = "default";
pinctrl-0 = <&uart0_xfer>;
status = "disabled";
};
通过compatible字段关键字查找,可以找到其对于的driver是8250_dw.c
在该驱动的probe里面,显示申请了uart_port,然后解析dts,使用从dts解析的参数初始化uart_port成员.然后调用serial8250_register_8250_port注册该uart_port.
这个所谓注册,就是从serial8250_ports这个port里面,找到一个和当前uart_port匹配或者空虚的port,完成虚拟uart_port和硬件实际uart_port的关联,进而使得字符设备/dev/ttySx具有了灵魂和肉体,
下一步工作,就是实现串口start_up,serail_in,serial_out相关与具体硬件相关的功能实现.
对于uart,或者整个tty,已经非常的完善,需要修改的东西比较少,我们只需要了解其过程,能否理解应用层的操作逻辑,能应付基本的任务.
由于时间和能力关系,暂分析到此.