/*串口read函数分析
* 当应用程序调用read系统调用时,会调用tty_fops中的tty_read
* 接下来分析tty_read函数
*
* 其中最重要的就是ld->ops->read(tty,file,buf,count);
* 也就是调用线路规程中read函数
*/static ssize_t tty_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)
{int i;struct inode *inode = file->f_path.dentry->d_inode;struct tty_struct *tty = file_tty(file);struct tty_ldisc *ld;if (tty_paranoia_check(tty, inode, "tty_read"))return -EIO;if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))return -EIO;/* We want to wait for the line discipline to sort out in thissituation */ld = tty_ldisc_ref_wait(tty);if (ld->ops->read)i = (ld->ops->read)(tty, file, buf, count);elsei = -EIO;tty_ldisc_deref(ld);if (i > 0)inode->i_atime = current_fs_time(inode->i_sb);return i;
}/* 线路规程的ops是: tty_ldisc_N_TTY
* read =   = n_tty_read,
* buf代表的是用户空间传下来的buf, 将来需要我们把数据写到buf中去
*/
static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,unsigned char __user *buf, size_t nr)
{unsigned char __user *b = buf;/*其实n_tty_read就是调用copy_from_read_buf将tty->read_buf中的数据送到用户传下来的buf中。 前面都是一些合法的判断*/uncopied = copy_from_read_buf(tty, &b, &nr);uncopied += copy_from_read_buf(tty, &b, &nr);
}/*** 其实从copy_from_read_buf中可以很明显的看见*/
static int copy_from_read_buf(struct tty_struct *tty,unsigned char __user **b,size_t *nr)
{/*很明显的可以看见copy_to_user函数。数据是从tty->read_buf中拷贝到b中去的。* 那么tty->read中的数据那又是从那里来的?*/if (n) {retval = copy_to_user(*b, &tty->read_buf[tty->read_tail], n);n -= retval;tty_audit_add_data(tty, &tty->read_buf[tty->read_tail], n);spin_lock_irqsave(&tty->read_lock, flags);tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1);tty->read_cnt -= n;/* Turn single EOF into zero-length read */if (L_EXTPROC(tty) && tty->icanon && n == 1) {if (!tty->read_cnt && (*b)[n-1] == EOF_CHAR(tty))n--;}spin_unlock_irqrestore(&tty->read_lock, flags);*b += n;*nr -= n;}return retval;
}/*接下来分析tty->read_buf中的数据是从那里来的?
* 首先: 数据当然是从硬件里read出来的。
* 那么当我们的串口有数据的话,当然就调用我们以前注册的rx中断函数了。
*/static irqreturn_t s3c24xx_serial_rx_chars(int irq, void *dev_id)
{struct s3c24xx_uart_port *ourport = dev_id;struct uart_port *port = &ourport->port;struct tty_struct *tty = port->state->port.tty;unsigned int ufcon, ch, flag, ufstat, uerstat;int max_count = 64;while (max_count-- > 0) {/*读取UFCON串口配置寄存器*/ufcon = rd_regl(port, S3C2410_UFCON);/*读取 UFSTAT串口状态寄存器。*/ufstat = rd_regl(port, S3C2410_UFSTAT);/*根据读出的ufstat判断UFSTAT中rx的fifo是否为0*/if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)break;/*读取UERSTAT错误状态寄存器*/uerstat = rd_regl(port, S3C2410_UERSTAT);/*读取URXH寄存器*/ch = rd_regb(port, S3C2410_URXH);/*进行流量控制*/if (port->flags & UPF_CONS_FLOW) {int txe = s3c24xx_serial_txempty_nofifo(port);if (rx_enabled(port)) {if (!txe) {rx_enabled(port) = 0;continue;}} else {if (txe) {ufcon |= S3C2410_UFCON_RESETRX;wr_regl(port, S3C2410_UFCON, ufcon);rx_enabled(port) = 1;goto out;}continue;}}/* insert the character into the buffer */flag = TTY_NORMAL;port->icount.rx++;if (unlikely(uerstat & S3C2410_UERSTAT_ANY)) {dbg("rxerr: port ch=0x%02x, rxs=0x%08x\n",ch, uerstat);/* check for break */if (uerstat & S3C2410_UERSTAT_BREAK) {dbg("break!\n");port->icount.brk++;if (uart_handle_break(port))goto ignore_char;}if (uerstat & S3C2410_UERSTAT_FRAME)port->icount.frame++;if (uerstat & S3C2410_UERSTAT_OVERRUN)port->icount.overrun++;uerstat &= port->read_status_mask;if (uerstat & S3C2410_UERSTAT_BREAK)flag = TTY_BREAK;else if (uerstat & S3C2410_UERSTAT_PARITY)flag = TTY_PARITY;else if (uerstat & (S3C2410_UERSTAT_FRAME |S3C2410_UERSTAT_OVERRUN))flag = TTY_FRAME;}if (uart_handle_sysrq_char(port, ch))goto ignore_char;/*插入ch也就是数据到tty->tty_bufhead中去。 当whiel大循环完后, 整个64字节数据都存放到tty->tty_bufhead中去*/uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN,ch, flag);}/*这是才将整个数据送tty->read_buf中去*/tty_flip_buffer_push(tty);
}
/* 将串口产生的数据送进tty->buf.tail中去。 */
static inline int tty_insert_flip_char(struct tty_struct *tty,unsigned char ch, char flag)
{struct tty_buffer *tb = tty->buf.tail;if (tb && tb->used < tb->size) {tb->flag_buf_ptr[tb->used] = flag;       /*用于存放flag,也就是状态位*/tb->char_buf_ptr[tb->used++] = ch;      /*用于存放真正的数据*/return 1;}return tty_insert_flip_string_flags(tty, &ch, &flag, 1);
}static void flush_to_ldisc(struct work_struct *work)
{char_buf = head->char_buf_ptr + head->read;        /*char_buf用于存放真实数据*/flag_buf = head->flag_buf_ptr + head->read;   /*flag_buf用于存放flag标志*/head->read += count;                                                       spin_unlock_irqrestore(&tty->buf.lock, flags);disc->ops->receive_buf(tty, char_buf,            /*调用tty_ldisc_N_TTY中的recive_buf函数*/flag_buf, count);
}static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,char *fp, int count)
{if (tty->real_raw) {spin_lock_irqsave(&tty->read_lock, cpuflags);i = min(N_TTY_BUF_SIZE - tty->read_cnt,         /*判断大小*/N_TTY_BUF_SIZE - tty->read_head);i = min(count, i);memcpy(tty->read_buf + tty->read_head, cp, i);        /*这才是真正的拷贝数据到tty->read_buf中去*/tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1); /*其实read_buf是一个环形缓冲区。 每次写进数据都会调正read_head的位置。 同时改变read_cnt*/tty->read_cnt += i;cp += i;count -= i;i = min(N_TTY_BUF_SIZE - tty->read_cnt,N_TTY_BUF_SIZE - tty->read_head);i = min(count, i);memcpy(tty->read_buf + tty->read_head, cp, i);tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);tty->read_cnt += i;spin_unlock_irqrestore(&tty->read_lock, cpuflags);}
}

Linux串口驱动分析read相关推荐

  1. linux串口驱动分析

    linux串口驱动分析 硬件资源及描写叙述 s3c2440A 通用异步接收器和发送器(UART)提供了三个独立的异步串行 I/O(SIO)port,每一个port都能够在中断模式或 DMA 模式下操作 ...

  2. linux串口驱动分析【转】

    转自:http://blog.csdn.net/hanmengaidudu/article/details/11946591 硬件资源及描述 s3c2440A 通用异步接收器和发送器(UART)提供了 ...

  3. linux串口发送数据程序,linux串口驱动分析——发送数据

    一.应用程序中write函数到底层驱动历程 和前文提到的一样,首先先注册串口,使用uart_register_driver函数,依次分别为tty_register_driver,cdev_init函数 ...

  4. LINUX串口驱动分析——发送数据

    https://www.cnblogs.com/51qianrushi/p/4324845.html

  5. GPS NMEA 0183 4.10协议/GPS Linux串口驱动

      NMEA 0183是美国国家海洋电子协会(National Marine Electronics Association)为海用电子设备制定的标准格式.现在已经成为GPS导航设备统一的RTCM(R ...

  6. Linux UART驱动分析及测试

    1.Linux TTY驱动程序框架 Linux TTY驱动程序代码位于/drivers/tty下面.TTY的层次接口包括TTY应用层.TTY文件层.TTY线路规程层.TTY驱动层.TTY设备驱动层.T ...

  7. Linux串口驱动(2) - 线路规程

    1. 注册tty的ldisc ldisc全称 line discipline(线路规程),因为历史原因,tty属于一类设备,而串口设备只是其中一种,所以该模块负责将用户操作桥接到不同的tty驱动.从代 ...

  8. Linux串口驱动(3) - open详解

    1. 用户空间open的操作实现 串口设备是被注册为字符设备的,在注册过程中填充了struct file_operations tty_fops结构体,该结构体中的成员open.read.write等 ...

  9. wince串口驱动分析(转)

    wince串口驱动分析 串行通讯接口主要是指UART(通用串行)和IRDA两种.通常的串行连接电气连接上有3wire和9wire两种.3wire的接线方式下定义了发送.接收和地三根连接.其用途就如名称 ...

  10. Linux spi驱动分析(四)----SPI设备驱动(W25Q32BV)

    一.W25Q32BV芯片简介 W25X是一系列SPI接口Flash芯片的简称,它采用SPI接口和CPU通信,本文使用的W25Q32BV容量为32M,具体特性如下: 1.1.基本特性 该芯片最大支持10 ...

最新文章

  1. Python基础知识梳理(一)
  2. c语言节点导入数据编程,编程小白。用C语言计算SR E2E ARQ模式下节点的数据分析...
  3. 包装类 || 装箱与拆箱
  4. jQuery Mobile 图标无法显示
  5. 人工机器:TM、VNM和NTM的内存机制
  6. OpenCV中的HOG+SVM在自动驾驶车辆检测中的应用实例
  7. java method_JAVA Method的解析
  8. c++ 静态变量赋值_Python变量及常量解释说明
  9. inflate简介,LayoutInflater和inflate()方法的用法
  10. 逐步创建ASP.NET Core 2.2应用程序
  11. linux内核与用户之间的通信方式——虚拟文件系统、ioctl以及netlink .
  12. 国内外php主流开源cms汇总
  13. 阿里云移动推送 - 异常记录
  14. 【算法】给定一个数组,除了一个数出现1次之外,其余数都出现3次,输出出现一次的那个数。
  15. 数据挖掘项目---航空公司客户价值分析
  16. 敏捷经验教训, 开发总结!
  17. 添加Aforge控件,报错“在.....中没有可放置在工具箱上的控件”
  18. CSS3属性之text-overflow:ellipsis详解
  19. 什么是LIDAR(激光雷达),如何标注激光点云数据?
  20. 约瑟夫环问题(C语言数组和循环链表)

热门文章

  1. 403 for URL: http://www.terracotta.org/kit/reflector
  2. 1.bootstrapTable data-table
  3. 计算机基础知识_进制转化
  4. 在dos窗口中删除services中的服务
  5. CNBlog客户端--第一阶段记录
  6. 将table导出为Excel的标准无乱码写法
  7. dba_tables 和 dba_segments 表中 blocks 的区别
  8. 用Java写一个浪费cpu的程序_Java程序是如何浪费内存的
  9. Yarn的原理与资源调度
  10. Socket网络编程初探