一、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);
}