跳到主要内容

15_编写虚拟UART驱动程序/_实现数据传输

编写虚拟UART驱动程序/_实现数据传输

  • 参考代码

    硬件相关:
    drivers/tty/serial/imx.c
    drivers/tty/serial/stm32-usart.c

    串口核心层:
    drivers/tty/serial/serial_core.c

    TTY层:
    drivers/tty/tty_io.c
  • 本节课程源码在GIT仓库里

    doc_and_source_for_drivers\IMX6ULL\source\09_UART
    06_virtual_uart_driver_txrx
    doc_and_source_for_drivers\STM32MP157\source\A7\09_UART
    06_virtual_uart_driver_txrx

1. 虚拟UART的驱动组成

image-20210730142813809

2. 虚拟UART的数据流程

image-20210730150445225

为了做实验,我们还要创建一个虚拟文件:/proc/virt_uart_buf

  • 要发数据给虚拟串口时,执行:echo "xxx" > /proc/virt_uart_buf
  • 要读取虚拟串口的数据时,执行:cat /proc/virt_uart_buf

3. 实现/proc文件

参考/proc/cmdline,怎么找到它对应的驱动?在Linux内核源码下执行以下命令搜索:

grep "cmdline" * -nr | grep proc

得到:

fs/proc/cmdline.c:26: proc_create("cmdline", 0, NULL, &cmdline_proc_fops);

4. 触发中断

使用如下函数:

int irq_set_irqchip_state(unsigned int irq, enum irqchip_irq_state which,
bool val);

怎么找到它的?在中断子系统中,我们知道往GIC寄存器GICD_ISPENDRn写入某一位就可以触发中断。内核代码中怎么访问这些寄存器? 在drivers/irqchip/irq-gic.c中可以看到irq_chip中的"irq_set_irqchip_state"被用来设置中断状态:

static struct irq_chip gic_chip = {
.irq_mask = gic_mask_irq,
.irq_unmask = gic_unmask_irq,
.irq_eoi = gic_eoi_irq,
.irq_set_type = gic_set_type,
.irq_get_irqchip_state = gic_irq_get_irqchip_state,
.irq_set_irqchip_state = gic_irq_set_irqchip_state, /* 2. 继续搜"irq_set_irqchip_state" */
.flags = IRQCHIP_SET_TYPE_MASKED |
IRQCHIP_SKIP_SET_WAKE |
IRQCHIP_MASK_ON_SUSPEND,
};

static int gic_irq_set_irqchip_state(struct irq_data *d,
enum irqchip_irq_state which, bool val)
{
u32 reg;

switch (which) {
case IRQCHIP_STATE_PENDING:
reg = val ? GIC_DIST_PENDING_SET : GIC_DIST_PENDING_CLEAR; /* 1. 找到寄存器 */
break;

case IRQCHIP_STATE_ACTIVE:
reg = val ? GIC_DIST_ACTIVE_SET : GIC_DIST_ACTIVE_CLEAR;
break;

case IRQCHIP_STATE_MASKED:
reg = val ? GIC_DIST_ENABLE_CLEAR : GIC_DIST_ENABLE_SET;
break;

default:
return -EINVAL;
}

gic_poke_irq(d, reg);
return 0;
}

继续搜"irq_set_irqchip_state",在drivers/irqchip/irq-gic.c中可以看到:

int irq_set_irqchip_state(unsigned int irq, enum irqchip_irq_state which,
bool val)
{
......
}

EXPORT_SYMBOL_GPL(irq_set_irqchip_state);

以后就可与使用如下代码触发某个中断:

irq_set_irqchip_state(irq, IRQCHIP_STATE_PENDING, 1);