2016年1月3日 星期日

Linux Kernel(15.1)- platform_driver_register()之如何调用driver.probe()



轉自platform_driver_register()--如何match之后调用probe

int platform_driver_register(struct platform_driver *drv)
{
    drv->driver.bus = &platform_bus_type;/*关联总线*/
    /*关联driver的设备方法*/
    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);/*注册驱动*/
}

/******************************************************************************/
struct platform_driver {
    int (*probe)(struct platform_device *);/*匹配到设备后调用,下面分析内核代码怎么调用的*/
    int (*remove)(struct platform_device *);
    void (*shutdown)(struct platform_device *);
    int (*suspend)(struct platform_device *, pm_message_t state);
    int (*resume)(struct platform_device *);
    struct device_driver driver;
    const struct platform_device_id *id_table;
};

struct bus_type platform_bus_type = {
    .name        = "platform",
    .dev_attrs    = platform_dev_attrs,
    .match        = platform_match,
    .uevent        = platform_uevent,
    .pm        = &platform_dev_pm_ops,
};
/********************************************************************************/

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

    if (!drv->suppress_bind_attrs) {
        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:
    kobject_put(&priv->kobj);
    kfree(drv->p);
    drv->p = NULL;
out_put_bus:
    bus_put(bus);
    return error;
}

int driver_attach(struct device_driver *drv)
{
    /*对总线上的每一个设备都调用__driver_attach*/
    return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}

static int __driver_attach(struct device *dev, void *data)
{
    struct device_driver *drv = data;

    /*
     * Lock device and try to bind to it. We drop the error
     * here and always return 0, because we need to keep trying
     * to bind to devices and some drivers will return an error
     * simply if it didn't support the device.
     *
     * driver_probe_device() will spit a warning if there
     * is an error.
     */

    if (!driver_match_device(drv, dev))
        return 0;

    if (dev->parent)    /* Needed for USB */
        device_lock(dev->parent);
    device_lock(dev);
    if (!dev->driver)
        driver_probe_device(drv, dev);
    device_unlock(dev);
    if (dev->parent)
        device_unlock(dev->parent);

    return 0;
}

static inline int driver_match_device(struct device_driver *drv,
                      struct device *dev)
{
    /*调用总线的match去匹配设备和驱动*/
    return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}

int driver_probe_device(struct device_driver *drv, struct device *dev)
{
    int ret = 0;

    if (!device_is_registered(dev))
        return -ENODEV;

    pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
         drv->bus->name, __func__, dev_name(dev), drv->name);

    pm_runtime_get_noresume(dev);
    pm_runtime_barrier(dev);
    ret = really_probe(dev, drv);
    pm_runtime_put_sync(dev);

    return ret;
}
static int really_probe(struct device *dev, struct device_driver *drv)
{
    int ret = 0;

    atomic_inc(&probe_count);
    pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
         drv->bus->name, __func__, drv->name, dev_name(dev));
    WARN_ON(!list_empty(&dev->devres_head));

    dev->driver = drv;
    if (driver_sysfs_add(dev)) {
        printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
            __func__, dev_name(dev));
        goto probe_failed;
    }
/**********************************************************************************/
    if (dev->bus->probe) {/*首先看总线有没有probe函数,若有则调用,而平台总线没有probe*/
        ret = dev->bus->probe(dev);
        if (ret)
            goto probe_failed;
    } else if (drv->probe) {/*然后看驱动有没有probe函数,若有则调用,*/
        ret = drv->probe(dev);
        if (ret)
            goto probe_failed;
    }
/************************************************************************************/

    driver_bound(dev);
    ret = 1;
    pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
         drv->bus->name, __func__, dev_name(dev), drv->name);
    goto done;

probe_failed:
    devres_release_all(dev);
    driver_sysfs_remove(dev);
    dev->driver = NULL;

    if (ret != -ENODEV && ret != -ENXIO) {
        /* driver matched but the probe failed */
        printk(KERN_WARNING
               "%s: probe of %s failed with error %d\n",
               drv->name, dev_name(dev), ret);
    }
    /*
     * Ignore errors returned by ->probe so that the next driver can try
     * its luck.
     */
    ret = 0;
done:
    atomic_dec(&probe_count);
    wake_up(&probe_waitqueue);
    return ret;
}


/*平台总线的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;

    /* fall-back to driver name match */
    return (strcmp(pdev->name, drv->name) == 0);/*驱动名字与设备名字要匹配*/
}




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




2015年12月26日 星期六

OpenEmbedded -- Creating and using an SDK


SDK包含了開發時所需的相關檔案,有些還包含了整個開發環境(tool/command),避免系統的tool/command跟SDK需要的不同造成的error,一個SDK應該包含以下內容:(YOCTO Slides)
  1. Compilers or cross-compilers
  2. Linkers
  3. Library headers
  4. Debuggers
  5. Custom utilities


這篇文章就是在介紹如何使用Openembedded建立一個generic SDK供他人開發application,使用指令bitbake -v meta-toolchain即可產生generic SDK,並產生一個install的script,放置於oe/oe-core/build/tmp-eglibc/deploy/sdk/底下,執行過程如下
brook@vista:/home/brook/oe/oe-core/build$ bitbake -v meta-toolchain

...

+ chmod +x /home/brook/oe/oe-core/build/tmp-eglibc/deploy/sdk/oecore-x86_64-armv7a-vfp-neon-toolchain-oe-core.0.sh

+ cat /home/brook/oe/oe-core/build/tmp-eglibc/deploy/sdk/oecore-x86_64-armv7a-vfp-neon-toolchain-oe-core.0.tar.bz2

+ rm /home/brook/oe/oe-core/build/tmp-eglibc/deploy/sdk/oecore-x86_64-armv7a-vfp-neon-toolchain-oe-core.0.tar.bz2


NOTE: Tasks Summary: Attempted 1685 tasks of which 1312 didn't need to be rerun and all succeeded.
brook@vista:/home/brook/oe/oe-core/build$ tree tmp-eglibc/deploy/sdk/
tmp-eglibc/deploy/sdk/
`-- oecore-x86_64-armv7a-vfp-neon-toolchain-oe-core.0.sh

0 directories, 1 file

接著直要將底下的script(oecore-x86_64-armv7a-vfp-neon-toolchain-oe-core.0.sh)給developer開發即可,-d後面帶安裝目錄,安裝過程需要SUDO權限,
brook@vista:/home/brook/oe/oe-core/build$ ./tmp-eglibc/deploy/sdk/oecore-x86_64-armv7a-vfp-neon-toolchain-oe-core.0.sh --help
Usage: oecore-x86_64-armv7a-vfp-neon-toolchain-oe-core.0.sh [-y] [-d <dir>]
  -y         Automatic yes to all prompts
  -d <dir>   Install the SDK to <dir>
======== Advanced DEBUGGING ONLY OPTIONS ========
  -S         Save relocation scripts
  -R         Do not relocate executables
  -D         use set -x to see what is going on
brook@vista:~$ /home/brook/oe/oe-core/build/tmp-eglibc/deploy/sdk/oecore-x86_64-armv7a-vfp-neon-toolchain-oe-core.0.sh -d /opt/oe -D
+ printf 'Enter target directory for SDK (default: /usr/local/oecore-x86_64): '
Enter target directory for SDK (default: /usr/local/oecore-x86_64): + '[' /opt/oe = '' ']'
+ echo /opt/oe
/opt/oe

...

+ '[' 0 = 0 ']'
+ /usr/bin/sudo rm /SSD/opt/oe/relocate_sdk.py /SSD/opt/oe/relocate_sdk.sh
+ echo 'SDK has been successfully set up and is ready to be used.'
SDK has been successfully set up and is ready to be used.
+ exit 0

要使用SDK之前,必須先source安裝目錄下開頭為environment-setup-的script,設置相關的環境變數,我的script內容如下
export PATH=/SSD/opt/oe/sysroots/x86_64-oesdk-linux/usr/bin:/SSD/opt/oe/sysroots/x86_64-oesdk-linux/usr/bin/armv
7a-vfp-neon-oe-linux-gnueabi:$PATH
export PKG_CONFIG_SYSROOT_DIR=/SSD/opt/oe/sysroots/armv7a-vfp-neon-oe-linux-gnueabi
export PKG_CONFIG_PATH=/SSD/opt/oe/sysroots/armv7a-vfp-neon-oe-linux-gnueabi/usr/lib/pkgconfig
export CONFIG_SITE=/SSD/opt/oe/site-config-armv7a-vfp-neon-oe-linux-gnueabi
export CC="arm-oe-linux-gnueabi-gcc  -march=armv7-a -mthumb-interwork -mfloat-abi=softfp -mfpu=neon -mthumb-inte
rwork --sysroot=/SSD/opt/oe/sysroots/armv7a-vfp-neon-oe-linux-gnueabi"
export CXX="arm-oe-linux-gnueabi-g++  -march=armv7-a -mthumb-interwork -mfloat-abi=softfp -mfpu=neon -mthumb-int
erwork --sysroot=/SSD/opt/oe/sysroots/armv7a-vfp-neon-oe-linux-gnueabi"
export CPP="arm-oe-linux-gnueabi-gcc -E  -march=armv7-a -mthumb-interwork -mfloat-abi=softfp -mfpu=neon -mthumb-interwork --sysroot=/SSD/opt/oe/sysroots/armv7a-vfp-neon-oe-linux-gnueabi"
export AS="arm-oe-linux-gnueabi-as "
export LD="arm-oe-linux-gnueabi-ld  --sysroot=/SSD/opt/oe/sysroots/armv7a-vfp-neon-oe-linux-gnueabi"
export GDB=arm-oe-linux-gnueabi-gdb
export STRIP=arm-oe-linux-gnueabi-strip
export RANLIB=arm-oe-linux-gnueabi-ranlib
export OBJCOPY=arm-oe-linux-gnueabi-objcopy
export OBJDUMP=arm-oe-linux-gnueabi-objdump
export AR=arm-oe-linux-gnueabi-ar
export NM=arm-oe-linux-gnueabi-nm
export M4=m4
export TARGET_PREFIX=arm-oe-linux-gnueabi-
export CONFIGURE_FLAGS="--target=arm-oe-linux-gnueabi --host=arm-oe-linux-gnueabi --build=x86_64-linux --with-libtool-sysroot=/SSD/opt/oe/sysroots/armv7a-vfp-neon-oe-linux-gnueabi"
export CFLAGS=" -O2 -fexpensive-optimizations -frename-registers -fomit-frame-pointer"
export CXXFLAGS=" -O2 -fexpensive-optimizations -frename-registers -fomit-frame-pointer -fpermissive"
export LDFLAGS="-Wl,-O1 -Wl,--hash-style=gnu -Wl,--as-needed"
export CPPFLAGS=""
export OECORE_NATIVE_SYSROOT="/SSD/opt/oe/sysroots/x86_64-oesdk-linux"
export OECORE_TARGET_SYSROOT="/SSD/opt/oe/sysroots/armv7a-vfp-neon-oe-linux-gnueabi"
export OECORE_ACLOCAL_OPTS="-I /SSD/opt/oe/sysroots/x86_64-oesdk-linux/usr/share/aclocal"
export OECORE_DISTRO_VERSION="20151225"
export OECORE_SDK_VERSION="oe-core.0"
export PYTHONHOME=/SSD/opt/oe/sysroots/x86_64-oesdk-linux/usr
export ARCH=arm
export CROSS_COMPILE=arm-oe-linux-gnueabi-


以下是我用這SDK編譯一個檔案,並放入target中執行
brook@vista:~$ source /opt/oe/environment-setup-armv7a-vfp-neon-oe-linux-gnueabi
brook@vista:~$ cat main.c
#include <stdio.h>

int main(int argc, char *argv[])
{
        printf("hello\n");
        return 0;
}

brook@vista:~$ $CC -o brook-sdk main.c

D:\Projects>adb push z:\brook-sdk /
688 KB/s (5644 bytes in 0.008s)

D:\Projects>adb shell chmod +x /brook-sdk

D:\Projects>adb shell /brook-sdk
hello

開發環境架構圖


    參考資料:
  1. Yocto Project and OpenEmbedded development course
  2. Yocto Project Quick Start -- SDK Generation





熱門文章