303 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Test managed platform driver4 */5 6#include <linux/completion.h>7#include <linux/device/bus.h>8#include <linux/device/driver.h>9#include <linux/platform_device.h>10 11#include <kunit/platform_device.h>12#include <kunit/resource.h>13 14struct kunit_platform_device_alloc_params {15 const char *name;16 int id;17};18 19static int kunit_platform_device_alloc_init(struct kunit_resource *res, void *context)20{21 struct kunit_platform_device_alloc_params *params = context;22 struct platform_device *pdev;23 24 pdev = platform_device_alloc(params->name, params->id);25 if (!pdev)26 return -ENOMEM;27 28 res->data = pdev;29 30 return 0;31}32 33static void kunit_platform_device_alloc_exit(struct kunit_resource *res)34{35 struct platform_device *pdev = res->data;36 37 platform_device_put(pdev);38}39 40/**41 * kunit_platform_device_alloc() - Allocate a KUnit test managed platform device42 * @test: test context43 * @name: device name of platform device to alloc44 * @id: identifier of platform device to alloc.45 *46 * Allocate a test managed platform device. The device is put when the test completes.47 *48 * Return: Allocated platform device on success, NULL on failure.49 */50struct platform_device *51kunit_platform_device_alloc(struct kunit *test, const char *name, int id)52{53 struct kunit_platform_device_alloc_params params = {54 .name = name,55 .id = id,56 };57 58 return kunit_alloc_resource(test,59 kunit_platform_device_alloc_init,60 kunit_platform_device_alloc_exit,61 GFP_KERNEL, ¶ms);62}63EXPORT_SYMBOL_GPL(kunit_platform_device_alloc);64 65static void kunit_platform_device_add_exit(struct kunit_resource *res)66{67 struct platform_device *pdev = res->data;68 69 platform_device_unregister(pdev);70}71 72static bool73kunit_platform_device_alloc_match(struct kunit *test,74 struct kunit_resource *res, void *match_data)75{76 struct platform_device *pdev = match_data;77 78 return res->data == pdev && res->free == kunit_platform_device_alloc_exit;79}80 81KUNIT_DEFINE_ACTION_WRAPPER(platform_device_unregister_wrapper,82 platform_device_unregister, struct platform_device *);83/**84 * kunit_platform_device_add() - Register a KUnit test managed platform device85 * @test: test context86 * @pdev: platform device to add87 *88 * Register a test managed platform device. The device is unregistered when the89 * test completes.90 *91 * Return: 0 on success, negative errno on failure.92 */93int kunit_platform_device_add(struct kunit *test, struct platform_device *pdev)94{95 struct kunit_resource *res;96 int ret;97 98 ret = platform_device_add(pdev);99 if (ret)100 return ret;101 102 res = kunit_find_resource(test, kunit_platform_device_alloc_match, pdev);103 if (res) {104 /*105 * Transfer the reference count of the platform device if it106 * was allocated with kunit_platform_device_alloc(). In this107 * case, calling platform_device_put() when the test exits from108 * kunit_platform_device_alloc_exit() would lead to reference109 * count underflow because platform_device_unregister_wrapper()110 * calls platform_device_unregister() which also calls111 * platform_device_put().112 *113 * Usually callers transfer the refcount initialized in114 * platform_device_alloc() to platform_device_add() by calling115 * platform_device_unregister() when platform_device_add()116 * succeeds or platform_device_put() when it fails. KUnit has to117 * keep this straight by redirecting the free routine for the118 * resource to the right function. Luckily this only has to119 * account for the success scenario.120 */121 res->free = kunit_platform_device_add_exit;122 kunit_put_resource(res);123 } else {124 ret = kunit_add_action_or_reset(test, platform_device_unregister_wrapper, pdev);125 if (ret)126 return ret;127 }128 129 return 0;130}131EXPORT_SYMBOL_GPL(kunit_platform_device_add);132 133struct kunit_platform_device_probe_nb {134 struct completion *x;135 struct device *dev;136 struct notifier_block nb;137};138 139static int kunit_platform_device_probe_notify(struct notifier_block *nb,140 unsigned long event, void *data)141{142 struct kunit_platform_device_probe_nb *knb;143 struct device *dev = data;144 145 knb = container_of(nb, struct kunit_platform_device_probe_nb, nb);146 if (event != BUS_NOTIFY_BOUND_DRIVER || knb->dev != dev)147 return NOTIFY_DONE;148 149 complete(knb->x);150 151 return NOTIFY_OK;152}153 154static void kunit_platform_device_probe_nb_remove(void *nb)155{156 bus_unregister_notifier(&platform_bus_type, nb);157}158 159/**160 * kunit_platform_device_prepare_wait_for_probe() - Prepare a completion161 * variable to wait for a platform device to probe162 * @test: test context163 * @pdev: platform device to prepare to wait for probe of164 * @x: completion variable completed when @dev has probed165 *166 * Prepare a completion variable @x to wait for @pdev to probe. Waiting on the167 * completion forces a preemption, allowing the platform driver to probe.168 *169 * Example170 *171 * .. code-block:: c172 *173 * static int kunit_platform_driver_probe(struct platform_device *pdev)174 * {175 * return 0;176 * }177 *178 * static void kunit_platform_driver_test(struct kunit *test)179 * {180 * struct platform_device *pdev;181 * struct platform_driver *pdrv;182 * DECLARE_COMPLETION_ONSTACK(comp);183 *184 * pdev = kunit_platform_device_alloc(test, "kunit-platform", -1);185 * KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);186 * KUNIT_ASSERT_EQ(test, 0, kunit_platform_device_add(test, pdev));187 *188 * pdrv = kunit_kzalloc(test, sizeof(*pdrv), GFP_KERNEL);189 * KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdrv);190 *191 * pdrv->probe = kunit_platform_driver_probe;192 * pdrv->driver.name = "kunit-platform";193 * pdrv->driver.owner = THIS_MODULE;194 *195 * KUNIT_ASSERT_EQ(test, 0, kunit_platform_device_prepare_wait_for_probe(test, pdev, &comp));196 * KUNIT_ASSERT_EQ(test, 0, kunit_platform_driver_register(test, pdrv));197 *198 * KUNIT_EXPECT_NE(test, 0, wait_for_completion_timeout(&comp, 3 * HZ));199 * }200 *201 * Return: 0 on success, negative errno on failure.202 */203int kunit_platform_device_prepare_wait_for_probe(struct kunit *test,204 struct platform_device *pdev,205 struct completion *x)206{207 struct device *dev = &pdev->dev;208 struct kunit_platform_device_probe_nb *knb;209 bool bound;210 211 knb = kunit_kzalloc(test, sizeof(*knb), GFP_KERNEL);212 if (!knb)213 return -ENOMEM;214 215 knb->nb.notifier_call = kunit_platform_device_probe_notify;216 knb->dev = dev;217 knb->x = x;218 219 device_lock(dev);220 bound = device_is_bound(dev);221 if (bound) {222 device_unlock(dev);223 complete(x);224 kunit_kfree(test, knb);225 return 0;226 }227 228 bus_register_notifier(&platform_bus_type, &knb->nb);229 device_unlock(&pdev->dev);230 231 return kunit_add_action_or_reset(test, kunit_platform_device_probe_nb_remove, &knb->nb);232}233EXPORT_SYMBOL_GPL(kunit_platform_device_prepare_wait_for_probe);234 235KUNIT_DEFINE_ACTION_WRAPPER(platform_driver_unregister_wrapper,236 platform_driver_unregister, struct platform_driver *);237/**238 * kunit_platform_driver_register() - Register a KUnit test managed platform driver239 * @test: test context240 * @drv: platform driver to register241 *242 * Register a test managed platform driver. This allows callers to embed the243 * @drv in a container structure and use container_of() in the probe function244 * to pass information to KUnit tests.245 *246 * Example247 *248 * .. code-block:: c249 *250 * struct kunit_test_context {251 * struct platform_driver pdrv;252 * const char *data;253 * };254 *255 * static inline struct kunit_test_context *256 * to_test_context(struct platform_device *pdev)257 * {258 * return container_of(to_platform_driver(pdev->dev.driver),259 * struct kunit_test_context,260 * pdrv);261 * }262 *263 * static int kunit_platform_driver_probe(struct platform_device *pdev)264 * {265 * struct kunit_test_context *ctx;266 *267 * ctx = to_test_context(pdev);268 * ctx->data = "test data";269 *270 * return 0;271 * }272 *273 * static void kunit_platform_driver_test(struct kunit *test)274 * {275 * struct kunit_test_context *ctx;276 *277 * ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);278 * KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);279 *280 * ctx->pdrv.probe = kunit_platform_driver_probe;281 * ctx->pdrv.driver.name = "kunit-platform";282 * ctx->pdrv.driver.owner = THIS_MODULE;283 *284 * KUNIT_EXPECT_EQ(test, 0, kunit_platform_driver_register(test, &ctx->pdrv));285 * <... wait for driver to probe ...>286 * KUNIT_EXPECT_STREQ(test, ctx->data, "test data");287 * }288 *289 * Return: 0 on success, negative errno on failure.290 */291int kunit_platform_driver_register(struct kunit *test,292 struct platform_driver *drv)293{294 int ret;295 296 ret = platform_driver_register(drv);297 if (ret)298 return ret;299 300 return kunit_add_action_or_reset(test, platform_driver_unregister_wrapper, drv);301}302EXPORT_SYMBOL_GPL(kunit_platform_driver_register);303