首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

linux UART串口驱动开发文档 05

linux UART串口驱动开发文档 05

看到这里,不得不再多注意一个结构,那就是irq_cpustat_t, 先前我们讲是否有软中断产生的标志位,但没有提到__softirq_pending,这个变量就是记载32个软中断是否产生的标志,每一个软中断对应一个位; 在中断执行的do_softirq中有如下几个重要的动作,说明如下:
    in_interrupt判断是否可以进行软中断处理,判断的条件就是没有没处在硬件中断环境中,而且还没有软中断正在执行(即不允许软中断嵌套),软中断的嵌套避免是通过local_bh_disable()/local_bh_enable()实现,至于带有bh,其意也即指softirq是中断底半(bh), 在处理硬件中断时,一进行即会调用irq_enter来表示已经进入硬件中断处理程序,处理完硬件中断后再调用irq_exit表示已经完成处理;
    pending判断是否有软中断须要处理, 每个位用作当作一个软中断是否产生的标志。
    清除所有软中断标志位,因为下面即将处理; 但清除之前先缓存起来, 因为下面还要使用这个变量一次。
    在进入软中断处理后,会关闭bh功能的执行,执行完后才打开,这样在in_interrupt判断当中就会直接发现已经有bh在执行,不会再次进入bh执行了,这严格保证了bh执行的串行化。
    打开硬件中断,让软中断在有硬件中断的环境下执行。
    处理完软中断后关闭硬中断,再次检测是否有新的软中断产生,如果有的话,却只须立即处理本次软中断过程未发生过的软中断向量。 之所以会有新的软中断产生,那是因为软中断是在开硬件中断的情况下执行,硬件中断处理是可能又产生了新的软中断。 之所以只处理本次软中断未发生的软中断向量,依据我自己的理解,其目的是为了不加重软中断处理的负担而不马上处理,只是相应的唤醒一个wakeup_softirqd线程,这是专门处理软中断的,这样虽然延误了软中断的处理,但避免了在硬中断服务程序中拖延太长的时间。[关于软中断的处理在后绪版本变化也很大,可以进一步学习研究,如何使软中断不至影响中断处理效率]
    软中断处理这个函数虽然不长,但是相当的关键,每一句代码都很重要,结合上面所说的几点,与源码交互起来理解才能根本理解软中断的设计机制:
    asmlinkage void do_softirq()
    {
    int cpu = smp_processor_id();
    __u32 pending;
    unsigned long flags;
    __u32 mask;
    if (in_interrupt()) return;
    local_irq_save(flags);
    pending = softirq_pending(cpu);
    if (pending) {
    struct softirq_action *h;
    mask = ~pending;
    local_bh_disable();
    restart:
    /* Reset the pending bitmask before enabling irqs */
    softirq_pending(cpu) = 0;
    local_irq_enable();
    h = softirq_vec;
    do {
    if (pending & 1)
    h->action(h);
    h++;
    pending 》= 1;
    } while (pending);
    local_irq_disable();
    pending = softirq_pending(cpu);
    if (pending & mask) {
    mask &= ~pending;
    goto restart;
    }
    __local_bh_enable();
    if (pending)
    wakeup_softirqd(cpu);
    }
    local_irq_restore(flags);
    }
    }
    四。 TTY与串口的具体关联。
    串口设备可以当作TTY终端来使用,这又使串口设备比一般的设备稍微复杂一些,因为他还必须与终端驱动关联起来,虽然这部分与TTY的关联已经是属于公用部分的代码,并不须要驱动编写者特别做些什么来进行支持,但对它与TTY的层次关联的了解有助于理解整个串口的数据流向。
    串口要能够成为终端,必须客外加入终端注册及初始化的代码,这部分很简单,基本上所有的串口驱动都是固定的模式,并无什么修改,主要包括如下结构:
    static struct console cs_amba_console = {
    .name = “ttyBM”,
    .write = w83697uart_console_write,
    .device = w83697uart_console_device,
    .setup = w83697uart_console_setup,
    .flags = CON_PRINTBUFFER,
    .index = -1,
    };
    串口终端的注册通过下面的函数,将cs_amba_console注册成为终端, 这个函数调用路径是:
    start_kernel()→console_init()→ep93xxuart_w83697_console_init()
    void __init ep93xxuart_w83697_console_init(void)
    终端会对应一种具体设备的driver, 相对于串口这个结构是uart_driver, 在驱动中我们已经提供了一个这样的结构。 static struct uart_driver amba_reg, uart_register_driver会将它注册成为终端对应的driver, 因此真正串口与终端的关联就在此处建立。
    函数: static int __init w83697uart_init(void)
    描述: 调用uart_register_driver()完成串口与终端的关联,将串口注册成为一种TTY设备,在uart_register_driver()当中调用tty_register_driver()完成TTY设备注册; 其次是完成串口port口的注册,将静态描述的所有串口port(结构为struct uart_port)注册到uart_driver当中。
    特别说明: 注册串口TTY设备时,由于历史的原因会注册两个TTY设备,一个是normal, 另一个是callout, 是两个设备来的, 在我们这里两者没有什么差别,请看源码中的注解:
    .normal_name = “ttyBM”,
    .callout_name = “cuaam”,
    /*
    * The callout device is just like the normal device except for
    * the major number and the subtype code.
    */
    函数: static void __exit w83697uart_exit(void)
    描述: 卸截设备,卸截port口,因为我编译的驱动是与内核绑定在一起的,因此实际上根本不会调用此函数。
返回列表