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

          新聞中心

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

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

          作者: 時間:2016-11-22 來源:網(wǎng)絡(luò) 收藏
          四.TTY層內(nèi)核代碼

          TTY驅(qū)動程序有三種:控制臺、串口和pty。在此我們主要分析Mini2440串口驅(qū)動。

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

          我們現(xiàn)在跟蹤uart_register_driver和uart_add_one_port發(fā)現(xiàn),他們的原函數(shù)定義在TTY層驅(qū)動serial_core.o中。

          int uart_register_driver(struct uart_driver *drv)

          {

          struct tty_driver *normal = NULL;

          int i, retval;

          BUG_ON(drv->state);

          drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);

          retval = -ENOMEM;

          if (!drv->state)

          goto out;

          normal = alloc_tty_driver(drv->nr); //分配TTY驅(qū)動

          if (!normal)

          goto out;

          drv->tty_driver = normal;

          normal->owner = drv->owner;

          normal->driver_name = drv->driver_name;

          normal->name = drv->dev_name;

          normal->major = drv->major;

          normal->minor_start = drv->minor;

          normal->type = TTY_DRIVER_TYPE_SERIAL;

          normal->subtype = SERIAL_TYPE_NORMAL;

          normal->init_termios = tty_std_termios; //初始的termios

          normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;//控制模式設(shè)置

          normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600; //設(shè)置輸入/出速度

          normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;

          normal->driver_state = drv; //私有數(shù)據(jù)

          tty_set_operations(normal, &uart_ops); //設(shè)置TTY驅(qū)動操作

          for (i = 0; i < drv->nr; i++) { //初始化UART狀態(tài)

          struct uart_state *state = drv->state + i;

          struct tty_port *port = &state->port;

          tty_port_init(port);

          port->close_delay = 500;

          port->closing_wait = 30000;

          tasklet_init(&state->tlet, uart_tasklet_action,

          (unsigned long)state);

          }

          retval = tty_register_driver(normal); //注冊TTY驅(qū)動

          out:

          if (retval < 0) {

          put_tty_driver(normal);

          kfree(drv->state);

          }

          return retval;

          }

          在上面uart_register_driver這個函數(shù)里我們首先分配了TTY驅(qū)動,然后對其進(jìn)行填充,初始的termios,并設(shè)置TTY驅(qū)動操作,最后注冊TTY驅(qū)動。其中設(shè)置TTY驅(qū)動操作時用到uart_ops,我們看看這個uart_ops到底是什么。

          static const struct tty_operations uart_ops = {

          .open = uart_open,

          .close = uart_close,

          .write = uart_write,

          .put_char = uart_put_char,

          .flush_chars = uart_flush_chars,

          .write_room = uart_write_room,

          .chars_in_buffer= uart_chars_in_buffer,

          .flush_buffer = uart_flush_buffer,

          .ioctl = uart_ioctl,

          .throttle = uart_throttle,

          .unthrottle = uart_unthrottle,

          .send_xchar = uart_send_xchar,

          .set_termios = uart_set_termios,

          .set_ldisc = uart_set_ldisc,

          .stop = uart_stop,

          .start = uart_start,

          .hangup = uart_hangup,

          .break_ctl = uart_break_ctl,

          .wait_until_sent= uart_wait_until_sent,

          #ifdef CONFIG_PROC_FS

          .proc_fops = &uart_proc_fops,

          #endif

          .tiocmget = uart_tiocmget,

          .tiocmset = uart_tiocmset,

          #ifdef CONFIG_CONSOLE_POLL

          .poll_init = uart_poll_init,

          .poll_get_char = uart_poll_get_char,

          .poll_put_char = uart_poll_put_char,

          #endif

          };

          終端設(shè)備可以完成收發(fā)數(shù)據(jù)的功能,當(dāng)用戶在有數(shù)據(jù)發(fā)送給終端設(shè)備時候,通過”write()系統(tǒng)調(diào)用—tty核心—線路規(guī)程”的層層調(diào)用,最終調(diào)用tty_driver結(jié)構(gòu)體中的write()函數(shù)完成發(fā)送。因?yàn)閭鬏斔俣群蛅ty硬件緩沖區(qū)容量的原因,不是所有的寫程序要求的字符都可以在調(diào)用寫函數(shù)時候被發(fā)送出去,因此,寫函數(shù)應(yīng)當(dāng)返回能夠發(fā)給硬件的字節(jié)數(shù)以便用戶程序檢查是否所有的數(shù)據(jù)被真正寫入。如果在write()調(diào)用期間發(fā)生任何錯誤,一個負(fù)的錯誤碼應(yīng)當(dāng)被返回。在上面的uart_ops結(jié)構(gòu)體中,我們先看看寫函數(shù)uart_write的實(shí)現(xiàn)吧。

          static int uart_write(struct tty_struct *tty, const unsigned char *buf, int count)

          {

          struct uart_state *state = tty->driver_data; //獲取設(shè)備私有信息結(jié)構(gòu)體

          struct uart_port *port;

          struct circ_buf *circ;

          unsigned long flags;

          int c, ret = 0;

          if (!state) {

          WARN_ON(1);

          return -EL3HLT;

          }

          port = state->uart_port; //UART端口

          circ = &state->xmit; //數(shù)據(jù)緩沖區(qū)

          if (!circ->buf)

          return 0;

          spin_lock_irqsave(&port->lock, flags); //獲取UART端口操作的鎖

          while (1) {

          //返回可用緩存空間的大小

          c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);

          if (count < c)

          c = count;

          if (c <= 0) //緩存區(qū)太小則退出

          break;

          //將用戶空間buf中大小為c的內(nèi)容拷貝到緩存中

          memcpy(circ->buf + circ->head, buf, c);

          circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);

          buf += c; //緩存區(qū)指針后移

          count -= c; //當(dāng)一次發(fā)送的字節(jié)過多,需要分次發(fā)送

          ret += c; //已經(jīng)發(fā)送的字節(jié)數(shù)

          }

          spin_unlock_irqrestore(&port->lock, flags); //釋放UART端口操作的鎖

          uart_start(tty); //開始發(fā)送

          return ret;

          }

          根據(jù)上面對uart_write的分析,我們知道tty_driver的write()函數(shù)接收三個參數(shù)tty_struct,發(fā)送數(shù)據(jù)指針和要發(fā)送的字節(jié)數(shù)。uart_state作為這個驅(qū)動tty的私有數(shù)據(jù),其中circ_buf定義了緩沖區(qū),我們向這個緩沖區(qū)拷貝待發(fā)送的內(nèi)容后,執(zhí)行uart_start(tty)進(jìn)行發(fā)送數(shù)據(jù)。那我們繼續(xù)看跟蹤uart_start函數(shù)

          static void uart_start(struct tty_struct *tty)

          {

          struct uart_state *state = tty->driver_data;

          struct uart_port *port = state->uart_port;

          unsigned long flags;

          spin_lock_irqsave(&port->lock, flags); //獲取UART端口操作的鎖

          __uart_start(tty);

          spin_unlock_irqrestore(&port->lock, flags); //釋放UART端口操作的鎖

          }

          static void __uart_start(struct tty_struct *tty)

          {

          struct uart_state *state = tty->driver_data;

          struct uart_port *port = state->uart_port;

          if (!uart_circ_empty(&state->xmit) && state->xmit.buf &&

          !tty->stopped && !tty->hw_stopped) //緩沖區(qū)有數(shù)據(jù)并開啟發(fā)送狀態(tài)

          port->ops->start_tx(port); //調(diào)用uart_ops下的start_tx,即s3c24xx_serial_start_tx

          }

          注意__uart_start函數(shù)中的port->ops->start_tx(port)便實(shí)現(xiàn)了tty層和uart層的相連,由tty層的write()調(diào)用uart層的write()。

          好了,上面講的是發(fā)送數(shù)據(jù),讀者可能注意到struct tty_operations uart_ops中沒有提到read()函數(shù)。因?yàn)榘l(fā)送是用戶主動的,而接收拾用戶調(diào)用read()從一片緩沖區(qū)讀取已經(jīng)放好的數(shù)據(jù),這個緩沖區(qū)由struct tty_flip_buffer結(jié)構(gòu)體實(shí)現(xiàn)。因?yàn)閠ty核提供了這樣的緩沖邏輯,所以每個tty驅(qū)動并非一定要實(shí)現(xiàn)它自身的緩沖邏輯。Tty驅(qū)動不需要關(guān)注struct tty_flip_buffe的細(xì)節(jié),從tty驅(qū)動接收到的來自硬件層的字符將被tty_insert_filp_char()函數(shù)插入filp緩沖區(qū)。如果傳輸?shù)淖止?jié)數(shù)count大于或等于TTY_FLIPBUF_SIEZE,這個flip緩沖區(qū)就需要被刷新到用戶,刷新是通過調(diào)用tty_flip_buffer_push()實(shí)現(xiàn)的。

          接著,我們繼續(xù)看struct tty_operations uart_ops中對termios的設(shè)置函數(shù)set_termios,即uart_set_termios。這個set_termios需要根據(jù)用戶對termios的設(shè)置完成實(shí)際的硬件設(shè)置。新的設(shè)置被保存在tty_struct中,舊的設(shè)置被保存在old參數(shù)中,若新舊參數(shù)相同,則什么都不需要做,對于被改的設(shè)置,需要完成硬件上的設(shè)置。好了,下面我們還是看看uart_set_termios的實(shí)現(xiàn)吧。

          static void uart_set_termios(struct tty_struct *tty,

          struct ktermios *old_termios)

          {

          struct uart_state *state = tty->driver_data; //獲取私有數(shù)據(jù)

          unsigned long flags;

          unsigned int cflag = tty->termios->c_cflag; //獲取當(dāng)前線路設(shè)置

          #define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))

          //如果新舊線路設(shè)置的控制狀態(tài),輸入輸出速度等信息一樣,則退出

          if ((cflag ^ old_termios->c_cflag) == 0 &&

          tty->termios->c_ospeed == old_termios->c_ospeed &&

          tty->termios->c_ispeed == old_termios->c_ispeed &&

          RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) {

          return;

          }

          uart_change_speed(state, old_termios); //用新的線路規(guī)程的速度更新舊的線路規(guī)程

          //處理波特率為B0情況

          if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))

          uart_clear_mctrl(state->uart_port, TIOCM_RTS | TIOCM_DTR);

          /處理波特率為非B0情況

          if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {

          unsigned int mask = TIOCM_DTR;

          if (!(cflag & CRTSCTS) ||

          !test_bit(TTY_THROTTLED, &tty->flags))

          mask |= TIOCM_RTS;

          uart_set_mctrl(state->uart_port, mask); //設(shè)置modem控制

          }

          //處理無數(shù)據(jù)流控制情況

          if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) {

          spin_lock_irqsave(&state->uart_port->lock, flags);

          tty->hw_stopped = 0;

          __uart_start(tty);

          spin_unlock_irqrestore(&state->uart_port->lock, flags);

          }

          //處理有數(shù)據(jù)流控制情況

          if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) {

          spin_lock_irqsave(&state->uart_port->lock, flags);

          if (!(state->uart_port->ops->get_mctrl(state->uart_port) & TIOCM_CTS)) {

          tty->hw_stopped = 1;

          state->uart_port->ops->stop_tx(state->uart_port);

          }

          spin_unlock_irqrestore(&state->uart_port->lock, flags);

          }

          }

          好了我們已經(jīng)講解了write,set_termiox,下面我們講講tiocmget和tiocmset。Tiocmget()函數(shù)用于獲取tty設(shè)備的線路設(shè)置,對應(yīng)的tiocmset()用于設(shè)置tty設(shè)備的線路設(shè)置。

          static int uart_tiocmget(struct tty_struct *tty, struct file *file)

          {

          struct uart_state *state = tty->driver_data;

          struct tty_port *port = &state->port;

          struct uart_port *uport = state->uart_port;

          int result = -EIO;

          mutex_lock(&port->mutex); //獲取對tty_port操作的鎖

          if ((!file || !tty_hung_up_p(file)) &&

          !(tty->flags & (1 << TTY_IO_ERROR))) {

          result = uport->mctrl;

          spin_lock_irq(&uport->lock);

          result |= uport->ops->get_mctrl(uport); //調(diào)用UART層get_mctrl獲取modem控制

          spin_unlock_irq(&uport->lock);

          }

          mutex_unlock(&port->mutex); //釋放對tty_port操作的鎖

          return result;

          }

          static int uart_tiocmset(struct tty_struct *tty, struct file *file,

          unsigned int set, unsigned int clear)

          {

          struct uart_state *state = tty->driver_data;

          struct uart_port *uport = state->uart_port;

          struct tty_port *port = &state->port;

          int ret = -EIO;

          mutex_lock(&port->mutex); //獲取對tty_port操作的鎖

          if ((!file || !tty_hung_up_p(file)) &&

          !(tty->flags & (1 << TTY_IO_ERROR))) {

          uart_update_mctrl(uport, set, clear); //獲取modem控制

          ret = 0;

          }

          mutex_unlock(&port->mutex); //釋放對tty_port操作的鎖

          return ret;

          }

          上面uart_tiocmset中調(diào)用了uart_update_mctrl(uport, set, clear)函數(shù),它最終是通過調(diào)用port->ops->set_mctrl(port, port->mctrl)完成,而set_mctrl在UART層的uart_ops實(shí)現(xiàn)了。

          綜上,TTY層的ops中的uart_tiocmget和uart_tiocmset其實(shí)最終是調(diào)用UART層uart_ops中的get_mctrl和set_mctrl實(shí)現(xiàn)的。

          當(dāng)用戶在tty設(shè)備節(jié)點(diǎn)上進(jìn)行ioctl調(diào)用時,tty_operations中的ioctl()函數(shù)會被tty核心調(diào)用。我們接下來看看struct tty_operations uart_ops下的.ioctl也就是uart_ioctl。

          static int uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd,

          unsigned long arg)

          {

          struct uart_state *state = tty->driver_data;

          struct tty_port *port = &state->port;

          void __user *uarg = (void __user *)arg;

          int ret = -ENOIOCTLCMD;

          switch (cmd) { //這些ioctl不依賴硬件

          case TIOCGSERIAL: //獲得串口線信息

          ret = uart_get_info(state, uarg);

          break;

          case TIOCSSERIAL: //設(shè)置串口線信息

          ret = uart_set_info(state, uarg);

          break;

          case TIOCSERCONFIG: //自動配置

          ret = uart_do_autoconfig(state);

          break;

          case TIOCSERGWILD:

          case TIOCSERSWILD:

          ret = 0;

          break;

          }

          if (ret != -ENOIOCTLCMD)

          goto out;

          if (tty->flags & (1 << TTY_IO_ERROR)) {

          ret = -EIO;

          goto out;

          }

          switch (cmd) { //這些ioctl依賴硬件

          case TIOCMIWAIT: //等待MSR改變

          ret = uart_wait_modem_status(state, arg);

          break;

          case TIOCGICOUNT: //獲得中斷計數(shù)

          ret = uart_get_count(state, uarg);

          break;

          }

          if (ret != -ENOIOCTLCMD)

          goto out;

          mutex_lock(&port->mutex);

          if (tty_hung_up_p(filp)) {

          ret = -EIO;

          goto out_up;

          }

          switch (cmd) { //這些ioctl依賴硬件,并且需要保護(hù),房子tty被掛起

          case TIOCSERGETLSR: //獲得這個tty設(shè)備的線路狀態(tài)寄存器LSR的值

          ret = uart_get_lsr_info(state, uarg);

          break;

          default: {

          struct uart_port *uport = state->uart_port;

          if (uport->ops->ioctl)

          ret = uport->ops->ioctl(uport, cmd, arg);

          break;

          }

          }

          out_up:

          mutex_unlock(&port->mutex);

          out:

          return ret;

          }

          當(dāng)TTY核心想知道由TTY驅(qū)動程序提供的可用寫入緩沖區(qū)的大小時,就會調(diào)用write_room。在清空寫緩沖區(qū),或者調(diào)用write函數(shù)向緩沖區(qū)添加數(shù)據(jù)時,該值是變化的。接下來我們看看TTY層ops中write_room,也就是uart_write_room。跟蹤發(fā)現(xiàn)其實(shí)這個函數(shù)實(shí)現(xiàn)主要是把緩沖區(qū)頭尾相減得到剩余空間大小。

          除了write_room外,還有其他一些緩沖函數(shù),例如TTY層ops中chars_in_buffer,也就是uart_chars_in_buffer,當(dāng)tty核心在tty驅(qū)動程序的寫緩沖區(qū)中還有多少個需要傳輸?shù)淖址麜r調(diào)用該函數(shù)。

          除此之外TTY層ops中還有三個回調(diào)函數(shù),用來刷新驅(qū)動程序保存的任何數(shù)據(jù),并不一定要實(shí)現(xiàn),但是如果tty驅(qū)動程序能在發(fā)送給硬件前緩沖數(shù)據(jù),還是推薦實(shí)現(xiàn)它們的,它們分別是flush_buffer,wait_until_sent,flush_buffer。

          回顧一下,我們在TTY層的ops中,主要講了write,set_termiox, tiocmget,tiocmset,ioctl,五個函數(shù),還簡單介紹了write_room,chars_in_buffer,flush_buffer,wait_until_sent,flush_buffer五個函數(shù)。到目前為止,我們已經(jīng)分析好了uart_register_driver函數(shù),現(xiàn)在該分析uart_add_one_port函數(shù)了。

          int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)

          {

          struct uart_state *state;

          struct tty_port *port;

          int ret = 0;

          struct device *tty_dev;

          BUG_ON(in_interrupt());

          if (uport->line >= drv->nr)

          return -EINVAL;

          state = drv->state + uport->line;

          port = &state->port;

          mutex_lock(&port_mutex);

          mutex_lock(&port->mutex);

          if (state->uart_port) {

          ret = -EINVAL;

          goto out;

          }

          state->uart_port = uport;

          state->pm_state = -1;

          uport->cons = drv->cons;

          uport->state = state;

          //如果這個端口是控制臺,那么這個鎖就已經(jīng)初始化了

          if (!(uart_console(uport) && (uport->cons->flags & CON_ENABLED))) {

          spin_lock_init(&uport->lock);

          lockdep_set_class(&uport->lock, &port_lock_key);

          }

          uart_configure_port(drv, state, uport); //配置端口

          tty_dev = tty_register_device(drv->tty_driver, uport->line, uport->dev);//注冊端口

          if (likely(!IS_ERR(tty_dev))) {

          device_init_wakeup(tty_dev, 1);

          device_set_wakeup_enable(tty_dev, 0);

          } else

          printk(KERN_ERR "Cannot register tty device on line %dn",

          uport->line);

          //確保UPF_DEAD沒有被置位

          uport->flags &= ~UPF_DEAD;

          out:

          mutex_unlock(&port->mutex);

          mutex_unlock(&port_mutex);

          return ret;

          }

          對于uart_add_one_port,我們發(fā)現(xiàn)其中最核心的一句代碼就是tty_register_device,僅有tty_driver是不夠的,驅(qū)動必須依附于設(shè)備,tty_register_device函數(shù)用于注冊關(guān)聯(lián)于tty_driver的設(shè)備。

          總結(jié)下,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驅(qū)動主要涉及如下幾個重要結(jié)構(gòu)體,struct tty_struct包含了和打開的tty相關(guān)的所有狀態(tài)信息。其中一個重要的成員就是struct tty_bufhead buf,它是數(shù)據(jù)收集和處理機(jī)制的中樞,其定義如下

          struct tty_bufhead {

          struct delayed_work work;

          spinlock_t lock;

          struct tty_buffer *head;

          struct tty_buffer *tail;

          struct tty_buffer *free;

          int memory_used;

          };

          另一個重要結(jié)構(gòu)體是struct tty_driver,它規(guī)定了tty驅(qū)動程序和高層之間的編程接口。在我們這個TTY層,由uart_register_driver下的tty_register_driver注冊入內(nèi)核,其中這個結(jié)構(gòu)體中的成員部分是通過拷貝uart_driver中的參數(shù)得到。

          好了,對于TTY層驅(qū)動,一般而言,我們需要完成如下兩個任務(wù):

          其一,終端設(shè)備驅(qū)動模塊的加載函數(shù)和卸載函數(shù),完成注冊和注銷tty_driver,初始化和釋放終端設(shè)備對應(yīng)的tty_driver結(jié)構(gòu)體成員和硬件資源。

          其二,實(shí)現(xiàn)tty_operations結(jié)構(gòu)體中的一系列成員函數(shù),主要的是實(shí)現(xiàn)open()、close()、 write()、 tiocmget()、 tiocmset()。



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

          評論


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

          關(guān)閉