aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base/firmware_class.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base/firmware_class.c')
-rw-r--r--drivers/base/firmware_class.c129
1 files changed, 103 insertions, 26 deletions
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index d3a59c688fe4..8a267c427629 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -17,7 +17,7 @@
17#include <linux/bitops.h> 17#include <linux/bitops.h>
18#include <linux/mutex.h> 18#include <linux/mutex.h>
19#include <linux/kthread.h> 19#include <linux/kthread.h>
20 20#include <linux/highmem.h>
21#include <linux/firmware.h> 21#include <linux/firmware.h>
22#include "base.h" 22#include "base.h"
23 23
@@ -45,7 +45,10 @@ struct firmware_priv {
45 struct bin_attribute attr_data; 45 struct bin_attribute attr_data;
46 struct firmware *fw; 46 struct firmware *fw;
47 unsigned long status; 47 unsigned long status;
48 int alloc_size; 48 struct page **pages;
49 int nr_pages;
50 int page_array_size;
51 const char *vdata;
49 struct timer_list timeout; 52 struct timer_list timeout;
50}; 53};
51 54
@@ -122,6 +125,10 @@ static ssize_t firmware_loading_show(struct device *dev,
122 return sprintf(buf, "%d\n", loading); 125 return sprintf(buf, "%d\n", loading);
123} 126}
124 127
128/* Some architectures don't have PAGE_KERNEL_RO */
129#ifndef PAGE_KERNEL_RO
130#define PAGE_KERNEL_RO PAGE_KERNEL
131#endif
125/** 132/**
126 * firmware_loading_store - set value in the 'loading' control file 133 * firmware_loading_store - set value in the 'loading' control file
127 * @dev: device pointer 134 * @dev: device pointer
@@ -141,6 +148,7 @@ static ssize_t firmware_loading_store(struct device *dev,
141{ 148{
142 struct firmware_priv *fw_priv = dev_get_drvdata(dev); 149 struct firmware_priv *fw_priv = dev_get_drvdata(dev);
143 int loading = simple_strtol(buf, NULL, 10); 150 int loading = simple_strtol(buf, NULL, 10);
151 int i;
144 152
145 switch (loading) { 153 switch (loading) {
146 case 1: 154 case 1:
@@ -151,13 +159,30 @@ static ssize_t firmware_loading_store(struct device *dev,
151 } 159 }
152 vfree(fw_priv->fw->data); 160 vfree(fw_priv->fw->data);
153 fw_priv->fw->data = NULL; 161 fw_priv->fw->data = NULL;
162 for (i = 0; i < fw_priv->nr_pages; i++)
163 __free_page(fw_priv->pages[i]);
164 kfree(fw_priv->pages);
165 fw_priv->pages = NULL;
166 fw_priv->page_array_size = 0;
167 fw_priv->nr_pages = 0;
154 fw_priv->fw->size = 0; 168 fw_priv->fw->size = 0;
155 fw_priv->alloc_size = 0;
156 set_bit(FW_STATUS_LOADING, &fw_priv->status); 169 set_bit(FW_STATUS_LOADING, &fw_priv->status);
157 mutex_unlock(&fw_lock); 170 mutex_unlock(&fw_lock);
158 break; 171 break;
159 case 0: 172 case 0:
160 if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) { 173 if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) {
174 vfree(fw_priv->fw->data);
175 fw_priv->fw->data = vmap(fw_priv->pages,
176 fw_priv->nr_pages,
177 0, PAGE_KERNEL_RO);
178 if (!fw_priv->fw->data) {
179 dev_err(dev, "%s: vmap() failed\n", __func__);
180 goto err;
181 }
182 /* Pages will be freed by vfree() */
183 fw_priv->pages = NULL;
184 fw_priv->page_array_size = 0;
185 fw_priv->nr_pages = 0;
161 complete(&fw_priv->completion); 186 complete(&fw_priv->completion);
162 clear_bit(FW_STATUS_LOADING, &fw_priv->status); 187 clear_bit(FW_STATUS_LOADING, &fw_priv->status);
163 break; 188 break;
@@ -167,6 +192,7 @@ static ssize_t firmware_loading_store(struct device *dev,
167 dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading); 192 dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading);
168 /* fallthrough */ 193 /* fallthrough */
169 case -1: 194 case -1:
195 err:
170 fw_load_abort(fw_priv); 196 fw_load_abort(fw_priv);
171 break; 197 break;
172 } 198 }
@@ -191,8 +217,28 @@ firmware_data_read(struct kobject *kobj, struct bin_attribute *bin_attr,
191 ret_count = -ENODEV; 217 ret_count = -ENODEV;
192 goto out; 218 goto out;
193 } 219 }
194 ret_count = memory_read_from_buffer(buffer, count, &offset, 220 if (offset > fw->size)
195 fw->data, fw->size); 221 return 0;
222 if (count > fw->size - offset)
223 count = fw->size - offset;
224
225 ret_count = count;
226
227 while (count) {
228 void *page_data;
229 int page_nr = offset >> PAGE_SHIFT;
230 int page_ofs = offset & (PAGE_SIZE-1);
231 int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
232
233 page_data = kmap(fw_priv->pages[page_nr]);
234
235 memcpy(buffer, page_data + page_ofs, page_cnt);
236
237 kunmap(fw_priv->pages[page_nr]);
238 buffer += page_cnt;
239 offset += page_cnt;
240 count -= page_cnt;
241 }
196out: 242out:
197 mutex_unlock(&fw_lock); 243 mutex_unlock(&fw_lock);
198 return ret_count; 244 return ret_count;
@@ -201,27 +247,39 @@ out:
201static int 247static int
202fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size) 248fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
203{ 249{
204 u8 *new_data; 250 int pages_needed = ALIGN(min_size, PAGE_SIZE) >> PAGE_SHIFT;
205 int new_size = fw_priv->alloc_size; 251
252 /* If the array of pages is too small, grow it... */
253 if (fw_priv->page_array_size < pages_needed) {
254 int new_array_size = max(pages_needed,
255 fw_priv->page_array_size * 2);
256 struct page **new_pages;
257
258 new_pages = kmalloc(new_array_size * sizeof(void *),
259 GFP_KERNEL);
260 if (!new_pages) {
261 fw_load_abort(fw_priv);
262 return -ENOMEM;
263 }
264 memcpy(new_pages, fw_priv->pages,
265 fw_priv->page_array_size * sizeof(void *));
266 memset(&new_pages[fw_priv->page_array_size], 0, sizeof(void *) *
267 (new_array_size - fw_priv->page_array_size));
268 kfree(fw_priv->pages);
269 fw_priv->pages = new_pages;
270 fw_priv->page_array_size = new_array_size;
271 }
206 272
207 if (min_size <= fw_priv->alloc_size) 273 while (fw_priv->nr_pages < pages_needed) {
208 return 0; 274 fw_priv->pages[fw_priv->nr_pages] =
275 alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
209 276
210 new_size = ALIGN(min_size, PAGE_SIZE); 277 if (!fw_priv->pages[fw_priv->nr_pages]) {
211 new_data = vmalloc(new_size); 278 fw_load_abort(fw_priv);
212 if (!new_data) { 279 return -ENOMEM;
213 printk(KERN_ERR "%s: unable to alloc buffer\n", __func__); 280 }
214 /* Make sure that we don't keep incomplete data */ 281 fw_priv->nr_pages++;
215 fw_load_abort(fw_priv);
216 return -ENOMEM;
217 }
218 fw_priv->alloc_size = new_size;
219 if (fw_priv->fw->data) {
220 memcpy(new_data, fw_priv->fw->data, fw_priv->fw->size);
221 vfree(fw_priv->fw->data);
222 } 282 }
223 fw_priv->fw->data = new_data;
224 BUG_ON(min_size > fw_priv->alloc_size);
225 return 0; 283 return 0;
226} 284}
227 285
@@ -258,10 +316,25 @@ firmware_data_write(struct kobject *kobj, struct bin_attribute *bin_attr,
258 if (retval) 316 if (retval)
259 goto out; 317 goto out;
260 318
261 memcpy((u8 *)fw->data + offset, buffer, count);
262
263 fw->size = max_t(size_t, offset + count, fw->size);
264 retval = count; 319 retval = count;
320
321 while (count) {
322 void *page_data;
323 int page_nr = offset >> PAGE_SHIFT;
324 int page_ofs = offset & (PAGE_SIZE - 1);
325 int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
326
327 page_data = kmap(fw_priv->pages[page_nr]);
328
329 memcpy(page_data + page_ofs, buffer, page_cnt);
330
331 kunmap(fw_priv->pages[page_nr]);
332 buffer += page_cnt;
333 offset += page_cnt;
334 count -= page_cnt;
335 }
336
337 fw->size = max_t(size_t, offset, fw->size);
265out: 338out:
266 mutex_unlock(&fw_lock); 339 mutex_unlock(&fw_lock);
267 return retval; 340 return retval;
@@ -277,7 +350,11 @@ static struct bin_attribute firmware_attr_data_tmpl = {
277static void fw_dev_release(struct device *dev) 350static void fw_dev_release(struct device *dev)
278{ 351{
279 struct firmware_priv *fw_priv = dev_get_drvdata(dev); 352 struct firmware_priv *fw_priv = dev_get_drvdata(dev);
353 int i;
280 354
355 for (i = 0; i < fw_priv->nr_pages; i++)
356 __free_page(fw_priv->pages[i]);
357 kfree(fw_priv->pages);
281 kfree(fw_priv); 358 kfree(fw_priv);
282 kfree(dev); 359 kfree(dev);
283 360