Linux tty驱动学习 - UART驱动注册

8250/16550系列芯片为常见的UART芯片,该类芯片的驱动文件为8250.c,位于内核源码的/drivers/serial/目录下面。serial8250_init为该驱动的入口函数,nr_uarts和UART_NR在内核配置中定义,跟具体的平台相关,通常定义为4。由于CONFIG_SPARC没有被定义,所以做的第一件事情就是注册一个名为serial_8250_reg的UART驱动。
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;
	}
}


platform_driver_register(&serial8250_isa_driver)把serial8250_isa_driver注册到platform bus上,该driver支持的设备名字为"serial8250",所以如果当前platform bus上有名字为serial的设备,或者驱动注册之后有这类设备注册到platform bus上,都会调用serial8250_probe对设备进行初始化,并将设备和驱动绑定在一起。在probe函数中,首先获取匹配到platform device的IO资源,然后根据获取到的资源对uart_port进行初始化,即用uart_port来表示一个具体的uart设备,然后调用serial8250_register_port来注册该uart_port。

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设备注册起来。

你可能感兴趣的:(Linux内核和驱动学习)