底层串行 API¶
本文档旨在简要概述新的串行驱动程序的某些方面。 它并不完整,您有任何问题都应发送至 <rmk@arm.linux.org.uk>
参考实现包含在 amba-pl011.c 中。
底层串行硬件驱动程序¶
底层串行硬件驱动程序负责向核心串行驱动程序提供端口信息(由 uart_port 定义)和一组控制方法(由 uart_ops 定义)。 底层驱动程序还负责处理端口的中断,并提供任何控制台支持。
控制台支持¶
串行核心提供了一些辅助函数。 这包括识别正确的端口结构(通过 uart_get_console()
)和解码命令行参数 (uart_parse_options()
)。
还有一个辅助函数 (uart_console_write()
),它执行逐个字符的写入,并将换行符转换为 CRLF 序列。 建议驱动程序编写者使用此函数,而不是实现自己的版本。
锁定¶
底层硬件驱动程序负责使用 port->lock 执行必要的锁定。 有一些例外(在下面的 struct uart_ops
列表中进行了描述。)
有两个锁。 每个端口的自旋锁和一个总体信号量。
从核心驱动程序的角度来看,port->lock 锁定以下数据
port->mctrl
port->icount
port->state->xmit.head (circ_buf->head)
port->state->xmit.tail (circ_buf->tail)
底层驱动程序可以自由使用此锁来提供任何额外的锁定。
port_sem 信号量用于防止端口在不适当的时间添加/删除或重新配置。 自 v2.6.27 以来,此信号量一直是 tty_port 结构中的“mutex”成员,通常称为端口互斥锁。
uart_ops¶
-
struct uart_ops¶
serial_core 和驱动程序之间的接口
定义:
struct uart_ops {
unsigned int (*tx_empty)(struct uart_port *);
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);
void (*stop_rx)(struct uart_port *);
void (*start_rx)(struct uart_port *);
void (*enable_ms)(struct uart_port *);
void (*break_ctl)(struct uart_port *, int ctl);
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, const struct ktermios *old);
void (*set_ldisc)(struct uart_port *, struct ktermios *);
void (*pm)(struct uart_port *, unsigned int state, unsigned int oldstate);
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;
};
成员
tx_empty
unsigned int ()(struct uart_port *port)
此函数测试 port 的发送器 FIFO 和移位器是否为空。 如果为空,此函数应返回
TIOCSER_TEMT
,否则返回 0。如果端口不支持此操作,则应返回TIOCSER_TEMT
。锁定:无。 中断:调用者相关。 此调用不得休眠
set_mctrl
void ()(struct uart_port *port, unsigned int mctrl)
此函数将 port 的调制解调器控制线设置为 mctrl 描述的状态。 mctrl 的相关位是
TIOCM_RTS
RTS 信号。TIOCM_DTR
DTR 信号。TIOCM_OUT1
OUT1 信号。TIOCM_OUT2
OUT2 信号。TIOCM_LOOP
将端口设置为环回模式。
如果设置了相应的位,则应激活该信号。 如果该位已清除,则应取消激活该信号。
锁定:已获取 port->lock。 中断:本地禁用。 此调用不得休眠
get_mctrl
unsigned int ()(struct uart_port *port)
返回 port 的调制解调器控制输入的当前状态。 不应返回输出的状态,因为核心会跟踪其状态。 状态信息应包括
TIOCM_CAR
DCD 信号的状态TIOCM_CTS
CTS 信号的状态TIOCM_DSR
DSR 信号的状态TIOCM_RI
RI 信号的状态
如果当前激活信号,则设置该位。 如果端口不支持 CTS、DCD 或 DSR,则驱动程序应指示该信号永久激活。 如果 RI 不可用,则不应将该信号指示为激活。
锁定:已获取 port->lock。 中断:本地禁用。 此调用不得休眠
stop_tx
void ()(struct uart_port *port)
停止传输字符。 这可能是由于 CTS 线变为不活动状态,或者 tty 层指示我们要由于
XOFF
字符而停止传输。驱动程序应尽快停止传输字符。
锁定:已获取 port->lock。 中断:本地禁用。 此调用不得休眠
start_tx
void ()(struct uart_port *port)
开始传输字符。
锁定:已获取 port->lock。 中断:本地禁用。 此调用不得休眠
throttle
void ()(struct uart_port *port)
通知串行驱动程序线路规程的输入缓冲区即将满,它应以某种方式发出信号,指示不应再将字符发送到串行端口。 仅当启用硬件辅助流控制时才会调用此函数。
锁定:与 tty 层进行的 unthrottle() 和 termios 修改序列化。
unthrottle
void ()(struct uart_port *port)
通知串行驱动程序现在可以将字符发送到串行端口,而不必担心线路规程的输入缓冲区溢出。
仅当启用硬件辅助流控制时才会调用此函数。
锁定:与 tty 层进行的 throttle() 和 termios 修改序列化。
send_xchar
void ()(struct uart_port *port, char ch)
即使端口已停止,也传输高优先级字符。 这用于实现 XON/XOFF 流控制和 tcflow()。 如果串行驱动程序未实现此函数,则 tty 核心会将字符附加到循环缓冲区,然后调用 start_tx() / stop_tx() 以刷新数据。
如果 ch == '0' (
__DISABLED_CHAR
),则不传输。锁定:无。 中断:调用者相关。
stop_rx
void ()(struct uart_port *port)
停止接收字符; port 正在关闭。
锁定:已获取 port->lock。 中断:本地禁用。 此调用不得休眠
start_rx
void ()(struct uart_port *port)
开始接收字符。
锁定:已获取 port->lock。 中断:本地禁用。 此调用不得休眠
enable_ms
void ()(struct uart_port *port)
启用调制解调器状态中断。
可以多次调用此方法。 调用 shutdown() 方法时应禁用调制解调器状态中断。
锁定:已获取 port->lock。 中断:本地禁用。 此调用不得休眠
break_ctl
void ()(struct uart_port *port, int ctl)
控制中断信号的传输。 如果 ctl 非零,则应传输中断信号。 当使用零 ctl 进行另一次调用时,应终止该信号。
锁定:调用者持有 tty_port->mutex
startup
int ()(struct uart_port *port)
获取任何中断资源并初始化任何底层驱动程序状态。 启用端口以进行接收。 它不应激活 RTS 或 DTR; 这将通过对 set_mctrl() 的单独调用来完成。
仅当最初打开端口时才会调用此方法。
锁定:已获取 port_sem。 中断:全局禁用。
shutdown
void ()(struct uart_port *port)
禁用 port,禁用可能有效的任何中断条件,并释放任何中断资源。 它不应禁用 RTS 或 DTR; 这将已通过对 set_mctrl() 的单独调用来完成。
一旦此调用完成,驱动程序不得访问 port->state。
仅当没有更多用户使用此 port 时才会调用此方法。
锁定:已获取 port_sem。 中断:调用者相关。
flush_buffer
void ()(struct uart_port *port)
刷新任何写入缓冲区,重置任何 DMA 状态并停止任何正在进行的 DMA 传输。
每当清除 port->state->xmit 循环缓冲区时都会调用此函数。
锁定:已获取 port->lock。 中断:本地禁用。 此调用不得休眠
set_termios
void ()(struct uart_port *port, struct ktermios *new, struct ktermios *old)
更改 port 参数,包括字长、奇偶校验、停止位。 更新 port->read_status_mask 和 port->ignore_status_mask 以指示我们感兴趣接收的事件类型。 相关的 ktermios::c_cflag 位是
CSIZE
- 字大小CSTOPB
- 2 个停止位PARENB
- 启用奇偶校验PARODD
- 奇校验(当PARENB
有效时)ADDRB
- 地址位(通过 uart_port::rs485_config() 更改)。CREAD
- 启用字符接收(如果未设置,仍从端口接收字符,但将其丢弃)。CRTSCTS
- 如果设置,则启用 CTS 状态更改报告。CLOCAL
- 如果未设置,则启用调制解调器状态更改报告。
相关的 ktermios::c_iflag 位是
INPCK
- 启用帧和奇偶校验错误事件以传递到 TTY 层。BRKINT
/PARMRK
- 这两者都启用中断事件以传递到 TTY 层。IGNPAR
- 忽略奇偶校验和帧错误。IGNBRK
- 忽略中断错误。 如果还设置了IGNPAR
,则也忽略溢出错误。
ktermios::c_iflag 位的交互如下(以奇偶校验错误为例)
奇偶校验错误
INPCK
IGNPAR
n/a
0
n/a
接收到的字符,标记为
TTY_NORMAL
无
1
n/a
接收到的字符,标记为
TTY_NORMAL
是
1
0
接收到的字符,标记为
TTY_PARITY
是
1
1
丢弃的字符
如果您的硬件支持硬件“软”流控制,则可以使用其他标志(例如,xon/xoff 字符)。
锁定:调用者持有 tty_port->mutex 中断:调用者相关。 此调用不得休眠
set_ldisc
void ()(struct uart_port *port, struct ktermios *termios)
规程更改的通知程序。 请参阅 TTY 线路规程。
锁定:调用者持有 tty_port->mutex
pm
void ()(struct uart_port *port, unsigned int state, unsigned int oldstate)
对指定的 port 执行任何电源管理相关的活动。 state 指示新状态(由 enum uart_pm_state 定义),oldstate 指示以前的状态。
此函数不应用于获取任何资源。
当最初打开 port 并最终关闭时,将调用此函数,除非 port 也是系统控制台。 即使未设置
CONFIG_PM
,也会发生这种情况。锁定:无。 中断:调用者相关。
type
const char *()(struct uart_port *port)
返回指向描述指定 port 的字符串常量的指针,或者返回
NULL
,在这种情况下,将替换字符串“unknown”。锁定:无。 中断:调用者相关。
release_port
void ()(struct uart_port *port)
释放 port 当前使用的任何内存和 IO 区域资源。
锁定:无。 中断:调用者相关。
request_port
int ()(struct uart_port *port)
请求端口所需的任何内存和 IO 区域资源。 如果任何资源失败,则在此函数返回时,不应注册任何资源,并且应在失败时返回 -
EBUSY
。锁定:无。 中断:调用者相关。
config_port
void ()(struct uart_port *port, int type)
执行 port 所需的任何自动配置步骤。 type 包含所需配置的位掩码。
UART_CONFIG_TYPE
指示端口需要检测和识别。 port->type 应设置为找到的类型,如果未检测到端口,则设置为PORT_UNKNOWN
。UART_CONFIG_IRQ
指示中断信号的自动配置,应使用标准内核自动探测技术进行探测。 在端口在内部硬连线中断的平台上(例如,片上系统实现),这没有必要。锁定:无。 中断:调用者相关。
verify_port
int ()(struct uart_port *port, struct serial_struct *serinfo)
验证 serinfo 中包含的新串行端口信息是否适合此端口类型。
锁定:无。 中断:调用者相关。
ioctl
int ()(struct uart_port *port, unsigned int cmd, unsigned long arg)
执行任何特定于端口的 IOCTL。 IOCTL 命令必须使用 <asm/ioctl.h> 中找到的标准编号系统来定义。
锁定:无。 中断:调用者相关。
poll_init
int ()(struct uart_port *port)
由 kgdb 调用,以执行支持 poll_put_char() 和 poll_get_char() 所需的最少硬件初始化。 与 startup() 不同,这不应请求中断。
锁定:已获取
tty_mutex
和 tty_port->mutex。 中断:n/a。poll_put_char
void ()(struct uart_port *port, unsigned char ch)
由 kgdb 调用,以将单个字符 ch 直接写入串行 port。 它可以并且应该阻塞,直到 TX FIFO 中有空间。
锁定:无。 中断:调用者相关。 此调用不得休眠
poll_get_char
int ()(struct uart_port *port)
由 kgdb 调用,以直接从串行端口读取单个字符。 如果有数据可用,则应返回;否则,该函数应立即返回
NO_POLL_CHAR
。锁定:无。 中断:调用者相关。 此调用不得休眠
说明
此结构描述了可以在物理硬件上完成的所有操作。
其他函数¶
-
void uart_write_wakeup(struct uart_port *port)¶
计划写入处理
参数
struct uart_port *port
要处理的端口
说明
中断处理程序使用此例程来安排驱动程序的软件中断部分中的处理。 当传输缓冲区中的字符数已降至阈值以下时,驱动程序应调用此函数。
锁定:应持有 port->lock
-
void uart_update_timeout(struct uart_port *port, unsigned int cflag, unsigned int baud)¶
更新每个端口的帧定时信息
参数
struct uart_port *port
描述端口的 uart_port 结构
unsigned int cflag
termios cflag 值
unsigned int baud
端口速度
说明
设置 port 帧定时信息,从中导出 FIFO 超时值。 cflag 值应反映实际的硬件设置,因为此处考虑了位数、奇偶校验、停止位和波特率。
锁定:调用者应获取 port->lock
-
unsigned int uart_get_baud_rate(struct uart_port *port, struct ktermios *termios, const struct ktermios *old, unsigned int min, unsigned int max)¶
返回特定端口的波特率
参数
struct uart_port *port
描述相关端口的 uart_port 结构。
struct ktermios *termios
所需的 termios 设置
const struct ktermios *old
旧 termios(或
NULL
)unsigned int min
最小可接受的波特率
unsigned int max
最大可接受的波特率
说明
将 termios 结构解码为数字波特率,同时考虑神奇的 38400 波特率(带有 spd_* 标志),并将 B0
速率映射到 9600 波特。
如果新的波特率无效,请尝试 old termios 设置。 如果仍然无效,我们会尝试 9600 波特。 如果这也无效,则返回 0。
将更新 termios 结构以反映我们实际将使用的波特率。 对于请求 B0 的情况(“挂断”),不要这样做。
锁定:调用者相关
-
unsigned int uart_get_divisor(struct uart_port *port, unsigned int baud)¶
返回 uart 时钟除数
参数
struct uart_port *port
描述端口的 uart_port 结构
unsigned int baud
所需的波特率
说明
计算指定 baud 的除数 (baud_base / baud),并适当舍入。
如果选择了 38400 波特和自定义除数,则返回自定义除数。
锁定:调用者相关
-
int uart_get_lsr_info(struct tty_struct *tty, struct uart_state *state, unsigned int __user *value)¶
获取线路状态寄存器信息
参数
struct tty_struct *tty
与 UART 关联的 tty
struct uart_state *state
正在查询的 UART
unsigned int __user *value
返回的调制解调器值
-
void uart_console_write(struct uart_port *port, const char *s, unsigned int count, void (*putchar)(struct uart_port*, unsigned char))¶
将控制台消息写入串行端口
参数
struct uart_port *port
用于写入消息的端口
const char *s
字符数组
unsigned int count
字符串中要写入的字符数
void (*putchar)(struct uart_port *, unsigned char)
将字符写入端口的函数
-
struct uart_port *uart_get_console(struct uart_port *ports, int nr, struct console *co)¶
获取控制台的 uart 端口
参数
struct uart_port *ports
要在其中搜索的端口
int nr
ports 的数量
struct console *co
要搜索的控制台
返回值
控制台 co 的 uart_port
说明
检查是否已指定无效的 uart 编号(作为 co->index),如果是,则搜索第一个具有控制台支持的可用端口。
-
int uart_parse_earlycon(char *p, enum uart_iotype *iotype, resource_size_t *addr, char **options)¶
解析 earlycon 选项
参数
char *p
指向第二个字段的指针(即,刚刚超过“<name>,”)
enum uart_iotype *iotype
用于解码的 iotype 的指针(输出)
resource_size_t *addr
用于解码的 mapbase/iobase 的指针(输出)
char **options
用于 <options> 字段的指针; 如果不存在,则为
NULL
(输出)
说明
- 解码以下形式的 earlycon 内核命令行参数
earlycon=<name>,io|mmio|mmio16|mmio32|mmio32be|mmio32native,<addr>,<options>
console=<name>,io|mmio|mmio16|mmio32|mmio32be|mmio32native,<addr>,<options>
- 可选形式
earlycon=<name>,0x<addr>,<options>
console=<name>,0x<addr>,<options>
也被接受; 返回的 iotype 将为 UPIO_MEM
。
返回值
成功时为 0,失败时为 -EINVAL
-
void uart_parse_options(const char *options, int *baud, int *parity, int *bits, int *flow)¶
解析串口波特率/奇偶校验/数据位/流控。
参数
const char *options
选项字符串指针
int *baud
指向用于保存波特率的“int”变量的指针。
int *parity
指向用于保存奇偶校验的“int”变量的指针。
int *bits
指向用于保存数据位数的“int”变量的指针。
int *flow
指向用于保存流控字符的“int”变量的指针。
说明
uart_parse_options()
解码包含串口选项的字符串。字符串的格式为 <波特率><奇偶校验><数据位><流控>,例如:115200n8r
-
int uart_set_options(struct uart_port *port, struct console *co, int baud, int parity, int bits, int flow)¶
设置串口参数
参数
struct uart_port *port
指向串口 uart_port 结构的指针
struct console *co
控制台指针
int baud
波特率
int parity
奇偶校验字符 - ‘n’(无),‘o’(奇),‘e’(偶)
int bits
数据位数
int flow
流控字符 - ‘r’ (rts)
说明
锁定:调用者必须持有 console_list_lock 才能序列化串口锁的早期初始化。
-
int uart_register_driver(struct uart_driver *drv)¶
向 uart 核心层注册驱动程序
参数
struct uart_driver *drv
底层驱动程序结构
说明
向核心驱动程序注册 uart 驱动程序。反过来,我们向 tty 层注册,并初始化每个端口的核心驱动程序状态。
我们在 /proc/tty/driver 中有一个 proc 文件,它以普通驱动程序命名。
drv->port 应该为 NULL
,并且在成功调用此函数后,应使用 uart_add_one_port() 注册每个端口的结构。
锁定:无,中断:启用
-
void uart_unregister_driver(struct uart_driver *drv)¶
从 uart 核心层移除驱动程序
参数
struct uart_driver *drv
底层驱动程序结构
说明
从核心驱动程序中移除对驱动程序的所有引用。如果底层驱动程序使用 uart_add_one_port() 注册了所有端口,则必须通过 uart_remove_one_port() 移除所有端口。(即 drv->port 为 NULL
。)
锁定:无,中断:启用
-
bool uart_match_port(const struct uart_port *port1, const struct uart_port *port2)¶
两个端口是否等效?
参数
const struct uart_port *port1
第一个端口
const struct uart_port *port2
第二个端口
说明
此实用程序函数可用于确定两个 uart_port 结构是否描述同一端口。
-
void uart_handle_dcd_change(struct uart_port *uport, bool active)¶
处理载波检测状态的更改
参数
struct uart_port *uport
打开端口的 uart_port 结构
bool active
新的载波检测状态
说明
调用者必须持有 uport->lock。
-
void uart_handle_cts_change(struct uart_port *uport, bool active)¶
处理清除发送状态的更改
参数
struct uart_port *uport
打开端口的 uart_port 结构
bool active
新的清除发送状态
说明
调用者必须持有 uport->lock。
-
bool uart_try_toggle_sysrq(struct uart_port *port, u8 ch)¶
从串行线路启用 SysRq
参数
struct uart_port *port
在 BREAK 后遇到 char(s) 的 uart_port 结构
u8 ch
收到 BREAK 后序列中的新字符
说明
当端口上满足所需序列时,启用 magic SysRq(请参阅 CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE)。
返回值
如果 ch 超出启用序列并且应以其他方式处理,则为 false
,如果消耗了 ch,则为 true
。
-
uart_port_tx_limited¶
uart_port_tx_limited (port, ch, count, tx_ready, put_char, tx_done)
具有计数限制的 uart_port 的传输助手
参数
port
uart 端口
ch
用于存储要写入 HW 的字符的变量
count
要发送的字符数限制
tx_ready
HW 是否可以接受更多数据函数
put_char
写入字符的函数
tx_done
循环完成后要调用的函数
说明
此助手使用 put_char() 将字符从 xmit 缓冲区传输到硬件。这样做直到发送 count 个字符,并且当 tx_ready 的计算结果为 true 时。
- 宏参数中的表达式应设计如下
tx_ready:如果 HW 可以接受更多要发送的数据,则应评估为 true。此参数可以是
true
,这意味着 HW 始终准备就绪。put_char:应将 ch 写入 port 的设备。
tx_done:当写入循环完成后,这可以在 ops->stop_tx() 的潜在调用发生之前执行任意操作。如果驱动程序不需要执行任何操作,请使用例如 ({})。
对于所有这些,都持有 port->lock,本地禁用中断,并且表达式不得睡眠。
返回值
完成后 xmit 缓冲区中的字符数。
-
uart_port_tx¶
uart_port_tx (port, ch, tx_ready, put_char)
uart_port 的传输助手
参数
port
uart 端口
ch
用于存储要写入 HW 的字符的变量
tx_ready
HW 是否可以接受更多数据函数
put_char
写入字符的函数
说明
有关更多详细信息,请参见 uart_port_tx_limited()
。
其他说明¶
目标是在未来的某一天从 uart_port 中删除“unused”条目,并允许底层驱动程序向核心注册它们自己的 uart_port。这将允许驱动程序使用 uart_port 作为指向包含 uart_port 条目及其自身扩展的结构的指针,因此
struct my_port {
struct uart_port port;
int my_stuff;
};
通过 GPIO 的调制解调器控制线¶
提供了一些助手函数,以便通过 GPIO 设置/获取调制解调器控制线。
-
void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)¶
根据 mctrl 状态设置 gpios
参数
struct mctrl_gpios *gpios
要设置的 gpios
unsigned int mctrl
要设置的状态
说明
根据 mctrl 状态设置 gpios。
-
struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios, enum mctrl_gpio_idx gidx)¶
获取调制解调器线路索引的 gpio_desc
参数
struct mctrl_gpios *gpios
要查找的 gpios
enum mctrl_gpio_idx gidx
调制解调器线路的索引
返回值
与调制解调器线路索引关联的 gpio_desc 结构
-
unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl)¶
使用 gpios 值更新 mctrl。
参数
struct mctrl_gpios *gpios
从中获取信息的 gpios
unsigned int *mctrl
要设置的 mctrl
返回值
修改后的 mctrl(与 mctrl 中的值相同)
说明
使用 gpios 值更新 mctrl。
-
struct mctrl_gpios *mctrl_gpio_init(struct uart_port *port, unsigned int idx)¶
初始化 uart gpios
参数
struct uart_port *port
为其初始化 gpios 的端口
unsigned int idx
端口设备中 gpio 的索引
说明
如果设备树中存在 {cts,rts,...}-gpios,并且它们被请求,则将从设备树中获取它们,设置方向等,并返回分配的结构。使用 devm_* 函数,因此无需显式释放。由于这会设置 irq 处理,请确保也不要在驱动程序中处理 gpio 输入线的更改。
-
void mctrl_gpio_enable_ms(struct mctrl_gpios *gpios)¶
启用 irqs 并处理 ms 线的更改
参数
struct mctrl_gpios *gpios
要启用的 gpios
-
void mctrl_gpio_disable_ms_sync(struct mctrl_gpios *gpios)¶
禁用 irqs 并处理 ms 线的更改,并等待处理任何挂起的 IRQ
参数
struct mctrl_gpios *gpios
要禁用的 gpios
-
void mctrl_gpio_disable_ms_no_sync(struct mctrl_gpios *gpios)¶
禁用 irqs 并处理 ms 线的更改,并立即返回
参数
struct mctrl_gpios *gpios
要禁用的 gpios