2016年1月2日 星期六

Linux Kernel(15)- Platform Devices


很多人心中都有過一個問題What is the difference between Platform driver and normal device driver?,簡單的來說Platform devices就non-discoverable,也就是device本身沒辦法跟系統說"我在這裡",典型的就是I2C device,它不會通知kernel"我在這裡",通常是預先知道有個I2C device在那裡,再由software設定好,這類non-discoverable device就適用Platform devices架構來寫。
platform device會被connect在platform bus上,而platform bus是一個虛擬的bus(pseudo-bus),這樣可以讓整個架構platform driver符合Linux的標準driver model。

這篇會根據The platform device API教導如何寫一個簡單的Platform devices,基本上最基本的platform device只需要name,因為platform bus會根據platform device與platform driver的name是否match執行driver的probe(),而最簡單的platform driver只需要name,跟probe()與remove()即可。
#include <linux/module.h>
#include <linux/platform_device.h>

MODULE_AUTHOR("Brook");
MODULE_DESCRIPTION("Kernel module for demo");
MODULE_LICENSE("GPL");

#define DEVNAME "brook"

#define DYN_ALLOC 1

static struct platform_device brook_device = {
    .name = DEVNAME,
};

static int brook_probe(struct platform_device *pdev)
{
    pr_info("%s(#%d)\n", __func__, __LINE__);
    return 0;
}

static int brook_remove(struct platform_device *pdev)
{
    pr_info("%s(#%d)\n", __func__, __LINE__);
    return 0;
}

static struct platform_driver brook_driver = {
    .driver = {
        .name  = DEVNAME,
        .owner = THIS_MODULE,
    },
    .probe  = brook_probe,
    .remove = brook_remove,
};

static int __init brook_init(void)
{
    int err;
    pr_info("%s(#%d)\n", __func__, __LINE__);

    err = platform_device_register(&brook_device);
    if (err) {
        pr_err("%s(#%d): platform_device_register failed(%d)\n",
                __func__, __LINE__, err);
        return err;
    }

    err = platform_driver_register(&brook_driver);
    if (err) {
        dev_err(&(brook_device.dev), "%s(#%d): platform_driver_register fail(%d)\n",
                __func__, __LINE__, err);
        goto dev_reg_failed;
    }
    return err;

dev_reg_failed:
    platform_device_unregister(&brook_device);

    return err;
}
module_init(brook_init);

static void __exit brook_exit(void)
{
    dev_info(&(brook_device.dev), "%s(#%d)\n", __func__, __LINE__);
    platform_device_unregister(&brook_device);
    platform_driver_unregister(&brook_driver);
}
module_exit(brook_exit);


使用platform_device_register()會導致"brook.0" does not have a release() function, it is broken and must be fixed.的OOPS,可以改用platform_device_alloc() + platform_device_add(),platform_device_alloc()裡面就會做pa->pdev.dev.release = platform_device_release。
#include <linux/module.h>
#include <linux/platform_device.h>

MODULE_AUTHOR("Brook");
MODULE_DESCRIPTION("Kernel module for demo");
MODULE_LICENSE("GPL");

#define DEVNAME "brook"

#define DYN_ALLOC 1

static struct platform_device *brook_device;

static int brook_probe(struct platform_device *pdev)
{
    pr_info("%s(#%d)\n", __func__, __LINE__);
    return 0;
}

static int brook_remove(struct platform_device *pdev)
{
    pr_info("%s(#%d)\n", __func__, __LINE__);
    return 0;
}

static struct platform_driver brook_driver = {
    .driver = {
        .name  = DEVNAME,
        .owner = THIS_MODULE,
    },
    .probe  = brook_probe,
    .remove = brook_remove,
};

static int __init brook_init(void)
{
    int err;
    pr_info("%s(#%d)\n", __func__, __LINE__);

    /* using platform_device_alloc() + platform_device_add() 
     * instead of platform_device_register() to avoid the OOPS, 
     *     "Device 'brook.0' does not have a release() function,
     *      it is broken and must be fixed."
     */
    brook_device = platform_device_alloc(DEVNAME, 0);
    if (!brook_device) {
        pr_err("%s(#%d): platform_device_alloc fail\n",
               __func__, __LINE__);
        return -ENOMEM;
    }

    err = platform_device_add(brook_device);
    if (err) {
        pr_err("%s(#%d): platform_device_add failed\n",
               __func__, __LINE__);
        goto dev_add_failed;
    }

    err = platform_driver_register(&brook_driver);
    if (err) {
        dev_err(&(brook_device->dev), "%s(#%d): platform_driver_register fail(%d)\n",
                __func__, __LINE__, err);
        goto dev_reg_failed;
    }
    return err;

dev_add_failed:
    platform_device_put(brook_device);
dev_reg_failed:
    platform_device_unregister(brook_device);

    return err;
}
module_init(brook_init);

static void __exit brook_exit(void)
{
    dev_info(&(brook_device->dev), "%s(#%d)\n", __func__, __LINE__);
    platform_device_unregister(brook_device);
    platform_driver_unregister(&brook_driver);
}
module_exit(brook_exit);



    參考資料:
  1. Documentation/driver-model
  2. What is the difference between Platform driver and normal device driver?
  3. The platform device API
  4. Linux Kernel architecture for device drivers
  5. platform_driver_register()--如何match之后调用probe
  6. Improved dynamically allocated platform_device interface




1 則留言:

熱門文章