您的位置:首页 > 运维架构 > Linux

Linux多串口驱动

2015-12-03 14:30 681 查看
问题描述:机器集成了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闭源目前没有证据证明该系统采用的是中断模式,故可能采用的是轮询模式。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: