[PATCH v8 1/5] firmware: add extensible driver data params

From: Luis R. Rodriguez
Date: Fri May 19 2017 - 15:11:49 EST


As the firmware API evolves we keep extending functions with more arguments.
Stop this nonsense by proving an extensible data structure which can be used
to represent both user parameters and private internal parameters.

We introduce 3 data structures:

o struct driver_data_req_params - used for user specified parameters
o struct driver_data_priv_params - used for internal use only
o struct driver_data_params - stiches both of the the above together,
only for internal use

This starts off by just making the existing APIs use the new data
structures, it will make subsequent changes easier to review which will
be adding new flexible APIs.

A side consequences is get to replace all the old internal "firmware
behavior options" flags with enums we properly document, remove the
blinding #ifdefs, and compartamentlize the userhelper fallback code
more appropriately unde CONFIG_FW_LOADER_USER_HELPER_FALLBACK.

This commit should introduces no functional changes (TM).

Signed-off-by: Luis R. Rodriguez <mcgrof@xxxxxxxxxx>
---
drivers/base/firmware_class.c | 331 ++++++++++++++++++++++++++++++++----------
include/linux/driver_data.h | 88 +++++++++++
2 files changed, 345 insertions(+), 74 deletions(-)
create mode 100644 include/linux/driver_data.h

diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index b9f907eedbf7..db7c0bc0ed98 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -19,6 +19,7 @@
#include <linux/workqueue.h>
#include <linux/highmem.h>
#include <linux/firmware.h>
+#include <linux/driver_data.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/file.h>
@@ -40,6 +41,149 @@ MODULE_AUTHOR("Manuel Estrada Sainz");
MODULE_DESCRIPTION("Multi purpose firmware loading support");
MODULE_LICENSE("GPL");

+/**
+ * enum driver_data_mode - driver data mode of operation
+ * @DRIVER_DATA_SYNC: used to determine if we should look for the driver data
+ * file immediatley.
+ * @DRIVER_DATA_ASYNC: used to determine if we should schedule the search for
+ * your driver data file to be run at a later time.
+ */
+enum driver_data_mode {
+ DRIVER_DATA_SYNC = 0,
+ DRIVER_DATA_ASYNC,
+};
+
+/**
+ * enum driver_data_priv_reqs - private features only used internally
+ *
+ * @DRIVER_DATA_PRIV_REQ_FALLBACK: specifies that the driver data request
+ * will use a fallback mechanism if the kernel's direct filesystem
+ * lookup failed to find the requested driver data. If the flag
+ * %DRIVER_DATA_PRIV_REQ_FALLBACK is set but the flag
+ * %DRIVER_DATA_PRIV_REQ_FALLBACK_UEVENT is not set, it means the caller
+ * is relying on a custom interface for driver data lookup as a fallback
+ * mechanism. The custom interface is expected to find any found driver
+ * data using the exposed sysfs interface of the firmware_class. If the
+ * custom fallback mechanism is not compatible with the internal caching
+ * mechanism for driver data lookups at resume, it will be disabled.
+ * @DRIVER_DATA_PRIV_REQ_FALLBACK_UEVENT: indicates that the fallback mechanism
+ * this driver data request will rely on will be that of having the kernel
+ * issue a uevent to userspace. Userspace in turn is expected to be
+ * monitoring for uevents for the firmware_class and will use the
+ * exposted sysfs interface to upload the driver data for the caller.
+ * @DRIVER_DATA_PRIV_REQ_NO_CACHE: indicates that the driver data request
+ * should not set up and use the internal caching mechanism to assist
+ * drivers from fetching driver data at resume time after suspend.
+ */
+enum driver_data_priv_reqs {
+ DRIVER_DATA_PRIV_REQ_FALLBACK = 1 << 0,
+ DRIVER_DATA_PRIV_REQ_FALLBACK_UEVENT = 1 << 1,
+ DRIVER_DATA_PRIV_REQ_NO_CACHE = 1 << 2,
+};
+
+/**
+ * struct driver_data_priv_params - private driver data parameters
+ * @mode: mode of operation
+ * @priv_reqs: private set of &enum driver_data_reqs, private requirements for
+ * the driver data request
+ * @alloc_buf: buffer area allocated by the caller so we can place the
+ * respective driver data
+ * @alloc_buf_size: size of the @alloc_buf
+ * @old_async_cb: used only for request_firmware_nowait() since we won't change
+ * all async callbacks to get the return value on failure
+ */
+struct driver_data_priv_params {
+ enum driver_data_mode mode;
+ u64 priv_reqs;
+ void *alloc_buf;
+ size_t alloc_buf_size;
+ void (*old_async_cb)(const struct firmware *driver_data, void *context);
+};
+
+/**
+ * struct driver_data_params
+ * @driver_data: the driver data if found using the requirements specified
+ * in @req_params and @priv_params
+ * @req_params: caller's requirements for the driver data to look for
+ * @priv_params: private requirements for the driver data to look for
+ */
+struct driver_data_params {
+ const struct firmware *driver_data;
+ const struct driver_data_req_params req_params;
+ struct driver_data_priv_params priv_params;
+};
+
+/*
+ * These are kept to remain backward compatible with old behaviour. Do not
+ * modify them unless you know what you are doing. These are to be used only
+ * by the old API, so:
+ *
+ * Old sync APIs:
+ * o request_firmware(): __DATA_REQ_FIRMWARE()
+ * o request_firmware_direct(): __DATA_REQ_FIRMWARE_DIRECT()
+ * o request_firmware_into_buf(): __DATA_REQ_FIRMWARE_BUF()
+ *
+ * Old async API:
+ * o request_firmware_nowait(): __DATA_REQ_FIRMWARE_NOWAIT()
+ */
+#define __DATA_REQ_FIRMWARE() \
+ .priv_params = { \
+ .priv_reqs = DRIVER_DATA_PRIV_REQ_FALLBACK | \
+ DRIVER_DATA_PRIV_REQ_FALLBACK_UEVENT, \
+ }
+
+#define __DATA_REQ_FIRMWARE_DIRECT() \
+ .req_params = { \
+ .reqs = DRIVER_DATA_REQ_OPTIONAL, \
+ }, \
+ .priv_params = { \
+ .priv_reqs = DRIVER_DATA_PRIV_REQ_FALLBACK | \
+ DRIVER_DATA_PRIV_REQ_FALLBACK_UEVENT, \
+ }
+
+#define __DATA_REQ_FIRMWARE_BUF(buf, size) \
+ .priv_params = { \
+ .priv_reqs = DRIVER_DATA_PRIV_REQ_FALLBACK | \
+ DRIVER_DATA_PRIV_REQ_FALLBACK_UEVENT | \
+ DRIVER_DATA_PRIV_REQ_NO_CACHE, \
+ .alloc_buf = buf, \
+ .alloc_buf_size = size, \
+ }
+
+#define __DATA_REQ_FIRMWARE_NOWAIT(module, uevent, gfp, async_cb, async_ctx) \
+ .req_params = { \
+ .hold_module = module, \
+ .gfp = gfp, \
+ .cbs.async = { \
+ .found_cb = NULL, \
+ .found_ctx = async_ctx, \
+ }, \
+ }, \
+ .priv_params = { \
+ .mode = DRIVER_DATA_ASYNC, \
+ .old_async_cb = async_cb, \
+ .priv_reqs = DRIVER_DATA_PRIV_REQ_FALLBACK | \
+ (uevent ? \
+ DRIVER_DATA_PRIV_REQ_FALLBACK_UEVENT : 0),\
+ }
+
+#define driver_data_req_param_sync(params) \
+ ((params)->priv_params.mode == DRIVER_DATA_SYNC)
+#define driver_data_req_param_async(params) \
+ ((params)->priv_params.mode == DRIVER_DATA_ASYNC)
+
+#define driver_data_param_use_fallback(params) \
+ (!!((params)->priv_reqs & DRIVER_DATA_PRIV_REQ_FALLBACK))
+#define driver_data_param_uevent(params) \
+ (!!((params)->priv_reqs & DRIVER_DATA_PRIV_REQ_FALLBACK_UEVENT))
+#define driver_data_param_nocache(params) \
+ (!!((params)->priv_reqs & DRIVER_DATA_PRIV_REQ_NO_CACHE))
+
+#define driver_data_param_optional(params) \
+ (!!((params)->reqs & DRIVER_DATA_REQ_OPTIONAL))
+
+#define driver_data_async_ctx(params) ((params)->cbs.async.found_ctx)
+
/* Builtin firmware support */

#ifdef CONFIG_FW_LOADER
@@ -48,9 +192,19 @@ extern struct builtin_fw __start_builtin_fw[];
extern struct builtin_fw __end_builtin_fw[];

static bool fw_get_builtin_firmware(struct firmware *fw, const char *name,
- void *buf, size_t size)
+ struct driver_data_params *data_params)
{
struct builtin_fw *b_fw;
+ void *buf;
+ size_t size;
+
+ if (data_params) {
+ buf = data_params->priv_params.alloc_buf;
+ size = data_params->priv_params.alloc_buf_size;
+ } else {
+ buf = NULL;
+ size = 0;
+ }

for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++) {
if (strcmp(name, b_fw->name) == 0) {
@@ -79,9 +233,9 @@ static bool fw_is_builtin_firmware(const struct firmware *fw)

#else /* Module case - no builtin firmware support */

-static inline bool fw_get_builtin_firmware(struct firmware *fw,
- const char *name, void *buf,
- size_t size)
+static inline
+bool fw_get_builtin_firmware(struct firmware *fw, const char *name,
+ struct driver_data_params *data_params)
{
return false;
}
@@ -182,22 +336,6 @@ static int __fw_state_check(struct fw_state *fw_st, enum fw_status status)

#endif /* CONFIG_FW_LOADER_USER_HELPER */

-/* firmware behavior options */
-#define FW_OPT_UEVENT (1U << 0)
-#define FW_OPT_NOWAIT (1U << 1)
-#ifdef CONFIG_FW_LOADER_USER_HELPER
-#define FW_OPT_USERHELPER (1U << 2)
-#else
-#define FW_OPT_USERHELPER 0
-#endif
-#ifdef CONFIG_FW_LOADER_USER_HELPER_FALLBACK
-#define FW_OPT_FALLBACK FW_OPT_USERHELPER
-#else
-#define FW_OPT_FALLBACK 0
-#endif
-#define FW_OPT_NO_WARN (1U << 3)
-#define FW_OPT_NOCACHE (1U << 4)
-
struct firmware_cache {
/* firmware_buf instance will be added into the below list */
spinlock_t lock;
@@ -296,9 +434,19 @@ static struct firmware_cache fw_cache;

static struct firmware_buf *__allocate_fw_buf(const char *fw_name,
struct firmware_cache *fwc,
- void *dbuf, size_t size)
+ struct driver_data_params *data_params)
{
struct firmware_buf *buf;
+ void *dbuf;
+ size_t size;
+
+ if (data_params) {
+ dbuf = data_params->priv_params.alloc_buf;
+ size = data_params->priv_params.alloc_buf_size;
+ } else {
+ dbuf = NULL;
+ size = 0;
+ }

buf = kzalloc(sizeof(*buf), GFP_ATOMIC);
if (!buf)
@@ -337,8 +485,8 @@ static struct firmware_buf *__fw_lookup_buf(const char *fw_name)

static int fw_lookup_and_allocate_buf(const char *fw_name,
struct firmware_cache *fwc,
- struct firmware_buf **buf, void *dbuf,
- size_t size)
+ struct firmware_buf **buf,
+ struct driver_data_params *data_params)
{
struct firmware_buf *tmp;

@@ -350,7 +498,7 @@ static int fw_lookup_and_allocate_buf(const char *fw_name,
*buf = tmp;
return 1;
}
- tmp = __allocate_fw_buf(fw_name, fwc, dbuf, size);
+ tmp = __allocate_fw_buf(fw_name, fwc, data_params);
if (tmp)
list_add(&tmp->list, &fwc->head);
spin_unlock(&fwc->lock);
@@ -556,7 +704,7 @@ static int fw_add_devm_name(struct device *dev, const char *name)
#endif

static int assign_firmware_buf(struct firmware *fw, struct device *device,
- unsigned int opt_flags)
+ struct driver_data_params *data_params)
{
struct firmware_buf *buf = fw->priv;

@@ -574,15 +722,16 @@ static int assign_firmware_buf(struct firmware *fw, struct device *device,
* should be fixed in devres or driver core.
*/
/* don't cache firmware handled without uevent */
- if (device && (opt_flags & FW_OPT_UEVENT) &&
- !(opt_flags & FW_OPT_NOCACHE))
+ if (device &&
+ driver_data_param_uevent(&data_params->priv_params) &&
+ !driver_data_param_nocache(&data_params->priv_params))
fw_add_devm_name(device, buf->fw_id);

/*
* After caching firmware image is started, let it piggyback
* on request firmware.
*/
- if (!(opt_flags & FW_OPT_NOCACHE) &&
+ if (!driver_data_param_nocache(&data_params->priv_params) &&
buf->fwc->state == FW_LOADER_START_CACHE) {
if (fw_cache_piggyback_on_request(buf->fw_id))
kref_get(&buf->ref);
@@ -1025,7 +1174,8 @@ static const struct attribute_group *fw_dev_attr_groups[] = {

static struct firmware_priv *
fw_create_instance(struct firmware *firmware, const char *fw_name,
- struct device *device, unsigned int opt_flags)
+ struct device *device,
+ struct driver_data_params *data_params)
{
struct firmware_priv *fw_priv;
struct device *f_dev;
@@ -1036,7 +1186,7 @@ fw_create_instance(struct firmware *firmware, const char *fw_name,
goto exit;
}

- fw_priv->nowait = !!(opt_flags & FW_OPT_NOWAIT);
+ fw_priv->nowait = driver_data_req_param_async(data_params);
fw_priv->fw = firmware;
f_dev = &fw_priv->dev;

@@ -1051,7 +1201,8 @@ fw_create_instance(struct firmware *firmware, const char *fw_name,

/* load a firmware via user helper */
static int _request_firmware_load(struct firmware_priv *fw_priv,
- unsigned int opt_flags, long timeout)
+ struct driver_data_params *data_params,
+ long timeout)
{
int retval = 0;
struct device *f_dev = &fw_priv->dev;
@@ -1073,7 +1224,7 @@ static int _request_firmware_load(struct firmware_priv *fw_priv,
list_add(&buf->pending_list, &pending_fw_head);
mutex_unlock(&fw_lock);

- if (opt_flags & FW_OPT_UEVENT) {
+ if (driver_data_param_uevent(&data_params->priv_params)) {
buf->need_uevent = true;
dev_set_uevent_suppress(f_dev, false);
dev_dbg(f_dev, "firmware: requesting %s\n", buf->fw_id);
@@ -1102,14 +1253,14 @@ static int _request_firmware_load(struct firmware_priv *fw_priv,

static int fw_load_from_user_helper(struct firmware *firmware,
const char *name, struct device *device,
- unsigned int opt_flags)
+ struct driver_data_params *data_params)
{
struct firmware_priv *fw_priv;
long timeout;
int ret;

timeout = firmware_loading_timeout();
- if (opt_flags & FW_OPT_NOWAIT) {
+ if (driver_data_req_param_async(data_params)) {
timeout = usermodehelper_read_lock_wait(timeout);
if (!timeout) {
dev_dbg(device, "firmware: %s loading timed out\n",
@@ -1125,17 +1276,17 @@ static int fw_load_from_user_helper(struct firmware *firmware,
}
}

- fw_priv = fw_create_instance(firmware, name, device, opt_flags);
+ fw_priv = fw_create_instance(firmware, name, device, data_params);
if (IS_ERR(fw_priv)) {
ret = PTR_ERR(fw_priv);
goto out_unlock;
}

fw_priv->buf = firmware->priv;
- ret = _request_firmware_load(fw_priv, opt_flags, timeout);
+ ret = _request_firmware_load(fw_priv, data_params, timeout);

if (!ret)
- ret = assign_firmware_buf(firmware, device, opt_flags);
+ ret = assign_firmware_buf(firmware, device, data_params);

out_unlock:
usermodehelper_read_unlock();
@@ -1146,7 +1297,8 @@ static int fw_load_from_user_helper(struct firmware *firmware,
#else /* CONFIG_FW_LOADER_USER_HELPER */
static inline int
fw_load_from_user_helper(struct firmware *firmware, const char *name,
- struct device *device, unsigned int opt_flags)
+ struct device *device,
+ struct driver_data_params *data_params)
{
return -ENOENT;
}
@@ -1161,7 +1313,8 @@ static inline void kill_pending_fw_fallback_reqs(bool only_kill_custom) { }
*/
static int
_request_firmware_prepare(struct firmware **firmware_p, const char *name,
- struct device *device, void *dbuf, size_t size)
+ struct device *device,
+ struct driver_data_params *data_params)
{
struct firmware *firmware;
struct firmware_buf *buf;
@@ -1174,12 +1327,12 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name,
return -ENOMEM;
}

- if (fw_get_builtin_firmware(firmware, name, dbuf, size)) {
+ if (fw_get_builtin_firmware(firmware, name, data_params)) {
dev_dbg(device, "using built-in %s\n", name);
return 0; /* assigned */
}

- ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf, dbuf, size);
+ ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf, data_params);

/*
* bind with 'buf' now to avoid warning in failure path
@@ -1200,11 +1353,33 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name,
return 1; /* need to load */
}

+#ifdef CONFIG_FW_LOADER_USER_HELPER_FALLBACK
+static int driver_data_fallback(struct firmware *fw, const char *name,
+ struct device *device,
+ struct driver_data_params *data_params,
+ int ret)
+{
+ if (!driver_data_param_use_fallback(&data_params->priv_params))
+ return ret;
+
+ dev_warn(device, "Falling back to user helper\n");
+ return fw_load_from_user_helper(fw, name, device, data_params);
+}
+#else
+static inline int driver_data_fallback(struct firmware *fw, const char *name,
+ struct device *device,
+ struct driver_data_params *data_params,
+ int ret)
+{
+ return ret;
+}
+#endif
+
/* called from request_firmware() and request_firmware_work_func() */
static int
_request_firmware(const struct firmware **firmware_p, const char *name,
- struct device *device, void *buf, size_t size,
- unsigned int opt_flags)
+ struct driver_data_params *data_params,
+ struct device *device)
{
struct firmware *fw = NULL;
int ret;
@@ -1217,7 +1392,7 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
goto out;
}

- ret = _request_firmware_prepare(&fw, name, device, buf, size);
+ ret = _request_firmware_prepare(&fw, name, device, data_params);
if (ret <= 0) /* error or already assigned */
goto out;

@@ -1229,17 +1404,13 @@ _request_firmware(const struct firmware **firmware_p, const char *name,

ret = fw_get_filesystem_firmware(device, fw->priv);
if (ret) {
- if (!(opt_flags & FW_OPT_NO_WARN))
+ if (!driver_data_param_optional(&data_params->req_params))
dev_warn(device,
"Direct firmware load for %s failed with error %d\n",
name, ret);
- if (opt_flags & FW_OPT_USERHELPER) {
- dev_warn(device, "Falling back to user helper\n");
- ret = fw_load_from_user_helper(fw, name, device,
- opt_flags);
- }
+ ret = driver_data_fallback(fw, name, device, data_params, ret);
} else
- ret = assign_firmware_buf(fw, device, opt_flags);
+ ret = assign_firmware_buf(fw, device, data_params);

out:
if (ret < 0) {
@@ -1276,11 +1447,13 @@ request_firmware(const struct firmware **firmware_p, const char *name,
struct device *device)
{
int ret;
+ struct driver_data_params data_params = {
+ __DATA_REQ_FIRMWARE(),
+ };

/* Need to pin this module until return */
__module_get(THIS_MODULE);
- ret = _request_firmware(firmware_p, name, device, NULL, 0,
- FW_OPT_UEVENT | FW_OPT_FALLBACK);
+ ret = _request_firmware(firmware_p, name, &data_params, device);
module_put(THIS_MODULE);
return ret;
}
@@ -1301,10 +1474,12 @@ int request_firmware_direct(const struct firmware **firmware_p,
const char *name, struct device *device)
{
int ret;
+ struct driver_data_params data_params = {
+ __DATA_REQ_FIRMWARE_DIRECT(),
+ };

__module_get(THIS_MODULE);
- ret = _request_firmware(firmware_p, name, device, NULL, 0,
- FW_OPT_UEVENT | FW_OPT_NO_WARN);
+ ret = _request_firmware(firmware_p, name, &data_params, device);
module_put(THIS_MODULE);
return ret;
}
@@ -1330,12 +1505,14 @@ request_firmware_into_buf(const struct firmware **firmware_p, const char *name,
struct device *device, void *buf, size_t size)
{
int ret;
+ struct driver_data_params data_params = {
+ __DATA_REQ_FIRMWARE_BUF(buf, size),
+ };

__module_get(THIS_MODULE);
- ret = _request_firmware(firmware_p, name, device, buf, size,
- FW_OPT_UEVENT | FW_OPT_FALLBACK |
- FW_OPT_NOCACHE);
+ ret = _request_firmware(firmware_p, name, &data_params, device);
module_put(THIS_MODULE);
+
return ret;
}
EXPORT_SYMBOL(request_firmware_into_buf);
@@ -1357,27 +1534,30 @@ EXPORT_SYMBOL(release_firmware);
/* Async support */
struct firmware_work {
struct work_struct work;
- struct module *module;
+ struct driver_data_params data_params;
const char *name;
struct device *device;
- void *context;
- void (*cont)(const struct firmware *fw, void *context);
- unsigned int opt_flags;
};

static void request_firmware_work_func(struct work_struct *work)
{
struct firmware_work *fw_work;
- const struct firmware *fw;
+ struct driver_data_params *data_params;
+ const struct driver_data_req_params *req_params;
+ const struct driver_data_priv_params *priv_params;

fw_work = container_of(work, struct firmware_work, work);
-
- _request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0,
- fw_work->opt_flags);
- fw_work->cont(fw, fw_work->context);
+ data_params = &fw_work->data_params;
+ req_params = &data_params->req_params;
+ priv_params = &data_params->priv_params;
+
+ _request_firmware(&data_params->driver_data, fw_work->name,
+ data_params, fw_work->device);
+ priv_params->old_async_cb(data_params->driver_data,
+ driver_data_async_ctx(req_params));
put_device(fw_work->device); /* taken in request_firmware_nowait() */

- module_put(fw_work->module);
+ module_put(req_params->hold_module);
kfree_const(fw_work->name);
kfree(fw_work);
}
@@ -1412,22 +1592,25 @@ request_firmware_nowait(
void (*cont)(const struct firmware *fw, void *context))
{
struct firmware_work *fw_work;
+ struct driver_data_params data_params = {
+ __DATA_REQ_FIRMWARE_NOWAIT(module, uevent, gfp, cont, context),
+ };
+
+ if (!cont)
+ return -EINVAL;

fw_work = kzalloc(sizeof(struct firmware_work), gfp);
if (!fw_work)
return -ENOMEM;

- fw_work->module = module;
fw_work->name = kstrdup_const(name, gfp);
if (!fw_work->name) {
kfree(fw_work);
return -ENOMEM;
}
fw_work->device = device;
- fw_work->context = context;
- fw_work->cont = cont;
- fw_work->opt_flags = FW_OPT_NOWAIT | FW_OPT_FALLBACK |
- (uevent ? FW_OPT_UEVENT : FW_OPT_USERHELPER);
+ memcpy(&fw_work->data_params, &data_params,
+ sizeof(struct driver_data_params));

if (!try_module_get(module)) {
kfree_const(fw_work->name);
@@ -1505,7 +1688,7 @@ static int uncache_firmware(const char *fw_name)

pr_debug("%s: %s\n", __func__, fw_name);

- if (fw_get_builtin_firmware(&fw, fw_name, NULL, 0))
+ if (fw_get_builtin_firmware(&fw, fw_name, NULL))
return 0;

buf = fw_lookup_buf(fw_name);
diff --git a/include/linux/driver_data.h b/include/linux/driver_data.h
new file mode 100644
index 000000000000..272d618a5719
--- /dev/null
+++ b/include/linux/driver_data.h
@@ -0,0 +1,88 @@
+#ifndef _LINUX_DRIVER_DATA_H
+#define _LINUX_DRIVER_DATA_H
+
+#include <linux/types.h>
+#include <linux/compiler.h>
+#include <linux/gfp.h>
+#include <linux/device.h>
+
+/*
+ * Driver Data internals
+ *
+ * Copyright (C) 2017 Luis R. Rodriguez <mcgrof@xxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or at your option) any
+ * later version; or, when distributed separately from the Linux kernel or
+ * incorporated into other software packages, subject to the following license:
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of copyleft-next (version 0.3.1 or later) as published
+ * at http://copyleft-next.org/.
+ */
+
+/**
+ * struct driver_data_async_cbs - callbacks for handling driver data requests
+ * @found_cb: callback to be used when the driver data has been found. A
+ * callback is required. If the requested driver data is found it will
+ * passed on the callback, using the context set on @found_ctx.
+ * @found_ctx: preferred context to be used as the second argument to
+ * @found_cb.
+ *
+ * Used for specifying callbacks and contexts used for when asynchronous driver
+ * data requests have completed. If no driver data is found the error will be
+ * passed on the respective callback.
+ */
+struct driver_data_async_cbs {
+ void (*found_cb)(const struct firmware *driver_data,
+ void *context,
+ int error);
+ void *found_ctx;
+};
+
+/**
+ * union driver_data_cbs - callbacks for driver data request
+ * @async: callbacks for handling driver data when asynchronous requests
+ * are made.
+ *
+ * Used for placement of callbacks used for handling results from driver
+ * data requests.
+ */
+union driver_data_cbs {
+ struct driver_data_async_cbs async;
+};
+
+/**
+ * enum driver_data_reqs - requirements of the driver data request
+ * @DRIVER_DATA_REQ_OPTIONAL: if set it is not a hard requirement by the
+ * caller that the file requested be present. An error will not be recorded
+ * if the file is not found.
+ */
+enum driver_data_reqs {
+ DRIVER_DATA_REQ_OPTIONAL = 1 << 0,
+};
+
+/**
+ * struct driver_data_req_params - driver data request parameters
+ * @hold_module: module to hold during the driver data request operation. By
+ * default if sync requests set this to NULL the firmware_class module
+ * will be refcounted during operation.
+ * @gfp: flags to use for allocations when constructing the driver data request,
+ * prior to scheduling. Unused on driver_data_request_sync().
+ * @reqs: set of &enum driver_data_reqs flags used to configure the driver
+ * data request. All of the specified requirements must be met.
+ * @cbs: set of callbacks to use for the driver data request.
+ *
+ * This data structure is intended to carry all requirements and specifications
+ * required to complete the task to get the requested driver date file to the
+ * caller.
+ */
+struct driver_data_req_params {
+ struct module *hold_module;
+ gfp_t gfp;
+ u64 reqs;
+ const union driver_data_cbs cbs;
+};
+
+#endif /* _LINUX_DRIVER_DATA_H */
--
2.11.0