串口驱动分析-发送

xiaoxiao2021-02-28  12

一、TTY数据发送调用关系 这个上一篇open有点相似: 从samsung.c>>uart_register_driver>>tty_register_driver>注册>tty_fops(file_operations结构)>>tty_write: static ssize_t tty_write(struct file *file, const char __user *buf,                         size_t count, loff_t *ppos) {     struct tty_struct *tty;     struct inode *inode = file->f_path.dentry->d_inode;     ssize_t ret;     struct tty_ldisc *ld;     tty = (struct tty_struct *)file->private_data;     if (tty_paranoia_check(tty, inode, "tty_write"))         return -EIO;     if (!tty || !tty->ops->write ||         (test_bit(TTY_IO_ERROR, &tty->flags)))             return -EIO;     /* Short term debug to catch buggy drivers */     if (tty->ops->write_room == NULL)         printk(KERN_ERR "tty driver %s lacks a write_room method.\n",             tty->driver->name);     ld = tty_ldisc_ref_wait(tty);     if (!ld->ops->write)         ret = -EIO;     else         ret = do_tty_write(ld->ops->write, tty, file, buf, count);           //ld->ops是线路规程的结构,并调用了write     tty_ldisc_deref(ld);     return ret; } tty_ldisc_N_tty结构: struct tty_ldisc_ops tty_ldisc_N_TTY = {     .magic = TTY_LDISC_MAGIC,     .name = "n_tty",     .open = n_tty_open,     .close = n_tty_close,     .flush_buffer = n_tty_flush_buffer,     .chars_in_buffer = n_tty_chars_in_buffer,     .read = n_tty_read,     .write = n_tty_write,                                          //write指向的是n_tty_write     .ioctl = n_tty_ioctl,     .set_termios = n_tty_set_termios,     .poll = n_tty_poll,     .receive_buf = n_tty_receive_buf,     .write_wakeup = n_tty_write_wakeup }; n_tty_write: static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,              const unsigned char *buf, size_t nr) {     const unsigned char *b = buf;     DECLARE_WAITQUEUE(wait, current);     int c;     ssize_t retval = 0;     /* Job control check -- must be done at start (POSIX.1 7.1.1.4). */     if (L_TOSTOP(tty) && file->f_op->write != redirected_tty_write) {         retval = tty_check_change(tty);         if (retval)             return retval;     }     /* Write out any echoed characters that are still pending */     process_echoes(tty);     add_wait_queue(&tty->write_wait, &wait);                                           //将当前进程放到等待队列中     while (1) {         set_current_state(TASK_INTERRUPTIBLE);                             //进入此处继续执行的原因可能是被信号打断,而不是条件得到了满足         if (signal_pending(current)) {             retval = -ERESTARTSYS;             break;         }                                                                   //只有条件得到了满足,我们才会继续,否则,直接返回!                 if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) {             retval = -EIO;             break;         }         if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) {             while (nr > 0) {                 ssize_t num = process_output_block(tty, b, nr);                 if (num < 0) {                     if (num == -EAGAIN)                         break;                     retval = num;                     goto break_out;                 }                 b += num;                 nr -= num;                 if (nr == 0)                     break;                 c = *b;                 if (process_output(c, tty) < 0)                     break;                 b++; nr--;             }             if (tty->ops->flush_chars)                 tty->ops->flush_chars(tty);         } else {             while (nr > 0) {                 c = tty->ops->write(tty, b, nr);                        //调用到具体的驱动中的write函数                 if (c < 0) {                     retval = c;                     goto break_out;                 }                 if (!c)                     break;                 b += c;                 nr -= c;             }         }         if (!nr)             break;         if (file->f_flags & O_NONBLOCK) {                //全部写入,返回             retval = -EAGAIN;             break;         }         schedule();                                      ///执行到这里,当前进程才会真正让出cpu!!!     } break_out:     __set_current_state(TASK_RUNNING);     remove_wait_queue(&tty->write_wait, &wait);     if (b - buf != nr && tty->fasync)         set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);     return (b - buf) ? b - buf : retval; } tty->ops->write是uart_ops,里面的uart_write: static int uart_write(struct tty_struct *tty, const unsigned char *buf, int count) {     ......     uart_start(tty);                    //调用了uart_start函数     return ret; } uart_start函数: static void __uart_start(struct tty_struct *tty) {     struct uart_state *state = tty->driver_data;     struct uart_port *port = state->port;     if (!uart_circ_empty(&state->info.xmit) && state->info.xmit.buf &&      !tty->stopped && !tty->hw_stopped)         port->ops->start_tx(port);                                            //找到了uart_ops ,调用了stat_tx指向的函数 } 这里就和上一篇的open类似,只不过调用的函数指针不一样,这次是start_tx(s3c24xx_serial_start_tx)函数: static void s3c24xx_serial_start_tx(struct uart_port *port) {     struct s3c24xx_uart_port *ourport = to_ourport(port);     //dump_stack();     if (!tx_enabled(port)) {                                             //如果没有打开发送         if (port->flags & UPF_CONS_FLOW)             s3c24xx_serial_rx_disable(port);         enable_irq(ourport->tx_irq);                                     //使能发送中断:接收发送中断有FIFO,FIFO里面数据够了,如果激活了中断,中断处理程序被调用。         tx_enabled(port) = 1;     } } 总结:这里路线也一样file_operations->write,调用了线路规程n_tty_write,然后调用uart_ops中的uart_write函数,最后找到了 s3c24xx_serial_start_tx的驱动。 二、串口发送函数 在write中其实只是判断了是否有发送端口打开,但是真正去发送数据的。是串口中断程序,所以我们需要把串口中断程序流程了解。 应用程序中的write对应于2440串口内核中,是uart_write: static int uart_write(struct tty_struct *tty, const unsigned char *buf, int count) {     struct uart_state *state = tty->driver_data;     struct uart_port *port;     struct circ_buf *circ;     unsigned long flags;     int c, ret = 0;     /*      * This means you called this function _after_ the port was      * closed. No cookie for you.      */     if (!state) {         WARN_ON(1);         return -EL3HLT;     }     port = state->port;     circ = &state->info.xmit;     if (!circ->buf)         return 0;     spin_lock_irqsave(&port->lock, flags);     while (1) {         c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);         if (count < c)             c = count;         if (c <= 0)             break;         memcpy(circ->buf + circ->head, buf, c);                                       //把buf中的数据复制到circ循环缓冲中去         circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);         buf += c;         count -= c;         ret += c;     }     spin_unlock_irqrestore(&port->lock, flags);     uart_start(tty);     return ret; } 串口发送函数s3c24xx_serial_tx_chars: static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id) {     struct s3c24xx_uart_port *ourport = id;     struct uart_port *port = &ourport->port;     struct circ_buf *xmit = &port->info->xmit;     int count = 256;     if (port->x_char) {                                                                              //如果port中有字符内容,如果有将x_char写入S3C2410_UTXH中         wr_regb(port, S3C2410_UTXH, port->x_char);         port->icount.tx++;         port->x_char = 0;         goto out;     }     /* if there isnt anything more to transmit, or the uart is now      * stopped, disable the uart and exit     */     if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {                                             //判断循环缓冲是否为空,或者发送功能关闭         s3c24xx_serial_stop_tx(port);                                                                 //关闭串口中断         goto out;     }     /* try and drain the buffer... */     while (!uart_circ_empty(xmit) && count-- > 0) {                                                   //如果循环缓冲非空         if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)                               //判断FIFO是否满了             break;         wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);                                           //将数据写入UTXH中         xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);                                         //调整循环缓冲的位置         port->icount.tx++;     }     if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)                                                 //WAKEUP_CHARS是256,循环缓冲的数据量低于256时。满了必须把想发送串口的pending         uart_write_wakeup(port);                                                                      //唤醒     if (uart_circ_empty(xmit))                                                                        //当循环缓冲为空         s3c24xx_serial_stop_tx(port);                                                                 //关闭发送功能,保证不会一直产生中断  out:     return IRQ_HANDLED; } write------->uart_write(将数据写入循环缓冲)------>start_tx(激活中断) 发送中断处理函数,从循环缓冲中读取数据,发送到串口。     <script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script> 阅读(34) | 评论(0) | 转发(0) | 0

上一篇:串口驱动分析-打开设备

下一篇:串口驱动分析-接收

相关热门文章 SHTML是什么_SSI有什么用...查看linux中某个端口(port)...卡尔曼滤波的原理说明...shell中字符串操作关于java中的“错误:找不到或... 给主人留下些什么吧!~~ 评论热议
转载请注明原文地址: https://www.6miu.com/read-450078.html

最新回复(0)