Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2793869
  • 博文数量: 154
  • 博客积分: 7136
  • 博客等级: 准将
  • 技术积分: 1428
  • 用 户 组: 管理员
  • 注册时间: 2010-02-21 11:26
文章分类

全部博文(154)

文章存档

2016年(2)

2014年(2)

2013年(4)

2012年(16)

2011年(51)

2010年(68)

2009年(3)

2006年(3)

2005年(5)

分类:

2011-10-01 10:01:59

原文地址:我对linux理解之tty一 作者:amingriyue

------------------------------------------
本文系本站原创,欢迎转载!
转载请注明出处:amingriyue.blog.chinaunix.net
------------------------------------------

我们从具体的uart驱动开始追踪它的层次,我的系统是mx51,故从mxc_uart.c进行。
1,初始化注册过程:
static int __init mxcuart_init(void)
{
    int ret = 0;

    printk(KERN_INFO "Serial: MXC Internal UART driver\n");
    ret = uart_register_driver(&mxc_reg); //注册tty驱动接口
    if (ret == 0) {
        /* Register the device driver structure. */
        ret = platform_driver_register(&mxcuart_driver);
        if (ret != 0) {
            uart_unregister_driver(&mxc_reg);
        }
    }
    return ret;
}
我们先看uart_register_driver(&mxc_reg),mxc_reg的定义如下:
static struct uart_driver mxc_reg = {
    .owner = THIS_MODULE,
    .driver_name = "ttymxc",
    .dev_name = "ttymxc",
    .major = SERIAL_MXC_MAJOR,
    .minor = SERIAL_MXC_MINOR,
    .nr = MXC_UART_NR,
    .cons = MXC_CONSOLE,
};
uart_register_driver定义在serial_core中
int uart_register_driver(struct uart_driver *drv)
{
    struct tty_driver *normal = NULL;
    int i, retval;

    BUG_ON(drv->state);

    /*
     * 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);
    retval = -ENOMEM;
    if (!drv->state)
        goto out;

    normal  = alloc_tty_driver(drv->nr); //申请一个有nr根lines的drvier
    if (!normal)
        goto out;

    drv->tty_driver = normal; //赋值给uart_driver的私有变量,drv对应mxc_uart中的mxc_reg

    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;  //私有数据,linux经常有这样的用法
    tty_set_operations(normal, &uart_ops); //注意这边的uart_ops,最后调用到了mxc_uart中的mxc_ops
    //上面主要是相关结构体的初始化和一些赋值
    /*
     * Initialise the UART state(s).
     */
    for (i = 0; i < drv->nr; i++) {
        struct uart_state *state = drv->state + i;  //开始的时候申请了drv->nr个state,现在逐个初始化

        state->close_delay     = 500;    /* .5 seconds */
        state->closing_wait    = 30000;    /* 30 seconds */
        mutex_init(&state->mutex);

        tty_port_init(&state->info.port);
        init_waitqueue_head(&state->info.delta_msr_wait);
        tasklet_init(&state->info.tlet, uart_tasklet_action,
                 (unsigned long)state);
    }

    retval = tty_register_driver(normal); //注册到tty core
 out:
    if (retval < 0) {
        put_tty_driver(normal);
        kfree(drv->state);
    }
    return retval;
}
我们再看下tty_register_driver这个定义,它定义在tty_core中:
int tty_register_driver(struct tty_driver *driver)
{
    int error;
    int i;
    dev_t dev;
    void **p = NULL;

    if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {
        p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL);
        if (!p)
            return -ENOMEM;
    }

    if (!driver->major) {//根据主设备号决定是动态还是静态创建字符设备编号
        error = alloc_chrdev_region(&dev, driver->minor_start,
                        driver->num, driver->name);
        if (!error) {
            driver->major = MAJOR(dev);
            driver->minor_start = MINOR(dev);
        }
    } else {
        dev = MKDEV(driver->major, driver->minor_start);
        error = register_chrdev_region(dev, driver->num, driver->name);
    }
    if (error < 0) {
        kfree(p);
        return error;
    }

    if (p) {
        driver->ttys = (struct tty_struct **)p;
        driver->termios = (struct ktermios **)(p + driver->num);
    } else {
        driver->ttys = NULL;
        driver->termios = NULL;
    }
    //注册字符设备
    cdev_init(&driver->cdev, &tty_fops);
    driver->cdev.owner = driver->owner;
    error = cdev_add(&driver->cdev, dev, driver->num);  //将字符设备添加到系统中
    if (error) {
        unregister_chrdev_region(dev, driver->num);
        driver->ttys = NULL;
        driver->termios = NULL;
        kfree(p);
        return error;
    }

    mutex_lock(&tty_mutex);
    list_add(&driver->tty_drivers, &tty_drivers); //添加到tty core的drivers链表里面,tty_core维护着这个链表
    mutex_unlock(&tty_mutex);

    if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) { /*根据动态设备标记决定是否此时注册tty设备,就是上面uart_register_driver里的normal->flags,在这里mxc_uart中有设置,
它是在mxcuart_probe中的mxcuart_probe里注册的,device注册后将在sys中形成层次结构,系统启动后,udev系统(android中为vold)将在/dev/下面
生成节点文件,这些节点文件操作都是按照该字符设备的tty_fops进行*/
        for (i = 0; i < driver->num; i++)
            tty_register_device(driver, i, NULL);
    }
    proc_tty_register_driver(driver);
    driver->flags |= TTY_DRIVER_INSTALLED;  //设置完成标记
    return 0;
}
我们看到这个tty_core中的注册函数主要工作是添加了字符设备。我们可以简单地看到注册一个uart驱动架构:
mxc_uart---->serial_core---->tty_core
我们下面再看mxcuart_init中的platform_driver_register(&mxcuart_driver)。要读懂这个过程需要理解platform bus的工作流程。
2,platform_driver_register(&mxcuart_driver)
int platform_driver_register(struct platform_driver *drv)
{
    drv->driver.bus = &platform_bus_type;
    if (drv->probe)
        drv->driver.probe = platform_drv_probe;
    if (drv->remove)
        drv->driver.remove = platform_drv_remove;
    if (drv->shutdown)
        drv->driver.shutdown = platform_drv_shutdown;

    return driver_register(&drv->driver);
}
platform_bus_type定义如下:
struct bus_type platform_bus_type = {
    .name        = "platform",
    .dev_attrs    = platform_dev_attrs,
    .match        = platform_match,
    .uevent        = platform_uevent,
    .pm        = PLATFORM_PM_OPS_PTR,
};
我们还记得driver_register分析中用的bus macth函数吗?platform总线的match就是对应这里的platform_match。我们下面看下它的定义
static int platform_match(struct device *dev, struct device_driver *drv)
{
    struct platform_device *pdev = to_platform_device(dev);
    struct platform_driver *pdrv = to_platform_driver(drv);

    /* match against the id table first */
    if (pdrv->id_table)
        return platform_match_id(pdrv->id_table, pdev) != NULL;  //如果有id_table,去匹配id

    /* fall-back to driver name match */
    return (strcmp(pdev->name, drv->name) == 0); //匹配设备的名字和驱动的名字
}
我们看下platform_match_id()定义:
static const struct platform_device_id *platform_match_id(
            struct platform_device_id *id,
            struct platform_device *pdev)
{
    while (id->name[0]) {
        if (strcmp(pdev->name, id->name) == 0) { //匹配设备的名字和id的名字
            pdev->id_entry = id;
            return id; //返回匹配的的id
        }
        id++;
    }
    return NULL;
}
所以我们看到platform匹配过程是先匹配设备的名字和id的名字,然后去匹配设备的名字和驱动的名字。
我们由driver_register分析知道,如果匹配成功的话,将会执行probe函数。
好了,我们知道platform工作流程以后,我们继续我们的uart分析platform_driver_register(&mxcuart_driver),先看下mxcuart_driver定义:
static struct platform_driver mxcuart_driver = {
    .driver = {
           .name = "mxcintuart",
           },
    .probe = mxcuart_probe,
    .remove = mxcuart_remove,
    .suspend = mxcuart_suspend,
    .resume = mxcuart_resume,
};
我们发现它没有定义驱动的id_table,只有他的名字是"mxcintuart",所以会根据这个名字去匹配设备,我们在arch/arm/mach-mx5/serial.c发现了它的设备定义:
static struct platform_device mxc_uart_device1 = {
    .name = "mxcintuart",
    .id = 0,
    .num_resources = ARRAY_SIZE(mxc_uart_resources1),
    .resource = mxc_uart_resources1,
    .dev = {
        .platform_data = &mxc_ports[0],
        },
};
这里只贴出第一个口子的设备定义,其它的都类似,驱动会一一去匹配的,所以这里系统初始化的时候会成功匹配,匹配完了会执行probe函数!
我们下面看他的probe函数:
static int mxcuart_probe(struct platform_device *pdev)
{
    int id = pdev->id;//编号,从设备定义中获得
    struct resource *res;
    void __iomem *base;

    mxc_ports[id] = pdev->dev.platform_data; //也是从设备定义中获得,从上面看就是port口子
    mxc_ports[id]->port.ops = &mxc_ops; //对应的口子操作

    /* Do not use UARTs that are disabled during integration */
    if (mxc_ports[id]->enabled == 1) {
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //从设备那边获得资源,从设备定义那边看,这些都一目了然
        if (!res)
            return -ENODEV;

        base = ioremap(res->start, res->end - res->start + 1);
        if (!base)
            return -ENOMEM;

        mxc_ports[id]->port.membase = base;
        mxc_ports[id]->port.mapbase = res->start;
        mxc_ports[id]->port.dev = &pdev->dev;
        mxc_ports[id]->port.irq = platform_get_irq(pdev, 0);
        mxc_ports[id]->irqs[0] = platform_get_irq(pdev, 1);
        mxc_ports[id]->irqs[1] = platform_get_irq(pdev, 2);
        spin_lock_init(&mxc_ports[id]->port.lock);
        /* Enable the low latency flag for DMA UART ports */
        if (mxc_ports[id]->dma_enabled == 1) { //这个在设备定义中可以查看的,有的打开,有的没打开
            mxc_ports[id]->port.flags |= UPF_LOW_LATENCY;
        }

        mxc_ports[id]->clk = clk_get(&pdev->dev, "uart_clk");
        if (mxc_ports[id]->clk == NULL)
            return -1;

        uart_add_one_port(&mxc_reg, &mxc_ports[id]->port);
        platform_set_drvdata(pdev, mxc_ports[id]);  //设置私有数据
    }
    return 0;
}
probe主要工作转入uart_add_one_port(&mxc_reg, &mxc_ports[id]->port):
int uart_add_one_port(struct uart_driver *drv, struct uart_port *port)
{
    struct uart_state *state;
    int ret = 0;
    struct device *tty_dev;

    BUG_ON(in_interrupt());

    if (port->line >= drv->nr)
        return -EINVAL;

    state = drv->state + port->line;

    mutex_lock(&port_mutex);
    mutex_lock(&state->mutex);
    if (state->port) {
        ret = -EINVAL;
        goto out;
    }

    state->port = port; //将port和state联系起来
    state->pm_state = -1;

    port->cons = drv->cons;
    port->info = &state->info;

    /*
     * If this port is a console, then the spinlock is already
     * initialised.
     */
    if (!(uart_console(port) && (port->cons->flags & CON_ENABLED))) {
        spin_lock_init(&port->lock);
        lockdep_set_class(&port->lock, &port_lock_key);
    }

    uart_configure_port(drv, state, port); //配置口子类型

    /*
     * Register the port whether it's detected or not.  This allows
     * setserial to be used to alter this ports parameters.
     */
    tty_dev = tty_register_device(drv->tty_driver, port->line, port->dev); //注册到tty core
    if (likely(!IS_ERR(tty_dev))) {
        device_init_wakeup(tty_dev, 1); //pm部分
        device_set_wakeup_enable(tty_dev, 0);
    } else
        printk(KERN_ERR "Cannot register tty device on line %d\n",
               port->line);

    /*
     * Ensure UPF_DEAD is not set.
     */
    port->flags &= ~UPF_DEAD;

 out:
    mutex_unlock(&state->mutex);
    mutex_unlock(&port_mutex);

    return ret;
}
我们看下tty_register_device(drv->tty_driver, port->line, port->dev):
struct device *tty_register_device(struct tty_driver *driver, unsigned index,
                   struct device *device)
{
    char name[64];
    dev_t dev = MKDEV(driver->major, driver->minor_start) + index;

    if (index >= driver->num) {
        printk(KERN_ERR "Attempt to register invalid tty line number "
               " (%d).\n", index);
        return ERR_PTR(-EINVAL);
    }

    if (driver->type == TTY_DRIVER_TYPE_PTY)
        pty_line_name(driver, index, name); //虚拟终端的名字
    else
        tty_line_name(driver, index, name);//生成tty*的名字

    return device_create(tty_class, device, dev, NULL, name); //创建属于tty的设备,最终将进入以前分析的device_register,也就是添加到sys层次结构中
}
这样整个probe的工作就完了,其实从上面看下来,主要就是注册tty device。同样的的注册流程也是:mxc_uart---->serial_core---->tty_core
阅读(3729) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~