static int __init serial8250_init(void)
{
int ret;
if (nr_uarts > UART_NR)
nr_uarts = UART_NR;
printk(KERN_INFO "Serial: 8250/16550 driver, "
"%d ports, IRQ sharing %sabled\n", nr_uarts,
share_irqs ? "en" : "dis");
#ifdef CONFIG_SPARC
ret = sunserial_register_minors(&serial8250_reg, UART_NR);
#else
serial8250_reg.nr = UART_NR;
ret = uart_register_driver(&serial8250_reg);
#endif
if (ret)
goto out;
serial8250_isa_devs = platform_device_alloc("serial8250",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:
#ifdef CONFIG_SPARC
sunserial_unregister_minors(&serial8250_reg, UART_NR);
#else
uart_unregister_driver(&serial8250_reg);
#endif
out:
return ret;
}
uart_register_driver(struct uart_driver *drv)向uart核心层注册一个uart驱动。在uart驱动中,用uart_state来表示一个uart设备。所以首先为drv->state分配内存空间,驱动支持drv->nr个设备,就分配对应个数的内存空间。接着调用alloc_tty_dirver()申请一个tty_driver,该函数会先分配tty_driver的内存空间,然后做简单的初始化。tty_driver大部分的初始化工作在该函数之后完成,利用uart_driver的信息初始化tty_driver,比如驱动和设备的名字,以及主次设备号的值,最重要的是会绑定uart_driver和tty_driver之间的链接关系。在ldd3的tty驱动中有提到tty_driver中的重要成员tty_operations,所以调用tty_set_operations()把uart_ops赋值给tty_driver的tty_operations变量。在uart驱动层这边,用uart_state和uart_port来表示具体的设备,所以最后是对uart_state进行初始化,而且主要是对里面的tty_port进行初始化操作。uart驱动注册的最后就是调用tty_register_dirver()注册tty驱动,从注册过程可以看出uart驱动是作为tty的驱动的成员被注册到tty核心层,用户空间通过系统调用是先跟tty核心沟通,往下层就是tty驱动,最后是通过tty_driver的成员driver_state找到最终的uart驱动。
/*
* Maybe we should be using a slab cache for this, especially if
* we have a large number of ports to handle.
*/
drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
if (!drv->state)
goto out;
normal = alloc_tty_driver(drv->nr);
if (!normal)
goto out_kfree;
drv->tty_driver = normal;
normal->owner = drv->owner;
normal->driver_name = drv->driver_name;
normal->name = drv->dev_name;
normal->major = drv->major;
normal->minor_start = drv->minor;
normal->type = TTY_DRIVER_TYPE_SERIAL;
normal->subtype = SERIAL_TYPE_NORMAL;
normal->init_termios = tty_std_termios;
normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;
normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
normal->driver_state = drv;
tty_set_operations(normal, &uart_ops);
/*
* Initialise the UART state(s).
*/
for (i = 0; i < drv->nr; i++) {
struct uart_state *state = drv->state + i;
struct tty_port *port = &state->port;
tty_port_init(port);
port->close_delay = 500; /* .5 seconds */
port->closing_wait = 30000; /* 30 seconds */
tasklet_init(&state->tlet, uart_tasklet_action,
(unsigned long)state);
}
retval = tty_register_driver(normal);
if (retval >= 0)
return retval;
put_tty_driver(normal);
platform_device_alloc("serial8250",PLAT8250_DEV_LEGACY)用来创建一个名为"serial8250"的platform device,然后用platform_device_add()把该设备注册到platform bus上。但是因为没有为该platform device分配IO资源,所以该设备并没有对应到具体的设备,它只是作为第一个阶段注册uart设备的父设备使用,在完成整个uart驱动的注册后会被释放掉。
serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev)用来注册uart驱动所支持的设备。首先会初始化一个名为serial8250_ports[]的数组,该数组成员类型为uart_8250_port,表示8250这一类型的芯片。接下来会调用serial8250_isa_init_ports对数组内的每个成员进行具体的初始化,在第一个for循环中,最重要的一个事情就是把serial8250_pops赋值给uart_port的成员ops,这样的话就有了对8250操作的方法。第二个for循环是否会执行跟具体的平台相关,old_serail_port定义为SERIAL_PORT_DFNS, 这个值定义在asm/serial.h里面。在X86平台下面,被定义为包含4个元素的数组,对应ttyS0,ttyS1,ttyS2,ttyS3。而在ARM平台下面,该值没有被定义,所以第二个for循环里面的初始化操作不会被执行,也就是说在该阶段注册的uart设备都没有对应到具体的设备。最后调用uart_add_one_port()注册uart设备,在uart_configure_port()里面会根据uart_port的配置进行相应的硬件初始化。最后调用tty_register_device()把uart设备注册成字符设备,在用户空间的/dev目录下面可以看到ttyS0,ttyS1,ttyS2,ttyS3四个设备文件。如果在注册之前没有绑定具体的设备,即使在用户空间看到对应的设备文件,也不能进行具体的应用操作。
static void __init serial8250_register_ports(struct uart_driver *drv, struct device *dev)
{
int i;
for (i = 0; i < nr_uarts; i++) {
struct uart_8250_port *up = &serial8250_ports[i];
up->cur_iotype = 0xFF;
}
serial8250_isa_init_ports();
for (i = 0; i < nr_uarts; i++) {
struct uart_8250_port *up = &serial8250_ports[i];
up->port.dev = dev;
if (up->port.flags & UPF_FIXED_TYPE)
serial8250_init_fixed_type_port(up, up->port.type);
uart_add_one_port(drv, &up->port);
}
}
static void __init serial8250_isa_init_ports(void)
{
struct uart_8250_port *up;
static int first = 1;
int i, irqflag = 0;
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;
}
if (share_irqs)
irqflag = IRQF_SHARED;
for (i = 0, up = serial8250_ports;
i < ARRAY_SIZE(old_serial_port) && i < nr_uarts;
i++, up++) {
printk("[Mike Debug] old_serial_port num %d\n", i);
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;
}
}
static struct platform_driver serial8250_isa_driver = {
.probe = serial8250_probe,
.remove = __devexit_p(serial8250_remove),
.suspend = serial8250_suspend,
.resume = serial8250_resume,
.driver = {
.name = "serial8250",
.owner = THIS_MODULE,
},
};
int serial8250_register_port(struct uart_port *port)
{
struct uart_8250_port *uart;
int ret = -ENOSPC;
if (port->uartclk == 0)
return -EINVAL;
mutex_lock(&serial_mutex);
uart = serial8250_find_match_or_unused(port);
if (uart) {
uart_remove_one_port(&serial8250_reg, &uart->port);
uart->port.iobase = port->iobase;
uart->port.membase = port->membase;
uart->port.irq = port->irq;
uart->port.irqflags = port->irqflags;
uart->port.uartclk = port->uartclk;
uart->port.fifosize = port->fifosize;
uart->port.regshift = port->regshift;
uart->port.iotype = port->iotype;
uart->port.flags = port->flags | UPF_BOOT_AUTOCONF;
uart->port.mapbase = port->mapbase;
uart->port.private_data = port->private_data;
if (port->dev)
uart->port.dev = port->dev;
if (port->flags & UPF_FIXED_TYPE)
serial8250_init_fixed_type_port(uart, port->type);
set_io_from_upio(&uart->port);
/* Possibly override default I/O functions. */
if (port->serial_in)
uart->port.serial_in = port->serial_in;
if (port->serial_out)
uart->port.serial_out = port->serial_out;
ret = uart_add_one_port(&serial8250_reg, &uart->port);
if (ret == 0)
ret = uart->port.line;
}
mutex_unlock(&serial_mutex);
return ret;
}
serial8250_find_match_or_unused()函数从名字上就可以知道是去寻找一个匹配的或者没有使用的port,从哪里找呢?就是从上一阶段初始化的serial8250_ports[]数组里面去找。首先是找一个完全匹配的uart_port,主要是根据uart_port的IO资源的类型来判断。如果没有这样的port存在,那么就会查找第一个没有被使用的port。如果这样的port也不存在,最后会去查找没有绑定具体设备的port。在ARM平台上,第一阶段注册的uart_port都没有绑定具体设备,所以注册的uart设备会按注册的顺序替换掉serial8250_ports[]里面对应的port。如果最后没有找到符合条件的uart_port,说明uart驱动目前管理的设备已经达到它所能管理的最大数目。如果有这样的uart_port存在,那么就调用uart_remove_one_port()把旧的uart_port从uart驱动的设备uart_state中移除,之后再调用tty_unregister_device()把uart设备从linux的设备模型总移除掉。移除旧的uart设备后,调用uart_add_one_port()注册新的uart设备,这个注册过程跟第一阶段注册虚拟的uart设备是完全相同的。在这之后uart设备才算正式工作起来,在用户空间可以使用/dev/ttyS*去操作设备。
uart设备是作为platform device注册的,而platform device的注册一般都放在平台相关的目录下面,比如ARM平台是放在/arch/arm/mach-*目录下面的core.c里面。在8250.c里面注册的serial8250_isa_driver的名字为serial8250,所以可以看到注册的uart设备名字也是serial8250。在platform里面比较重要的两个数据结构为platform_data和resource,platform_data定义该设备的特性,resource定义了该设备使用的IO资源。比如mv_uart0_data[]里面就定义8250寄存器的基地址,IO资源的类型以及使用的中断号。
static struct platform_device mv_uart = {
.name = "serial8250",
.id = PLAT8250_DEV_PLATFORM,
.dev = {
.platform_data = mv_uart0_data,
},
.num_resources = 2, /*ARRAY_SIZE(mv_uart_resources),*/
.resource = mv_uart0_resources,
};
static struct plat_serial8250_port mv_uart0_data[] = {
{
.mapbase = PORT0_BASE,
.membase = (char *)PORT0_BASE,
.irq = IRQ_UART0,
.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF,
.iotype = UPIO_MEM,
.regshift = 2,
},
{ },
};
static struct resource mv_uart0_resources[] = {
{
.start = PORT0_BASE,
.end = PORT0_BASE + 0xff,
.flags = IORESOURCE_MEM,
},
{
.start = IRQ_UART0,
.end = IRQ_UART0,
.flags = IORESOURCE_IRQ,
},
};
从代码分析上可以看出,uart 8250驱动的注册过程完成了uart驱动以及uart设备的注册工作。uart驱动的注册就是把uart_driver注册到tty核心层,通过tty驱动框架呈现在用户空间。而uart设备的主要是完成了静态设备的注册,也就是说注册的只是定义在serial8250_ports[]数组里面的设备,这个跟具体的平台相关,有些平台有把真实的uart设备定义到该数组中,所以注册完成后真实的uart设备就可以工作了。而有些平台在该阶段注册的只是虚拟的uart设备,并没有绑定到具体的物理设备上,所以还需要通过serial8250_isa_driver这个platform driver把真实的uart设备注册起来。