基于Mips架构linux下设备树串口驱动(二)
本设备树串口驱动基于linux3.0.4内核版本
serial8250_console_init()
从serial8250_console_init()函数实体开始,追踪分析标准设备驱动流程。函数原型如下,函数实体路径见./drivers/tty/serial/8250.c。可以看出该函数第一次初始化port,第二次注册
console驱动。
static int __init serial8250_console_init(void)
{
if (nr_uarts > UART_NR)
nr_uarts = UART_NR;
serial8250_isa_init_ports();
register_console(&serial8250_console);
return 0;
}
console_initcall(serial8250_console_init);
serial8250_isa_init_ports()
继续追踪函数实体serial8250_isa_init_ports()实现,函数原型如下,函数路径见
./drivers/tty/serial/8250.c。博主主要关主两个for循环内的操作。第一个for循环功能是
初始化定时器功能函数up->timer.function = serial8250_timeout;初始化serial8250 port的操作函数 up->port.ops = &serial8250_pops;这个接口serial8250_pops内有关于port操作函数挂载。第二个for循环功能是初始化默认port硬件参数old_serial_port[i].port;
static void __init serial8250_isa_init_ports(void)
{
.................................
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;
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.irqflags = old_serial_port[i].irqflags;
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;
set_io_from_upio(&up->port);
up->port.irqflags |= irqflag;
if (serial8250_isa_config != NULL)
serial8250_isa_config(i, &up->port, &up->capabilities);
}
.................................
}
接下来,先分析第一个for循环 up->port.ops = &serial8250_pops;有关于定时器初始化本文不作赘述。函数原型如下,函数实体路径见./drivers/tty/serial/8250.c,重点分析结构体struct uart_ops成员变量.config_port = serial8250_config_port,这是个关于port与硬件配置相关的函数。
static struct uart_ops serial8250_pops = {
.tx_empty = serial8250_tx_empty,
.set_mctrl = serial8250_set_mctrl,
.get_mctrl = serial8250_get_mctrl,
.stop_tx = serial8250_stop_tx,
.start_tx = serial8250_start_tx,
.stop_rx = serial8250_stop_rx,
.enable_ms = serial8250_enable_ms,
.break_ctl = serial8250_break_ctl,
.startup = serial8250_startup,
.shutdown = serial8250_shutdown,
.set_termios = serial8250_set_termios,
.set_ldisc = serial8250_set_ldisc,
.pm = serial8250_pm,
.type = serial8250_type,
.release_port = serial8250_release_port,
.request_port = serial8250_request_port,
.config_port = serial8250_config_port,
.verify_port = serial8250_verify_port,
#ifdef CONFIG_CONSOLE_POLL
.poll_get_char = serial8250_get_poll_char,
.poll_put_char = serial8250_put_poll_char,
#endif
};
再分析第二个for循环函数,结构体struct old_serial_port默认初始值是串口号,串口波特率,串口时钟频率,串口中断号,以及flags。结合下方第二段代码段图例展示struct old_serial_port定义的内容初始值,思路就逐渐清晰了。而SERIAL_PORT_DFNS宏定义由开发者自行针对硬件平台进行设计,下方只是博主列出一个示例应用。
#define SERIAL_PORT_DFNS \
/* UART CLK PORT IRQ FLAGS */ \
{ 0, BASE_BAUD, 0x3F8, PLD_IRQ_UART0, STD_COM_FLAGS }, /* ttyS0 */ \
{ 0, BASE_BAUD, 0x2F8, PLD_IRQ_UART1, STD_COM_FLAGS }, /* ttyS1 */
static const struct old_serial_port old_serial_port[] = {
SERIAL_PORT_DFNS /* defined in asm/serial.h */
};
struct old_serial_port定义如下,路径见./drivers/tty/serial/8250.h
struct old_serial_port {
unsigned int uart;
unsigned int baud_base;
unsigned int port;
unsigned int irq;
unsigned int flags;
unsigned char hub6;
unsigned char io_type;
unsigned char *iomem_base;
unsigned short iomem_reg_shift;
unsigned long irqflags;
};
serial8250_config_port()
现在我们回到.config_port = serial8250_config_port,继续追踪分析, 函数实体原型如下,函数路径见./drivers/tty/serial/8250.c,此函数本文只分析介绍三个子函数实现,分别为serial8250_request_std_resource(),serial8250_release_std_resource(),autoconfig()
其它函数本文不作赘述,第一个函数功能是将port的物理地址获取到转换成虚拟地址,类似于ioremap函数。第二个函数与之相反是取消port地址映射,类似于iounmap函数。第三个函树博主续追踪一下其实现。
static void serial8250_config_port(struct uart_port *port, int flags)
{
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
int probeflags = PROBE_ANY;
int ret;
ret = serial8250_request_std_resource(up);
if (ret < 0)
return;
ret = serial8250_request_rsa_resource(up);
if (ret < 0)
probeflags &= ~PROBE_RSA;
if (up->port.iotype != up->cur_iotype)
set_io_from_upio(port);
if (flags & UART_CONFIG_TYPE)
autoconfig(up, probeflags);
/* 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;
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);
}
autoconfig()
继续追踪函数实体autoconfig(),函数原型如下,函数路径见./drivers/tty/serial/8250.c
,此函数主要实现关于硬件参数读取写入操作,此处不作赘述,重点关注UART_IIR值获取到后进行不同类型的串口驱动适配注册。
static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
{
................................
scratch = serial_in(up, UART_IIR) >> 6;
DEBUG_AUTOCONF("iir=%d ", scratch);
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;
}
................................
}
serial8250_console()
继续追踪serial8250_console()实现,函数原型如下,函数路径见./drivers/tty/serial/8250.c
,此结构体是第一步是cosole基本操作初始化接口,将uart驱动绑定到cosole驱动内。
.data = &serial8250_reg
static struct console serial8250_console = {
.name = "ttyS",
.write = serial8250_console_write,
.device = uart_console_device,
.setup = serial8250_console_setup,
.early_setup = serial8250_console_early_setup,
.flags = CON_PRINTBUFFER | CON_ANYTIME,
.index = -1,
.data = &serial8250_reg,
};
至此底层硬件操作接口注册分析完成,小结如下:
第一步初始化serial8250底层操作函数接口,将serial8250操作函数绑定到uart驱动中。
up->port.ops = &serial8250_pops;
第二步初始化console用户层操作函数接口,将uart驱动操作函数绑定到console驱动中。
.data = &serial8250_reg,
上一篇: Vue显示和隐藏如何用动画形式显示
下一篇: jQuery显示和隐藏动画