Chinaunix首页 | 论坛 | 博客
  • 博客访问: 498874
  • 博文数量: 223
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 2145
  • 用 户 组: 普通用户
  • 注册时间: 2014-03-01 10:23
个人简介

该坚持的时候坚持,该妥协的时候妥协,该放弃的时候放弃

文章分类

全部博文(223)

文章存档

2017年(56)

2016年(118)

2015年(3)

2014年(46)

我的朋友

分类: 嵌入式

2016-11-13 21:44:28

TTY驱动程序架构
1. TTY感念解析
在Linux系统中,终端是一类字符型设备,它包括多种类型,通常使用tty来简称各种类型的终端设备。
  串口终端(/dev/ttyS*)
串口终端是使用计算机串口连接的终端设备。Linux每个串行端口都看作是一个字符设备。这些串行端口所对应的设备名称是 /dev/ttySAC0;
/dev/ttySAC1……
  控制台终端(/dev/console)
在Linux系统中,计算机的输出设备通常被称为控制台终端(Console),这里特指printk信息输出到的设备。
/dev/console是一个虚拟的设备,它需要映射到真正的tty上,比如通过内核启动参数” console=ttySAC0”就把console映射到了串口0。
  虚拟终端(/dev/tty*)
当用户登录时,使用的是虚拟终端。使用Ctcl+Alt+[F1—F6]组合键时,我们就可以切换到tty1、tty2、tty3等上面去。tty1–tty6等称为虚拟终端,而tty0则是当前所使用虚拟终端的一个别名.

串口终端和控制台终端是从内核启动时就关联的,内核通过printk来调用控制台终端,从而在串口中显示。
串口终端又有多种虚拟终端tty1,tty2,tty3...等用来个应用程序使用。

2.TTY架构分析 
Linux tty子系统包含:tty核心,tty线路规程和tty驱动
① 
tty核心是对整个tty设备的抽象,对用户提供统一的接口。
② 
tty线路规程是对传输数据的格式化。
③ tty驱动则是面向tty
设备的硬件驱动。


3.查看tty调用流程(回溯dump_stack())
linux-tq2440/drivers/serial/samsung.c:
在s3c24xx_serial_start_tx()中添加一行:
dump_stack();
目的是调用回溯函数。然后编译内核make uImage ARCH=arm CROSS_COMPILE=arm-linux-,启动开发板调试:
  1. Backtrace:
  2. [] (dump_backtrace+0x0/0x10c) from [] (dump_stack+0x18/0x1c)
  3. r7:c394c802 r6:00000000 r5:c38bda00 r4:c04caf20
  4. [] (dump_stack+0x0/0x1c) from [] (s3c24xx_serial_start_tx+0x14/0x64)
  5. [] (s3c24xx_serial_start_tx+0x0/0x64) from [] (uart_start+0x68/0x6c)
  6. r5:c38bda00 r4:60000013
  7. [] (uart_start+0x0/0x6c) from [] (uart_write+0xc0/0xe4)
  8. r5:c38bda00 r4:00000000
  9. [] (uart_write+0x0/0xe4) from [] (n_tty_write+0x1d8/0x448)
  10. [] (n_tty_write+0x0/0x448) from [] (tty_write+0x14c/0x244)
  11. [] (tty_write+0x0/0x244) from [] (redirected_tty_write+0x88/0x98)
  12. [] (redirected_tty_write+0x0/0x98) from [] (vfs_write+0xb4/0xe8)
  13. r9:c39ca000 r8:c0045008 r7:00000004 r6:c39cbf78 r5:40000000
  14. r4:c3953680
  15. [] (vfs_write+0x0/0xe8) from [] (sys_write+0x4c/0x84)
  16. r7:00000004 r6:c3953680 r5:00000000 r4:00000000
  17. [] (sys_write+0x0/0x84) from [] (ret_fast_syscall+0x0/0x2c)
  18. r6:001d27f8 r5:40000000 r4:00000002
redirected_tty_write:
  1. ssize_t redirected_tty_write(struct file *file, const char __user *buf,
  2.                         size_t count, loff_t *ppos)
  3. {
  4.     struct file *p = NULL;

  5.     spin_lock(&redirect_lock);
  6.     if (redirect) {
  7.         get_file(redirect);
  8.         p = redirect;
  9.     }
  10.     spin_unlock(&redirect_lock);

  11.     if (p) {
  12.         ssize_t res;
  13.         res = vfs_write(p, buf, count, &p->f_pos);
  14.         fput(p);
  15.         return res;
  16.     }
  17.     return tty_write(file, buf, count, ppos);                    //这里调用了tty_write
  18. }
tty_write:
  1. static ssize_t tty_write(struct file *file, const char __user *buf,
  2.                         size_t count, loff_t *ppos)
  3. {
  4.     struct tty_struct *tty;
  5.     struct inode *inode = file->f_path.dentry->d_inode;
  6.     ssize_t ret;
  7.     struct tty_ldisc *ld;

  8.     tty = (struct tty_struct *)file->private_data;
  9.     if (tty_paranoia_check(tty, inode, "tty_write"))
  10.         return -EIO;
  11.     if (!tty || !tty->ops->write ||
  12.         (test_bit(TTY_IO_ERROR, &tty->flags)))
  13.             return -EIO;
  14.     /* Short term debug to catch buggy drivers */
  15.     if (tty->ops->write_room == NULL)
  16.         printk(KERN_ERR "tty driver %s lacks a write_room method.\n",
  17.             tty->driver->name);
  18.     ld = tty_ldisc_ref_wait(tty);
  19.     if (!ld->ops->write)
  20.         ret = -EIO;
  21.     else
  22.         ret = do_tty_write(ld->ops->write, tty, file, buf, count);      //这里调用了do_tty_write,ld就是结构体tty_ldisc(tty线路规程)
  23.     tty_ldisc_deref(ld);
  24.     return ret;
  25. }
tty_write>>struct tty_ldisc:
  1. struct tty_ldisc {
  2.     struct tty_ldisc_ops *ops;                   //此处就是它调用的tty_ldisc_ops结构
  3.     int refcount;
  4. };
然后tty_ldisc中的tty_ldisc_ops在n_tty.c中被定义好了:
  1. struct tty_ldisc_ops tty_ldisc_N_TTY = {
  2.     .magic = TTY_LDISC_MAGIC,
  3.     .name = "n_tty",
  4.     .open = n_tty_open,
  5.     .close = n_tty_close,
  6.     .flush_buffer = n_tty_flush_buffer,
  7.     .chars_in_buffer = n_tty_chars_in_buffer,
  8.     .read = n_tty_read,
  9.     .write = n_tty_write,                              //在这,所以调用do_tty_write其实就是调用了n_tty_write
  10.     .ioctl = n_tty_ioctl,
  11.     .set_termios = n_tty_set_termios,
  12.     .poll = n_tty_poll,
  13.     .receive_buf = n_tty_receive_buf,
  14.     .write_wakeup = n_tty_write_wakeup
  15. };
n_tty_write:
  1. /**
  2.  *    n_tty_write        -    write function for tty
  3.  *    @tty: tty device
  4.  *    @file: file object
  5.  *    @buf: userspace buffer pointer
  6.  *    @nr: size of I/O
  7.  *
  8.  *    Write function of the terminal device. This is serialized with
  9.  *    respect to other write callers but not to termios changes, reads
  10.  *    and other such events. Since the receive code will echo characters,
  11.  *    thus calling driver write methods, the output_lock is used in
  12.  *    the output processing functions called here as well as in the
  13.  *    echo processing function to protect the column state and space
  14.  *    left in the buffer.
  15.  *
  16.  *    This code must be sure never to sleep through a hangup.
  17.  *
  18.  *    Locking: output_lock to protect column state and space left
  19.  *         (note that the process_output*() functions take this
  20.  *         lock themselves)
  21.  */

  22. static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
  23.              const unsigned char *buf, size_t nr)
  24. {
  25.     const unsigned char *b = buf;
  26.     DECLARE_WAITQUEUE(wait, current);
  27.     int c;
  28.     ssize_t retval = 0;

  29.     /* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
  30.     if (L_TOSTOP(tty) && file->f_op->write != redirected_tty_write) {
  31.         retval = tty_check_change(tty);
  32.         if (retval)
  33.             return retval;
  34.     }

  35.     /* Write out any echoed characters that are still pending */
  36.     process_echoes(tty);

  37.     add_wait_queue(&tty->write_wait, &wait);
  38.     while (1) {
  39.         set_current_state(TASK_INTERRUPTIBLE);
  40.         if (signal_pending(current)) {
  41.             retval = -ERESTARTSYS;
  42.             break;
  43.         }
  44.         if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) {
  45.             retval = -EIO;
  46.             break;
  47.         }
  48.         if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) {
  49.             while (nr > 0) {
  50.                 ssize_t num = process_output_block(tty, b, nr);
  51.                 if (num < 0) {
  52.                     if (num == -EAGAIN)
  53.                         break;
  54.                     retval = num;
  55.                     goto break_out;
  56.                 }
  57.                 b += num;
  58.                 nr -= num;
  59.                 if (nr == 0)
  60.                     break;
  61.                 c = *b;
  62.                 if (process_output(c, tty) < 0)
  63.                     break;
  64.                 b++; nr--;
  65.             }
  66.             if (tty->ops->flush_chars)
  67.                 tty->ops->flush_chars(tty);
  68.         } else {
  69.             while (nr > 0) {
  70.                 c = tty->ops->write(tty, b, nr);         //这里的tty->ops->write和上面的类似,tty_struct结构,调用了uart_write。
  71.                 if (c < 0) {
  72.                     retval = c;
  73.                     goto break_out;
  74.                 }
  75.                 if (!c)
  76.                     break;
  77.                 b += c;
  78.                 nr -= c;
  79.             }
  80.         }
  81.         if (!nr)
  82.             break;
  83.         if (file->f_flags & O_NONBLOCK) {
  84.             retval = -EAGAIN;
  85.             break;
  86.         }
  87.         schedule();
  88.     }
  89. break_out:
  90.     __set_current_state(TASK_RUNNING);
  91.     remove_wait_queue(&tty->write_wait, &wait);
  92.     if (b - buf != nr && tty->fasync)
  93.         set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
  94.     return (b - buf) ? b - buf : retval;
  95. }
serial_core.c:
  1. static int
  2. uart_write(struct tty_struct *tty, const unsigned char *buf, int count)
  3. {
  4.     struct uart_state *state = tty->driver_data;
  5.     struct uart_port *port;
  6.     struct circ_buf *circ;
  7.     unsigned long flags;
  8.     int c, ret = 0;

  9.     /*
  10.      * This means you called this function _after_ the port was
  11.      * closed. No cookie for you.
  12.      */
  13.     if (!state) {
  14.         WARN_ON(1);
  15.         return -EL3HLT;
  16.     }

  17.     port = state->port;
  18.     circ = &state->info.xmit;

  19.     if (!circ->buf)
  20.         return 0;

  21.     spin_lock_irqsave(&port->lock, flags);
  22.     while (1) {
  23.         c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
  24.         if (count < c)
  25.             c = count;
  26.         if (c <= 0)
  27.             break;
  28.         memcpy(circ->buf + circ->head, buf, c);
  29.         circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
  30.         buf += c;
  31.         count -= c;
  32.         ret += c;
  33.     }
  34.     spin_unlock_irqrestore(&port->lock, flags);

  35.     uart_start(tty);                                      //这里调用了uart_start
  36.     return ret;
  37. }
同一个文件:
  1. static void __uart_start(struct tty_struct *tty)
  2. {
  3.     struct uart_state *state = tty->driver_data;
  4.     struct uart_port *port = state->port;

  5.     if (!uart_circ_empty(&state->info.xmit) && state->info.xmit.buf &&
  6.      !tty->stopped && !tty->hw_stopped)
  7.         port->ops->start_tx(port);                              //这里的port->ops->start_tx,估计就是指向s3c24xx_serial_start_tx
  8. }

  9. static void uart_start(struct tty_struct *tty)
  10. {
  11.     struct uart_state *state = tty->driver_data;
  12.     struct uart_port *port = state->port;
  13.     unsigned long flags;

  14.     spin_lock_irqsave(&port->lock, flags);
  15.     __uart_start(tty);                                          //这里调用了__uart_start
  16.     spin_unlock_irqrestore(&port->lock, flags);
  17. }


阅读(791) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~