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;
}