linux - amba-pl011 Linux 内核模块中 pl011_tx_chars() 背后的逻辑不清楚

标签 linux linux-kernel kernel-module uart interrupt-handling

我想了解 AMBA 串行端口 (amba-pl011.c) 的 Linux 驱动程序如何在非 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() 是然后调用)。如我们所见,pl011_start_tx() 和 pl011_stop_tx() 列在 AMBA 端口操作中(因此尽管它们的本地静态声明,它们仍可作为回调调用)。看起来很合理,事实是,这两个函数做的事情非常简单:

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() 只是 stub :

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® UART (PL011) 技术引用手册),第 3 章:程序员模型,第 3.4 节:中断,第 3.4.3 小节 UARTTXINTR。它说的是:

....

当以下事件之一发生时,发送中断改变状态:

• 如果启用 FIFO 并且发送 FIFO 达到编程触发 等级。发生这种情况时,发送中断被置为高电平。传输 通过将数据写入发送 FIFO 直到它变得更大来清除中断 高于触发电平,或通过清除中断。

• 如果 FIFO 被禁用(具有一个位置的深度)并且没有数据 存在于发送器的单一位置,发送中断被置为高电平。 通过对发送 FIFO 执行单次写入或清除 打断。

....

下面的注释更有趣:

.... 传输中断是基于通过一个级别的转换,而不是基于级别 本身。当中断和 UART 在任何数据写入之前被启用 发送 FIFO 中断未设置。 只有写入数据离开时才会设置中断 发送 FIFO 的单个位置,它变为空。 ....

上面的强调是我的。不知道是不是我的英文不够好,从上面的文字中我找不到哪里说解锁传输中断可以用来触发传输例程。我错过了什么?

最佳答案

ARM 文档说 PL011 是“16550-ish”UART。这种方式让他们摆脱了完全指定其行为的束缚,而是将您发送到 16550 文档,该文档在“FIFO 中断模式操作”部分中说明...

When the XMIT FIFO and transmitter interrupts are enabled (FCR0e1, IER1e1), XMIT interrupts will occur as follows: A. The transmitter holding register interrupt (02) occurs when the XMIT FIFO is empty; it is cleared as soon as the transmitter holding register is written to (1 to 16 characters may be written to the XMIT FIFO while servicing this interrupt) or the IIR is read.

因此,如果 FIFO 和 TX 保持寄存器为空并且您启用 TX 中断,您应该立即看到 TX 中断启动发送过程并填充保持寄存器,然后填充 FIFO。一旦这些数据流回到 FIFO 触发器以下,只要有更多缓冲数据要发送,就会产生另一个中断以保持进程继续进行。

关于linux - amba-pl011 Linux 内核模块中 pl011_tx_chars() 背后的逻辑不清楚,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22439247/

相关文章:

linux - 强制 Linux 库驻留在物理 RAM 中?

Php 到 Php 参数以不同的路径发送

c++ - 错误 : argument of type "void (opca_hello::)()" does not match "void* (*)(void*)"

linux - 我需要构建什么才能直接访问 InfiniBand HCA 端口并注入(inject) IPoIB 帧位?

makefile - 使 [1] : ***/lib/modules//build/: No such file or directory. 停止

c - 键盘中断处理程序给出空值

linux - 为什么进程在 Linux 内核中忙于循环时被剥夺 CPU 的时间太长?

linux - 如何使用 dropbox 为 Windows 和 Linux 设置通用的 Maven 存储库?

linux - 构建内核补丁 : Error MODPOST 0

python - 使python进程写入立即安排回写而不被标记为脏