platform device和platform driver

來源:互聯網
上載者:User

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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.