色婷婷AⅤ一区二区三区|亚洲精品第一国产综合亚AV|久久精品官方网视频|日本28视频香蕉

          新聞中心

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

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

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

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

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

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

          struct ktermios tty_std_termios = {

          .c_iflag = ICRNL | IXON, //輸入標志

          .c_oflag = OPOST | ONLCR, //輸出標志

          .c_cflag = B38400 | CS8 | CREAD | HUPCL, //控制標志

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

          ECHOCTL | ECHOKE | IEXTEN, //本地標志

          .c_cc = INIT_C_CC, //字符控制

          .c_ispeed = 38400, //輸入速率

          .c_ospeed = 38400 //輸出速率

          };

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

          假如用戶空間程序打開和觸摸控制器相連的串行端口時,N_TCH將被綁定到底層的串行驅(qū)動程序,但假如你想編寫程序清空觸摸控制器接收的所有原始數(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ī)程是怎么實現(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的實現(xiàn)在線路規(guī)程中tty_io.c中實現(xiàn)的,我們可以打開tty_io.c這個文件。

          首先我們看tty_init函數(shù),在tty_init函數(shù)中執(zhí)行了cdev_init(&tty_cdev, &tty_fops)一行代碼,說明向內(nèi)核中添加了一個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,

          };

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

          我們就來看看這個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; //獲取目標設(shè)備的設(shè)備號

          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)前進程的控制終端,/dev/tty

          tty = get_current_tty();

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

          mutex_unlock(&tty_mutex);

          return -ENXIO;

          }

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

          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)前虛擬控制臺,/dev/tty0

          extern struct tty_driver *console_driver;

          driver = tty_driver_kref_get(console_driver);

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

          noctty = 1; //因為虛擬控制臺原來就打開,故置位

          goto got_driver;

          }

          #endif

          if (device == MKDEV(TTYAUX_MAJOR, 1)) { //用于外接的控制臺,/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ù)打開一個已經(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;

          }

          在上面這個open函數(shù)中,我們主要涉及為需要打開的終端建立tty_struct結(jié)構(gòu)體而執(zhí)行的一條代碼tty_init_dev(driver, index, 0),同時看到了怎么調(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ù)實現(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_TTY

          if (IS_ERR(ld))

          panic("n_tty: init_tty");

          tty_ldisc_assign(tty, ld);

          }

          在tty_ldisc_init里,我們終于找到了N_TTY,這是默認的線路規(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,進行鏈路的初始化

          return 0;

          }

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

          前面tty_open函數(shù)中也有個open調(diào)用,這是為什么呢?因為具體的終端類型也可能有需要在打開文件時加以調(diào)用的函數(shù)。對于用作控制臺的虛擬終端,其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,最終實現(xiàn)打開操作。

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

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

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

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



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

          評論


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

          關(guān)閉