1. UART介绍

UART是一类tty设备, 是一种串行端口终端, 具体可参考<UART接口介绍>
在Linux中UART属于tty驱动的一部分, 具体实现包括驱动抽象层和硬件实现层

本文主要介绍了UART驱动抽象层, 代码主要是drivers/tty/serial/serial_core.c

2. UART接口

UART抽象层提供了一系列API供硬件实现层使用, 主要包括

/* 注册/释放uart驱动 */
int uart_register_driver(struct uart_driver *drv);
void uart_unregister_driver(struct uart_driver *drv);
/* 添加uart端口/设备 */
int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport);
int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport);
/* 端口挂起和恢复 */
int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport);
int uart_resume_port(struct uart_driver *drv, struct uart_port *uport);
/* 读写相关 */
void uart_write_wakeup(struct uart_port *port);
void uart_insert_char(struct uart_port *port, unsigned int status, unsigned int overrun, unsigned int ch, unsigned int flag);

uart_register_driver完成了如下事宜
1. 为uart_driver分配uart_driver::nr个uart_state数据结构
2. 调用alloc_tty_driver分配tty_driver数据结构并赋值给uart_driver::tty_driver
3. 从uart_driver对应变量赋值tty_driver的driver_name、name、major、minor_start成员
4. 设置tty_driver::type为TTY_DRIVER_TYPE_SERIAL, tty_driver::subtype为SERIAL_TYPE_NORMAL
5. 设置tty_driver::init_termios为tty_std_termios, 并定制init_termios::c_cflag和init_termios::c_ispeed
6. 设置tty_driver::flags为TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV
7. 设置tty_driver::driver_state指向uart私有数据uart_driver
8. 调用tty_set_operations将uart_ops赋值给tty_driver::ops
9. 调用tty_port_init初始化uart_driver::uart_state::tty_port
9.1 调用tty_buffer_init初始化tty_port::buf, 以及buf关联的工作队列tty_bufhead::work, 工作函数为flush_to_ldisc
9.2 初始化tty_port::open_wait和tty_port::delta_msr_wait等待队列
9.3 初始化tty_port::mutex和tty_port::buf_mutex互斥锁
9.4 初始化tty_port::close_delay为50、tty_port::closing_wait为3000, 为什么?
10. 将uart_port_ops赋值给uart_driver::uart_state::tty_port::tty_port_operations
11. 调用tty_register_driver注册tty驱动

uart_add_one_port完成了如下事宜
1. 将参数uport赋值给uart_driver::uart_state::uart_port, 将uart_driver::uart_state赋值给uport::uart_state
2. 将uart_driver::console赋值给uport::console
3. 获取uart端口的次设备号并赋值给uport::minor
4. 调用uart_configure_port进行uart端口配置
4.1 首先判断如果该端口没有配置物理信息则直接返回
4.2 如果uport::flags包含了UPF_BOOT_AUTOCONF, 则调用uport::uart_ops::config_port进行端口自动配置
4.3 通过uart_report_port来向内核打印uart端口相关信息
4.4 调用uart_change_pm给端口上电, 实际是调用uport::uart_ops::pm
4.5 调用uport::uart_ops::set_mctrl禁用串口控制
4.6 如果该端口关联控制台, 则调用register_console来注册控制台
4.7 如果该端口不是控制台端口, 则调用uart_change_pm给端口断电
5. 为uport::tty_groups分配2/3个数据结构并赋值, [0]为tty_dev_attr_group; [1]为uport::attr_group
6. 调用tty_port_register_device_attr注册tty设备

3. UART数据结构

UART抽象层包含如下几个重要数据结构

uart_driver是uart的私有驱动结构, 包括一些和tty_driver相同的变量和uart相关的变量
一个uart_driver通常对应多个uart_state, 一个uart_state对应一个uart_port, 一个uart_port对应一个串口设备

struct uart_driver {
struct module *owner;
const char *driver_name; /* 驱动名称, 如serial */
const char *dev_name; /* 设备名称, 如ttyS, 体现到/dev/文件系统下 */
int major; /* 主设备号, 如TTY_MAJOR*/
int minor; /* 次设备号 */
int nr; /* 串口设备数 */
struct console *cons; /* 控制台设备 */
struct uart_state *state; /* 串口状态 */
struct tty_driver *tty_driver; /* tty驱动 */
};

uart_port用于描述串口端口的物理信息, 如I/O端口或I/O内存地址、FIFO大小、端口类型、串口时钟等

struct uart_port {
spinlock_t lock; /* port lock */
unsigned long iobase; /* io端口基地址 */
unsigned char __iomem *membase; /* 内存端口基地址 */
unsigned int (*serial_in)(struct uart_port *, int); /* 串口读函数 */
void (*serial_out)(struct uart_port *, int, int); /* 串口写函数 */
void (*set_termios)(struct uart_port *, struct ktermios *new,
struct ktermios *old);           /* 串口配置函数 */
unsigned int (*get_mctrl)(struct uart_port *); /* 获取串口控制 */
void (*set_mctrl)(struct uart_port *, unsigned int); /* 设置串口控制 */
int (*startup)(struct uart_port *port);
void (*shutdown)(struct uart_port *port);
void (*throttle)(struct uart_port *port);
void (*unthrottle)(struct uart_port *port);
int (*handle_irq)(struct uart_port *);
void (*pm)(struct uart_port *, unsigned int state, unsigned int old);
void (*handle_break)(struct uart_port *);
int (*rs485_config)(struct uart_port *, struct serial_rs485 *rs485);
unsigned int irq; /* 中断号 */
unsigned long irqflags; /* 中断标志 */
unsigned int uartclk; /* 串口时钟 */
unsigned int fifosize; /* fifo大小 */
unsigned char x_char; /* xon/xoff char */
unsigned char regshift; /* 寄存器偏移值 */
unsigned char iotype; /* io访问类型 */
unsigned char unused1; unsigned int read_status_mask; /* driver specific */
unsigned int ignore_status_mask; /* driver specific */
struct uart_state *state; /* 指向对用uart_state */
struct uart_icount icount; /* 串口使用计数 */ struct console *cons; /* 控制台设备 */
#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ)
unsigned long sysrq; /* sysrq timeout */
#endif
upf_t flags;
upstat_t status;
int hw_stopped; /* sw-assisted CTS flow state */
unsigned int mctrl; /* 当前的串口控制设置 */
unsigned int timeout; /* character-based timeout */
unsigned int type; /* 端口类型 */
const struct uart_ops *ops; /* 串口操作函数集 */
unsigned int custom_divisor;
unsigned int line; /* 端口号 */
unsigned int minor;
resource_size_t mapbase; /* 串口寄存器基地址 */
resource_size_t mapsize;
struct device *dev; /* 父设备 */
unsigned char hub6; /* this should be in the 8250 driver */
unsigned char suspended;
unsigned char irq_wake;
unsigned char unused[];
struct attribute_group *attr_group; /* port specific attributes */
const struct attribute_group **tty_groups; /* all attributes (serial core use only) */
struct serial_rs485 rs485;
void *private_data; /* 端口私有数据, 一般为platform数据指针 */
};

uart_state代表uart设备的状态

struct uart_state {
struct tty_port port; enum uart_pm_state pm_state;
struct circ_buf xmit; atomic_t refcount;
wait_queue_head_t remove_wait;
struct uart_port *uart_port;
};

uart_ops是uart端口操作集, 与硬件相关

struct uart_ops {
unsigned int (*tx_empty)(struct uart_port *); /* 发送TX FIFO缓冲区是否为空 */
void (*set_mctrl)(struct uart_port *, unsigned int mctrl); /* 设置串口控制模式 */
unsigned int (*get_mctrl)(struct uart_port *); /* 获取串口控制状态 */
void (*stop_tx)(struct uart_port *); /* 停止发送 */
void (*start_tx)(struct uart_port *); /* 开始发送 */
void (*throttle)(struct uart_port *); /**/
void (*unthrottle)(struct uart_port *); /**/
void (*send_xchar)(struct uart_port *, char ch); /* XON/XOFF发送函数 */
void (*stop_rx)(struct uart_port *); /* 停止接收 */
void (*enable_ms)(struct uart_port *); /* 串口状态使能使能 */
void (*break_ctl)(struct uart_port *, int ctl); /* 控制break信号的传输 */
int (*startup)(struct uart_port *); /* 启动串口 */
void (*shutdown)(struct uart_port *); /* 关闭串口 */
void (*flush_buffer)(struct uart_port *); /* 刷新缓冲区 */
void (*set_termios)(struct uart_port *, struct ktermios *new,
struct ktermios *old); /* 设置串口参数 */
void (*set_ldisc)(struct uart_port *, struct ktermios *); /* 设置线路规程 */
void (*pm)(struct uart_port *, unsigned int state,
                                   unsigned int oldstate);     /* 电源管理 */
void (*wake_peer)(struct uart_port *); /**/ const char *(*type)(struct uart_port *); /* 端口描述符 */
void (*release_port)(struct uart_port *); /* 释放端口物理资源 */
int (*request_port)(struct uart_port *); /* 申请端口物理资源 */
void (*config_port)(struct uart_port *, int); /* 端口配置 */
int (*verify_port)(struct uart_port *, struct serial_struct *); /* 校验端口信息 */
    int          (*ioctl)(struct uart_port *, unsigned int, unsigned long);  /* 控制端口 */
#ifdef CONFIG_CONSOLE_POLL
int (*poll_init)(struct uart_port *);
void (*poll_put_char)(struct uart_port *, unsigned char);
int (*poll_get_char)(struct uart_port *);
#endif
};

4. UART驱动编写

uart驱动的编写主要步骤如下:

1. 定义uart_driver静态变量, 实现driver_name、dev_name、nr、cons、major、minor等
2. 定义并实现uart_ops和console数据结构
3. 根据具体的硬件进行实现uart_port数据结构, 或者从DTS获取对应的数据
3. 调用uart_register_driver注册uart驱动
4. 调用uart_add_one_port添加uart端口

参考:
<tty驱动分析>
<Linux串口(serial、uart)驱动程序设计>

Linux UART介绍的更多相关文章

  1. linux UART串口驱动开发文档

    转:http://www.360doc.com/content/10/0417/18/829197_23519037.shtml linux UART串口驱动开发文档时间:2010-01-09 14: ...

  2. 01 Linux入门介绍

    一.Linux 初步介绍 Linux的优点 免费的,开源的 支持多线程,多用户 安全性好 对内存和文件管理优越 系统稳定 消耗资源少 Linux的缺点 操作相对困难 一些专业软件以及游戏支持度不足 L ...

  3. Linux Epoll介绍和程序实例

    Linux Epoll介绍和程序实例 1. Epoll是何方神圣? Epoll但是当前在Linux下开发大规模并发网络程序的热门人选,Epoll 在Linux2.6内核中正式引入,和select类似, ...

  4. Linux入门介绍

    Linux入门介绍 一.Linux 初步介绍 Linux的优点 免费的,开源的 支持多线程,多用户 安全性好 对内存和文件管理优越 系统稳定 消耗资源少 Linux的缺点 操作相对困难 一些专业软件以 ...

  5. Linux 系统目录介绍

    bin : bin 是Binary 二进制的缩写,就是可执行文件了.Bin目录下是用户常用的命令. sbin: 此目录下也是二进制文件 ,不过这里的命令是 超级用户如 root 这样的用户使用的. e ...

  6. I.MX6 Android Linux UART send receive with multi-thread and multi-mode demo

    /******************************************************************************************* * I.MX6 ...

  7. Linux命令介绍

    资料链接:(Linux基本命令介绍)http://note.youdao.com/share/?id=36c07917f8d3e6437c1e764c3516a3f2&type=note#/ ...

  8. 1.Linux入门介绍

    1.1 Linux概述 1.1.1 Linux简要介绍 Linux的由来: Linux的内核最初是由芬兰人李纳斯·托瓦茨在上大学的时候编写的一个内核,它是基于Unix操作系统编写的 大多服务器使用的是 ...

  9. Linux SELinux 介绍详解

    Linux SELinux 介绍详解 SElinux 简介 SElinux (Security Enhanced Linux)是由美国国家安全局(NSA)开发的.它已被植入到了Linux系统的内核当中 ...

随机推荐

  1. SecureCRT-登录unix/linux服务器主机的软件

    百度百科说辞: SecureCRT是一款支持SSH(SSH1和SSH2)的终端仿真程序,简单地说是Windows下登录UNIX或Linux服务器主机的软件. SecureCRT支持SSH,同时支持Te ...

  2. Python实现斐波那契递归和尾递归计算

    ##斐波那契递归测试 def fibonacciRecursive(deepth): if deepth == 1: return 1 elif deepth == 2: return 1 else: ...

  3. C++实现 企业信息管理系统

    2.1总体需求 ​ 2.2管理需求 ​ ​ 3.总体架构 ​ 由于代码量比较大,请移步GitHub或码云 码云:传送门 , GitHub:传送门 话不多说,直接上效果 我是在Linux Ubuntu1 ...

  4. Go语言【数据结构】数组

    数组 简介 数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整形.字符串或者自定义类型.数组形式 numbers[0], numbers[1] ..., nu ...

  5. MongoDB 概述

    一.概述: 1.NoSQL数据库(非关系型数据库) 2.文档存储 3.格式类似JSON,BSON 4.最终一致性(非ACID) , CAP定理(C 一致性,A 高可用,P 分区性) 5.高可扩展性(分 ...

  6. linux设置定时任务的方法(自己总结)

    Linux设置定时任务步骤 linux设置定时任务的关键字是:crontab 1:查看现在已经有的定时任务的命令是  crontab -l,执行命令如下图: 2:新建定时任务的命令是:crontab ...

  7. C#泛型集合之——链表

    链表基础 1.概述:C#中泛型集合中的链表—LinkedList 是一个双向链表,其结点为LinkedListNode 结构 其中,结点结构包含:Next,Previous,Value三部分.且结点中 ...

  8. MongoDB和Java(6):Spring Data整合MongoDB副本集、分片集群

    最近花了一些时间学习了下MongoDB数据库,感觉还是比较全面系统的,涉及了软件安装.客户端操作.安全认证.副本集和分布式集群搭建,以及使用Spring Data连接MongoDB进行数据操作,收获很 ...

  9. 【转载】华为荣耀V9手机如何设置WiFi热点共享

    有时候我们在电脑的时候发现没有无线网络以及有线网络,如果你的手机有相应网络,并且流量足够(当前很多手机流量套餐都是不限量了),可以开启手机上的Wifi热点进行流量共享使用,开启Wifi流量热点后,电脑 ...

  10. Appscan漏洞 之 加密会话(SSL)Cookie 中缺少 Secure 属性

    近期 Appscan扫描出漏洞 加密会话(SSL)Cookie 中缺少 Secure 属性,已做修复,现进行总结如下: 1.1.攻击原理 任何以明文形式发送到服务器的 cookie.会话令牌或用户凭证 ...