打通linux的tty驅(qū)動的數(shù)據(jù)鏈路
………
}
}
就是根據(jù)cmd的值進行相關操作,有對線路規(guī)程操作的,有直接通過tty_driver操作的。
三、TTY驅(qū)動層分析
接下來看,TTY驅(qū)動層是怎樣的:
TTY驅(qū)動層是根據(jù)不同的硬件操作來完成相應的操作,這里我們以串口為例。
串口作為一個標準的設備,把共性的分離出來,就成了uart層,特性成了serial層。
主要是serial層作為一個驅(qū)動模塊加載。以8250.c為例:
static int __init serial8250_init(void)
{
………
serial8250_reg.nr= UART_NR;
ret= uart_register_driver(serial8250_reg);
………
serial8250_register_ports(serial8250_reg,serial8250_isa_devs->dev);
………
}
#define UART_NR CONFIG_SERIAL_8250_NR_UARTS
CONFIG_SERIAL_8250_NR_UARTS是在配置內(nèi)核的時候定義的,表示支持串口的個數(shù)。
static struct uart_driver serial8250_reg = {
。owner =THIS_MODULE,
。driver_name =serial,
。dev_name =ttyS,
。major =TTY_MAJOR,
。minor =64,
。cons =SERIAL8250_CONSOLE,
};
在驅(qū)動層里有幾個重要的數(shù)據(jù)結(jié)構(gòu):
structuart_driver;
structuart_state ;
structuart_port;
structtty_driver;
structtty_port;
實際上,理清了這幾個結(jié)構(gòu)體的關系,也就理清了TTY驅(qū)動層。
uart_register_driver:
這個函數(shù)主要是向TTY核心層注冊一個TTY驅(qū)動:
retval= tty_register_driver(normal);
其中normal是tty_driver.
另外,還會對tty_driver和uart_driver之間進行某些賦值和指針連接。我們最關心的是,給tty_driver初始化了操作函數(shù)uart_ops,這樣,在tty核心層就可以通過uart_ops來對UART層進行操作。
serial8250_register_ports:
最重要的兩個函數(shù):serial8250_isa_init_ports和uart_add_one_port
serial8250_isa_init_ports主要的工作是初始化uart_8250_port:開啟定時器和初始化uart_port.
uart_add_one_port顧名思議,就是為uart_driver增加一個端口,在uart_driver里的state指向NR個slot, 然后,這個函數(shù)的主要工作就是為slot增加一個port.這樣,uart_driver就可以通過port對ops操作函數(shù)集進行最底層的操作。
現(xiàn)在來分析下連接部分,也就是tty_driver如何工作,如何連接tty核心層(或者ldisc層)和串口層uart_port.關于操作部分主要是uart_ops.
uart_open:
staticint uart_open(struct tty_struct *tty, struct file *filp)
{
………
retval= uart_startup(tty, state, 0);
……
}
staticint uart_startup(struct tty_struct *tty, struct uart_state *state,int init_hw)
{
……
retval= uport->ops->startup(uport);
………
}
調(diào)用了uart_port的操作函數(shù)ops的startup,在這個函數(shù)里作了一些串口初始化的工作,其中有申請接收數(shù)據(jù)中斷或建立超時輪詢處理。
在startup里面申請了接收數(shù)據(jù)中斷,那么這個中斷服務程序就跟讀操作密切相關了,從tty核心層的讀操作可知,接收到的數(shù)據(jù)一定是傳送到read_buf中的?,F(xiàn)在來看是中斷服務程序。
調(diào)用receive_chars來接收數(shù)據(jù),在receive_chars中,出現(xiàn)了兩個傳輸數(shù)據(jù)的函數(shù):
tty_insert_flip_char和tty_flip_buffer_push.
static inline int tty_insert_flip_char(struct tty_struct *tty,
unsigned char ch, char flag)
{
struct tty_buffer *tb = tty->buf.tail;
if(tb tb->used tb->size) {
tb->flag_buf_ptr[tb->used]= flag;
tb->char_buf_ptr[tb->used++]= ch;
return1;
}
return tty_insert_flip_string_flags(tty, ch, flag, 1);
}
當當前的tty_buffer空間不夠時調(diào)用tty_insert_flip_string_flags,在這個函數(shù)里會去查找下一個tty_buffer,并將數(shù)據(jù)放到下一個tty_buffer的char_buf_ptr里。
那么char_buf_ptr的數(shù)據(jù)怎樣與線路規(guī)程中的read_buf關聯(lián)的呢,我們看,在初始化tty_buffer的時候,也就是在tty_buffer_init函數(shù)中:
void tty_buffer_init(struct tty_struct *tty)
{
spin_lock_init(tty->buf.lock);
tty->buf.head= NULL;
tty->buf.tail= NULL;
tty->buf.free= NULL;
tty->buf.memory_used= 0;
INIT_DELAYED_WORK(tty->buf.work,flush_to_ldisc);
}
在函數(shù)的最后,初始化了一個工作隊列。
而這個隊列在什么時候調(diào)度呢,在驅(qū)動層里receive_chars的最后調(diào)用了tty_flip_buffer_push這個函數(shù)。
void tty_flip_buffer_push(struct tty_struct *tty)
{
unsigned long flags;spin_lock_irqsave(tty->buf.lock, flags);if (tty->buf.tail != NULL)
tty->buf.tail->commit = tty->buf.tail->used;spin_unlock_irqrestore(tty->buf.lock, flags);
if (tty->low_latency)
flush_to_ldisc(tty->buf.work.work);else schedule_delayed_work(tty->buf.work, 1);
}
那么,在push數(shù)據(jù)到tty_buffer的時候有兩種方式,一種是flush_to_ldisc,另一種就是調(diào)度tty緩沖區(qū)的工作隊列。
flush_to_ldisc是隊列調(diào)用的函數(shù):
static void flush_to_ldisc(struct work_struct *work)
評論