UART驱动情景分析-read

一、源码框架回顾

在这里插入图片描述
shell读数据,一开始的时候没有就休眠。数据从串口发送到驱动,驱动接收到中断,驱动读取串口数据,这个数据会传给行规程。
行规程获取到数据后,会回显。按下删除就删除一个字符,按下回车,就返回一个命令。如果找不到就提示找不到命令。

drivers/tty/tty_ldisc.c

int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc)		//行规程注册函数

搜索它的使用情况就可以看到有多少的行规程

   #   line  filename / context / line
   1    701  include/linux/tty.h <<GLOBAL>>
             extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc);
   2     77  drivers/tty/tty_ldisc.c <<GLOBAL>>
             EXPORT_SYMBOL(tty_register_ldisc);
   3    701  include/linux/tty.h <<GLOBAL>>
             extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc);
   4    837  drivers/bluetooth/hci_ldisc.c <<hci_uart_init>>
             err = tty_register_ldisc(N_HCI, &hci_uart_ldisc);
   5    298  input/serio/serport.c <<serport_init>>
             retval = tty_register_ldisc(N_MOUSE, &serport_ldisc);
   6    763  isdn/gigaset/ser-gigaset.c <<ser_gigaset_init>>
             rc = tty_register_ldisc(N_GIGASET_M101, &gigaset_ldisc);
   7    858  misc/ti-st/st_core.c <<st_core_init>>
             err = tty_register_ldisc(N_TI_WL, &st_ldisc_ops);
   8    405  net/caif/caif_serial.c <<register_ldisc>>
             result = tty_register_ldisc(N_CAIF, &caif_ldisc);
//...
  16    137  pps/clients/pps-ldisc.c <<pps_tty_init>>
             err = tty_register_ldisc(N_PPS, &pps_ldisc_ops);
  17    189  staging/speakup/spk_ttyio.c <<spk_ttyio_register_ldisc>>
             if (tty_register_ldisc(N_SPEAKUP, &spk_ttyio_ldisc_ops))
//...
  26    473  nfc/nci/uart.c <<nci_uart_init>>
             return tty_register_ldisc(N_NCI, &nci_uart_ldisc);
  27    503  soc/omap/ams-delta.c <<ams_delta_cx20442_init>>
             ret = tty_register_ldisc(N_V253, &cx81801_ops);

有各种行规程,声卡的、网络的、蓝牙的。找一个最通用的驱动来分析drivers\tty\n_tty.c

二、读之前通过open确定了行规层

2.1 行规程注册

在tty设备中最常用的就是它,n_tty
文件:drivers\tty\n_tty.c

void __init n_tty_init(void)
{
    
    
	tty_register_ldisc(N_TTY, &n_tty_ops);		//使用N_TTY来找到行规程
}

以后可以通过标号N_TTY找到这个行规程。搜索使用的地方就是:

Cscope tag: N_TTY
   #   line  filename / context / line
   //...
   7   2501  drivers/tty/n_tty.c <<n_tty_init>>
             tty_register_ldisc(N_TTY, &n_tty_ops);
   8     66  drivers/tty/tty_ldisc.c <<tty_register_ldisc>>
             if (disc < N_TTY || disc >= NR_LDISCS)
   9     96  drivers/tty/tty_ldisc.c <<tty_unregister_ldisc>>
             if (disc < N_TTY || disc >= NR_LDISCS)
  10    171  drivers/tty/tty_ldisc.c <<tty_ldisc_get>>
             if (disc < N_TTY || disc >= NR_LDISCS)
  11    532  drivers/tty/tty_ldisc.c <<tty_ldisc_restore>>
             if (tty_ldisc_failto(tty, N_TTY) < 0 &&
  12    676  drivers/tty/tty_ldisc.c <<tty_ldisc_reinit>>
             BUG_ON(disc == N_TTY);
  13    747  drivers/tty/tty_ldisc.c <<tty_ldisc_hangup>>
             tty_ldisc_reinit(tty, N_TTY) < 0)
  14    821  drivers/tty/tty_ldisc.c <<tty_ldisc_init>>
             struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY);		//这里就获得了行规程的结构

继续反向查找

   #   line  filename / context / line
   1    706  include/linux/tty.h <<GLOBAL>>
             extern int __must_check tty_ldisc_init(struct tty_struct *tty);
   2    706  include/linux/tty.h <<GLOBAL>>
             extern int __must_check tty_ldisc_init(struct tty_struct *tty);
   3   2842  drivers/tty/tty_io.c <<alloc_tty_struct>>
             if (tty_ldisc_init(tty)) {			//在这里调用了tty_ldisc_init
   4    819  drivers/tty/tty_ldisc.c <<tty_ldisc_init>>
             int tty_ldisc_init(struct tty_struct *tty)

2.2 open设备时确定行规程

tty_open:打开串口时,会调用tty_open
    tty_open_by_driver:调用driver来打开一个tty结构体
    	tty_init_dev:分配和设置tty结构体
    		tty = alloc_tty_struct(driver, idx);		//分配一个行规程
					tty_ldisc_init(tty);		//初始化行规程
						struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY);		//根据N_TTY整数找到之前的行规程
						tty->ldisc = ld;	//行规程会放在tty结构体里面,以后使用它来处理数据

三、read过程分析

流程为:

  • APP读
    • 使用行规程来读
    • 无数据则休眠
  • UART接收到数据,产生中断
    • 中断程序从硬件上读入数据
  • 发给行规程
    • 行规程处理后存入buffer
    • 行规程唤醒APP
  • APP被唤醒后,从行规程buffer中读入数据,返回

3.1 tty_read

static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
			loff_t *ppos)
{
    
    
	int i;
	struct inode *inode = file_inode(file);
	struct tty_struct *tty = file_tty(file);
	struct tty_ldisc *ld;

	//...
	if (ld->ops->read)
		i = ld->ops->read(tty, file, buf, count);		//用到了行规程里的ops的read函数
	else
		i = -EIO;
	tty_ldisc_deref(ld);

	if (i > 0)
		tty_update_time(&inode->i_atime);

	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;
	DEFINE_WAIT_FUNC(wait, woken_wake_function);			//这里申明了一个等待队列,没有数据就休眠
	int c;
	int minimum, time;
	ssize_t retval = 0;
	long timeout;
	int packet;
	size_t tail;

	c = job_control(tty, file);
	if (c < 0)
		return c;

	/*
	 *	Internal serialization of reads.
	 */
	if (file->f_flags & O_NONBLOCK) {
    
    
		if (!mutex_trylock(&ldata->atomic_read_lock))
			return -EAGAIN;
	} else {
    
    
		if (mutex_lock_interruptible(&ldata->atomic_read_lock))
			return -ERESTARTSYS;
	}

	down_read(&tty->termios_rwsem);

	minimum = time = 0;
	timeout = MAX_SCHEDULE_TIMEOUT;
	//...

	packet = tty->packet;
	tail = ldata->read_tail;

	add_wait_queue(&tty->read_wait, &wait);
	while (nr) {
    
    
		/* First test for status change. */
		if (packet && tty->link->ctrl_status) {
    
    
			//...
		}

		if (!input_available_p(tty, 0)) {
    
    
			up_read(&tty->termios_rwsem);
			tty_buffer_flush_work(tty->port);
			down_read(&tty->termios_rwsem);
			if (!input_available_p(tty, 0)) {
    
    	//如果没有数据就等待
				//...

				timeout = wait_woken(&wait, TASK_INTERRUPTIBLE,
						timeout);			//没有数据就休眠

				down_read(&tty->termios_rwsem);
				continue;
			}
		}

		if (ldata->icanon && !L_EXTPROC(tty)) {
    
    
			retval = canon_copy_from_read_buf(tty, &b, &nr);	//有数据后读取数据、返回
			if (retval)
				break;
		} else {
    
    
			int uncopied;

			/* Deal with packet mode. */
			if (packet && b == buf) {
    
    
				if (put_user(TIOCPKT_DATA, b)) {
    
    
					retval = -EFAULT;
					break;
				}
				b++;
				nr--;
			}

			uncopied = copy_from_read_buf(tty, &b, &nr);		//行规程读函数
			uncopied += copy_from_read_buf(tty, &b, &nr);
			if (uncopied) {
    
    
				retval = -EFAULT;
				break;
			}
		}

		n_tty_check_unthrottle(tty);
		//...
	}
	//...

	return retval;
}

copy_from_read_buf函数解析

static int copy_from_read_buf(struct tty_struct *tty,
				      unsigned char __user **b,
				      size_t *nr)

{
    
    
	//...
	if (n) {
    
    
		unsigned char *from = read_buf_addr(ldata, tail);		//从行规程读取数据
		retval = copy_to_user(*b, from, n);		//返回给App层
		n -= retval;
		is_eof = n == 1 && *from == EOF_CHAR(tty);
		tty_audit_add_data(tty, from, n);
		zero_buffer(tty, from, n);
		smp_store_release(&ldata->read_tail, ldata->read_tail + n);
		//...
	}
	return retval;
}

四、数据的源头

drivers\tty\serial\imx.c
函数:imx_rxint,数据存入对应的tty_port,也就是每个tty_port对应一个串口

static irqreturn_t imx_uart_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;

	spin_lock_irqsave(&sport->port.lock, flags);

	while (imx_uart_readl(sport, USR2) & USR2_RDR) {
    
    		//读取硬件状态
		u32 usr2;

		flg = TTY_NORMAL;
		sport->port.icount.rx++;		//在对应的uart_port中更新统计信息

		rx = imx_uart_readl(sport, URXD0);		//得到数据

		usr2 = imx_uart_readl(sport, USR2);
		if (usr2 & USR2_BRCD) {
    
    
			imx_uart_writel(sport, USR2_BRCD, USR2);		
			if (uart_handle_break(&sport->port))
				continue;
		}

		//各种记录数据

		if (sport->port.ignore_status_mask & URXD_DUMMY_READ)
			goto out;

		if (tty_insert_flip_char(port, rx, flg) == 0)		//把数据存入tty_port里的tty_buffer
			sport->port.icount.buf_overrun++;
	}

out:
	spin_unlock_irqrestore(&sport->port.lock, flags);
	tty_flip_buffer_push(port);		//通知行规程来处理
	return IRQ_HANDLED;
}

void tty_flip_buffer_push(struct tty_port *port)
{
    
    
	tty_schedule_flip(port);
}

void tty_schedule_flip(struct tty_port *port)
{
    
    
	struct tty_bufhead *buf = &port->buf;

	/* paired w/ acquire in flush_to_ldisc(); ensures
	 * flush_to_ldisc() sees buffer data.
	 */
	smp_store_release(&buf->tail->commit, buf->tail->used);
	queue_work(system_unbound_wq, &buf->work);		//使用工作队列来处理,对应flush_to_ldisc函数
}

在imx.c的porbe函数里有注册中断函数的过程:

static int imx_uart_probe(struct platform_device *pdev)
{
    
    
	//...

	rxirq = platform_get_irq(pdev, 0);			//从设备树里获取的rxirq的中断号
	txirq = platform_get_irq(pdev, 1);
	rtsirq = platform_get_irq(pdev, 2);

	sport->port.dev = &pdev->dev;
	sport->port.mapbase = res->start;
	sport->port.membase = base;
	sport->port.type = PORT_IMX,
	sport->port.iotype = UPIO_MEM;
	sport->port.irq = rxirq;
	sport->port.fifosize = 32;
	sport->port.ops = &imx_uart_pops;
	sport->port.rs485_config = imx_uart_rs485_config;
	sport->port.flags = UPF_BOOT_AUTOCONF;
	timer_setup(&sport->timer, imx_uart_timeout, 0);

	//...
	if (txirq > 0) {
    
    
		ret = devm_request_irq(&pdev->dev, rxirq, imx_uart_rxint, 0,
				       dev_name(&pdev->dev), sport);		//在这里使用rxirq和imx_uart_rxint注册中断函数
		//...
	} else {
    
    
		ret = devm_request_irq(&pdev->dev, rxirq, imx_uart_int, 0,
				       dev_name(&pdev->dev), sport);
		if (ret) {
    
    
			dev_err(&pdev->dev, "failed to request irq: %d\n", ret);
			return ret;
		}
	}

	imx_uart_ports[sport->port.line] = sport;

	platform_set_drvdata(pdev, sport);

	return uart_add_one_port(&imx_uart_uart_driver, &sport->port);
}


猜你喜欢

转载自blog.csdn.net/ch122633/article/details/129876718