What is my parent device and how do I get a pointer to it?

Viewed 226

I'm writing a pseudo-driver to emulate a piece of hardware that does not yet exist. The pseudo driver will emulate some of the functionality of the hardware (obviously lying about actual underlying data). This is to test other parts of the system while waiting on hardware.

Obviously the actual hardware will have a valid parent device (it will be on a PCI bus, or USB, or something else), but my emulated device isn't hosted by any of these. However, I am not writing a top level device.

Driver registration requires a pointer to a struct device *parent. How do I get this information for a virtual driver?

1 Answers

You could allocate a dummy platform device and set up a platform device driver for testing typical driver "probe" and "remove" functionality. Here is a somewhat minimal example:

// SPDX-License-Identifier: GPL-2.0 OR MIT
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>

MODULE_AUTHOR("Ian Abbott");
MODULE_LICENSE("Dual MIT/GPL");
MODULE_DESCRIPTION("FooBar driver example with dummy platform device");

#define FOOBAR_NAME "foobar"

/* Private data for a foobar device. */
struct foobar_private {
    /* ... */
    struct device *hwdev;   /* Pointer to hardware device. */
    /* ... */
};

static int foobar_platform_probe(struct platform_device *pdev)
{
    struct device *hwdev = &pdev->dev;
    struct foobar_private *foobar;

    dev_info(hwdev, "probe\n");

    /* Allocate private data. */
    foobar = kzalloc(sizeof(*foobar), GFP_KERNEL);
    if (!foobar)
        return -ENOMEM;

    /* Set up private data. */
    foobar->hwdev = hwdev;

    /* Link private data to hardware device. */
    dev_set_drvdata(hwdev, foobar);

    /* ... other stuff ... */

    return 0;
}

static int foobar_platform_remove(struct platform_device *pdev)
{
    struct device *hwdev = &pdev->dev;
    struct foobar_private *foobar = dev_get_drvdata(hwdev);

    dev_info(hwdev, "remove\n");

    dev_set_drvdata(hwdev, NULL);

    /* ... other stuff ... */

    kfree(foobar);

    return 0;
}


/* Platform driver. */
static struct platform_driver foobar_driver = {
    .probe = foobar_platform_probe,
    .remove = foobar_platform_remove,
    .driver = {
        .name = FOOBAR_NAME,
        .owner = THIS_MODULE,
    },
};

/* Pointer to dummy platform device for testing purposes. */
static struct platform_device *foobar_test_device;

static int __init foobar_init(void)
{
    int err;

    /* Register the platform driver. */
    err = platform_driver_register(&foobar_driver);
    if (err)
        return err;

    /*
     * Create a dummy platform device for testing.
     * Make the platform device name the same as the driver name
     * so that the driver's "probe" function will be called when
     * the device is registered later.
     *
     * Note: PLATFORM_DEVID_AUTO allocates an "automatic" instance
     * ID for the device.  Can use PLATFORM_DEVID_NONE for no instance
     * ID (for a single device), or use a non-zero integer to set a
     * specific instance ID.
     */
    foobar_test_device =
        platform_device_alloc(FOOBAR_NAME, PLATFORM_DEVID_AUTO);
    if (!foobar_test_device) {
        err = -ENOMEM;
        goto err_unregister_driver;
    }

    /*
     * Can call functions such as platform_device_add_resources(),
     * platform_device_add_data(), platform_device_add_properties(),
     * etc. here for playing around.
     */

    /*
     * Add (register) the dummy platform device.
     * This should result in the driver's "probe" function being called.
     */
    err = platform_device_add(foobar_test_device);
    if (err)
        goto err_free_device;

    return 0;

err_free_device:
    /* Undo platform_device_alloc(). */
    platform_device_put(foobar_test_device);
err_unregister_driver:
    /* Undo platform_driver_register(). */
    platform_driver_unregister(&foobar_driver);
    return err;
}

static void __exit foobar_exit(void)
{
    /*
     * Unregister the dummy platform device used for testing.
     * This should result in the driver's "remove" function being called.
     */
    platform_device_unregister(foobar_test_device);

    /* Unregister the platform driver. */
    platform_driver_unregister(&foobar_driver);
}

module_init(foobar_init);
module_exit(foobar_exit);

If the struct platform_device's dev.parent member is NULL, platform_device_add() will change it to &platform_device.

Related