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

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

文章分类

全部博文(223)

文章存档

2017年(56)

2016年(118)

2015年(3)

2014年(46)

我的朋友

分类: 嵌入式

2016-11-18 22:00:19

一、TTY数据发送调用关系
这个上一篇open有点相似:
从samsung.c>>uart_register_driver>>tty_register_driver>注册>tty_fops(file_operations结构)>>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);           //ld->ops是线路规程的结构,并调用了write
  23.     tty_ldisc_deref(ld);
  24.     return ret;
  25. }
tty_ldisc_N_tty结构:
  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,                                          //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. static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
  2.              const unsigned char *buf, size_t nr)
  3. {
  4.     const unsigned char *b = buf;
  5.     DECLARE_WAITQUEUE(wait, current);
  6.     int c;
  7.     ssize_t retval = 0;

  8.     /* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
  9.     if (L_TOSTOP(tty) && file->f_op->write != redirected_tty_write) {
  10.         retval = tty_check_change(tty);
  11.         if (retval)
  12.             return retval;
  13.     }

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

  16.     add_wait_queue(&tty->write_wait, &wait);                                           //将当前进程放到等待队列中
  17.     while (1) {
  18.         set_current_state(TASK_INTERRUPTIBLE);                             //进入此处继续执行的原因可能是被信号打断,而不是条件得到了满足
  19.         if (signal_pending(current)) {
  20.             retval = -ERESTARTSYS;
  21.             break;
  22.         }                                                                   //只有条件得到了满足,我们才会继续,否则,直接返回!        
  23.         if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) {
  24.             retval = -EIO;
  25.             break;
  26.         }
  27.         if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) {
  28.             while (nr > 0) {
  29.                 ssize_t num = process_output_block(tty, b, nr);
  30.                 if (num < 0) {
  31.                     if (num == -EAGAIN)
  32.                         break;
  33.                     retval = num;
  34.                     goto break_out;
  35.                 }
  36.                 b += num;
  37.                 nr -= num;
  38.                 if (nr == 0)
  39.                     break;
  40.                 c = *b;
  41.                 if (process_output(c, tty) < 0)
  42.                     break;
  43.                 b++; nr--;
  44.             }
  45.             if (tty->ops->flush_chars)
  46.                 tty->ops->flush_chars(tty);
  47.         } else {
  48.             while (nr > 0) {
  49.                 c = tty->ops->write(tty, b, nr);                        //调用到具体的驱动中的write函数
  50.                 if (c < 0) {
  51.                     retval = c;
  52.                     goto break_out;
  53.                 }
  54.                 if (!c)
  55.                     break;
  56.                 b += c;
  57.                 nr -= c;
  58.             }
  59.         }
  60.         if (!nr)
  61.             break;
  62.         if (file->f_flags & O_NONBLOCK) {                //全部写入,返回
  63.             retval = -EAGAIN;
  64.             break;
  65.         }
  66.         schedule();                                      ///执行到这里,当前进程才会真正让出cpu!!!
  67.     }
  68. break_out:
  69.     __set_current_state(TASK_RUNNING);
  70.     remove_wait_queue(&tty->write_wait, &wait);
  71.     if (b - buf != nr && tty->fasync)
  72.         set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
  73.     return (b - buf) ? b - buf : retval;
  74. }
tty->ops->write是uart_ops,里面的uart_write:
  1. static int
  2. uart_write(struct tty_struct *tty, const unsigned char *buf, int count)
  3. {
  4.     ......
  5.     uart_start(tty);                    //调用了uart_start函数
  6.     return ret;
  7. }
uart_start函数:
  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);                                            //找到了uart_ops ,调用了stat_tx指向的函数
  8. }
这里就和上一篇的open类似,只不过调用的函数指针不一样,这次是start_tx(s3c24xx_serial_start_tx)函数:
  1. static void s3c24xx_serial_start_tx(struct uart_port *port)
  2. {
  3.     struct s3c24xx_uart_port *ourport = to_ourport(port);

  4.     //dump_stack();

  5.     if (!tx_enabled(port)) {                                             //如果没有打开发送
  6.         if (port->flags & UPF_CONS_FLOW)
  7.             s3c24xx_serial_rx_disable(port);

  8.         enable_irq(ourport->tx_irq);                                     //使能发送中断:接收发送中断有FIFO,FIFO里面数据够了,如果激活了中断,中断处理程序被调用。
  9.         tx_enabled(port) = 1;
  10.     }
  11. }
总结:这里路线也一样file_operations->write,调用了线路规程n_tty_write,然后调用uart_ops中的uart_write函数,最后找到了s3c24xx_serial_start_tx的驱动。

二、串口发送函数
在write中其实只是判断了是否有发送端口打开,但是真正去发送数据的。是串口中断程序,所以我们需要把串口中断程序流程了解。
应用程序中的write对应于2440串口内核中,是uart_write:
  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);                                       //把buf中的数据复制到circ循环缓冲中去
  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);
  36.     return ret;
  37. }
串口发送函数s3c24xx_serial_tx_chars:
  1. static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
  2. {
  3.     struct s3c24xx_uart_port *ourport = id;
  4.     struct uart_port *port = &ourport->port;
  5.     struct circ_buf *xmit = &port->info->xmit;
  6.     int count = 256;

  7.     if (port->x_char) {                                                                              //如果port中有字符内容,如果有将x_char写入S3C2410_UTXH中
  8.         wr_regb(port, S3C2410_UTXH, port->x_char);
  9.         port->icount.tx++;
  10.         port->x_char = 0;
  11.         goto out;
  12.     }

  13.     /* if there isnt anything more to transmit, or the uart is now
  14.      * stopped, disable the uart and exit
  15.     */

  16.     if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {                                             //判断循环缓冲是否为空,或者发送功能关闭
  17.         s3c24xx_serial_stop_tx(port);                                                                 //关闭串口中断
  18.         goto out;
  19.     }

  20.     /* try and drain the buffer... */

  21.     while (!uart_circ_empty(xmit) && count-- > 0) {                                                   //如果循环缓冲非空
  22.         if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)                               //判断FIFO是否满了
  23.             break;

  24.         wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);                                           //将数据写入UTXH中
  25.         xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);                                         //调整循环缓冲的位置
  26.         port->icount.tx++;
  27.     }

  28.     if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)                                                 //WAKEUP_CHARS是256,循环缓冲的数据量低于256时。满了必须把想发送串口的pending
  29.         uart_write_wakeup(port);                                                                      //唤醒

  30.     if (uart_circ_empty(xmit))                                                                        //当循环缓冲为空
  31.         s3c24xx_serial_stop_tx(port);                                                                 //关闭发送功能,保证不会一直产生中断

  32.  out:
  33.     return IRQ_HANDLED;
  34. }
write------->uart_write(将数据写入循环缓冲)------>start_tx(激活中断)
发送中断处理函数,从循环缓冲中读取数据,发送到串口。


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