本文共 2300 字,大约阅读时间需要 7 分钟。
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是一样的,可以回看串口驱动(四) */ if (ld->ops->read) i = (ld->ops->read)(tty, file, buf, count); /* ld->ops->read即n_tty_read */ else i = -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里读取,这一点可以回看串口驱动(三)的分析线路2-3部分。
关于DMA搬运地址的配置,在open时会同时配置发送消息时DMA搬运的目标地址和接收消息时DMA搬运的源地址。因为发送数据时要将数据搬运到哪个地址是确定的,但是从哪个地址搬运数据是不确定的,接收数据时则反之。
不确定的那个地址,会在启动DMA搬运的时候进行配置。
write是SOC端主动发起的动作,所以DMA搬运的启动操作是在write函数的底层操作里调用的;而read是SOC端的被动操作,所以串口在open的时候就要启动DMA搬运,将数据搬运到tty 线路规程的一个buffer里,用户读的时候不用去底层读取,去线路规程的buffer里读取即可。
在IMX6d这款SOC的串口驱动中使用了DMA来搬运数据,比如串口发送数据的时候,将数据放到串口寄存器FIFO的步骤是由DMA 来完成的;但是三星的S5Pv210 这款SOC的串口驱动中并没有使用DMA来搬运数据,将数据放到串口寄存器FIFO的步骤是由中断服务程序来完成的,至于是为什么用中断服务程序来做FIFO填充的动作,我还是不太明白!我猜测是因为FIFO为空时会触发中断以唤醒上层,这得仔细研究它的数据手册了······
转载地址:http://hhuen.baihongyu.com/