Files
android_kernel_samsung_sm8750/include/linux/samsung/builder_pattern.h
2025-08-12 22:16:57 +02:00

313 lines
7.2 KiB
C
Executable File

// SPDX-License-Identifier: GPL-2.0
/*
* COPYRIGHT(C) 2020-2021 Samsung Electronics Co., Ltd. All Right Reserved.
*/
#ifndef __BUILDER_PATTERN_H__
#define __BUILDER_PATTERN_H__
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/of.h>
struct director_threaded;
/* An interface 'Builder' struct
* TODO: This struct should be embedded if the drvdata use
* this 'Builder Pattern'.
*/
struct builder {
struct device *dev;
struct director_threaded *drct;
};
#define DT_BUILDER(__parse_dt) \
{ .parse_dt = __parse_dt, }
/* The prototype of 'Concrete Builde' parsing a device tree */
typedef int (*parse_dt_t)(struct builder *bd, struct device_node *np);
struct dt_builder {
parse_dt_t parse_dt;
};
#define DEVICE_BUILDER(__construct_dev, __destruct_dev) \
{ .construct_dev = __construct_dev, .destruct_dev = __destruct_dev, }
/* The prototype of 'Concrete Builde' constructing a device */
typedef int (*construct_dev_t)(struct builder *bd);
/* The prototype of 'Concrete Builde' destructing a device */
typedef void (*destruct_dev_t)(struct builder *bd);
struct dev_builder {
construct_dev_t construct_dev;
destruct_dev_t destruct_dev;
};
#ifdef _DEBUG_BUILDER_PATTERN
static inline int __bp_call_parse_dt(parse_dt_t parse_dt,
struct builder *bd, struct device_node *np)
{
ktime_t calltime, rettime;
int err;
calltime = ktime_get();
err = parse_dt(bd, np);
rettime = ktime_get();
dev_info(bd->dev, "%ps returned %d after %llu\n",
parse_dt, err,
(unsigned long long)ktime_sub(rettime, calltime));
return err;
}
static inline int __bp_call_construct_dev(construct_dev_t construct_dev,
struct builder *bd)
{
ktime_t calltime, rettime;
int err;
calltime = ktime_get();
err = construct_dev(bd);
rettime = ktime_get();
dev_info(bd->dev, "%ps returned %d after %llu\n",
construct_dev , err,
(unsigned long long)ktime_sub(rettime, calltime));
return err;
}
static inline void __bp_call_destruct_dev(destruct_dev_t destruct_dev,
struct builder *bd)
{
ktime_t calltime, rettime;
calltime = ktime_get();
destruct_dev(bd);
rettime = ktime_get();
dev_info(bd->dev, "%ps returned after %llu\n",
destruct_dev,
(unsigned long long)ktime_sub(rettime, calltime));
}
#else
static inline int __bp_call_parse_dt(parse_dt_t parse_dt,
struct builder *bd, struct device_node *np)
{
return parse_dt(bd, np);
}
static inline int __bp_call_construct_dev(construct_dev_t construct_dev,
struct builder *bd)
{
return construct_dev(bd);
}
static inline void __bp_call_destruct_dev(destruct_dev_t destruct_dev,
struct builder *bd)
{
destruct_dev(bd);
}
#endif
static inline int __bp_call_concrete_parse_dt(struct builder *bd,
const struct dt_builder *builder, size_t i)
{
struct device_node *np = dev_of_node(bd->dev);
int err;
parse_dt_t parse_dt = builder[i].parse_dt;
err = __bp_call_parse_dt(parse_dt, bd, np);
if (err)
dev_err(bd->dev, "failed to parse a device tree - [%zu] %ps (%d)\n",
i, parse_dt, err);
return err;
}
static inline int __bp_call_concrete_construct_dev(struct builder *bd,
const struct dev_builder *builder, size_t i)
{
int err;
construct_dev_t construct_dev = builder[i].construct_dev;
if (!construct_dev)
return 0;
err = __bp_call_construct_dev(construct_dev, bd);
if (err)
dev_err(bd->dev, "failed to construct_dev a device - [%zu] %ps (%d)\n",
i, construct_dev, err);
return err;
}
static inline void __bp_call_concrete_destruct_dev(struct builder *bd,
const struct dev_builder *builder, size_t i)
{
destruct_dev_t destruct_dev = builder[i].destruct_dev;
if (!destruct_dev)
return;
__bp_call_destruct_dev(destruct_dev, bd);
}
/* A common 'Director' parsing a device tree
* @return - 0 on success. 'errno' of last failed on failure.
*/
static inline int sec_director_parse_dt(struct builder *bd,
const struct dt_builder *builder, size_t n)
{
int err;
size_t i;
for (i = 0; i < n; i++) {
err = __bp_call_concrete_parse_dt(bd, builder, i);
if (err)
return err;
}
return 0;
}
/* A common 'Director' constructing a device
* @last_failed - The number of called builders on success.
* The "NEGATIVE" index of last failed on failure.
* @return - 0 on success.
* return value of the last concrete builder on failure.
*/
static inline int sec_director_construct_dev(struct builder *bd,
const struct dev_builder *builder, ssize_t n,
ssize_t *last_failed)
{
int err;
ssize_t i;
for (i = 0; i < n; i++) {
err = __bp_call_concrete_construct_dev(bd, builder, i);
if (err) {
*last_failed = -i;
return err;
}
}
*last_failed = n;
return 0;
}
/* A common 'Director' destructing a device */
static inline void sec_director_destruct_dev(struct builder *bd,
const struct dev_builder *builder, ssize_t n,
ssize_t last_failed)
{
ssize_t i;
BUG_ON((last_failed > n) || (last_failed < 0));
for (i = last_failed - 1; i >= 0; i--)
__bp_call_concrete_destruct_dev(bd, builder, i);
}
/* A wrapper function for probe call-backs */
static inline int sec_director_probe_dev(struct builder *bd,
const struct dev_builder *builder, ssize_t n)
{
int err;
ssize_t last_failed;
err = sec_director_construct_dev(bd, builder, n, &last_failed);
if (last_failed <= 0)
goto err_dev_director;
return 0;
err_dev_director:
sec_director_destruct_dev(bd, builder, n, -last_failed);
return err;
}
struct director_threaded {
struct builder *bd;
const struct dev_builder *builder;
ssize_t n;
int *construct_result;
};
/* A common 'Director' constructing a device - threaded */
static int sec_director_construct_dev_threaded(void *__drct)
{
struct director_threaded *drct = __drct;
struct builder *bd = drct->bd;
const struct dev_builder *builder = drct->builder;
ssize_t n = drct->n;
int *construct_result = drct->construct_result;
ssize_t i;
for (i = 0; i < n; i++)
construct_result[i] =
__bp_call_concrete_construct_dev(bd, builder, i);
return 0;
}
/* A common 'Director' destructing a device */
static inline void sec_director_destruct_dev_threaded(
struct director_threaded *drct)
{
struct builder *bd = drct->bd;
const struct dev_builder *builder = drct->builder;
ssize_t n = drct->n;
int *construct_result = drct->construct_result;
ssize_t i;
for (i = n - 1; i >= 0; i--) {
if (!construct_result[i])
__bp_call_concrete_destruct_dev(bd, builder, i);
}
}
/* A wrapper function for probe call-backs - threaded */
static inline int sec_director_probe_dev_threaded(struct builder *bd,
const struct dev_builder *builder, ssize_t n, const char *name)
{
struct device *dev = bd->dev;
struct director_threaded *drct;
int *construct_result;
struct task_struct *thread;
drct = devm_kzalloc(dev, sizeof(*drct), GFP_KERNEL);
if (!drct)
return -ENOMEM;
construct_result = devm_kcalloc(dev, n, sizeof(*construct_result),
GFP_KERNEL);
if (!construct_result)
return -ENOMEM;
drct->bd = bd;
drct->builder = builder;
drct->n = n;
drct->construct_result = construct_result;
thread = kthread_run(sec_director_construct_dev_threaded,
drct, "drct-%s", name);
if (IS_ERR_OR_NULL(thread)) {
dev_err(dev, "failed to created drct thread - (%ld)!\n",
PTR_ERR(thread));
return -ENOMEM;
}
bd->drct = drct;
return 0;
}
#endif /* __BUILDER_PATTERN_H__ */