新聞中心

        EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > 打通linux的tty驅(qū)動(dòng)的數(shù)據(jù)鏈路

        打通linux的tty驅(qū)動(dòng)的數(shù)據(jù)鏈路

        作者: 時(shí)間:2016-10-08 來源:網(wǎng)絡(luò) 收藏

        ………

        }

        就是根據(jù)cmd的值進(jìn)行相關(guān)操作,有對(duì)線路規(guī)程操作的,有直接通過tty_driver操作的。

        三、TTY驅(qū)動(dòng)層分析

        接下來看,TTY驅(qū)動(dòng)層是怎樣的:

        TTY驅(qū)動(dòng)層是根據(jù)不同的硬件操作來完成相應(yīng)的操作,這里我們以串口為例。

        串口作為一個(gè)標(biāo)準(zhǔn)的設(shè)備,把共性的分離出來,就成了uart層,特性成了serial層。

        主要是serial層作為一個(gè)驅(qū)動(dòng)模塊加載。以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í)候定義的,表示支持串口的個(gè)數(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ū)動(dòng)層里有幾個(gè)重要的數(shù)據(jù)結(jié)構(gòu):

        structuart_driver;

        structuart_state ;

        structuart_port;

        structtty_driver;

        structtty_port;

        實(shí)際上,理清了這幾個(gè)結(jié)構(gòu)體的關(guān)系,也就理清了TTY驅(qū)動(dòng)層。

        uart_register_driver:

        這個(gè)函數(shù)主要是向TTY核心層注冊(cè)一個(gè)TTY驅(qū)動(dòng):

        retval= tty_register_driver(normal);

        其中normal是tty_driver.

        另外,還會(huì)對(duì)tty_driver和uart_driver之間進(jìn)行某些賦值和指針連接。我們最關(guān)心的是,給tty_driver初始化了操作函數(shù)uart_ops,這樣,在tty核心層就可以通過uart_ops來對(duì)UART層進(jìn)行操作。

        serial8250_register_ports:

        最重要的兩個(gè)函數(shù):serial8250_isa_init_ports和uart_add_one_port

        serial8250_isa_init_ports主要的工作是初始化uart_8250_port:開啟定時(shí)器和初始化uart_port.

        uart_add_one_port顧名思議,就是為uart_driver增加一個(gè)端口,在uart_driver里的state指向NR個(gè)slot, 然后,這個(gè)函數(shù)的主要工作就是為slot增加一個(gè)port.這樣,uart_driver就可以通過port對(duì)ops操作函數(shù)集進(jìn)行最底層的操作。

        現(xiàn)在來分析下連接部分,也就是tty_driver如何工作,如何連接tty核心層(或者ldisc層)和串口層uart_port.關(guān)于操作部分主要是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,在這個(gè)函數(shù)里作了一些串口初始化的工作,其中有申請(qǐng)接收數(shù)據(jù)中斷或建立超時(shí)輪詢處理。

        在startup里面申請(qǐng)了接收數(shù)據(jù)中斷,那么這個(gè)中斷服務(wù)程序就跟讀操作密切相關(guān)了,從tty核心層的讀操作可知,接收到的數(shù)據(jù)一定是傳送到read_buf中的。現(xiàn)在來看是中斷服務(wù)程序。

        調(diào)用receive_chars來接收數(shù)據(jù),在receive_chars中,出現(xiàn)了兩個(gè)傳輸數(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);

        }

        當(dāng)當(dāng)前的tty_buffer空間不夠時(shí)調(diào)用tty_insert_flip_string_flags,在這個(gè)函數(shù)里會(huì)去查找下一個(gè)tty_buffer,并將數(shù)據(jù)放到下一個(gè)tty_buffer的char_buf_ptr里。

        那么char_buf_ptr的數(shù)據(jù)怎樣與線路規(guī)程中的read_buf關(guān)聯(lián)的呢,我們看,在初始化tty_buffer的時(shí)候,也就是在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ù)的最后,初始化了一個(gè)工作隊(duì)列。

        而這個(gè)隊(duì)列在什么時(shí)候調(diào)度呢,在驅(qū)動(dòng)層里receive_chars的最后調(diào)用了tty_flip_buffer_push這個(gè)函數(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的時(shí)候有兩種方式,一種是flush_to_ldisc,另一種就是調(diào)度tty緩沖區(qū)的工作隊(duì)列。

        flush_to_ldisc是隊(duì)列調(diào)用的函數(shù):

        static void flush_to_ldisc(struct work_struct *work)



        關(guān)鍵詞:

        評(píng)論


        相關(guān)推薦

        技術(shù)專區(qū)

        關(guān)閉
        主站蜘蛛池模板: 永寿县| 桦川县| 武安市| 洪江市| 雷州市| 安乡县| 镇平县| 内江市| 康保县| 齐齐哈尔市| 新建县| 开江县| 前郭尔| 栾城县| 赤峰市| 东丽区| 图木舒克市| 屯留县| 吴桥县| 会宁县| 淳安县| 抚顺县| 尚义县| 平昌县| 灌阳县| 兰考县| 新巴尔虎右旗| 林周县| 梧州市| 奈曼旗| 揭东县| 星座| 繁昌县| 榆林市| 锡林浩特市| 松潘县| 平谷区| 阳高县| 镇江市| 平阳县| 武冈市|