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)
+static bool fw_get_builtin_firmware(struct firmware *fw, const char *name,
+ void *buf, size_t size)
{
struct builtin_fw *b_fw;
if (strcmp(name, b_fw->name) == 0) {
fw->size = b_fw->size;
fw->data = b_fw->data;
+
+ if (buf && fw->size <= size)
+ memcpy(buf, fw->data, fw->size);
return true;
}
}
#else /* Module case - no builtin firmware support */
-static inline bool fw_get_builtin_firmware(struct firmware *fw, const char *name)
+static inline bool fw_get_builtin_firmware(struct firmware *fw,
+ const char *name, void *buf,
+ size_t size)
{
return false;
}
unsigned long status;
void *data;
size_t size;
+ size_t allocated_size;
#ifdef CONFIG_FW_LOADER_USER_HELPER
bool is_paged_buf;
bool need_uevent;
static struct firmware_cache fw_cache;
static struct firmware_buf *__allocate_fw_buf(const char *fw_name,
- struct firmware_cache *fwc)
+ struct firmware_cache *fwc,
+ void *dbuf, size_t size)
{
struct firmware_buf *buf;
kref_init(&buf->ref);
buf->fwc = fwc;
+ buf->data = dbuf;
+ buf->allocated_size = size;
init_completion(&buf->completion);
#ifdef CONFIG_FW_LOADER_USER_HELPER
INIT_LIST_HEAD(&buf->pending_list);
static int fw_lookup_and_allocate_buf(const char *fw_name,
struct firmware_cache *fwc,
- struct firmware_buf **buf)
+ struct firmware_buf **buf, void *dbuf,
+ size_t size)
{
struct firmware_buf *tmp;
*buf = tmp;
return 1;
}
- tmp = __allocate_fw_buf(fw_name, fwc);
+ tmp = __allocate_fw_buf(fw_name, fwc, dbuf, size);
if (tmp)
list_add(&tmp->list, &fwc->head);
spin_unlock(&fwc->lock);
vfree(buf->pages);
} else
#endif
+ if (!buf->allocated_size)
vfree(buf->data);
kfree_const(buf->fw_id);
kfree(buf);
mutex_unlock(&fw_lock);
}
-static int fw_get_filesystem_firmware(struct device *device,
- struct firmware_buf *buf)
+static int
+fw_get_filesystem_firmware(struct device *device, struct firmware_buf *buf)
{
loff_t size;
int i, len;
int rc = -ENOENT;
char *path;
+ enum kernel_read_file_id id = READING_FIRMWARE;
+ size_t msize = INT_MAX;
+
+ /* Already populated data member means we're loading into a buffer */
+ if (buf->data) {
+ id = READING_FIRMWARE_PREALLOC_BUFFER;
+ msize = buf->allocated_size;
+ }
path = __getname();
if (!path)
}
buf->size = 0;
- rc = kernel_read_file_from_path(path, &buf->data, &size,
- INT_MAX, READING_FIRMWARE);
+ rc = kernel_read_file_from_path(path, &buf->data, &size, msize,
+ id);
if (rc) {
if (rc == -ENOENT)
dev_dbg(device, "loading %s failed with error %d\n",
static DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store);
+static void firmware_rw_buf(struct firmware_buf *buf, char *buffer,
+ loff_t offset, size_t count, bool read)
+{
+ if (read)
+ memcpy(buffer, buf->data + offset, count);
+ else
+ memcpy(buf->data + offset, buffer, count);
+}
+
static void firmware_rw(struct firmware_buf *buf, char *buffer,
loff_t offset, size_t count, bool read)
{
ret_count = count;
- firmware_rw(buf, buffer, offset, count, true);
+ if (buf->data)
+ firmware_rw_buf(buf, buffer, offset, count, true);
+ else
+ firmware_rw(buf, buffer, offset, count, true);
out:
mutex_unlock(&fw_lock);
goto out;
}
- retval = fw_realloc_buffer(fw_priv, offset + count);
- if (retval)
- goto out;
+ if (buf->data) {
+ if (offset + count > buf->allocated_size) {
+ retval = -ENOMEM;
+ goto out;
+ }
+ firmware_rw_buf(buf, buffer, offset, count, false);
+ retval = count;
+ } else {
+ retval = fw_realloc_buffer(fw_priv, offset + count);
+ if (retval)
+ goto out;
- retval = count;
- firmware_rw(buf, buffer, offset, count, false);
+ retval = count;
+ firmware_rw(buf, buffer, offset, count, false);
+ }
buf->size = max_t(size_t, offset + count, buf->size);
out:
struct firmware_buf *buf = fw_priv->buf;
/* fall back on userspace loading */
- buf->is_paged_buf = true;
+ if (!buf->data)
+ buf->is_paged_buf = true;
dev_set_uevent_suppress(f_dev, true);
if (is_fw_load_aborted(buf))
retval = -EAGAIN;
- else if (!buf->data)
+ else if (buf->is_paged_buf && !buf->data)
retval = -ENOMEM;
device_del(f_dev);
*/
static int
_request_firmware_prepare(struct firmware **firmware_p, const char *name,
- struct device *device)
+ struct device *device, void *dbuf, size_t size)
{
struct firmware *firmware;
struct firmware_buf *buf;
return -ENOMEM;
}
- if (fw_get_builtin_firmware(firmware, name)) {
+ if (fw_get_builtin_firmware(firmware, name, dbuf, size)) {
dev_dbg(device, "using built-in %s\n", name);
return 0; /* assigned */
}
- ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf);
+ ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf, dbuf, size);
/*
* bind with 'buf' now to avoid warning in failure path
/* called from request_firmware() and request_firmware_work_func() */
static int
_request_firmware(const struct firmware **firmware_p, const char *name,
- struct device *device, unsigned int opt_flags)
+ struct device *device, void *buf, size_t size,
+ unsigned int opt_flags)
{
struct firmware *fw = NULL;
long timeout;
goto out;
}
- ret = _request_firmware_prepare(&fw, name, device);
+ ret = _request_firmware_prepare(&fw, name, device, buf, size);
if (ret <= 0) /* error or already assigned */
goto out;
/* Need to pin this module until return */
__module_get(THIS_MODULE);
- ret = _request_firmware(firmware_p, name, device,
+ ret = _request_firmware(firmware_p, name, device, NULL, 0,
FW_OPT_UEVENT | FW_OPT_FALLBACK);
module_put(THIS_MODULE);
return ret;
int ret;
__module_get(THIS_MODULE);
- ret = _request_firmware(firmware_p, name, device,
+ ret = _request_firmware(firmware_p, name, device, NULL, 0,
FW_OPT_UEVENT | FW_OPT_NO_WARN);
module_put(THIS_MODULE);
return ret;
}
EXPORT_SYMBOL_GPL(request_firmware_direct);
+/**
+ * request_firmware_into_buf - load firmware into a previously allocated buffer
+ * @firmware_p: pointer to firmware image
+ * @name: name of firmware file
+ * @device: device for which firmware is being loaded and DMA region allocated
+ * @buf: address of buffer to load firmware into
+ * @size: size of buffer
+ *
+ * This function works pretty much like request_firmware(), but it doesn't
+ * allocate a buffer to hold the firmware data. Instead, the firmware
+ * is loaded directly into the buffer pointed to by @buf and the @firmware_p
+ * data member is pointed at @buf.
+ *
+ * This function doesn't cache firmware either.
+ */
+int
+request_firmware_into_buf(const struct firmware **firmware_p, const char *name,
+ struct device *device, void *buf, size_t size)
+{
+ int ret;
+
+ __module_get(THIS_MODULE);
+ ret = _request_firmware(firmware_p, name, device, buf, size,
+ FW_OPT_UEVENT | FW_OPT_FALLBACK |
+ FW_OPT_NOCACHE);
+ module_put(THIS_MODULE);
+ return ret;
+}
+EXPORT_SYMBOL(request_firmware_into_buf);
+
/**
* release_firmware: - release the resource associated with a firmware image
* @fw: firmware resource to release
fw_work = container_of(work, struct firmware_work, work);
- _request_firmware(&fw, fw_work->name, fw_work->device,
+ _request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0,
fw_work->opt_flags);
fw_work->cont(fw, fw_work->context);
put_device(fw_work->device); /* taken in request_firmware_nowait() */
pr_debug("%s: %s\n", __func__, fw_name);
- if (fw_get_builtin_firmware(&fw, fw_name))
+ if (fw_get_builtin_firmware(&fw, fw_name, NULL, 0))
return 0;
buf = fw_lookup_buf(fw_name);