blob: ec4995168a46514a5ee7a3e16b5144fe40136482 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* KUnit basic device implementation
*
* Implementation of struct kunit_device helpers.
*
* Copyright (C) 2023, Google LLC.
* Author: David Gow <davidgow@google.com>
*/
#include <linux/device.h>
#include <kunit/test.h>
#include <kunit/resource.h>
static struct device kunit_bus = {
.init_name = "kunit"
};
/* A device owned by a KUnit test. */
struct kunit_device {
struct device dev;
struct kunit *owner;
/* Force binding to a specific driver. */
struct device_driver *driver;
};
static inline struct kunit_device *to_kunit_device(struct device *d)
{
return container_of(d, struct kunit_device, dev);
}
static int kunit_bus_match(struct device *dev, struct device_driver *driver)
{
struct kunit_device *kunit_dev = to_kunit_device(dev);
if (kunit_dev->driver == driver)
return 1;
return 0;
}
static struct bus_type kunit_bus_type = {
.name = "kunit",
.match = kunit_bus_match
};
int kunit_bus_init(void)
{
int error;
error = bus_register(&kunit_bus_type);
if (!error) {
error = device_register(&kunit_bus);
if (error)
bus_unregister(&kunit_bus_type);
}
return error;
}
late_initcall(kunit_bus_init);
static void kunit_device_release(struct device *d)
{
kfree(to_kunit_device(d));
}
struct device_driver *kunit_driver_create(struct kunit *test, const char *name)
{
struct device_driver *driver;
int err = -ENOMEM;
driver = kunit_kzalloc(test, sizeof(*driver), GFP_KERNEL);
if (!driver)
return ERR_PTR(err);
driver->name = name;
driver->bus = &kunit_bus_type;
/* TODO: Make this point to the individual test module, not KUnit itself. */
driver->owner = THIS_MODULE;
/* TODO: mod_name */
err = driver_register(driver);
if (err) {
kunit_kfree(test, driver);
return ERR_PTR(err);
}
kunit_defer(test, (kunit_defer_function_t)driver_unregister, driver, GFP_KERNEL);
return driver;
}
struct device *kunit_device_register_with_driver(struct kunit *test, const char *name, struct device_driver *drv)
{
struct kunit_device *kunit_dev;
int err = -ENOMEM;
kunit_dev = kzalloc(sizeof(struct kunit_device), GFP_KERNEL);
if (!kunit_dev)
return ERR_PTR(err);
kunit_dev->owner = test;
err = dev_set_name(&kunit_dev->dev, "%s.%s", test->name, name);
if (err) {
kfree(kunit_dev);
return ERR_PTR(err);
}
/* Set the expected driver pointer, so we match. */
kunit_dev->driver = drv;
kunit_dev->dev.release = kunit_device_release;
kunit_dev->dev.bus = &kunit_bus_type;
kunit_dev->dev.parent = &kunit_bus;
err = device_register(&kunit_dev->dev);
if (err) {
put_device(&kunit_dev->dev);
return ERR_PTR(err);
}
kunit_defer(test, (kunit_defer_function_t)device_unregister, &kunit_dev->dev, GFP_KERNEL);
return &kunit_dev->dev;
}
EXPORT_SYMBOL_GPL(kunit_device_register_with_driver);
struct device *kunit_device_register(struct kunit *test, const char *name)
{
struct device_driver *drv = kunit_driver_create(test, name);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drv);
return kunit_device_register_with_driver(test, name, drv);
}
EXPORT_SYMBOL_GPL(kunit_device_register);
void kunit_device_unregister(struct kunit *test, struct device *dev)
{
kunit_defer_trigger(test, (kunit_defer_function_t)device_unregister, dev);
}
EXPORT_SYMBOL_GPL(kunit_device_unregister);