串口驱动程序设计

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;
}
发布了5 篇原创文章 · 获赞 60 · 访问量 18万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章