Commit c59a264c authored by Linus Torvalds's avatar Linus Torvalds
parents 6cb8a911 6e03a201
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/highmem.h>
#include <linux/firmware.h> #include <linux/firmware.h>
#include "base.h" #include "base.h"
...@@ -45,7 +45,10 @@ struct firmware_priv { ...@@ -45,7 +45,10 @@ struct firmware_priv {
struct bin_attribute attr_data; struct bin_attribute attr_data;
struct firmware *fw; struct firmware *fw;
unsigned long status; unsigned long status;
int alloc_size; struct page **pages;
int nr_pages;
int page_array_size;
const char *vdata;
struct timer_list timeout; struct timer_list timeout;
}; };
...@@ -122,6 +125,10 @@ static ssize_t firmware_loading_show(struct device *dev, ...@@ -122,6 +125,10 @@ static ssize_t firmware_loading_show(struct device *dev,
return sprintf(buf, "%d\n", loading); return sprintf(buf, "%d\n", loading);
} }
/* Some architectures don't have PAGE_KERNEL_RO */
#ifndef PAGE_KERNEL_RO
#define PAGE_KERNEL_RO PAGE_KERNEL
#endif
/** /**
* firmware_loading_store - set value in the 'loading' control file * firmware_loading_store - set value in the 'loading' control file
* @dev: device pointer * @dev: device pointer
...@@ -141,6 +148,7 @@ static ssize_t firmware_loading_store(struct device *dev, ...@@ -141,6 +148,7 @@ static ssize_t firmware_loading_store(struct device *dev,
{ {
struct firmware_priv *fw_priv = dev_get_drvdata(dev); struct firmware_priv *fw_priv = dev_get_drvdata(dev);
int loading = simple_strtol(buf, NULL, 10); int loading = simple_strtol(buf, NULL, 10);
int i;
switch (loading) { switch (loading) {
case 1: case 1:
...@@ -151,13 +159,30 @@ static ssize_t firmware_loading_store(struct device *dev, ...@@ -151,13 +159,30 @@ static ssize_t firmware_loading_store(struct device *dev,
} }
vfree(fw_priv->fw->data); vfree(fw_priv->fw->data);
fw_priv->fw->data = NULL; fw_priv->fw->data = NULL;
for (i = 0; i < fw_priv->nr_pages; i++)
__free_page(fw_priv->pages[i]);
kfree(fw_priv->pages);
fw_priv->pages = NULL;
fw_priv->page_array_size = 0;
fw_priv->nr_pages = 0;
fw_priv->fw->size = 0; fw_priv->fw->size = 0;
fw_priv->alloc_size = 0;
set_bit(FW_STATUS_LOADING, &fw_priv->status); set_bit(FW_STATUS_LOADING, &fw_priv->status);
mutex_unlock(&fw_lock); mutex_unlock(&fw_lock);
break; break;
case 0: case 0:
if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) { if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) {
vfree(fw_priv->fw->data);
fw_priv->fw->data = vmap(fw_priv->pages,
fw_priv->nr_pages,
0, PAGE_KERNEL_RO);
if (!fw_priv->fw->data) {
dev_err(dev, "%s: vmap() failed\n", __func__);
goto err;
}
/* Pages will be freed by vfree() */
fw_priv->pages = NULL;
fw_priv->page_array_size = 0;
fw_priv->nr_pages = 0;
complete(&fw_priv->completion); complete(&fw_priv->completion);
clear_bit(FW_STATUS_LOADING, &fw_priv->status); clear_bit(FW_STATUS_LOADING, &fw_priv->status);
break; break;
...@@ -167,6 +192,7 @@ static ssize_t firmware_loading_store(struct device *dev, ...@@ -167,6 +192,7 @@ static ssize_t firmware_loading_store(struct device *dev,
dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading); dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading);
/* fallthrough */ /* fallthrough */
case -1: case -1:
err:
fw_load_abort(fw_priv); fw_load_abort(fw_priv);
break; break;
} }
...@@ -191,8 +217,28 @@ firmware_data_read(struct kobject *kobj, struct bin_attribute *bin_attr, ...@@ -191,8 +217,28 @@ firmware_data_read(struct kobject *kobj, struct bin_attribute *bin_attr,
ret_count = -ENODEV; ret_count = -ENODEV;
goto out; goto out;
} }
ret_count = memory_read_from_buffer(buffer, count, &offset, if (offset > fw->size)
fw->data, fw->size); return 0;
if (count > fw->size - offset)
count = fw->size - offset;
ret_count = count;
while (count) {
void *page_data;
int page_nr = offset >> PAGE_SHIFT;
int page_ofs = offset & (PAGE_SIZE-1);
int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
page_data = kmap(fw_priv->pages[page_nr]);
memcpy(buffer, page_data + page_ofs, page_cnt);
kunmap(fw_priv->pages[page_nr]);
buffer += page_cnt;
offset += page_cnt;
count -= page_cnt;
}
out: out:
mutex_unlock(&fw_lock); mutex_unlock(&fw_lock);
return ret_count; return ret_count;
...@@ -201,27 +247,39 @@ out: ...@@ -201,27 +247,39 @@ out:
static int static int
fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size) fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
{ {
u8 *new_data; int pages_needed = ALIGN(min_size, PAGE_SIZE) >> PAGE_SHIFT;
int new_size = fw_priv->alloc_size;
/* If the array of pages is too small, grow it... */
if (fw_priv->page_array_size < pages_needed) {
int new_array_size = max(pages_needed,
fw_priv->page_array_size * 2);
struct page **new_pages;
new_pages = kmalloc(new_array_size * sizeof(void *),
GFP_KERNEL);
if (!new_pages) {
fw_load_abort(fw_priv);
return -ENOMEM;
}
memcpy(new_pages, fw_priv->pages,
fw_priv->page_array_size * sizeof(void *));
memset(&new_pages[fw_priv->page_array_size], 0, sizeof(void *) *
(new_array_size - fw_priv->page_array_size));
kfree(fw_priv->pages);
fw_priv->pages = new_pages;
fw_priv->page_array_size = new_array_size;
}
if (min_size <= fw_priv->alloc_size) while (fw_priv->nr_pages < pages_needed) {
return 0; fw_priv->pages[fw_priv->nr_pages] =
alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
new_size = ALIGN(min_size, PAGE_SIZE); if (!fw_priv->pages[fw_priv->nr_pages]) {
new_data = vmalloc(new_size); fw_load_abort(fw_priv);
if (!new_data) { return -ENOMEM;
printk(KERN_ERR "%s: unable to alloc buffer\n", __func__); }
/* Make sure that we don't keep incomplete data */ fw_priv->nr_pages++;
fw_load_abort(fw_priv);
return -ENOMEM;
}
fw_priv->alloc_size = new_size;
if (fw_priv->fw->data) {
memcpy(new_data, fw_priv->fw->data, fw_priv->fw->size);
vfree(fw_priv->fw->data);
} }
fw_priv->fw->data = new_data;
BUG_ON(min_size > fw_priv->alloc_size);
return 0; return 0;
} }
...@@ -258,10 +316,25 @@ firmware_data_write(struct kobject *kobj, struct bin_attribute *bin_attr, ...@@ -258,10 +316,25 @@ firmware_data_write(struct kobject *kobj, struct bin_attribute *bin_attr,
if (retval) if (retval)
goto out; goto out;
memcpy((u8 *)fw->data + offset, buffer, count);
fw->size = max_t(size_t, offset + count, fw->size);
retval = count; retval = count;
while (count) {
void *page_data;
int page_nr = offset >> PAGE_SHIFT;
int page_ofs = offset & (PAGE_SIZE - 1);
int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
page_data = kmap(fw_priv->pages[page_nr]);
memcpy(page_data + page_ofs, buffer, page_cnt);
kunmap(fw_priv->pages[page_nr]);
buffer += page_cnt;
offset += page_cnt;
count -= page_cnt;
}
fw->size = max_t(size_t, offset, fw->size);
out: out:
mutex_unlock(&fw_lock); mutex_unlock(&fw_lock);
return retval; return retval;
...@@ -277,7 +350,11 @@ static struct bin_attribute firmware_attr_data_tmpl = { ...@@ -277,7 +350,11 @@ static struct bin_attribute firmware_attr_data_tmpl = {
static void fw_dev_release(struct device *dev) static void fw_dev_release(struct device *dev)
{ {
struct firmware_priv *fw_priv = dev_get_drvdata(dev); struct firmware_priv *fw_priv = dev_get_drvdata(dev);
int i;
for (i = 0; i < fw_priv->nr_pages; i++)
__free_page(fw_priv->pages[i]);
kfree(fw_priv->pages);
kfree(fw_priv); kfree(fw_priv);
kfree(dev); kfree(dev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment