linux设备驱动之8250串口驱动(2)

2019-04-08 22:11

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


linux设备驱动之8250串口驱动(2).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:曾仕强经典语录

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: