linux设备驱动之8250串口驱动

更新时间:2024-06-11 00:20:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

linux设备驱动之8250串口驱动 一:前言

前一段时间自己实践了一下8250芯片串口驱动的编写。今天就在此基础上分析一下linux kernel自带的串口驱动。毕竟只有对比专业的驱动代码才能更好的进步,同以往一样,基于linix kernel2.6.25.相应驱动代码位于:linux-2.6.25/drivers/serial/8250.c。 二:8250串口驱动初始化

相应的初始化函数为serial8250_init().代码如下: static int __init serial8250_init(void) {

int ret, i;

if (nr_uarts > UART_NR) nr_uarts = UART_NR;

printk(KERN_INFO \ \ share_irqs ? \

for (i = 0; i < NR_IRQS; i++) spin_lock_init(&irq_lists[i].lock);

ret = uart_register_driver(&serial8250_reg); if (ret)

goto out;

serial8250_isa_devs = platform_device_alloc(\ PLAT8250_DEV_LEGACY); if (!serial8250_isa_devs) { ret = -ENOMEM; goto unreg_uart_drv; }

ret = platform_device_add(serial8250_isa_devs); if (ret)

goto put_dev;

serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev);

ret = platform_driver_register(&serial8250_isa_driver); if (ret == 0)

goto out;

platform_device_del(serial8250_isa_devs);

put_dev:

platform_device_put(serial8250_isa_devs); unreg_uart_drv:

uart_unregister_driver(&serial8250_reg); out:

return ret;

}

这段代码涉及到的知识要求,如platform ,uart等我们在之前都已经做过详细的分析。这里不再重复。在代码中UART_NR:表示串口的个数。这个参数在编译内核的时候可以自己配置,默认为32。

我们按照代码中的流程一步一步进行研究。 1:注册uart_driver.

对应uart-driver的结构为serial8250_reg.定义如下: static struct uart_driver serial8250_reg = { .owner = THIS_MODULE, .driver_name = \ .dev_name = \

.major = TTY_MAJOR, .minor = 64, .nr = UART_NR,

.cons = SERIAL8250_CONSOLE, };

TTY_MAJOR定义如下:

#define TTY_MAJOR 4

从上面可以看出。串口对应的设备节点为/dev/ ttyS0 ~ /dev/ ttyS0(UART_NR).设备节点号为(4。64)起始的UART_NR个节点..

2:初始化并注册platform_device 相关代码如下:

serial8250_isa_devs = platform_device_alloc(\ PAT8250_DEV_LEGACY); platform_device_add(serial8250_isa_devs);

可以看出。serial8250_isa_devs.->name为serial8250。这个参数是在匹配platform_device和platform_driver使用的.

3:为uart-driver添加port. 相关代码如下:

serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev) 跟进这个函数看一下: static void __init

serial8250_register_ports(struct uart_driver *drv, struct device *dev) {

int i;

serial8250_isa_init_ports();

for (i = 0; i < nr_uarts; i++) {

struct uart_8250_port *up = &serial8250_ports[i];

up->port.dev = dev;

uart_add_one_port(drv, &up->port); }

}

在这里函数里,初始化了port.然后将挂添加到uart-driver中。我们还注意到。生成的deivce节点,在sysfs中是位于platform_deivce对应目录的下面. serial8250_isa_init_ports()代码如下所示: static void __init serial8250_isa_init_ports(void) {

struct uart_8250_port *up; static int first = 1; int i;

if (!first) return; first = 0;

for (i = 0; i < nr_uarts; i++) {

struct uart_8250_port *up = &serial8250_ports[i];

up->port.line = i;

spin_lock_init(&up->port.lock);

init_timer(&up->timer);

up->timer.function = serial8250_timeout;

/*

* ALPHA_KLUDGE_MCR needs to be killed. */

up->mcr_mask = ~ALPHA_KLUDGE_MCR; up->mcr_force = ALPHA_KLUDGE_MCR;

up->port.ops = &serial8250_pops; }

for (i = 0, up = serial8250_ports;

i < ARRAY_SIZE(old_serial_port) && i < nr_uarts; i++, up++) {

up->port.iobase = old_serial_port[i].port;

up->port.irq = irq_canonicalize(old_serial_port[i].irq);

up->port.uartclk = old_serial_port[i].baud_base * 16;

up->port.flags = old_serial_port[i].flags; up->port.hub6 = old_serial_port[i].hub6;

up->port.membase = old_serial_port[i].iomem_base; up->port.iotype = old_serial_port[i].io_type;

up->port.regshift = old_serial_port[i].iomem_reg_shift; if (share_irqs)

up->port.flags |= UPF_SHARE_IRQ; }

}

在这里,我们关注一下注要成员的初始化。Uart_port的各项操作位于serial8250_pops中.iobase irq等成员是从old_serial_por这个结构中得来的,这个结构如下所示: static const struct old_serial_port old_serial_port[] = { SERIAL_PORT_DFNS /* defined in asm/serial.h */ }

#define SERIAL_PORT_DFNS

/* UART CLK PORT IRQ FLAGS */ { 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS }, /* ttyS0 */ { 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS }, /* ttyS1 */ { 0, BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS }, /* ttyS2 */

{ 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS }, /* ttyS3 */

从上面看到。前两项对应了com1 com2的各项参数。如寄存器首始地址,Irq号等。后面两项不太清楚。

在上面的代码中,我们看到了uart_port各项成员的初始化。在后面很多操作中需要用到这个成员。我们等分析相关部份的时候,再到这个地方来看相关成员的值。

4:注册platform_driver 相关代码如下:

platform_driver_register(&serial8250_isa_driver); serial8250_isa_driver定义如下:

static struct platform_driver serial8250_isa_driver = { .probe = serial8250_probe,

.remove = __devexit_p(serial8250_remove), .suspend = serial8250_suspend,

.resume = serial8250_resume, .driver = {

.name = \ .owner = THIS_MODULE, }, }

为了以后把分析集中到具体的驱动部份.我们先把这个platform_driver引会的事件讲述完. 经过前面有关platform的分析我们知道.这个platform的name为” serial8250”.刚好跟前面

注册的platform_device相匹配.会调用platform_driver-> probe.在这里,对应的接口为: serial8250_probe().代码如下:

static int __devinit serial8250_probe(struct platform_device *dev) {

struct plat_serial8250_port *p = dev->dev.platform_data; struct uart_port port; int ret, i;

memset(&port, 0, sizeof(struct uart_port));

for (i = 0; p && p->flags != 0; p++, i++) { port.iobase = p->iobase; port.membase = p->membase; port.irq = p->irq; port.uartclk = p->uartclk; port.regshift = p->regshift; port.iotype = p->iotype; port.flags = p->flags;

port.mapbase = p->mapbase; port.hub6 = p->hub6;

port.private_data = p->private_data; port.dev = &dev->dev; if (share_irqs)

port.flags |= UPF_SHARE_IRQ; ret = serial8250_register_port(&port);

if (ret < 0) {

dev_err(&dev->dev, \ \

p->iobase, (unsigned long long)p->mapbase, p->irq, ret); } }

return 0; }

从上述代码可以看出.会将dev->dev.platform_data所代表的port添加到uart_driver中.这个dev->dev.platform_data究竟代表什么.我们在看到的时候再来研究它. 现在,我们把精力集中到uart_port的操作上.

三:config_port过程

在初始化uart_port的过程中,在以下代码片段: serial8250_isa_init_ports(void) {

…… ……

for (i = 0, up = serial8250_ports;

i < ARRAY_SIZE(old_serial_port) && i < nr_uarts; i++, up++) {

up->port.iobase = old_serial_port[i].port;

up->port.irq = irq_canonicalize(old_serial_port[i].irq); up->port.uartclk = old_serial_port[i].baud_base * 16; up->port.flags = old_serial_port[i].flags; up->port.hub6 = old_serial_port[i].hub6;

up->port.membase = old_serial_port[i].iomem_base; up->port.iotype = old_serial_port[i].io_type; up->port.regshift = old_serial_port[i].iomem_reg_shift; if (share_irqs)

up->port.flags |= UPF_SHARE_IRQ; }

}

而old_serial_port又定义如下:

static const struct old_serial_port old_serial_port[] = { SERIAL_PORT_DFNS /* defined in asm/serial.h */ };

#define SERIAL_PORT_DFNS

/* UART CLK PORT IRQ FLAGS */

{ 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS }, /* ttyS0 */ { 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS }, /* ttyS1 */ { 0, BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS }, /* ttyS2 */ { 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS }, /* ttyS3 */

由此可见.port->flags被定义成了STD_COM_FLAGS,定义如下:

#ifdef CONFIG_SERIAL_DETECT_IRQ

#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ)

#define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_AUTO_IRQ) #else

#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST) #define STD_COM4_FLAGS ASYNC_BOOT_AUTOCONF

#endif

从这里看到,不管是否自己探测IRQ,都会定义ASYNC_BOOT_AUTOCONF.这样,在uart_add_one_port()的时候.就会进入到port->config_port来配置端口.在8250中,对应的接口为: serial8250_config_port().代码如下:

static void serial8250_config_port(struct uart_port *port, int flags) {

struct uart_8250_port *up = (struct uart_8250_port *)port; int probeflags = PROBE_ANY; int ret;

/*

* Find the region that we can probe for. This in turn * tells us whether we can probe for the type of port. */

ret = serial8250_request_std_resource(up); if (ret < 0) return;

ret = serial8250_request_rsa_resource(up); if (ret < 0)

probeflags &= ~PROBE_RSA;

if (flags & UART_CONFIG_TYPE) autoconfig(up, probeflags);

if (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ) autoconfig_irq(up);

if (up->port.type != PORT_RSA && probeflags & PROBE_RSA) serial8250_release_rsa_resource(up); if (up->port.type == PORT_UNKNOWN) serial8250_release_std_resource(up);

}

serial8250_request_std_resource和serial8250_request_rsa_resource都是分配操作的端口.回顾在前面的分析中.port的相关参数会从old_serial_port中取得.而old_serial_port中又没有定义port->iotype和port-> regshift.也就是说对应这两项全为0.而 #define UPIO_PORT (0) 即表示是要操作I/O端口.

自己阅读这两个函数代表.会发现在serial8250_request_rsa_resource()中是会返回失败的. 另外,在uart_add_one_port()在进行端口匹配时,会先置flags为UART_CONFIG_TYPE.

这样,在本次操作中, if (flags & UART_CONFIG_TYPE)是会满足的.相应的就会进入autoconfig().

代码如下,这段代码比较长,分段分析如下:

static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) {

unsigned char status1, scratch, scratch2, scratch3; unsigned char save_lcr, save_mcr; unsigned long flags;

if (!up->port.iobase && !up->port.mapbase && !up->port.membase) return;

DEBUG_AUTOCONF(\ up->port.line, up->port.iobase, up->port.membase);

/*

* We really do need global IRQs disabled here - we're going to * be frobbing the chips IRQ enable register to see if it exists. */

spin_lock_irqsave(&up->port.lock, flags);

up->capabilities = 0; up->bugs = 0;

if (!(up->port.flags & UPF_BUGGY_UART)) {

/*

* Do a simple existence test first; if we fail this, * there's no point trying anything else.

*

* 0x80 is used as a nonsense port to prevent against * false positives due to ISA bus float. The * assumption is that 0x80 is a non-existent port; * which should be safe since include/asm/io.h also * makes this assumption. *

* Note: this is safe as long as MCR bit 4 is clear * and the device is in \ */

scratch = serial_inp(up, UART_IER); serial_outp(up, UART_IER, 0); #ifdef __i386__

outb(0xff, 0x080); #endif

/*

* Mask out IER[7:4] bits for test as some UARTs (e.g. TL * 16C754B) allow only to modify them if an EFR bit is set. */

scratch2 = serial_inp(up, UART_IER) & 0x0f; serial_outp(up, UART_IER, 0x0F); #ifdef __i386__

outb(0, 0x080); #endif

scratch3 = serial_inp(up, UART_IER) & 0x0f; serial_outp(up, UART_IER, scratch); if (scratch2 != 0 || scratch3 != 0x0F) { /*

* We failed; there's nothing here

*/

DEBUG_AUTOCONF(\ scratch2, scratch3); goto out; }

}

在这里,先对8250是否存在做一个简单的判断.先将IER中的值取得,这样可以在测试之后恢复IER中的值.然后往IER中写放0.再将IER中的值取出.又往IER中写入0xOF.然后再将IER中的值取出.最后将IER中的值恢复到原值.这样就可以根据写入的值和读出的值是否相等来判断该寄存器是否存在.

save_mcr = serial_in(up, UART_MCR);

save_lcr = serial_in(up, UART_LCR);

在这里,先将MCR和LCR中的值取出.因为在后面的操作中会使用这两个寄存器.方便使用完了恢复 /*

* Check to see if a UART is really there. Certain broken * internal modems based on the Rockwell chipset fail this * test, because they apparently don't implement the loopback * test mode. So this test is skipped on the COM 1 through * COM 4 ports. This *should* be safe, since no board * manufacturer would be stupid enough to design a board * that conflicts with COM 1-4 --- we hope! */

if (!(up->port.flags & UPF_SKIP_TEST)) {

serial_outp(up, UART_MCR, UART_MCR_LOOP | 0x0A); status1 = serial_inp(up, UART_MSR) & 0xF0; serial_outp(up, UART_MCR, save_mcr); if (status1 != 0x90) {

DEBUG_AUTOCONF(\ status1); goto out; }

}

在这里,将MCR的自检位置位,并允许向中断控制器产生中断.而且产生RTS信号.这样MSR寄存器应该可以检测到这个信号.如果没有检测到.自测失败!MCR寄存器已经操作完了,恢复MCR寄存器的原值. /*

* We're pretty sure there's a port here. Lets find out what * type of port it is. The IIR top two bits allows us to find * out if it's 8250 or 16450, 16550, 16550A or later. This * determines what we test for next. *

* We also initialise the EFR (if any) to zero for later. The * EFR occupies the same register location as the FCR and IIR. */

serial_outp(up, UART_LCR, 0xBF); serial_outp(up, UART_EFR, 0); serial_outp(up, UART_LCR, 0);

serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); scratch = serial_in(up, UART_IIR) >> 6;

DEBUG_AUTOCONF(\

switch (scratch) { case 0:

autoconfig_8250(up); break;

case 1:

up->port.type = PORT_UNKNOWN; break; case 2:

up->port.type = PORT_16550; break; case 3:

autoconfig_16550a(up); break;

}

在这里,先允许使用FIFO寄存器,然后通过IIR寄存的高二位来判断芯片的类型

#ifdef CONFIG_SERIAL_8250_RSA /*

* Only probe for RSA ports if we got the region. */

if (up->port.type == PORT_16550A && probeflags & PROBE_RSA) { int i;

for (i = 0 ; i < probe_rsa_count; ++i) { if (probe_rsa[i] == up->port.iobase && __enable_rsa(up)) {

up->port.type = PORT_RSA; break; } } } #endif

#ifdef CONFIG_SERIAL_8250_AU1X00

/* if access method is AU, it is a 16550 with a quirk */

if (up->port.type == PORT_16550A && up->port.iotype == UPIO_AU) up->bugs |= UART_BUG_NOMSR; #endif

serial_outp(up, UART_LCR, save_lcr);

if (up->capabilities != uart_config[up->port.type].flags) { printk(KERN_WARNING

\ up->port.line, up->capabilities, uart_config[up->port.type].flags); }

up->port.fifosize = uart_config[up->port.type].fifo_size; up->capabilities = uart_config[up->port.type].flags; up->tx_loadsz = uart_config[up->port.type].tx_loadsz;

if (up->port.type == PORT_UNKNOWN) goto out;

/*

* Reset the UART.

*/

#ifdef CONFIG_SERIAL_8250_RSA

if (up->port.type == PORT_RSA)

serial_outp(up, UART_RSA_FRR, 0); #endif

serial_outp(up, UART_MCR, save_mcr); serial8250_clear_fifos(up);

serial_in(up, UART_RX);

if (up->capabilities & UART_CAP_UUE)

serial_outp(up, UART_IER, UART_IER_UUE); else

serial_outp(up, UART_IER, 0); out:

spin_unlock_irqrestore(&up->port.lock, flags);

DEBUG_AUTOCONF(\ }

最后,复位串口控制器

我们假设使用的是8250串口芯片.在芯片类型判断的时候就会进入autoconfig_8250().代码如下:

static void autoconfig_8250(struct uart_8250_port *up) {

unsigned char scratch, status1, status2;

up->port.type = PORT_8250;

scratch = serial_in(up, UART_SCR); serial_outp(up, UART_SCR, 0xa5); status1 = serial_in(up, UART_SCR); serial_outp(up, UART_SCR, 0x5a); status2 = serial_in(up, UART_SCR); serial_outp(up, UART_SCR, scratch);

if (status1 == 0xa5 && status2 == 0x5a)

up->port.type = PORT_16450; }

如果存在SCR寄存器,则芯片是16450类型的.这不是我们需要研究的芯片.

回到serial8250_config_port()中,代码片段如下所示:

static void serial8250_config_port(struct uart_port *port, int flags) {

…… ……

if (flags & UART_CONFIG_TYPE) autoconfig(up, probeflags);

if (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ) autoconfig_irq(up);

if (up->port.type != PORT_RSA && probeflags & PROBE_RSA) serial8250_release_rsa_resource(up); if (up->port.type == PORT_UNKNOWN)

serial8250_release_std_resource(up); }

如果定义了自己控测IRQ号(CONFIG_SERIAL_8250_DETECT_IRQ).一般情况下,编译内核的时候一般都将其赋值为CONFIG_SERIAL_8250_DETECT_IRQ = y.此时就会进入autoconfig_irq().代码如下:

static void autoconfig_irq(struct uart_8250_port *up) {

unsigned char save_mcr, save_ier; unsigned char save_ICP = 0; unsigned int ICP = 0; unsigned long irqs; int irq;

if (up->port.flags & UPF_FOURPORT) { ICP = (up->port.iobase & 0xfe0) | 0x1f; save_ICP = inb_p(ICP); outb_p(0x80, ICP); (void) inb_p(ICP); }

/* forget possible initially masked and pending IRQ */ probe_irq_off(probe_irq_on());

save_mcr = serial_inp(up, UART_MCR); save_ier = serial_inp(up, UART_IER);

serial_outp(up, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2);

irqs = probe_irq_on();

serial_outp(up, UART_MCR, 0); udelay(10);

if (up->port.flags & UPF_FOURPORT) { serial_outp(up, UART_MCR,

UART_MCR_DTR | UART_MCR_RTS); } else {

serial_outp(up, UART_MCR,

UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); }

serial_outp(up, UART_IER, 0x0f); /* enable all intrs */ (void)serial_inp(up, UART_LSR); (void)serial_inp(up, UART_RX); (void)serial_inp(up, UART_IIR); (void)serial_inp(up, UART_MSR); serial_outp(up, UART_TX, 0xFF); udelay(20);

irq = probe_irq_off(irqs);

serial_outp(up, UART_MCR, save_mcr); serial_outp(up, UART_IER, save_ier);

if (up->port.flags & UPF_FOURPORT) outb_p(save_ICP, ICP);

up->port.irq = (irq > 0) ? irq : 0;

}

在上述代码的操作中,先将8250相关中断允许寄存器全打开.然后调用驱动使用的函数, 当它不得不探测来决定哪个中断线被设备在使用. probe_irq_on()将中断暂时关掉,然后配置MCR寄存器使之发送DTR和RTS.之后再用probe_irq_off()来检测IRQ号.如果检测成功,则值赋值

给port->irq.

进行到这里,conifg_port动作就完成了.

经过这个config_port过程后,我们发现,并没有对serial8250_isa_devs->dev-> platform_data赋值,也就是说platform_driver->probe函数并无实质性的处理.在第一次for循环的时,就会因条件不符而退出.

四: startup操作

在前面分析uart驱动架构的时候,曾说过,在open的时候,会调用port->startup().在本次分析的驱动中,对应接口为serial8250_startup().分段分析如下: static int serial8250_startup(struct uart_port *port) {

struct uart_8250_port *up = (struct uart_8250_port *)port; unsigned long flags; unsigned char lsr, iir; int retval;

up->capabilities = uart_config[up->port.type].flags; up->mcr = 0;

if (up->port.type == PORT_16C950) { /* Wake up and initialize UART */ up->acr = 0;

serial_outp(up, UART_LCR, 0xBF);

serial_outp(up, UART_EFR, UART_EFR_ECB); serial_outp(up, UART_IER, 0); serial_outp(up, UART_LCR, 0);

serial_icr_write(up, UART_CSR, 0); /* Reset the UART */ serial_outp(up, UART_LCR, 0xBF);

serial_outp(up, UART_EFR, UART_EFR_ECB); serial_outp(up, UART_LCR, 0); }

#ifdef CONFIG_SERIAL_8250_RSA

/*

* If this is an RSA port, see if we can kick it up to the * higher speed clock. */

enable_rsa(up); #endif /*

* Clear the FIFO buffers and disable them. * (they will be reenabled in set_termios()) */

serial8250_clear_fifos(up);

上面的代码都不是对应8250芯片的情况 /*

* Clear the interrupt registers. */

(void) serial_inp(up, UART_LSR); (void) serial_inp(up, UART_RX); (void) serial_inp(up, UART_IIR); (void) serial_inp(up, UART_MSR); 复位LSR,RX,IIR,MSR寄存器

/*

* At this point, there's no way the LSR could still be 0xff; * if it is, then bail out, because there's likely no UART * here.

*/

if (!(up->port.flags & UPF_BUGGY_UART) &&

(serial_inp(up, UART_LSR) == 0xff)) {

printk(\ return -ENODEV; }

若LSR寄存器中的值为0xFF.异常

/*

* For a XR16C850, we need to set the trigger levels */

if (up->port.type == PORT_16850) { unsigned char fctr;

serial_outp(up, UART_LCR, 0xbf);

fctr = serial_inp(up, UART_FCTR) & ~(UART_FCTR_RX|UART_FCTR_TX); serial_outp(up, UART_FCTR, fctr | UART_FCTR_TRGD | UART_FCTR_RX); serial_outp(up, UART_TRG, UART_TRG_96);

serial_outp(up, UART_FCTR, fctr | UART_FCTR_TRGD | UART_FCTR_TX); serial_outp(up, UART_TRG, UART_TRG_96);

serial_outp(up, UART_LCR, 0); }

16850系列芯片的处理,忽略

if (is_real_interrupt(up->port.irq)) { /*

* Test for UARTs that do not reassert THRE when the * transmitter is idle and the interrupt has already * been cleared. Real 16550s should always reassert

* this interrupt whenever the transmitter is idle and * the interrupt is enabled. Delays are necessary to * allow register changes to become visible. */

spin_lock_irqsave(&up->port.lock, flags);

wait_for_xmitr(up, UART_LSR_THRE);

serial_out_sync(up, UART_IER, UART_IER_THRI); udelay(1); /* allow THRE to set */ serial_in(up, UART_IIR);

serial_out(up, UART_IER, 0);

serial_out_sync(up, UART_IER, UART_IER_THRI); udelay(1); /* allow a working UART time to re-assert THRE */ iir = serial_in(up, UART_IIR); serial_out(up, UART_IER, 0);

spin_unlock_irqrestore(&up->port.lock, flags);

/*

* If the interrupt is not reasserted, setup a timer to * kick the UART on a regular basis.

*/

if (iir & UART_IIR_NO_INT) {

pr_debug(\ up->timer.function = serial8250_backup_timeout; up->timer.data = (unsigned long)up; mod_timer(&up->timer, jiffies +

poll_timeout(up->port.timeout) + HZ / 5); } }

如果中断号有效,还要进一步判断这个中断号是否有效.具体操作为,先等待8250发送寄存器空.然后允许发送中断空的中断.然后判断IIR寄存器是否收到中断.如果有没有收到中断,则说明这根中断线无效.只能采用轮询的方式.关于轮询方式,我们在之后再以独立章节的形式给出分析

/*

* If the \ * hardware interrupt, we use a timer-based system. The original * driver used to do this with IRQ0. */

if (!is_real_interrupt(up->port.irq)) {

up->timer.data = (unsigned long)up;

mod_timer(&up->timer, jiffies + poll_timeout(up->port.timeout)); } else {

retval = serial_link_irq_chain(up);

if (retval)

return retval; }

如果没有设置中断号,则采用轮询方式.如果中断后有效.流程转入serial_link_irq_chain().在这个里面.会注册中断处理函数. /*

* Now, initialize the UART */

serial_outp(up, UART_LCR, UART_LCR_WLEN8);

spin_lock_irqsave(&up->port.lock, flags); if (up->port.flags & UPF_FOURPORT) { if (!is_real_interrupt(up->port.irq)) up->port.mctrl |= TIOCM_OUT1; } else

/*

* Most PC uarts need OUT2 raised to enable interrupts. */

if (is_real_interrupt(up->port.irq)) up->port.mctrl |= TIOCM_OUT2;

serial8250_set_mctrl(&up->port, up->port.mctrl);

/*

* Do a quick test to see if we receive an * interrupt when we enable the TX irq. */

serial_outp(up, UART_IER, UART_IER_THRI); lsr = serial_in(up, UART_LSR); iir = serial_in(up, UART_IIR); serial_outp(up, UART_IER, 0);

if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) { if (!(up->bugs & UART_BUG_TXEN)) { up->bugs |= UART_BUG_TXEN;

pr_debug(\ port->line); } } else {

up->bugs &= ~UART_BUG_TXEN; }

spin_unlock_irqrestore(&up->port.lock, flags);

/*

* Clear the interrupt registers again for luck, and clear the * saved flags to avoid getting false values from polling * routines or the previous session. */

serial_inp(up, UART_LSR); serial_inp(up, UART_RX); serial_inp(up, UART_IIR); serial_inp(up, UART_MSR); up->lsr_saved_flags = 0; up->msr_saved_flags = 0;

/*

* Finally, enable interrupts. Note: Modem status interrupts * are set via set_termios(), which will be occurring imminently * anyway, so we don't enable them here. */

up->ier = UART_IER_RLSI | UART_IER_RDI; serial_outp(up, UART_IER, up->ier);

if (up->port.flags & UPF_FOURPORT) { unsigned int icp; /*

* Enable interrupts on the AST Fourport board */

icp = (up->port.iobase & 0xfe0) | 0x01f; outb_p(0x80, icp); (void) inb_p(icp); }

return 0;

}

最后,就是对8250芯片的初始化了.包括:在LCR中设置数据格式,在MCR中设置允许中断到8259.在IER中设置相关允许位.

另外在open的时候,还会调用port-> enable_ms ()接口,在本例中对应为: serial8250_enable_ms().代码如下:

static void serial8250_enable_ms(struct uart_port *port) {

struct uart_8250_port *up = (struct uart_8250_port *)port;

/* no MSR capabilities */

if (up->bugs & UART_BUG_NOMSR) return;

up->ier |= UART_IER_MSI;

serial_out(up, UART_IER, up->ier); }

即允许moden中断

五:数据发送的操作

在uart驱动架构中分析过,在发送数据的时候,uart层先会将数据放入circ_buffer.最后再调用port-> start_tx().

在这里,这个接口对应为serial8250_start_tx().代码如下: static void serial8250_start_tx(struct uart_port *port) {

struct uart_8250_port *up = (struct uart_8250_port *)port;

if (!(up->ier & UART_IER_THRI)) { up->ier |= UART_IER_THRI; serial_out(up, UART_IER, up->ier);

if (up->bugs & UART_BUG_TXEN) { , ; unsigned char lsr, iir; lsr = serial_in(up, UART_LSR);

up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; iir = serial_in(up, UART_IIR) & 0x0f; if ((up->port.type == PORT_RM9000) ? (lsr & UART_LSR_THRE &&

(iir == UART_IIR_NO_INT || iir == UART_IIR_THRI)) : (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT)) transmit_chars(up); } }

/*

* Re-enable the transmitter if we disabled it. */

if (up->port.type == PORT_16C950 && up->acr & UART_ACR_TXDIS) { up->acr &= ~UART_ACR_TXDIS; serial_icr_write(up, UART_ACR, up->acr);

} }

这个函数非常简单.如果没有定义发送空中断.则在IER中打开这个中断.关于TXEN上的bug修复和16C950类型的芯片不是我们所关注的部份.

那,这里只是打开了这个中断.写数据到芯片的这个过程是在什么地方完成的呢?

是在中断处理中.如果是发送空的中断,就将circ buffer中的数据写出发送寄存器.跟踪一下代码.中断处理函数为serial8250_interrupt().

static irqreturn_t serial8250_interrupt(int irq, void *dev_id) {

struct irq_info *i = dev_id;

struct list_head *l, *end = NULL; int pass_counter = 0, handled = 0;

DEBUG_INTR(\

spin_lock(&i->lock);

l = i->head; do {

struct uart_8250_port *up; unsigned int iir;

up = list_entry(l, struct uart_8250_port, list);

iir = serial_in(up, UART_IIR); if (!(iir & UART_IIR_NO_INT)) { serial8250_handle_port(up);

handled = 1;

end = NULL;

} else if (up->port.iotype == UPIO_DWAPB &&

(iir & UART_IIR_BUSY) == UART_IIR_BUSY) { /* The DesignWare APB UART has an Busy Detect (0x07) * interrupt meaning an LCR write attempt occured while the * UART was busy. The interrupt must be cleared by reading * the UART status register (USR) and the LCR re-written. */ unsigned int status;

status = *(volatile u32 *)up->port.private_data; serial_out(up, UART_LCR, up->lcr);

handled = 1;

end = NULL; } else if (end == NULL) end = l;

l = l->next;

if (l == i->head && pass_counter++ > PASS_LIMIT) { /* If we hit this, we're dead. */

本文来源:https://www.bwwdw.com/article/k3a6.html

Top