转自:http://www.cnblogs.com/haimeng2010/p/3582403.html

目录:

1、platform_device注册过程

2、platform_driver注册过程

从 Linux 2.6 起引入了一套新的驱动管理和注册机制 :Platform_device 和 Platform_driver 。

Linux 中大部分的设备驱动,都可以使用这套机制 ,  设备用 Platform_device 表示,驱动用 Platform_driver 进行注册。

Linux platform driver 机制和传统的 device driver  机制 ( 通过 driver_register 函数进行注册 ) 相比,一个十分明显的优势在于 platform 机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过 platform device 提供的标准接口进行申请并使用。这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性 ( 这些标准接口是安全的 ) 。

Platform 机制的本身使用并不复杂,由两部分组成: platform_device 和 platfrom_driver 。

通过 Platform 机制开发发底层驱动的大致流程为: 定义platform_add_devices --> 注册platform_device --> 定义platform_add_driver --> 注册platform_driver 。

1、platform_device注册过程

首先要确认的就是设备的资源信息,例如设备的地址,中断号等。

在 2.6 内核中 platform 设备用结构体 platform_device 来描述,该结构体定义在 kernel\include\linux\platform_device.h 中,

struct platform_device {
    const char    * name;
    int        id;
    struct device    dev;
    u32        num_resources;
    struct resource    *  resource ;
    struct platform_device_id    *id_entry;
};

该结构一个重要的元素是 resource ,该元素存入了最为重要的设备资源信息,定义在 kernel\include\linux\ioport.h 中,

struct resource {
    resource_size_t start;
    resource_size_t end;
    const char *name;
    unsigned long flags;
    struct resource *parent, *sibling, *child;
};

下面举 s3c6410 平台的 i2c 驱动作为例子来说明:

static struct platform_device *smdk6410_devices [] __initdata = {
#ifdef CONFIG_SMDK6410_SD_CH0
    &s3c_device_hsmmc0,
#endif
#ifdef CONFIG_SMDK6410_SD_CH1
    &s3c_device_hsmmc1,
#endif
    &s3c_device_i2c0 ,
    &s3c_device_i2c1,
    &s3c_device_fb,
    &s3c_device_usb,
    &s3c_device_usb_hsotg,
    &smdk6410_lcd_powerdev,

&smdk6410_smsc911x,
};
把一个或几个设备资源放在一起,便于集中管理,其中IIC设备 platform_device如下:
struct platform_device  s3c_device_i2c0  = {
    .name          = "s3c2410-i2c",
#ifdef CONFIG_S3C_DEV_I2C1
    .id          = 0,
#else
    .id          = -1,
#endif
    .num_resources      = ARRAY_SIZE(s3c_i2c_resource ),
    .resource      = s3c_i2c_resource,
};

具体resource如下:
static struct resource  s3c_i2c_resource [] = {
    [0] = {
        .start = S3C_PA_IIC,
        .end   = S3C_PA_IIC + SZ_4K - 1,
        .flags = IORESOURCE_MEM,
    },
    [1] = {
        .start = IRQ_IIC,
        .end   = IRQ_IIC,
        .flags = IORESOURCE_IRQ,
    },
};

这里定义了两组 resource ,它描述了一个 I2C 设备的资源,第 1 组描述了这个 I2C 设备所占用的总线地址范围, IORESOURCE_MEM 表示第 1 组描述的是内存类型的资源信息,第 2 组描述了这个 I2C 设备的中断号, IORESOURCE_IRQ 表示第 2 组描述的是中断资源信息。设备驱动会根据 flags 来获取相应的资源信息。

定义好了 platform_device 结构体后就可以调用函数 platform_add_devices 向系统中添加该设备了,之后可以调用 platform_driver_register() 进行设备注册。

s3c6410-i2c的platform_device是在系统启动时,在mach-smdk6410.c里的smdk6410_machine_init()函数里进行注册的,这个函数申明为arch_initcall的函数调用,arch_initcall的优先级高于module_init。所以会在Platform驱动注册之前调用。(详细参考imach-smdk6410.c)

static void __init smdk6410_machine_init(void)
{
    s3c_i2c0_set_platdata(NULL);
    s3c_i2c1_set_platdata(NULL);
    s3c_fb_set_platdata(&smdk6410_lcd_pdata);

gpio_request(S3C64XX_GPN(5), "LCD power");
    gpio_request(S3C64XX_GPF(13), "LCD power");
    gpio_request(S3C64XX_GPF(15), "LCD power");

i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
    i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));

platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices));
    //添加多设备

}

int platform_add_devices(struct platform_device **devs, int num)
{
    int i, ret = 0;

for (i = 0; i < num; i++) {
         ret = platform_device_register(devs[i]);
        if (ret) {
            while (--i >= 0)
                platform_device_unregister(devs[i]);
            break;
        }
    }

return ret;
}

int platform_device_register(struct platform_device *pdev)
{
    device_initialize(&pdev->dev);
    return platform_device_add(pdev);
}

int platform_device_add(struct platform_device *pdev)
{
    int i, ret = 0;

if (!pdev)
        return -EINVAL;

if (!pdev->dev.parent)
        pdev->dev.parent = &platform_bus;

pdev->dev.bus = &platform_bus_type;

if (pdev->id != -1)
        dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);//如果有id 表示有多个同类设备用 pdev->name和 pdev->id标识该设备
    else
        dev_set_name(&pdev->dev, "%s", pdev->name);
//否则,只用 pdev->name标识该设备

for (i = 0; i < pdev->num_resources; i++) {
        struct resource *p, *r = &pdev->resource[i];

if (r->name == NULL)
            r->name = dev_name(&pdev->dev);

p = r->parent;
        if (!p) {
            if (resource_type(r) == IORESOURCE_MEM)
                 p = &iomem_resource; //   作为 IOMEM 资源分配  
            else if (resource_type(r) == IORESOURCE_IO)
                 p = &ioport_resource; //   作为 IO PORT资源分配  
        }

if (p && insert_resource(p, r)) { // 将新的 resource 插入内核 resource tree
            printk(KERN_ERR
                   "%s: failed to claim resource %d\n",
                   dev_name(&pdev->dev), i);
            ret = -EBUSY;
            goto failed;
        }
    }

pr_debug("Registering platform device '%s'. Parent at %s\n",
         dev_name(&pdev->dev), dev_name(pdev->dev.parent));

ret = device_add(&pdev->dev);//添加设备到设备树
    if (ret == 0)
        return ret;

failed:
    while (--i >= 0) {
        struct resource *r = &pdev->resource[i];
        unsigned long type = resource_type(r);

if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
            release_resource(r);
    }

return ret;
}

2、platform_driver注册过程

在platform_device注册完成后,就可以进行platform_driver注册了,在驱动初始化函数中调用函数platform_driver_register() 注册 platform_driver ,需要注意的是 s3c_device_i2c 结构中 name 元素和 s3c6410_i2c_driver 结构中 driver.name 必须是相同的,这样在 platform_driver_register() 注册时会对所有已注册的所有 platform_device 中的 name 和当前注册的 platform_driver 的 driver.name 进行比较,只有找到相同的名称的 platfomr_device 才能注册成功。

static struct platform_driver s3c24xx_i2c_driver = {
    .probe        = s3c24xx_i2c_probe,
    .remove        = s3c24xx_i2c_remove,
    .suspend_late    = s3c24xx_i2c_suspend_late,
    .resume        = s3c24xx_i2c_resume,
    .id_table    = s3c24xx_driver_ids,
    .driver        = {
        .owner    = THIS_MODULE,
        .name    = "s3c-i2c",
    },
};

static int __init i2c_adap_s3c_init(void)
{
     return platform_driver_register(&s3c24xx_i2c_driver);//注册IIC驱动
}

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);
}

int driver_register(struct device_driver *drv)
{
    int ret;
    struct device_driver *other;

BUG_ON(!drv->bus->p);

if ((drv->bus->probe && drv->probe) ||
        (drv->bus->remove && drv->remove) ||
        (drv->bus->shutdown && drv->shutdown))
        printk(KERN_WARNING "Driver '%s' needs updating - please use "
            "bus_type methods\n", drv->name);

other = driver_find(drv->name, drv->bus);//检查Driver是否已经存在
    if (other) {
        put_driver(other);
        printk(KERN_ERR "Error: Driver '%s' is already registered, "
            "aborting...\n", drv->name);
        return -EBUSY;
    }
    //若不存在,则添加驱动到驱动树。
    ret = bus_add_driver(drv);
    if (ret)
        return ret;
    ret = driver_add_groups(drv, drv->groups);
    if (ret)
        bus_remove_driver(drv);
    return ret;
}

int  bus_add_driver (struct device_driver *drv)
{
    struct bus_type *bus;
    struct driver_private *priv;
    int error = 0;

bus = bus_get(drv->bus);
    if (!bus)
        return -EINVAL;

pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);

priv = kzalloc(sizeof(*priv), GFP_KERNEL);
    if (!priv) {
        error = -ENOMEM;
        goto out_put_bus;
    }
    klist_init(&priv->klist_devices, NULL, NULL);
    priv->driver = drv;
    drv->p = priv;
    priv->kobj.kset = bus->p->drivers_kset;
    error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
                     "%s", drv->name);
    if (error)
        goto out_unregister;

if (drv->bus->p->drivers_autoprobe) {
        error = driver_attach(drv);
        if (error)
            goto out_unregister;
    }
    klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
    module_add_driver(drv->owner, drv);

error = driver_create_file(drv, &driver_attr_uevent);
    if (error) {
        printk(KERN_ERR "%s: uevent attr (%s) failed\n",
            __func__, drv->name);
    }
    error = driver_add_attrs(bus, drv);
    if (error) {
       
        printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
            __func__, drv->name);
    }
    error = add_bind_files(drv);
    if (error) {
       
        printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
            __func__, drv->name);
    }

kobject_uevent(&priv->kobj, KOBJ_ADD);
    return 0;
out_unregister:
    kfree(drv->p);
    drv->p = NULL;
    kobject_put(&priv->kobj);
out_put_bus:
    bus_put(bus);
    return error;
}

 
 

[platform]linux platform device/driver(二)--Platform Device和Platform_driver注册过程之详细代码的更多相关文章

  1. [platform]linux platform device/driver(三)--Platform Device和Platform_driver注册过程之代码对比

    转自:http://blog.csdn.net/thl789/article/details/6723350 Linux 2.6的设备驱动模型中,所有的device都是通过Bus相连.device_r ...

  2. Platform device/driver注册过程

    Platform是一种虚拟总线,Platform机制将设备本身的资源注册进内核,有内核统一管理,在驱动程序使用这些资源时使用统一的接口,这样提高了程序的可移植性. Linux的大部分设备驱动都可以使用 ...

  3. Samsung_tiny4412(驱动笔记10)----mdev,bus,device,driver,platform

    /*********************************************************************************** * * mdev,bus,de ...

  4. linux 内核驱动--Platform Device和Platform_driver注册过程

    linux 内核驱动--Platform Device和Platform_driver注册过程 从 Linux 2.6 起引入了一套新的驱动管理和注册机制 :Platform_device 和 Pla ...

  5. 探究linux设备驱动模型之——platform虚拟总线(二)

    上回说到,platform_match是驱动和设备之间的媒人婆,那么platform_match是如何匹配驱动和设备的呢?platform总线定义的匹配条件很简单,主要就是查看驱动结构体和设备结构体的 ...

  6. 【linux设备模型】之platform设备驱动

    一.platform总线.设备和驱动     platform是一种虚拟总线,对应的设备称为platform_device,对应的驱动称为platform_driver. platform_devic ...

  7. I.MX6 Linux I2C device& driver hacking

    /******************************************************************************************* * I.MX6 ...

  8. Linux spi驱动分析(二)----SPI核心(bus、device_driver和device)

    一.spi总线注册 这里所说的SPI核心,就是指/drivers/spi/目录下spi.c文件中提供给其他文件的函数,首先看下spi核心的初始化函数spi_init(void).程序如下: 点击(此处 ...

  9. Linux Device Driver && Device File

    catalog . 设备驱动程序简介 . I/O体系结构 . 访问设备 . 与文件系统关联 . 字符设备操作 . 块设备操作 . 资源分配 . 总线系统 1. 设备驱动程序简介 设备驱动程序是内核的关 ...

随机推荐

  1. Apache HttpClient使用之阻塞陷阱

    前言: 之前做个一个数据同步的定时程序. 其内部集成了某电商的SDK(简单的Apache Httpclient4.x封装)+Spring Quartz来实现. 原本以为简单轻松, 喝杯咖啡就高枕无忧的 ...

  2. [转载]NoSQL by Martin Flower

    ============================================================== URL1 nosql ========================== ...

  3. JS初学之-for套for遍历二维数组

    <!doctype html><html><head><meta charset="utf-8"><title>无标题文 ...

  4. 三国游戏 2010年NOIP全国联赛普及组

    题目描述 Description 小涵很喜欢电脑游戏,这些天他正在玩一个叫做<三国>的游戏. 在游戏中,小涵和计算机各执一方,组建各自的军队进行对战.游戏中共有N 位武将(N 为偶数且不小 ...

  5. win7win8一键取得超级管理员权限

    win7win8有时会出现删除不了文件的情况,弹窗提示需要某某权限,下面为解决办法: 在普通帐户中快速获得最高的权限: 新建一个.txt文本,将下面的代码复制粘贴到文件中,另存为或重命名为.reg文件 ...

  6. Python正则表达式总结

    正则表达式也一直用,但是没系统的总结过,今天借这个时间梳理一下. Python中的正则表达式操作依靠re模块儿完成. 常用的方法: re.compile(pattern,flags=0) #返回一个编 ...

  7. java多线程:jdk并发包的总结(转载)

    转载地址:http://blog.csdn.net/yangbutao/article/details/8479520 1.java 高并发包所采用的几个机制(CAS,volatile,抽象队列同步) ...

  8. 如何在WTL和MFC中使用duilib及如何静态使用duilib库!(初级讲解 附带一个Demo)

    关于duilib的历史,我也就不多说了,能看到这篇文章的人都是有一定了解才能找到这个的. 我直接说下对这个库的基本使用吧. 我个人对一些好技术都是比较感兴趣的. 因为个人原因 喜欢接触一个好技术. 所 ...

  9. 免费web api接口大全

    下面的接口来自互联网,部分功能需要付费 查询手机 http://www.yodao.com/smartresult-xml/search.s?type=mobile&q=手机号码 查询IP h ...

  10. linux包-下载-curl与wget

    [root@localhost ~]# rpm -qa|grep curllibcurl-7.19.7-37.el6_4.x86_64python-pycurl-7.19.0-8.el6.x86_64 ...