UART驱动情景分析-write

一、write过程分析

  • App写:
    • 使用行规程来写
    • 数据最终存入uart_state->xmit的buffer里
  • 硬件发送:
    • 使用硬件驱动中uart_ops->start_tx开始发送
    • 具体的发送方式有两种:通过DMA、通过中断
  • 中断方式:
    • 方法1:直接使能tx empty中断,一开始tx buffer为空,在中断里填入数据
    • 方法2:写不封数据到tx fifo,使能中断,剩下的数据再次中断里继续发送

二、tty_write

文件在driver\tty\tty_io.c

static ssize_t tty_write(struct file *file, const char __user *buf,
						size_t count, loff_t *ppos)
{
	struct tty_struct *tty = file_tty(file);
 	struct tty_ldisc *ld;
	//...
	if (!ld->ops->write)
		ret = -EIO;
	else
		ret = do_tty_write(ld->ops->write, tty, file, buf, count);	//使用行规程里的惭怍函数,进行写入
		//tty_ldisc->tty_ldisc_ops->write
	tty_ldisc_deref(ld);
	return ret;
}

三、ldisk write

文件drivers\tty\n_tty.c

static struct tty_ldisc_ops n_tty_ops = {		//tty_ldisc_ops惭怍函数,实例结构体n_tty_ops
    .magic           = TTY_LDISC_MAGIC,
    .name            = "n_tty",
    .open            = n_tty_open,
    .close           = n_tty_close,
    .flush_buffer    = n_tty_flush_buffer,
    .read            = n_tty_read,
    .write           = n_tty_write,		//上面ld->ops->write,调用的是这个函数
	//...
};


static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
			   const unsigned char *buf, size_t nr)
{
	//...
	while (1) {
		//...
		if (O_OPOST(tty)) {
			//...
		} else {
			struct n_tty_data *ldata = tty->disc_data;

			while (nr > 0) {
				mutex_lock(&ldata->output_lock);
				c = tty->ops->write(tty, b, nr);		//tty_struct->tty_operations
				mutex_unlock(&ldata->output_lock);
				//...
			}
		}
		if (!nr)
			break;
		if (file->f_flags & O_NONBLOCK) {		//非阻塞方式的话,直接返回,不等数据发送完
			retval = -EAGAIN;
			break;
		}
		up_read(&tty->termios_rwsem);

		wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);		//阻塞方式,等待发送完毕

		down_read(&tty->termios_rwsem);
	}
	//...
}

四、uart_write

文件:drivers\tty\serial\serial_core.c

static const struct tty_operations uart_ops = {
	.open		= uart_open,
	.close		= uart_close,
	.write		= uart_write,
	//....
};

static int uart_write(struct tty_struct *tty,
					const unsigned char *buf, int count)
{
	struct uart_state *state = tty->driver_data;
	struct uart_port *port;
	struct circ_buf *circ;
	unsigned long flags;
	int c, ret = 0;

	//...
	port = uart_port_lock(state, flags);
	circ = &state->xmit;		//赋值寄存器地址
	//...

	while (port) {
		//...
		memcpy(circ->buf + circ->head, buf, c);		//把数据存入xmit buffer
		circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
		buf += c;
		count -= c;
		ret += c;
	}

	__uart_start(tty);		//启动串口发送数据
	uart_port_unlock(port, flags);
	return ret;
}

static void __uart_start(struct tty_struct *tty)
{
	struct uart_state *state = tty->driver_data;
	struct uart_port *port = state->uart_port;

	if (port && !uart_tx_stopped(port))
		port->ops->start_tx(port);		//uart_port->uart_ops->start_tx
}

五、硬件相关的发送

相关文件:drivers\tty\serial\imx.c

static const struct uart_ops imx_uart_pops = {
	.tx_empty	= imx_uart_tx_empty,
	.set_mctrl	= imx_uart_set_mctrl,
	.get_mctrl	= imx_uart_get_mctrl,
	.stop_tx	= imx_uart_stop_tx,
	.start_tx	= imx_uart_start_tx,
	.stop_rx	= imx_uart_stop_rx,
	//...
};

static void imx_uart_start_tx(struct uart_port *port)
{
	//...

	if (!sport->dma_is_enabled) {		//不适用DMA时,比较容易理解,以它为例
		ucr1 = imx_uart_readl(sport, UCR1);
		//Transimiter Empty Interrupt Enable,发送buffer为空时,就产生中断
		//在中断函数里发送字符
		imx_uart_writel(sport, ucr1 | UCR1_TXMPTYEN, UCR1);
	}

	if (sport->dma_is_enabled) {
		//...
	}
}

一开始时,发送buffer肯定为空,就会立即产生中断:

static irqreturn_t imx_uart_txint(int irq, void *dev_id)
{
	struct imx_port *sport = dev_id;
	unsigned long flags;

	spin_lock_irqsave(&sport->port.lock, flags);
	imx_uart_transmit_buffer(sport);
	spin_unlock_irqrestore(&sport->port.lock, flags);
	return IRQ_HANDLED;
}

static inline void imx_uart_transmit_buffer(struct imx_port *sport)
{
	struct circ_buf *xmit = &sport->port.state->xmit;
	//...

	while (!uart_circ_empty(xmit) &&
	       !(imx_uart_readl(sport, imx_uart_uts_reg(sport)) & UTS_TXFULL)) {
		/* send xmit->buf[xmit->tail]
		 * out the port here */
		//UART TX FIFO未满时,从xmit buffer取出数据写入FIFO,更新统计信息
		imx_uart_writel(sport, xmit->buf[xmit->tail], URTX0);
		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
		sport->port.icount.tx++;
	}

	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
		uart_write_wakeup(&sport->port);

	if (uart_circ_empty(xmit))
		imx_uart_stop_tx(&sport->port);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

习惯就好zz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值