1. 用户空间read的操作实现

static ssize_t tty_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{int i;struct tty_struct *tty = file_tty(file);struct tty_ldisc *ld;ld = tty_ldisc_ref_wait(tty); /* 获取tty对应的线路规程ldisc,和tty_write是一样的,可以回看《Linux串口驱动(4) - write详解》 */if (ld->ops->read)i = (ld->ops->read)(tty, file, buf, count);   /* ld->ops->read即n_tty_read */elsei = -EIO;tty_ldisc_deref(ld);return i;
}static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,unsigned char __user *buf, size_t nr)
{struct n_tty_data *ldata = tty->disc_data;unsigned char __user *b = buf;DECLARE_WAITQUEUE(wait, current);if (!ldata->icanon)       /*icanon默认为1,icanon表示标准模式*/······add_wait_queue(&tty->read_wait, &wait);while (nr) {set_current_state(TASK_INTERRUPTIBLE);if (!input_available_p(tty, 0)) {       /*没有可读数据时会休眠*/timeout = schedule_timeout(timeout);     /*定时休眠*/continue;}__set_current_state(TASK_RUNNING);if (ldata->icanon && !L_EXTPROC(tty)) {while (nr && ldata->read_cnt) {int eol;eol = test_and_clear_bit(ldata->read_tail,ldata->read_flags);c = ldata->read_buf[ldata->read_tail];       /*从tty线路规程的缓冲区ldata->read_buf读数据*/ldata->read_tail = ((ldata->read_tail+1) &(N_TTY_BUF_SIZE-1));ldata->read_cnt--;raw_spin_unlock_irqrestore(&ldata->read_lock, flags);if (!eol || (c != __DISABLED_CHAR)) {if (tty_put_user(tty, c, b++)) {    /*将读到的数据放入用户buffer*/retval = -EFAULT;b--;raw_spin_lock_irqsave(&ldata->read_lock, flags);break;}nr--;}raw_spin_lock_irqsave(&ldata->read_lock, flags);}raw_spin_unlock_irqrestore(&ldata->read_lock, flags);} }remove_wait_queue(&tty->read_wait, &wait);return retval;
}

write的数据为什么是从tty线路规程的buffer里读取,这一点可以回看《Linux串口驱动(3) - open详解》的分析线路2-3部分。

2. 总结

2.1 DMA

关于DMA搬运地址的配置,在open时会同时配置发送消息时DMA搬运的目标地址和接收消息时DMA搬运的源地址。因为发送数据时要将数据搬运到哪个地址是确定的,但是从哪个地址搬运数据是不确定的,接收数据时则反之。

不确定的那个地址,会在启动DMA搬运的时候进行配置。

2.2 write和read的不同

write是SOC端主动发起的动作,所以DMA搬运的启动操作是在write函数的底层操作里调用的;而read是SOC端的被动操作,所以串口在open的时候就要启动DMA搬运,将数据搬运到tty 线路规程的一个buffer里,用户读的时候不用去底层读取,去线路规程的buffer里读取即可。

3. DMA模式

static int start_rx_dma(struct imx_port *sport)
{struct dma_chan    *chan = sport->dma_chan_rx;struct dma_async_tx_descriptor *desc;desc = dmaengine_prep_dma_cyclic(chan, sport->rx_buf.dmaaddr,sport->rx_buf.buf_len, sport->rx_buf.period_len,DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);desc->callback = dma_rx_callback;    //DMA完成一次搬运后,会调用这个回调函数dmaengine_submit(desc);         //将该描述符插入dmaengine驱动的传输队列dma_async_issue_pending(chan);   //启动对应DMA通道上的传输sport->dma_is_rxing = 1;return 0;
}static void dma_rx_callback(void *data)
{dma_rx_work(sport);
}static void dma_rx_work(struct imx_port *sport)
{struct tty_struct *tty = sport->port.state->port.tty;unsigned int cur_idx = sport->rx_buf.cur_idx;dma_rx_push_data(sport, tty, 0, cur_idx);    / *最终串口接收到的数据都被放到了tty ldisc的一个buffer里,用户空间读的时候会从这个buffer里取* /
}

4. 非DMA模式

//如果在open阶段置位了中断使能位,所以当RX FIFO有数据的时候会触发中断
static irqreturn_t imx_int(int irq, void *dev_id)
{struct imx_port *sport = dev_id;unsigned int sts;unsigned int sts2;sts = readl(sport->port.membase + USR1);if ((sts & USR1_RRDY || sts & USR1_AGTIM) &&!sport->dma_is_enabled) {if (sts & USR1_AGTIM)writel(USR1_AGTIM, sport->port.membase + USR1);imx_rxint(irq, dev_id);}if (sts & USR1_TRDY &&readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN)imx_txint(irq, dev_id);return IRQ_HANDLED;
}static irqreturn_t imx_rxint(int irq, void *dev_id)
{struct imx_port *sport = dev_id;unsigned int rx, flg, ignored = 0;struct tty_port *port = &sport->port.state->port;unsigned long flags, temp;spin_lock_irqsave(&sport->port.lock, flags);while (readl(sport->port.membase + USR2) & USR2_RDR) {flg = TTY_NORMAL;sport->port.icount.rx++;//读取RX FIFO中的数据rx = readl(sport->port.membase + URXD0);temp = readl(sport->port.membase + USR2);if (temp & USR2_BRCD) {writel(USR2_BRCD, sport->port.membase + USR2);if (uart_handle_break(&sport->port))continue;}if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx))continue;//将读取的单个字节数据放到port的缓冲区中tty_insert_flip_char(port, rx, flg);}out:spin_unlock_irqrestore(&sport->port.lock, flags);//将port缓冲区中的数据全部拷贝至tty线路规程的缓冲区tty_flip_buffer_push(port);    return IRQ_HANDLED;
}

Linux串口驱动(5) - read详解相关推荐

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

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

  2. linux串口通信参数宏详解实例

    详解linux下的串口通讯开发 串行口是计算机一种常用的接口,具有连接线少,通讯简单,得到广泛的使用.常用的串口是RS-232-C接口(又称EIA RS-232-C)它是在1970年由美国电子工业协会 ...

  3. Linux串口编程最全详解-一个努力奋斗的少年

    NAME termios, tcgetattr, tcsetattr, tcsendbreak, tcdrain, tcflush, tcflow, cfmakeraw, cfgetospeed, c ...

  4. linux 串口 延迟,linux串口操作及设置详解

    串口操作需要的头文件 #include #include #include #include #include #include #include #include 1.打开串口 在前面已经提到lin ...

  5. linux两个进程同时打开串口,linux串口操作及设置详解

    串口操作需要的头文件 #include /*标准输入输出定义*/ #include /*标准函数库定义*/ #include /*Unix标准函数定义*/ #include #include #inc ...

  6. linux usb驱动教学视频教程,详解linux usb host驱动编写入门

    usb协议是一个复杂的协议,目前涉及到的版本就有usb1.0, usb2.0, usb3.0.大家如果打开kernel usb host目录,就会发现下面包含了ohci,uhci,ehci,xhci, ...

  7. linux驱动之总线详解

    linux驱动 第一章 linux驱动之设备与驱动 第二章 linux驱动之设备树与GPIO子系统 linux驱动之总线详解 linux驱动 一.总线bus 1.bus在linux中文件结构 2.bu ...

  8. 迅为linux下串口,迅为iMX6UL开发板多路串口开发平台接口详解

    原标题:迅为iMX6UL开发板多路串口开发平台接口详解 iMX6UL开发板 核心板参数 尺寸:38mm*42mm CPU:iMX6UL 主频528MHz ARM Cortex-A7架构 单核 内存:5 ...

  9. Linux文件系统的目录结构详解(转)

    Linux文件系统的目录结构详解(转) 原文链接https://www.cnblogs.com/cyjaysun/p/4462325.html 一.前 言 文章对Linux下所有目录一一说明,对比较重 ...

  10. ceph存储原理_赠书 | Linux 开源存储全栈详解——从Ceph到容器存储

    // 留言点赞赠书我有书,你有故事么?留言说出你的存储故事留言点赞前两名,免费送此书截止日期12.27号12.30号公布名单 // 内容简介 本书致力于帮助读者形成有关Linux开源存储世界的细致的拓 ...

最新文章

  1. 字符串去重、统计不同字符种类数问题
  2. oracle tranc,ORACLE TRUNC()函数
  3. 简单安装Harbor私有镜像仓库
  4. 正则表达式:日期格式的校验(日期+时间)
  5. 云原生实时数仓首次在2020双11核心数据场景落地
  6. C++ 读入一行字符串
  7. 【ES】ES 7.6报错 SearchPhaseExecutionException: all shards failed OutOfMemoryError
  8. 基于JAVA+SpringMVC+Mybatis+MYSQL的相亲管理系统
  9. mybatis 批量提交清除缓存_重学Mybatis(三)-------缓存 (含面试题)
  10. 【mmdetection】mmdetection数据处理pipline结果可视化
  11. Java自学之路(新手一定要看)
  12. 从零开始一起学习SLAM | 学习SLAM到底需要学什么?
  13. arduino 的显示——LCD1602、LiquidCrystal库的使用
  14. pycharm无法创建虚拟环境Virtualenv(Directory ...\python not found)
  15. 小迪安全第10天 信息收集,资产监控拓展
  16. Python零基础学习笔记(三十三)—— 窗体的控制...
  17. uni-app实现小程序身份证取景框
  18. fastadmin中selectpage条件wherein添加
  19. centos 6.2 bind dlz mysql 驱动,bind9 dlz mysql 配置 – daemon
  20. 2021年度训练联盟热身训练赛第五场 F,G,H,I

热门文章

  1. postgresql 中文排序
  2. UG NX 12 草图环境中使用鼠标的说明
  3. 广数系统加工中心编程_加工中心宏程序编程应用案例分享
  4. Centos中重置MySQL密码
  5. VCPKG 包下载失败解决思路
  6. Spoon工具的使用
  7. 概率图模型之贝叶斯网络的理解与应用
  8. python贝叶斯网络预测模型_高效灵活的概率建模方法基于Python
  9. The Podfile (Podfile 的写法和规范)
  10. 最新mysql数据库源码编译安装。