platform device和platform driver

platform device和platform driver从Linux2.6起引入了一套新的驱动管理和注册机制:Platform_device和Platform_driver。Linux中大部分的设备驱动,都可以使用这套机制,设备用Platform_device表示,驱动用Platform_driver进行注册。Li

大家好,又见面了,我是你们的朋友全栈君。

从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;
//  作为IOPORT资源分配
        }

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

PS:smdk6410_machine_init–>platform_add_devices–>platform_device_register–>platform_device_add
 

1、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驱动
}

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

        /* How the hell do we get out of this pickle? Give up */
        printk(KERN_ERR “%s: driver_add_attrs(%s) failed\n”,
            __func__, drv->name);
    }
    error = add_bind_files(drv);
    if (error) {

        /* Ditto */
        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;
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/163807.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 压测TPS_测压管原理

    压测TPS_测压管原理1.TPS、并发量是什么关系?为什么有的地⽅要⽤TPS?有的地⽅要⽤并发?⾸先,TPS是⼀个吞吐速度的概念,就是每秒处理多少请求。是衡量系统处理能⼒的指标,⽽往往TPS的最⼤值,并⾮系统资源耗尽的时点,因为TPS和系统资源是⼀个抛物线的关系,就是当资源最优配置时往往是TPS最⾼的时间,当资源耗尽时,往往TPS也是⾮常低的。每个TPS指标都会对应当时的并发量。然后说说并发量,并发量往往是对⼀个系统同时操作的⼈数的,或者说同时产⽣的请求数的预估,来衡量系统的承载能⼒。⾔外之意,这个指标⽬的在于看能否同时承载

    2022年10月21日
  • vue组件化的理解_vue的组件化是如何实现的

    vue组件化的理解_vue的组件化是如何实现的前言有时候有一组html结构的代码,并且这个上面可能还绑定了事件。然后这段代码可能有多个地方都被使用到了,如果都是拷贝来拷贝去,很多代码都是重复的,包括事件部分的代码都是重复的。那么这时候我们就可以

  • PostGIS 报错libcrypto[通俗易懂]

    PostGIS 报错libcrypto[通俗易懂]说明在安装完Postgresql以后,打postgis扩展时,报错ERROR:couldnotloadlibrary”/usr/pgsql-12/lib/rtpostgis.so”:/usr/pgsql-12/lib/libpq.so.10:symbolX509_get_signature_nid,versionlibcrypto.so.10notdefinedinfilelibcrypto.so.10withlinktimereference出现问题环境

  • 基于STM32F4单片机对步进电机的控制(有代码)

    基于STM32F4单片机对步进电机的控制(有代码)步进电机是将电脉冲控制信号转变为角位移或线位移的一种常用的数字控制执行元件,又称为脉冲电机。在驱动电源的作用下,步进电机受到脉冲的控制,其转子的角位移量和速度严格地与输入脉冲的数量和脉冲频率成正比。步进电机每接收一个电脉冲,转子就转过一个相应的角度(步距角)。改变通电顺序可改变步进电动机的旋转方向;改变通电频率可改变步进电动机的转速。因此,通过控制输入电脉冲的数目、频率及电动机绕组的通电顺序就可以…

  • python之抛出异常(raise语句)「建议收藏」

    python之抛出异常(raise语句)「建议收藏」用raise能够主动抛出异常,使用【raise异常名】抛出该类异常raiseIndexError也可以单独使用raise再次引发刚发生的异常try:1/0except:print(“错误”)raise同时能够指定抛出异常的描述信息raiseIndexError(“索引错误”)所以这样子就用【raise异常名from异…

  • c++面试选择题_C语言经典笔试题

    c++面试选择题_C语言经典笔试题1.new、delete、malloc、free关系delete会调用对象的析构函数,和new对应free只会释放内存,new调用构造函数。malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号