linux8250驱动支持5路串口,linux 设备驱动之8250串口驱动分析

linux 设备驱动之 8250 串口驱动

------------------------------------------

本文系本站原创,欢迎转载!

转载请注明出处:http://ericxiao.cublog.cn/

------------------------------------------

一:前言

前一段时间自己实践了一下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 "Serial: 8250/16550 driver $Revision: 1.90 $ "

"%d ports, IRQ sharing %sabled\n", nr_uarts,

share_irqs ? "en" : "dis");

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("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:

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       = "serial",

.dev_name     = "ttyS",

.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("serial8250",    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    = "serial8250",

.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, "unable to register port at index %d "

"(IO%lx MEM%llx IRQ%d): %d\n", i,

p->iobase, (unsigned long long)p->mapbase,

p->irq, ret);

}

}

re


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部