我是靠谱客的博主 瘦瘦溪流,最近开发中收集的这篇文章主要介绍Linux串口驱动分析read,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

/*串口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 this
	   situation */
	ld = tty_ldisc_ref_wait(tty);
	if (ld->ops->read)
		i = (ld->ops->read)(tty, file, buf, count);
	else
		i = -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%08xn",
			    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的全部内容,希望文章能够帮你解决Linux串口驱动分析read所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(45)

评论列表共有 0 条评论

立即
投稿
返回
顶部