串口驅動程序設計

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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章