TTY 驱动程序架构
在Linux系统中,终端是一类字符型设备,它包括多种类型,通常使用 tty来简称各种类型的终端设备。主要包括以下几种:
串口终端(/dev/ttyS*):
串口终端是使用计算机串口连接的终端设备。Linux把每个串行端口都看作是一个字符设备。这些串行端口所对应的设备名称是 /dev/ttySAC0; /dev/ttySAC1……
控制台终端(/dev/console):
在Linux系统中,计算机的输出设备通常被称为控制台终端(Console),这里特指printk信息输出到的设备。/dev/console 是一个虚拟的设备,它需要映射到真正的tty上,比如通过内核启动参数 ” console=ttySAC0 ”就把console映射到了串口0。
虚拟终端(/dev/tty*):
当用户登录时,使用的是虚拟终端。使用Ctcl+Alt+[F1—F6]组合键时,我们就可以切换到tty1、tty2、tty3等上面去。tty1–tty6等称为虚拟终端,而tty0则是当前所使用虚拟终端的一个别名。
TTY架构分析:
Linux 的 tty 子系统包含:tty核心,tty线路规程 和 tty驱动。tty核心是对整个tty设备的抽象,对用户提供统一的接口,tty线路规程是对传输数据的格式化,tty驱动则是面向tty设备的硬件驱动。
串口驱动模型
初始化:
UART驱动程序结构:struct uart_driver
UART端口结构: struct uart_port
UART相关操作函数结构: struct uart_ops
UART状态结构: struct uart_state
UART信息结构: struct uart_info
打开设备:
发送数据:
接收数据:
串口驱动代码编写
下面的两个例子都是在OK6410开发板的串口驱动程序,详细代码在内核代码linux-ok6410\drivers\tty\serial\samsung.c文件中。
发送数据中断处理函数:
//串口发送函数
static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
{
struct s3c24xx_uart_port *ourport = id;
struct uart_port *port = &ourport->port;
struct circ_buf *xmit = &port->state->xmit;//循环缓冲
int count = 256;
//1、判断 x_char 字符是否为零,不为0则发送
if (port->x_char)
{
wr_regb(port, S3C2410_UTXH, port->x_char);//参数2:寄存器,参数3:写入的数据
port->icount.tx++;
port->x_char = 0;
goto out;
}
//2、判断循环缓冲是否为空或者串口驱动是否关闭,若是则取消发送
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
s3c24xx_serial_stop_tx(port);//取消发送
goto out;
}
//3、若发送缓冲不为空,且发送数量小于256个,则循环发送
while (!uart_circ_empty(xmit) && count-- > 0)//大于0
{
//3.1 发送FIFO寄存器已经满,则取消发送
if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
break;
//3.2 将要发送的字符写入发送寄存器
wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
//向循环缓冲xmit中存入数据时,head头部会移动,取数据时tail尾部会移动
//3.3 修改循环缓冲的尾部位置
//tail指针加1,因为缓冲是循环的,与上(长度-1)可以从尾部循环到头部
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;//发送数据量计数加1
}
//4、如果发送缓冲中的剩余数据量小于256,则唤醒之前阻塞的发送进程
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
//5、如果发送缓冲为空,关闭发送使能
if (uart_circ_empty(xmit))
s3c24xx_serial_stop_tx(port);
out:
return IRQ_HANDLED;
}
接收数据中断处理函数:
//串口接收数据函数
static irqreturn_t s3c24xx_serial_rx_chars(int irq, void *dev_id)
{
struct s3c24xx_uart_port *ourport = dev_id;
struct uart_port *port = &ourport->port;
struct tty_struct *tty = port->state->port.tty;
unsigned int ufcon, ch, flag, ufstat, uerstat;
int max_count = 64; //最多64个字符
while (max_count-- > 0) {
//1、读取 UFCON 寄存器
ufcon = rd_regl(port, S3C2410_UFCON);
//2、读取 UFSTAT 寄存器,它保存了接收数据的数据量信息
ufstat = rd_regl(port, S3C2410_UFSTAT);
//3、如果接收FIFO中的数据量为0,则关闭
if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
break;
//4、读取 UERSTAT 寄存器
uerstat = rd_regl(port, S3C2410_UERSTAT);
//5、从 URXH 寄存器中读取接收到的字符
ch = rd_regb(port, S3C2410_URXH);
flag = TTY_NORMAL;
port->icount.rx++;
//6和7两部省略,可以不写
//8、如果收到的时sysrq字符,进行特殊处理
if (uart_handle_sysrq_char(port, ch))
goto ignore_char;
//9、把接收到的字符插入到接收缓冲中
uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN,
ch, flag);
ignore_char:
continue;
}
//10、将串口驱动接受到的数据送进线路规程中,导入到 read-buf 中
tty_flip_buffer_push(tty);
out:
return IRQ_HANDLED;
}