Linux多串口驱动

来源:互联网 发布:sinx幂级数展开知乎 编辑:程序博客网 时间:2024/06/07 03:06
问题描述:机器集成了13个串口,linux操作系统启动后,只能识别到4个串口且无法使用。
是否解决:已基本解决。
问题分析:
    经测试,该ATM定制机使用的串口控制芯片为通用串口控制器8250系列的16550A型号。该芯片内置3根地址线寻址范围达8字节,具有10个可编程寻址寄存器(通过地址复用),支持4级中断控制,收发双缓冲寄存器,拥有16字节fifo缓存。
    8250系列芯片默认的IO寄存器基地址以及中断号分布如下:
    /dev/ttyS0 (COM1), port 0x3f8, irq 4
    /dev/ttyS1 (COM2), port 0x2f8, irq 3
    /dev/ttyS2 (COM3), port 0x3e8, irq 4
    /dev/ttyS3 (COM4), port 0x2e8, irq 3
    但是**ATM机没有按照默认约定部署板级芯片,而是采用以下的方式分配IO寄存器地址:
    0x2E8,     /* COM1*/    
    0x3E8,     /* COM2 */    
    0x240,     /* COM3 */    
    0x248,     /* COM4 */    
    0x250,     /* COM5 */    
    0x258,     /* COM6 */    
    0x260,     /* COM7 */    
    0x268,     /* COM8 */    
    0x270,     /* COM9 */    
    0x278,     /* COM10 */    
    0x2F0,     /* COM11 */    
    0x2F8,     /* COM12 */        /* BIOS 中默认关闭 */
    0x3F8,     /* COM13 */        /* BIOS 中默认关闭 */
    而linux内核采用8250系列芯片约定的IO基地址进行配置,且默认只支持创建最多4个串口设备:
 
    ===================  _ASM_X86_SERIAL_H ==================
     #define SERIAL_PORT_DFNS                \
     /* UART    CLK    PORT    IRQ    FLAGS                */    \
    { .uart = 0,  BASE_BAUD,  0x3F8,  4,  STD_COMX_FLAGS }, /* ttyS0 */    \
    { .uart = 0,  BASE_BAUD,  0x2F8,  3,  STD_COMX_FLAGS }, /* ttyS1 */    \
    { .uart = 0,  BASE_BAUD,  0x3E8,  4,  STD_COMX_FLAGS }, /* ttyS2 */    \
    { .uart = 0,  BASE_BAUD,  0x2E8,  3,  STD_COM4_FLAGS  }, /* ttyS3 */


    ===================== MenuConfig ===========================
    [*]   Console on 8250/16550 and compatible serial port
    [*]   DMA support for 16550 compatible UART controllers 
    <*>   8250/16550 PCI device support 
    <M>   8250/16550 PCMCIA device support                
    (4)  Maximum number of 8250/16550 serial ports       
    (4)  Number of 8250/16550 serial ports to register at runtime 
    [*]   Extended 8250/16550 serial driver options      
    [*]     Support more than 4 legacy serial ports             
        < >       Support Fourport cards                          
        < >       Support Accent cards         
    因此linux默认无法操作识别全部13个串口且识别出的4个串口也因IObase地址错误无法工作,除非使用串口工具(linux下使用setserial)修改COM口对应的ioport及irq至正确方可。              

解决方案:
    1.根据以上分析测试修改linux内核配置,并更正x86平台下8250芯片的serial寄存器地址,然后重新编译打包内核。经测试已经能够识别全部的13个串口:
/dev/ttyS0, Line 0, UART: 16550A, Port: 0x02e8, IRQ: 7
    Baud_base: 115200, close_delay: 50, divisor: 0
    closing_wait: 3000
    Flags: spd_normal skip_test

/dev/ttyS1, Line 1, UART: 16550A, Port: 0x03e8, IRQ: 7
    Baud_base: 115200, close_delay: 50, divisor: 0
    closing_wait: 3000
    Flags: spd_normal skip_test

/dev/ttyS2, Line 2, UART: 16550A, Port: 0x0240, IRQ: 10
    Baud_base: 115200, close_delay: 50, divisor: 0
    closing_wait: 3000
    Flags: spd_normal skip_test

/dev/ttyS3, Line 3, UART: 16550A, Port: 0x0248, IRQ: 4
    Baud_base: 115200, close_delay: 50, divisor: 0
    closing_wait: 3000
    Flags: spd_normal skip_test

/dev/ttyS4, Line 4, UART: 16550A, Port: 0x0250, IRQ: 10
    Baud_base: 115200, close_delay: 50, divisor: 0
    closing_wait: 3000
    Flags: spd_normal skip_test

/dev/ttyS5, Line 5, UART: 16550A, Port: 0x0258, IRQ: 10
    Baud_base: 115200, close_delay: 50, divisor: 0
    closing_wait: 3000
    Flags: spd_normal skip_test

/dev/ttyS6, Line 6, UART: 16550A, Port: 0x0260, IRQ: 11
    Baud_base: 115200, close_delay: 50, divisor: 0
    closing_wait: 3000
    Flags: spd_normal skip_test

/dev/ttyS7, Line 7, UART: 16550A, Port: 0x0268, IRQ: 11
    Baud_base: 115200, close_delay: 50, divisor: 0
    closing_wait: 3000
    Flags: spd_normal skip_test

/dev/ttyS8, Line 8, UART: 16550A, Port: 0x0270, IRQ: 11
    Baud_base: 115200, close_delay: 50, divisor: 0
    closing_wait: 3000
    Flags: spd_normal skip_test

/dev/ttyS9, Line 9, UART: 16550A, Port: 0x0278, IRQ: 11
    Baud_base: 115200, close_delay: 50, divisor: 0
    closing_wait: 3000
    Flags: spd_normal skip_test

/dev/ttyS10, Line 10, UART: 16550A, Port: 0x02f0, IRQ: 3
    Baud_base: 115200, close_delay: 50, divisor: 0
    closing_wait: 3000
    Flags: spd_normal skip_test

/dev/ttyS11, Line 11, UART: 16550A, Port: 0x02f8, IRQ: 7
    Baud_base: 115200, close_delay: 50, divisor: 0
    closing_wait: 3000
    Flags: spd_normal skip_test

/dev/ttyS12, Line 12, UART: 16550A, Port: 0x03f8, IRQ: 7
    Baud_base: 115200, close_delay: 50, divisor: 0
    closing_wait: 3000
    Flags: spd_normal skip_test

但是识别出的串口依然无法正常工作。

    2.接下来使用串口工具将串口irq重设为irq0,则对应的串口可以正常工作。接下来分析内核源码:
=========== Data Structure ===========
up-port.handle_irq = serial8250_default_handle_irq;

static struct uart_ops serial8250_pops = {
    .startup    = serial8250_startup,
    ....
        .....
}

========== Function Call ============
serial8250_startup() -->
    serial8250_do_startup()

---------------------- request IRQ ----------------
    /*
     * If the "interrupt" for this port doesn't correspond with any
     * hardware interrupt, we use a timer-based system.  The original
     * driver used to do this with IRQ0.
     */
    if (!port->irq) {
        up->timer.data = (unsigned long)up;
        mod_timer(&up->timer, jiffies + uart_poll_timeout(port));
    } else {
        retval = serial_link_irq_chain(up);
        if (retval)
            goto out;
    }


serial_link_irq_chain(up) -->
    request_irq(up->port.irq, serial8250_interrupt,


up->timer.function = serial8250_timeout;

------------------------ IRQ Handler ---------------------------
/* whether we have or not IRQ, have the same result */

serial8250_interrupt() -->
    up->port.handle_irq(&up->port) == serial8250_default_handle_irq() -->
        serial8250_handle_irq(port, iir) -->
            serial8250_rx_chars(up, status);


serial8250_timeout() --> 
    up->port.handle_irq(&up->port) == serial8250_default_handle_irq() -->
        serial8250_handle_irq(port, iir) -->
            serial8250_rx_chars(up, status);

    可见,将串口irq重置为0实际上将串口的工作模式由中断模式变更为轮询模式(实际也是中断模式,在时钟中断控制下轮询串口),不会影响其他设备工作。缺点是可能会导致CPU占用率稍微高一些,但经过实际测试未见明显差异。
    由于轮询模式与中断模式使用的是同一个处理函数操作串口,中断模式下串口无法工作,轮询模式可以工作,因此可以确定串口的ioport等寄存器设置没有问题。
    接下来将串口irq重设为irq1(键盘),发现没有按键触发时串口无法接受数据,当连续按键(连续触发irq1)时串口可已正常工作。结合内核源码测试证明中断处理函数的注册没有问题。后续又测试了irq12(鼠标)/irq23(USB)等多个中断号均证明该结论。
    接下来通过继续分析源码以及各种修改测试均发现8050的IER(中断使能寄存器)设置正常(意味着CPU允许8250控制芯片触发中断),但是CPU依然无法接收来自8250控制器的中断。

问题结论:
    目前通过修改内核驱动增加轮询机制使得系统可以正确识别所有13个串口并能正常工作。
    但是8250中断触发问题依然没有妥善解决,经分析怀疑是芯片型号差异(即便主板使用的不是16550A,但是内核依然可能显示16550A)或板级线路设计问题,故需要该机器的主板电路原理图以作进一步分析。
    至于windows XP能够正常工作,由于windows闭源目前没有证据证明该系统采用的是中断模式,故可能采用的是轮询模式。
0 0