串口驱动流程分析

TTY驱动程序架构
TTY概念解析
TTY架构分析
##1.TTY概念解析
在Linux系统中,终端是一类字符型设备,它包括多种类型,通常使用tty来简称各种类型的终端设备。
1.1串口终端(/dev/ttyS*)
串口终端是使用计算机串口连接的终端设备。Linux把每个串行端口都看作是一个字符设备。这些串行
端口所对应的设备名称是/dev/ttySAC0;/dev/ttySAC1……
1.2控制台终端(/dev/console)
在Linux系统中,计算机的输出设备通常被称为控制台终端(Console),这里特指printk信息输出到的设备。
/dev/console是一个虚拟的设备,它需要映射到真正的tty上,比如通过内核启动参数”console=ttySAC0”就把console映射到了串口0
1.3虚拟终端(/dev/tty*)
当用户登录时,使用的是虚拟终端。使用Ctcl+Alt+[F1—F6]组合键时,我们就可以切换到tty1、tty2、tty3等上面去。tty1–tty6等称为虚拟终端,而tty0则是当前所使用虚拟终端的一个别名

2TTY架构

Linux tty子系统包含:tty核心,tty线路规程和tty驱动。tty核心是对整个tty设备的抽象,对用户提供统一的接口,tty线路规程是对传输数据的格式化,tty驱动则是面向tty设备的硬件驱动。

2.1回溯串口发送流程
dump_stack();
见图
串口驱动初始化
1.串口驱动程序结构
2.串口驱动中的重要参数
3.初始化分析
3.1串口驱动初始化
串口相关文件在内核代码的samsung.c和S5PV210.c中,
1.在module_init中注册串口驱动uart_register_driver()
2.调用probe函数初始化设备
2.1取出相应的uart_port
2.2初始化uart_port
2.2.1获取虚拟地址
2.2.2获取中断号
2.2.3复位FIFO
2.3添加uart_port_uart_add_one_port
2.4创建属性文件device_create_file
2.5初始化动态频率调节-s3c24xx_serial_cpufreq_register
3.2串口初始化之打开操作
当用户调用open打开函数,
1.调用tty_fops结构,调用tty_open
2.调用uart_ops结构,调用uart_open,调用uart_start
3.调用s3c24xx_serial_ops结构,调用s3c24xx_serial_startup
s3c24xx_serial_startup执行流程
1.使能串口接收功能
2.为数据接收注册中断处理程序
3.使能串口发送功能
4.为发送数据注册中断处理程序
3.3串口操作之数据发送write
3.3.1TTY数据发送调用关系
1.调用tty_fops结构,调用tty_write
2.调用线路规程n_tty_write
3.调用uart_ops,调用uart_write,
4.调用s3c24xx_serial_start_tx
驱动代码:s3x24xx_serial_start_tx()实现流程
1.使能中断,发现FIFO的数据量不够,串口立刻产生中断,此时调用中断处理程序进行数据发送
2.驱动代码中的中断处理程序实现数据发送
从哪里取数据
中断处理程序从循环缓冲中取数据
循环缓冲数据什么时候放进去uart_write
将应用程序buf数据写给循环缓冲circ_buf
s3x24xx_serial_start_tx()和中断处理程序
中断处理程序s3c24xx_serial_tx_chars(),
1.判断是否有需要发送的x_cahr字符,如果有,将其写入到UTXH寄存器发送。
2.如果串口缓冲为空或者串口状态是不允许发送的,则关闭发送中断。
3.利用while()循环发送数据,循环的条件时发送数据大条件时串口缓冲区数据不为空和发送的数据量不到256
3.1当发送TX_FIFIO满的时候退出发送
3.2从循环缓冲取出数据,放入UTXH缓冲区
3.3调整循环缓冲的位置
4当循环数据低于256时,唤醒之前阻塞的发送数据进程
5.当循环缓冲区为0,则关闭发送中断
3.4串口操作之接收数据
1TTY数据接收调用关系
1.调用tty_fops结构,调用tty_read
2.调用线路规程n_tty_read
2.1设置APP为阻塞态
2.2如果没有数据可读,让阻塞生效
2.3如果有数据,从read_buf把数据读走
3驱动程序:
3.1读取UFCON寄存器
3.2读取UFSTAT寄存器
3.3如果接收fifo里的数据量为0,则退出处理
4.读取UERSTAT寄存器
5.从URXH寄存器中取出接收到的字符
6.进行流控处理
7.根据UERSTAT寄存器
8.如果收到sysrq字符,进行特殊处理-uart_handle_sysrq_char
9.把接收的字符放到串口驱动的buf使用函数uart_insert_char
10.把串口驱动收到的数据送到线路规程的read_buf使用函数为tty_flip_buffer_push
什么是流控?
串口A和B的RX与TX交叉互联
A的CTS接B的RTS,如果B可以接收数据则B把RTS设置为高电平,此时A的CTS同位高电平,则A可以向B发送数据
A的RTS接B的CTS,如果A可以接收数据则把A的RTS设置为高电平,此时B的CTS同位高电平,则B可以向A发送数据

硬件流程分为非自动和自动的,linux系统中支持自动流控。

串口发送接收函数示例

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;
    //ufstat = rd_regl(port,S3C2410_UFSTAT);//读取UFSTAT寄存器的状态
    while(max_count-->0)
    {

        ufstat = rd_regl(port,S3C2410_UFSTAT);//读取UFSTAT寄存器的状态
        if((ufstat&0x3f)==0)//判断接收fifo是否为空
            break;
        uerstat = rd_regl(port,S3C2410_UERSTAT);//读取错误类型并保存
        ch = rd_regb(port,S3C2410_URXH);//从URXH读取收到的字符
        uart_handle_sysrq_char(port,ch);

        flag = TTY_NORMAL;
        uart_insert_char(port,uerstat,S3C2410_UERSTAT_OVERRUN,ch);//把接收的字符送进串口驱动
    }
    tty_flip_buffer_push(tty);//把临时缓冲的数据送入TTY线路规程
    return IRQ_HANDLED;
 out:
    return IRQ_HANDLED;
}
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;
    //判断X_char是否为0,如果不为0,则发送x_char
    if(port->x_cahr)
    {
        wr_regb(port,S3C2410_UTXH,port->x_cahr);
        goto out;

    }
    //判断发送缓冲或者驱动设置为停止发送的状态,则取消发送
    if(uart_circ_empty(xmit)||uart_tx_stopped(port))//判断循环缓冲是否为空
    {

        s3c24xx_serial_stop_tx(port);//关闭串口发送功能
        goto out;
    }
    while(!uart_circ_empty(xmit)&&((count--)>0))
    {   //3.1判断循环FIFO是否满
        if(rd_regl(port,S3C2410_UFATAT)&(1<<14))
        break;
        //将发送字符写入发送寄存器
        wr_regb(port,S3c2410_UTXH,xmit->buf[xmit->tail]);
        xmit->tail = (xmit->tail+1)&(UART_XMIT_SIZE-1);//修改循环缓冲中tail指针的位置
        port->icount.tx++;
    }
    if(uart_circ_chars_pending(xmit)<256)//如果发送缓冲的剩余量小于256,则关闭之前阻塞的发送进程
    {

        uart_writre_wakeup(port);
    }
    if(uart_circ_empty(xmit)//如果发送缓冲为空,则关闭串口使能
    s3c24xx_serial_stop_tx(port);
out:
    return IRQ_HANDLED;
}

你可能感兴趣的:(LINUX设备驱动进阶)