我试图了解AMBA串口的Linux驱动程序(amba-pl011.c)是如何在非DMA模式下发送字符的。对于端口操作,此驱动程序只注册以下回调:
static struct uart_ops amba_pl011_pops = {
.tx_empty = pl011_tx_empty,
.set_mctrl = pl011_set_mctrl,
.get_mctrl = pl011_get_mctrl,
.stop_tx = pl011_stop_tx,
.start_tx = pl011_start_tx,
.stop_rx = pl011_stop_rx,
.enable_ms = pl011_enable_ms,
.break_ctl = pl011_break_ctl,
.startup = pl011_startup,
.shutdown = pl011_shutdown,
.flush_buffer = pl011_dma_flush_buffer,
.set_termios = pl011_set_termios,
.type = pl011_type,
.release_port = pl011_release_port,
.request_port = pl011_request_port,
.config_port = pl011_config_port,
.verify_port = pl011_verify_port,
.poll_init = pl011_hwinit,
.poll_get_char = pl011_get_poll_char,
.poll_put_char = pl011_put_poll_char };如您所见,其中没有字符发送操作,即没有列出pl011_tx_chars()函数。由于pl011_tx_chars()是声明为静态的,所以它不会暴露在模块之外。我发现,在模块中,只从pl011_int()函数调用它,该函数是一个中断处理程序。每当发生UART011_TXIS时,都会调用它:
if (status & UART011_TXIS) pl011_tx_chars(uap);函数pl011_tx_chars()本身将字符从循环缓冲区写入UART01x_DR端口,直到达到fifo队列大小(函数返回,以便在下一个中断时写入更多数据)或直到循环缓冲区为空(然后调用pl011_stop_tx())。正如我们所看到的,AMBA端口操作中列出了pl011_start_tx()和pl011_stop_tx() (因此,尽管它们具有本地静态声明,它们还是可以作为回调来调用)。似乎是合理的,事情是,这两个功能做一些非常简单的事情:
static void pl011_stop_tx(struct uart_port *port)
{
struct uart_amba_port *uap = (struct uart_amba_port *)port;
uap->im &= ~UART011_TXIM;
writew(uap->im, uap->port.membase + UART011_IMSC);
pl011_dma_tx_stop(uap);
}
static void pl011_start_tx(struct uart_port *port)
{
struct uart_amba_port *uap = (struct uart_amba_port *)port;
if (!pl011_dma_tx_start(uap)) {
uap->im |= UART011_TXIM;
writew(uap->im, uap->port.membase + UART011_IMSC);
}
}由于我没有CONFIG_DMA_ENGINE集,所以pl011_dma_tx_start()和pl011_dma_tx_stop()只是存根:
static inline void pl011_dma_tx_stop(struct uart_amba_port *uap)
{
}
static inline bool pl011_dma_tx_start(struct uart_amba_port *uap)
{
return false;
}似乎pl011_start_tx()所做的唯一事情就是解除UART011_TXIM中断,而pl011_stop_tx()所做的唯一事情就是解除它。没有什么能启动传输!
我查看了serial_core.c --它是调用start_tx操作的唯一文件,有四个地方(通过注册的回调)。最容易混淆的地方是uart_write()函数。它用数据填充循环缓冲区,并调用本地静态uart_start()函数,这非常简单:
static void __uart_start(struct tty_struct *tty)
{
struct uart_state *state = tty->driver_data;
struct uart_port *port = state->uart_port;
if (!uart_circ_empty(&state->xmit) && state->xmit.buf &&
!tty->stopped && !tty->hw_stopped)
port->ops->start_tx(port);
}
static void uart_start(struct tty_struct *tty)
{
struct uart_state *state = tty->driver_data;
struct uart_port *port = state->uart_port;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
__uart_start(tty);
spin_unlock_irqrestore(&port->lock, flags);
}如您所见,没有人将初始字符发送到UART端口,循环缓冲区被填充,所有东西都在等待UART011_TXIS中断。武装UART011_TXIM中断是否有可能立即发射UART011_TXIS?我查看了DDI0183.pdf (PrimeCell (PL011)技术参考手册),第3章:程序员模型,第3.4节:中断,第3.4.3小节UARTTXINTR。它说的是:
……
当发生下列事件之一时,发送中断将更改状态:
·如果启动了FIFO,并且发送FIFO达到编程触发级别。当发生这种情况时,发送中断被断言为很高。传输中断是通过将数据写入发送FIFO,直到其大于触发级别,或者通过清除中断来清除的。
如果FIFO被禁用(深度为一个位置),并且在发射机的单个位置中没有数据,则发送中断被断言为很高。它是通过对发送FIFO执行一次写入或通过清除中断来清除的。
……
下面的说明更加有趣:
……传输中断是基于通过一个级别的转换,而不是基于级别本身。当中断和UART在任何数据被写入发送FIFO之前启用时,中断不会被设置。中断只设置一次写入数据离开发送FIFO的单一位置,它变成了空。……
上面的重点是我的。我不知道我的英语是否不够,但是从上面的单词中我找不到它所说的解锁传输中断可以用来触发传输例程。我遗漏了什么?
发布于 2014-07-16 05:06:41
ARM的医生说PL011是一个“16550”的UART。这类方法可以使它们脱离用于完全指定其行为的钩子,而是将您发送到16550文档,该文档位于"FIFO中断模式操作“部分.
当启用XMIT和发射机中断(FCR0e1,IER1e1)时,XMIT中断将发生如下情况: A.发射机保持寄存器中断(02)发生在XMIT为空时;一旦将发射机保持寄存器写入(在服务此中断时可将1至16个字符写入XMIT )或读取IIR,则立即清除该中断。
因此,如果FIFO和TX保持寄存器为空,并且启用了TX中断,则应立即看到启动发送进程并填充保存寄存器和FIFO的TX中断。一旦这些漏回低于FIFO触发器,然后将产生另一个中断,以保持进程持续,只要有更多的缓冲数据要发送。
https://stackoverflow.com/questions/22439247
复制相似问题