在线看毛片网站电影-亚洲国产欧美日韩精品一区二区三区,国产欧美乱夫不卡无乱码,国产精品欧美久久久天天影视,精品一区二区三区视频在线观看,亚洲国产精品人成乱码天天看,日韩久久久一区,91精品国产91免费

<menu id="6qfwx"><li id="6qfwx"></li></menu>
    1. <menu id="6qfwx"><dl id="6qfwx"></dl></menu>

      <label id="6qfwx"><ol id="6qfwx"></ol></label><menu id="6qfwx"></menu><object id="6qfwx"><strike id="6qfwx"><noscript id="6qfwx"></noscript></strike></object>
        1. <center id="6qfwx"><dl id="6qfwx"></dl></center>

            新聞中心

            EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > Linux下的串口總線驅(qū)動(dòng)(三)

            Linux下的串口總線驅(qū)動(dòng)(三)

            作者: 時(shí)間:2016-11-22 來源:網(wǎng)絡(luò) 收藏
            五.線路規(guī)程內(nèi)核代碼

            底層的物理驅(qū)動(dòng)程序和tty驅(qū)動(dòng)程序負(fù)責(zé)從硬件上收發(fā)數(shù)據(jù),而線路規(guī)程則負(fù)責(zé)處理這些數(shù)據(jù),并在用戶空間和內(nèi)核空間知覺傳遞數(shù)據(jù)。打開串行端口時(shí)系統(tǒng)默認(rèn)的線路規(guī)程是N_TTY,它實(shí)現(xiàn)終端I/O處理。線路規(guī)程也實(shí)現(xiàn)通過串行傳輸協(xié)議實(shí)現(xiàn)的網(wǎng)絡(luò)接口,PPP(N_PPP),SLIP(串行線路網(wǎng)際協(xié)議)(N_SLIP),紅外數(shù)據(jù)(N_IRDA),藍(lán)牙主機(jī)控制接口(N_HCI)。

            本文引用地址:http://www.biyoush.com/article/201611/319916.htm

            我們?cè)赥TY層uart_register_driver函數(shù)里初始化termios的時(shí)候用到tty_std_termios,這個(gè)是線路的原始設(shè)置,具體定義如下

            struct ktermios tty_std_termios = {

            .c_iflag = ICRNL | IXON, //輸入標(biāo)志

            .c_oflag = OPOST | ONLCR, //輸出標(biāo)志

            .c_cflag = B38400 | CS8 | CREAD | HUPCL, //控制標(biāo)志

            .c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |

            ECHOCTL | ECHOKE | IEXTEN, //本地標(biāo)志

            .c_cc = INIT_C_CC, //字符控制

            .c_ispeed = 38400, //輸入速率

            .c_ospeed = 38400 //輸出速率

            };

            如果需要對(duì)線路原始設(shè)置的部分加以修改,則可以添加其他操作。主要分為內(nèi)核空間修改線路規(guī)程和用戶空間修改線路規(guī)程兩個(gè)途徑。內(nèi)核空間修改線路規(guī)程很簡單,只需要對(duì)需要修改項(xiàng)進(jìn)行重新賦值就行了,對(duì)于用戶空間修改線路規(guī)程我們來講解下。

            假如用戶空間程序打開和觸摸控制器相連的串行端口時(shí),N_TCH將被綁定到底層的串行驅(qū)動(dòng)程序,但假如你想編寫程序清空觸摸控制器接收的所有原始數(shù)據(jù)而不處理它,那你就需要修改線路規(guī)程為N_TTY并清空所有接收的數(shù)據(jù)的程序。用戶空間修改線程代碼如下

            fd=open(“/dev/ttys0”,O_RDONLY|O_NOCTTY);

            ldisc=N_TTY;

            ioctl(fd,TIOCSETD,&ldisc);

            好了,前面我們從應(yīng)用角度分析了線路規(guī)程的設(shè)置,現(xiàn)在我們從理論角度,深度剖析下線路規(guī)程是怎么實(shí)現(xiàn)的吧。

            在TTY層我們講過TTY層的uart_register_driver和uart_register_port最終調(diào)用線路規(guī)程的tty_register_driver和tty_register_device。而tty_register_driver和tty_register_device的實(shí)現(xiàn)在線路規(guī)程中tty_io.c中實(shí)現(xiàn)的,我們可以打開tty_io.c這個(gè)文件。

            首先我們看tty_init函數(shù),在tty_init函數(shù)中執(zhí)行了cdev_init(&tty_cdev, &tty_fops)一行代碼,說明向內(nèi)核中添加了一個(gè)cdev設(shè)備,我們跟蹤tty_fops。

            static const struct file_operations tty_fops = {

            .llseek = no_llseek,

            .read = tty_read,

            .write = tty_write,

            .poll = tty_poll,

            .unlocked_ioctl = tty_ioctl,

            .compat_ioctl = tty_compat_ioctl,

            .open = tty_open,

            .release = tty_release,

            .fasync = tty_fasync,

            };

            這個(gè)結(jié)構(gòu)體我們很熟悉,在字符設(shè)備中,我們就是使用的這個(gè)結(jié)構(gòu)體吧。那說明我們用戶進(jìn)行open,read,write,ioctl等對(duì)串口操作時(shí),第一步調(diào)用就是這里的open,read,write,ioctl。那么我們就看看怎么由這里的open,read,write,ioctl跟TTY層,UART層的open,read,write,ioctl相聯(lián)系的。

            我們就來看看這個(gè)open吧

            static int __tty_open(struct inode *inode, struct file *filp)

            {

            struct tty_struct *tty = NULL;

            int noctty, retval;

            struct tty_driver *driver;

            int index;

            dev_t device = inode->i_rdev; //獲取目標(biāo)設(shè)備的設(shè)備號(hào)

            unsigned saved_flags = filp->f_flags;

            nonseekable_open(inode, filp);

            retry_open:

            noctty = filp->f_flags & O_NOCTTY;

            index = -1;

            retval = 0;

            mutex_lock(&tty_mutex);

            if (device == MKDEV(TTYAUX_MAJOR, 0)) { //當(dāng)前進(jìn)程的控制終端,/dev/tty

            tty = get_current_tty();

            if (!tty) { //該進(jìn)程還沒有控制終端

            mutex_unlock(&tty_mutex);

            return -ENXIO;

            }

            driver = tty_driver_kref_get(tty->driver); //如果打開的確實(shí)是控制終端的處理

            index = tty->index;

            filp->f_flags |= O_NONBLOCK;

            tty_kref_put(tty);

            goto got_driver;

            }

            #ifdef CONFIG_VT

            if (device == MKDEV(TTY_MAJOR, 0)) { //當(dāng)前虛擬控制臺(tái),/dev/tty0

            extern struct tty_driver *console_driver;

            driver = tty_driver_kref_get(console_driver);

            index = fg_console; // fg_console表示當(dāng)前的前臺(tái)控制臺(tái)

            noctty = 1; //因?yàn)樘摂M控制臺(tái)原來就打開,故置位

            goto got_driver;

            }

            #endif

            if (device == MKDEV(TTYAUX_MAJOR, 1)) { //用于外接的控制臺(tái),/dev/console

            struct tty_driver *console_driver = console_device(&index);

            if (console_driver) {

            driver = tty_driver_kref_get(console_driver);

            if (driver) {

            filp->f_flags |= O_NONBLOCK;

            noctty = 1;

            goto got_driver;

            }

            }

            mutex_unlock(&tty_mutex);

            return -ENODEV;

            }

            driver = get_tty_driver(device, &index);

            if (!driver) {

            mutex_unlock(&tty_mutex);

            return -ENODEV;

            }

            got_driver:

            if (!tty) {

            //檢查我們是否重復(fù)打開一個(gè)已經(jīng)存在的tty

            tty = tty_driver_lookup_tty(driver, inode, index);

            if (IS_ERR(tty)) {

            mutex_unlock(&tty_mutex);

            return PTR_ERR(tty);

            }

            }

            if (tty) {

            retval = tty_reopen(tty); //重新打開

            if (retval)

            tty = ERR_PTR(retval);

            } else

            tty = tty_init_dev(driver, index, 0); //初始化,為需要打開的終端建立tty_struct結(jié)構(gòu)體

            mutex_unlock(&tty_mutex);

            tty_driver_kref_put(driver);

            if (IS_ERR(tty))

            return PTR_ERR(tty);

            filp->private_data = tty; //設(shè)置私有數(shù)據(jù)

            file_move(filp, &tty->tty_files);

            check_tty_count(tty, "tty_open");

            if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&

            tty->driver->subtype == PTY_TYPE_MASTER)

            noctty = 1;

            #ifdef TTY_DEBUG_HANGUP

            printk(KERN_DEBUG "opening %s...", tty->name);

            #endif

            if (!retval) {

            if (tty->ops->open)

            retval = tty->ops->open(tty, filp); //調(diào)用tty_operations下的open函數(shù)

            else

            retval = -ENODEV;

            }

            filp->f_flags = saved_flags;

            if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) &&

            !capable(CAP_SYS_ADMIN))

            retval = -EBUSY;

            if (retval) {

            #ifdef TTY_DEBUG_HANGUP

            printk(KERN_DEBUG "error %d in opening %s...", retval,

            tty->name);

            #endif

            tty_release_dev(filp);

            if (retval != -ERESTARTSYS)

            return retval;

            if (signal_pending(current))

            return retval;

            schedule();

            //需要復(fù)位f_op,以防掛起

            if (filp->f_op == &hung_up_tty_fops)

            filp->f_op = &tty_fops;

            goto retry_open;

            }

            mutex_lock(&tty_mutex);

            spin_lock_irq(¤t->sighand->siglock);

            if (!noctty &&

            current->signal->leader &&

            !current->signal->tty &&

            tty->session == NULL)

            __proc_set_tty(current, tty);

            spin_unlock_irq(¤t->sighand->siglock);

            mutex_unlock(&tty_mutex);

            return 0;

            }

            在上面這個(gè)open函數(shù)中,我們主要涉及為需要打開的終端建立tty_struct結(jié)構(gòu)體而執(zhí)行的一條代碼tty_init_dev(driver, index, 0),同時(shí)看到了怎么調(diào)用tty_operations下的open函數(shù)。在此我們好好看看tty_init_dev(driver, index, 0)的內(nèi)幕吧。

            struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,

            int first_ok)

            {

            struct tty_struct *tty;

            int retval;

            //檢查是否pty被多次打開

            if (driver->subtype == PTY_TYPE_MASTER &&

            (driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok)

            return ERR_PTR(-EIO);

            if (!try_module_get(driver->owner))

            return ERR_PTR(-ENODEV);

            tty = alloc_tty_struct(); //分配tty_struct結(jié)構(gòu)體

            if (!tty)

            goto fail_no_mem;

            initialize_tty_struct(tty, driver, idx); //初始化tty_struct結(jié)構(gòu)體

            retval = tty_driver_install_tty(driver, tty);

            if (retval < 0) {

            free_tty_struct(tty);

            module_put(driver->owner);

            return ERR_PTR(retval);

            }

            retval = tty_ldisc_setup(tty, tty->link); //調(diào)用ldisc下open

            if (retval)

            goto release_mem_out;

            return tty;

            fail_no_mem:

            module_put(driver->owner);

            return ERR_PTR(-ENOMEM);

            release_mem_out:

            if (printk_ratelimit())

            printk(KERN_INFO "tty_init_dev: ldisc open failed, "

            "clearing slot %dn", idx);

            release_tty(tty, idx);

            return ERR_PTR(retval);

            }

            我們繼續(xù)跟蹤tty_init_dev中的initialize_tty_struct(tty, driver, idx)函數(shù)實(shí)現(xiàn)吧

            void initialize_tty_struct(struct tty_struct *tty,

            struct tty_driver *driver, int idx)

            {

            memset(tty, 0, sizeof(struct tty_struct));

            kref_init(&tty->kref);

            tty->magic = TTY_MAGIC;

            tty_ldisc_init(tty); // tty_ldisc的初始化,

            tty->session = NULL;

            tty->pgrp = NULL;

            tty->overrun_time = jiffies;

            tty->buf.head = tty->buf.tail = NULL;

            tty_buffer_init(tty);

            mutex_init(&tty->termios_mutex);

            mutex_init(&tty->ldisc_mutex);

            init_waitqueue_head(&tty->write_wait);

            init_waitqueue_head(&tty->read_wait);

            INIT_WORK(&tty->hangup_work, do_tty_hangup);

            mutex_init(&tty->atomic_read_lock);

            mutex_init(&tty->atomic_write_lock);

            mutex_init(&tty->output_lock);

            mutex_init(&tty->echo_lock);

            spin_lock_init(&tty->read_lock);

            spin_lock_init(&tty->ctrl_lock);

            INIT_LIST_HEAD(&tty->tty_files);

            INIT_WORK(&tty->SAK_work, do_SAK_work);

            tty->driver = driver;

            tty->ops = driver->ops;

            tty->index = idx;

            tty_line_name(driver, idx, tty->name);

            }

            我們繼續(xù)跟蹤initialize_tty_struct函數(shù)中的tty_ldisc_init(tty)函數(shù)

            void tty_ldisc_init(struct tty_struct *tty)

            {

            struct tty_ldisc *ld = tty_ldisc_get(N_TTY); //設(shè)置線路規(guī)程N(yùn)_TTY

            if (IS_ERR(ld))

            panic("n_tty: init_tty");

            tty_ldisc_assign(tty, ld);

            }

            在tty_ldisc_init里,我們終于找到了N_TTY,這是默認(rèn)的線路規(guī)程。

            繼續(xù)看tty_init_dev,我們發(fā)現(xiàn)retval = tty_ldisc_setup(tty, tty->link);繼續(xù)跟蹤

            int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)

            {

            struct tty_ldisc *ld = tty->ldisc;

            int retval;

            retval = tty_ldisc_open(tty, ld);

            if (retval)

            return retval;

            if (o_tty) {

            retval = tty_ldisc_open(o_tty, o_tty->ldisc);

            if (retval) {

            tty_ldisc_close(tty, ld);

            return retval;

            }

            tty_ldisc_enable(o_tty);

            }

            tty_ldisc_enable(tty);

            return 0;

            }

            然后我們跟蹤tty_ldisc_setup函數(shù)中的tty_ldisc_open函數(shù)

            static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)

            {

            WARN_ON(test_and_set_bit(TTY_LDISC_OPEN, &tty->flags));

            if (ld->ops->open)

            return ld->ops->open(tty); //打開ldisc下的open,進(jìn)行鏈路的初始化

            return 0;

            }

            在tty_ldisc_open這里已經(jīng)通過相應(yīng)tty_ldisc結(jié)構(gòu)所提供的函數(shù)指針調(diào)用了與鏈路規(guī)則有關(guān)的open操作。

            前面tty_open函數(shù)中也有個(gè)open調(diào)用,這是為什么呢?因?yàn)榫唧w的終端類型也可能有需要在打開文件時(shí)加以調(diào)用的函數(shù)。對(duì)于用作控制臺(tái)的虛擬終端,其tty_driver數(shù)據(jù)結(jié)構(gòu)為console_driver,其open函數(shù)則為con_open()。

            綜上,我們可以把tty_io.c看作是tty核心,然后tty核心里調(diào)用ldisc中的open,ldisc里的open調(diào)用tty層的open,tty層的open調(diào)用uart層的open,最終實(shí)現(xiàn)打開操作。

            最后再次總結(jié)如下幾點(diǎn):

            其一,內(nèi)核中有一個(gè)鏈表tty_drivers,系統(tǒng)在初始化時(shí),或者安裝某種終端設(shè)備的驅(qū)動(dòng)模塊時(shí),通過函數(shù)tty_register_driver()將各種終端設(shè)備的tty_driver結(jié)構(gòu)登記到這個(gè)鏈表中。每當(dāng)新打開一個(gè)終端設(shè)備時(shí),就要根據(jù)其設(shè)備號(hào)通過函數(shù)get_tty_driver()在這個(gè)鏈表中找到的tty_driver結(jié)構(gòu),并把它復(fù)制到具體的tty_struct結(jié)構(gòu)體中。

            其二,當(dāng)新創(chuàng)建一個(gè)tty_struct結(jié)構(gòu)時(shí),就把相應(yīng)的tty_ldisc結(jié)構(gòu)復(fù)制到tty_struct結(jié)構(gòu)體中的這個(gè)成員中。

            其三,另外內(nèi)核中的一個(gè)重要指針termios,這個(gè)數(shù)據(jù)結(jié)構(gòu)在某種程度上可以看作是對(duì)tty_ldisc結(jié)構(gòu)的補(bǔ)充,它規(guī)定了對(duì)接口上輸入和輸出的每個(gè)字符所作的處理以及傳輸?shù)乃俣?,即波特率?/p>



            關(guān)鍵詞: Linux串口總線驅(qū)

            評(píng)論


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

            關(guān)閉