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) {
…… ……