Blame view

drivers/base/firmware_class.c 17.2 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
  /*
   * firmware_class.c - Multi purpose firmware loading support
   *
87d37a4f4   Markus Rechberger   firmware: remove ...
4
   * Copyright (c) 2003 Manuel Estrada Sainz
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
5
6
7
8
   *
   * Please see Documentation/firmware_class/ for more information.
   *
   */
c59ede7b7   Randy.Dunlap   [PATCH] move capa...
9
  #include <linux/capability.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
11
12
13
14
15
16
  #include <linux/device.h>
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/timer.h>
  #include <linux/vmalloc.h>
  #include <linux/interrupt.h>
  #include <linux/bitops.h>
cad1e55d4   Laura Garcia   [PATCH] firmware_...
17
  #include <linux/mutex.h>
563d07570   Sukadev Bhattiprolu   [PATCH] kthread: ...
18
  #include <linux/kthread.h>
6e03a201b   David Woodhouse   firmware: speed u...
19
  #include <linux/highmem.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
20
  #include <linux/firmware.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
21
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22

e55c8790d   Greg Kroah-Hartman   Driver core: conv...
23
  #define to_dev(obj) container_of(obj, struct device, kobj)
87d37a4f4   Markus Rechberger   firmware: remove ...
24
  MODULE_AUTHOR("Manuel Estrada Sainz");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
26
  MODULE_DESCRIPTION("Multi purpose firmware loading support");
  MODULE_LICENSE("GPL");
bcb9bd18e   Dmitry Torokhov   firmware loader: ...
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
  /* Builtin firmware support */
  
  #ifdef CONFIG_FW_LOADER
  
  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)
  {
  	struct builtin_fw *b_fw;
  
  	for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++) {
  		if (strcmp(name, b_fw->name) == 0) {
  			fw->size = b_fw->size;
  			fw->data = b_fw->data;
  			return true;
  		}
  	}
  
  	return false;
  }
  
  static bool fw_is_builtin_firmware(const struct firmware *fw)
  {
  	struct builtin_fw *b_fw;
  
  	for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++)
  		if (fw->data == b_fw->data)
  			return true;
  
  	return false;
  }
  
  #else /* Module case - no builtin firmware support */
  
  static inline bool fw_get_builtin_firmware(struct firmware *fw, const char *name)
  {
  	return false;
  }
  
  static inline bool fw_is_builtin_firmware(const struct firmware *fw)
  {
  	return false;
  }
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
73
74
75
  enum {
  	FW_STATUS_LOADING,
  	FW_STATUS_DONE,
  	FW_STATUS_ABORT,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76
  };
2f65168de   Dave Jones   Driver Core: Incr...
77
  static int loading_timeout = 60;	/* In seconds */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78
79
80
  
  /* fw_lock could be moved to 'struct firmware_priv' but since it is just
   * guarding for corner cases a global lock should be OK */
cad1e55d4   Laura Garcia   [PATCH] firmware_...
81
  static DEFINE_MUTEX(fw_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82
83
  
  struct firmware_priv {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84
  	struct completion completion;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
86
  	struct firmware *fw;
  	unsigned long status;
6e03a201b   David Woodhouse   firmware: speed u...
87
88
89
  	struct page **pages;
  	int nr_pages;
  	int page_array_size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
  	struct timer_list timeout;
f8a4bd345   Dmitry Torokhov   firmware loader: ...
91
  	struct device dev;
e9045f917   Johannes Berg   firmware class: e...
92
  	bool nowait;
e177123f0   Dmitry Torokhov   firmware loader: ...
93
  	char fw_id[];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
  };
f8a4bd345   Dmitry Torokhov   firmware loader: ...
95
96
97
98
99
100
  static struct firmware_priv *to_firmware_priv(struct device *dev)
  {
  	return container_of(dev, struct firmware_priv, dev);
  }
  
  static void fw_load_abort(struct firmware_priv *fw_priv)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
101
102
103
104
105
  {
  	set_bit(FW_STATUS_ABORT, &fw_priv->status);
  	wmb();
  	complete(&fw_priv->completion);
  }
f8a4bd345   Dmitry Torokhov   firmware loader: ...
106
107
108
  static ssize_t firmware_timeout_show(struct class *class,
  				     struct class_attribute *attr,
  				     char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
110
111
112
113
114
  {
  	return sprintf(buf, "%d
  ", loading_timeout);
  }
  
  /**
eb8e31799   Randy Dunlap   [PATCH] firmware:...
115
116
   * firmware_timeout_store - set number of seconds to wait for firmware
   * @class: device class pointer
e59817bf0   Randy Dunlap   driver-core: fix ...
117
   * @attr: device attribute pointer
eb8e31799   Randy Dunlap   [PATCH] firmware:...
118
119
120
   * @buf: buffer to scan for timeout value
   * @count: number of bytes in @buf
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
121
   *	Sets the number of seconds to wait for the firmware.  Once
eb8e31799   Randy Dunlap   [PATCH] firmware:...
122
   *	this expires an error will be returned to the driver and no
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123
124
   *	firmware will be provided.
   *
eb8e31799   Randy Dunlap   [PATCH] firmware:...
125
   *	Note: zero means 'wait forever'.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126
   **/
f8a4bd345   Dmitry Torokhov   firmware loader: ...
127
128
129
  static ssize_t firmware_timeout_store(struct class *class,
  				      struct class_attribute *attr,
  				      const char *buf, size_t count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130
131
  {
  	loading_timeout = simple_strtol(buf, NULL, 10);
b92eac01c   Stanislaw W. Gruszka   [PATCH] request_f...
132
133
  	if (loading_timeout < 0)
  		loading_timeout = 0;
f8a4bd345   Dmitry Torokhov   firmware loader: ...
134

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
136
  	return count;
  }
673fae90d   Dmitry Torokhov   firmware loader: ...
137
138
139
140
141
  static struct class_attribute firmware_class_attrs[] = {
  	__ATTR(timeout, S_IWUSR | S_IRUGO,
  		firmware_timeout_show, firmware_timeout_store),
  	__ATTR_NULL
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142

673fae90d   Dmitry Torokhov   firmware loader: ...
143
144
  static void fw_dev_release(struct device *dev)
  {
f8a4bd345   Dmitry Torokhov   firmware loader: ...
145
  	struct firmware_priv *fw_priv = to_firmware_priv(dev);
673fae90d   Dmitry Torokhov   firmware loader: ...
146
147
148
149
150
  	int i;
  
  	for (i = 0; i < fw_priv->nr_pages; i++)
  		__free_page(fw_priv->pages[i]);
  	kfree(fw_priv->pages);
673fae90d   Dmitry Torokhov   firmware loader: ...
151
  	kfree(fw_priv);
673fae90d   Dmitry Torokhov   firmware loader: ...
152
153
154
  
  	module_put(THIS_MODULE);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155

7eff2e7a8   Kay Sievers   Driver core: chan...
156
  static int firmware_uevent(struct device *dev, struct kobj_uevent_env *env)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
  {
f8a4bd345   Dmitry Torokhov   firmware loader: ...
158
  	struct firmware_priv *fw_priv = to_firmware_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
159

7eff2e7a8   Kay Sievers   Driver core: chan...
160
  	if (add_uevent_var(env, "FIRMWARE=%s", fw_priv->fw_id))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
161
  		return -ENOMEM;
7eff2e7a8   Kay Sievers   Driver core: chan...
162
  	if (add_uevent_var(env, "TIMEOUT=%i", loading_timeout))
6897089c5   Kay Sievers   [PATCH] add TIMEO...
163
  		return -ENOMEM;
e9045f917   Johannes Berg   firmware class: e...
164
165
  	if (add_uevent_var(env, "ASYNC=%d", fw_priv->nowait))
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
167
168
  
  	return 0;
  }
1b81d6637   Adrian Bunk   [PATCH] drivers/b...
169
170
  static struct class firmware_class = {
  	.name		= "firmware",
673fae90d   Dmitry Torokhov   firmware loader: ...
171
  	.class_attrs	= firmware_class_attrs,
e55c8790d   Greg Kroah-Hartman   Driver core: conv...
172
173
  	.dev_uevent	= firmware_uevent,
  	.dev_release	= fw_dev_release,
1b81d6637   Adrian Bunk   [PATCH] drivers/b...
174
  };
e55c8790d   Greg Kroah-Hartman   Driver core: conv...
175
176
  static ssize_t firmware_loading_show(struct device *dev,
  				     struct device_attribute *attr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177
  {
f8a4bd345   Dmitry Torokhov   firmware loader: ...
178
  	struct firmware_priv *fw_priv = to_firmware_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
179
  	int loading = test_bit(FW_STATUS_LOADING, &fw_priv->status);
f8a4bd345   Dmitry Torokhov   firmware loader: ...
180

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
182
183
  	return sprintf(buf, "%d
  ", loading);
  }
dd336c554   David Woodhouse   firmware_class: f...
184
185
186
187
188
189
190
191
192
193
  static void firmware_free_data(const struct firmware *fw)
  {
  	int i;
  	vunmap(fw->data);
  	if (fw->pages) {
  		for (i = 0; i < PFN_UP(fw->size); i++)
  			__free_page(fw->pages[i]);
  		kfree(fw->pages);
  	}
  }
6e03a201b   David Woodhouse   firmware: speed u...
194
195
196
197
  /* Some architectures don't have PAGE_KERNEL_RO */
  #ifndef PAGE_KERNEL_RO
  #define PAGE_KERNEL_RO PAGE_KERNEL
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198
  /**
eb8e31799   Randy Dunlap   [PATCH] firmware:...
199
   * firmware_loading_store - set value in the 'loading' control file
e55c8790d   Greg Kroah-Hartman   Driver core: conv...
200
   * @dev: device pointer
af9997e42   Randy Dunlap   [PATCH] fix kerne...
201
   * @attr: device attribute pointer
eb8e31799   Randy Dunlap   [PATCH] firmware:...
202
203
204
   * @buf: buffer to scan for loading control value
   * @count: number of bytes in @buf
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
206
207
   *	The relevant values are:
   *
   *	 1: Start a load, discarding any previous partial load.
eb8e31799   Randy Dunlap   [PATCH] firmware:...
208
   *	 0: Conclude the load and hand the data to the driver code.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
210
   *	-1: Conclude the load with an error and discard any written data.
   **/
e55c8790d   Greg Kroah-Hartman   Driver core: conv...
211
212
213
  static ssize_t firmware_loading_store(struct device *dev,
  				      struct device_attribute *attr,
  				      const char *buf, size_t count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
  {
f8a4bd345   Dmitry Torokhov   firmware loader: ...
215
  	struct firmware_priv *fw_priv = to_firmware_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
216
  	int loading = simple_strtol(buf, NULL, 10);
6e03a201b   David Woodhouse   firmware: speed u...
217
  	int i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218

eea915bb0   Neil Horman   firmware: Fix an ...
219
220
221
222
  	mutex_lock(&fw_lock);
  
  	if (!fw_priv->fw)
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
223
224
  	switch (loading) {
  	case 1:
dd336c554   David Woodhouse   firmware_class: f...
225
226
227
  		firmware_free_data(fw_priv->fw);
  		memset(fw_priv->fw, 0, sizeof(struct firmware));
  		/* If the pages are not owned by 'struct firmware' */
6e03a201b   David Woodhouse   firmware: speed u...
228
229
230
231
232
233
  		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;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
234
  		set_bit(FW_STATUS_LOADING, &fw_priv->status);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
235
236
237
  		break;
  	case 0:
  		if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) {
dd336c554   David Woodhouse   firmware_class: f...
238
  			vunmap(fw_priv->fw->data);
6e03a201b   David Woodhouse   firmware: speed u...
239
240
241
242
243
244
245
246
  			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
  ", __func__);
  				goto err;
  			}
dd336c554   David Woodhouse   firmware_class: f...
247
248
249
  			/* Pages are now owned by 'struct firmware' */
  			fw_priv->fw->pages = fw_priv->pages;
  			fw_priv->pages = NULL;
6e03a201b   David Woodhouse   firmware: speed u...
250
251
  			fw_priv->page_array_size = 0;
  			fw_priv->nr_pages = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
253
254
255
256
257
  			complete(&fw_priv->completion);
  			clear_bit(FW_STATUS_LOADING, &fw_priv->status);
  			break;
  		}
  		/* fallthrough */
  	default:
266a813c0   Bjorn Helgaas   firmware: use dev...
258
259
  		dev_err(dev, "%s: unexpected value (%d)
  ", __func__, loading);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
261
  		/* fallthrough */
  	case -1:
6e03a201b   David Woodhouse   firmware: speed u...
262
  	err:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
263
264
265
  		fw_load_abort(fw_priv);
  		break;
  	}
eea915bb0   Neil Horman   firmware: Fix an ...
266
267
  out:
  	mutex_unlock(&fw_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
268
269
  	return count;
  }
e55c8790d   Greg Kroah-Hartman   Driver core: conv...
270
  static DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271

f8a4bd345   Dmitry Torokhov   firmware loader: ...
272
273
274
  static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
  				  struct bin_attribute *bin_attr,
  				  char *buffer, loff_t offset, size_t count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
  {
e55c8790d   Greg Kroah-Hartman   Driver core: conv...
276
  	struct device *dev = to_dev(kobj);
f8a4bd345   Dmitry Torokhov   firmware loader: ...
277
  	struct firmware_priv *fw_priv = to_firmware_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
278
  	struct firmware *fw;
f37e66173   Akinobu Mita   firmware: use mem...
279
  	ssize_t ret_count;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280

cad1e55d4   Laura Garcia   [PATCH] firmware_...
281
  	mutex_lock(&fw_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
  	fw = fw_priv->fw;
b92eac01c   Stanislaw W. Gruszka   [PATCH] request_f...
283
  	if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->status)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
284
285
286
  		ret_count = -ENODEV;
  		goto out;
  	}
308975fa7   Jiri Slaby   Firmware: firmwar...
287
288
289
290
  	if (offset > fw->size) {
  		ret_count = 0;
  		goto out;
  	}
6e03a201b   David Woodhouse   firmware: speed u...
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
  	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;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
311
  out:
cad1e55d4   Laura Garcia   [PATCH] firmware_...
312
  	mutex_unlock(&fw_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313
314
  	return ret_count;
  }
eb8e31799   Randy Dunlap   [PATCH] firmware:...
315

f8a4bd345   Dmitry Torokhov   firmware loader: ...
316
  static int fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
317
  {
6e03a201b   David Woodhouse   firmware: speed u...
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
  	int pages_needed = ALIGN(min_size, PAGE_SIZE) >> PAGE_SHIFT;
  
  	/* 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;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340

6e03a201b   David Woodhouse   firmware: speed u...
341
342
343
  	while (fw_priv->nr_pages < pages_needed) {
  		fw_priv->pages[fw_priv->nr_pages] =
  			alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
344

6e03a201b   David Woodhouse   firmware: speed u...
345
346
347
348
349
  		if (!fw_priv->pages[fw_priv->nr_pages]) {
  			fw_load_abort(fw_priv);
  			return -ENOMEM;
  		}
  		fw_priv->nr_pages++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
350
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351
352
353
354
  	return 0;
  }
  
  /**
eb8e31799   Randy Dunlap   [PATCH] firmware:...
355
   * firmware_data_write - write method for firmware
2c3c8bea6   Chris Wright   sysfs: add struct...
356
   * @filp: open sysfs file
e55c8790d   Greg Kroah-Hartman   Driver core: conv...
357
   * @kobj: kobject for the device
42e61f4ad   Randy Dunlap   kernel-doc fixes ...
358
   * @bin_attr: bin_attr structure
eb8e31799   Randy Dunlap   [PATCH] firmware:...
359
360
361
   * @buffer: buffer being written
   * @offset: buffer offset for write in total data store area
   * @count: buffer size
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
362
   *
eb8e31799   Randy Dunlap   [PATCH] firmware:...
363
   *	Data written to the 'data' attribute will be later handed to
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
364
365
   *	the driver as a firmware image.
   **/
f8a4bd345   Dmitry Torokhov   firmware loader: ...
366
367
368
  static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
  				   struct bin_attribute *bin_attr,
  				   char *buffer, loff_t offset, size_t count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
369
  {
e55c8790d   Greg Kroah-Hartman   Driver core: conv...
370
  	struct device *dev = to_dev(kobj);
f8a4bd345   Dmitry Torokhov   firmware loader: ...
371
  	struct firmware_priv *fw_priv = to_firmware_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372
373
374
375
376
  	struct firmware *fw;
  	ssize_t retval;
  
  	if (!capable(CAP_SYS_RAWIO))
  		return -EPERM;
b92eac01c   Stanislaw W. Gruszka   [PATCH] request_f...
377

cad1e55d4   Laura Garcia   [PATCH] firmware_...
378
  	mutex_lock(&fw_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
  	fw = fw_priv->fw;
b92eac01c   Stanislaw W. Gruszka   [PATCH] request_f...
380
  	if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->status)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
381
382
383
384
385
386
  		retval = -ENODEV;
  		goto out;
  	}
  	retval = fw_realloc_buffer(fw_priv, offset + count);
  	if (retval)
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
387
  	retval = count;
6e03a201b   David Woodhouse   firmware: speed u...
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
  
  	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);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
406
  out:
cad1e55d4   Laura Garcia   [PATCH] firmware_...
407
  	mutex_unlock(&fw_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
408
409
  	return retval;
  }
eb8e31799   Randy Dunlap   [PATCH] firmware:...
410

0983ca2d0   Dmitry Torokhov   firmware loader: ...
411
412
  static struct bin_attribute firmware_attr_data = {
  	.attr = { .name = "data", .mode = 0644 },
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
413
414
415
416
  	.size = 0,
  	.read = firmware_data_read,
  	.write = firmware_data_write,
  };
f8a4bd345   Dmitry Torokhov   firmware loader: ...
417
  static void firmware_class_timeout(u_long data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
418
419
  {
  	struct firmware_priv *fw_priv = (struct firmware_priv *) data;
f8a4bd345   Dmitry Torokhov   firmware loader: ...
420

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
421
422
  	fw_load_abort(fw_priv);
  }
f8a4bd345   Dmitry Torokhov   firmware loader: ...
423
424
425
  static struct firmware_priv *
  fw_create_instance(struct firmware *firmware, const char *fw_name,
  		   struct device *device, bool uevent, bool nowait)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
426
  {
f8a4bd345   Dmitry Torokhov   firmware loader: ...
427
428
429
  	struct firmware_priv *fw_priv;
  	struct device *f_dev;
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
430

f8a4bd345   Dmitry Torokhov   firmware loader: ...
431
432
  	fw_priv = kzalloc(sizeof(*fw_priv) + strlen(fw_name) + 1 , GFP_KERNEL);
  	if (!fw_priv) {
266a813c0   Bjorn Helgaas   firmware: use dev...
433
434
  		dev_err(device, "%s: kmalloc failed
  ", __func__);
f8a4bd345   Dmitry Torokhov   firmware loader: ...
435
436
  		error = -ENOMEM;
  		goto err_out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
437
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
438

f8a4bd345   Dmitry Torokhov   firmware loader: ...
439
440
  	fw_priv->fw = firmware;
  	fw_priv->nowait = nowait;
e177123f0   Dmitry Torokhov   firmware loader: ...
441
  	strcpy(fw_priv->fw_id, fw_name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
442
  	init_completion(&fw_priv->completion);
f8a4bd345   Dmitry Torokhov   firmware loader: ...
443
444
  	setup_timer(&fw_priv->timeout,
  		    firmware_class_timeout, (u_long) fw_priv);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
445

f8a4bd345   Dmitry Torokhov   firmware loader: ...
446
447
448
  	f_dev = &fw_priv->dev;
  
  	device_initialize(f_dev);
acc0e90fb   Greg Kroah-Hartman   driver core: fix ...
449
  	dev_set_name(f_dev, "%s", dev_name(device));
e55c8790d   Greg Kroah-Hartman   Driver core: conv...
450
451
  	f_dev->parent = device;
  	f_dev->class = &firmware_class;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
452

f8a4bd345   Dmitry Torokhov   firmware loader: ...
453
  	dev_set_uevent_suppress(f_dev, true);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
454
455
456
  
  	/* Need to pin this module until class device is destroyed */
  	__module_get(THIS_MODULE);
f8a4bd345   Dmitry Torokhov   firmware loader: ...
457
458
459
460
461
462
  	error = device_add(f_dev);
  	if (error) {
  		dev_err(device, "%s: device_register failed
  ", __func__);
  		goto err_put_dev;
  	}
e9045f917   Johannes Berg   firmware class: e...
463

f8a4bd345   Dmitry Torokhov   firmware loader: ...
464
465
  	error = device_create_bin_file(f_dev, &firmware_attr_data);
  	if (error) {
266a813c0   Bjorn Helgaas   firmware: use dev...
466
467
  		dev_err(device, "%s: sysfs_create_bin_file failed
  ", __func__);
f8a4bd345   Dmitry Torokhov   firmware loader: ...
468
  		goto err_del_dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
469
  	}
f8a4bd345   Dmitry Torokhov   firmware loader: ...
470
471
  	error = device_create_file(f_dev, &dev_attr_loading);
  	if (error) {
266a813c0   Bjorn Helgaas   firmware: use dev...
472
473
  		dev_err(device, "%s: device_create_file failed
  ", __func__);
f8a4bd345   Dmitry Torokhov   firmware loader: ...
474
  		goto err_del_bin_attr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
475
  	}
312c004d3   Kay Sievers   [PATCH] driver co...
476
  	if (uevent)
f8a4bd345   Dmitry Torokhov   firmware loader: ...
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
  		dev_set_uevent_suppress(f_dev, false);
  
  	return fw_priv;
  
  err_del_bin_attr:
  	device_remove_bin_file(f_dev, &firmware_attr_data);
  err_del_dev:
  	device_del(f_dev);
  err_put_dev:
  	put_device(f_dev);
  err_out:
  	return ERR_PTR(error);
  }
  
  static void fw_destroy_instance(struct firmware_priv *fw_priv)
  {
  	struct device *f_dev = &fw_priv->dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
494

f8a4bd345   Dmitry Torokhov   firmware loader: ...
495
496
  	device_remove_file(f_dev, &dev_attr_loading);
  	device_remove_bin_file(f_dev, &firmware_attr_data);
e55c8790d   Greg Kroah-Hartman   Driver core: conv...
497
  	device_unregister(f_dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
498
  }
f8a4bd345   Dmitry Torokhov   firmware loader: ...
499
500
501
  static int _request_firmware(const struct firmware **firmware_p,
  			     const char *name, struct device *device,
  			     bool uevent, bool nowait)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
502
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
503
504
  	struct firmware_priv *fw_priv;
  	struct firmware *firmware;
f8a4bd345   Dmitry Torokhov   firmware loader: ...
505
  	int retval = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
506
507
508
  
  	if (!firmware_p)
  		return -EINVAL;
4aed0644d   Jiri Slaby   [PATCH] drivers/b...
509
  	*firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
510
  	if (!firmware) {
266a813c0   Bjorn Helgaas   firmware: use dev...
511
512
513
  		dev_err(device, "%s: kmalloc(struct firmware) failed
  ",
  			__func__);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
514
515
516
  		retval = -ENOMEM;
  		goto out;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
517

bcb9bd18e   Dmitry Torokhov   firmware loader: ...
518
  	if (fw_get_builtin_firmware(firmware, name)) {
6f18ff91d   Rafael J. Wysocki   Driver core: Redu...
519
520
  		dev_dbg(device, "firmware: using built-in firmware %s
  ", name);
5658c7694   David Woodhouse   firmware: allow f...
521
522
  		return 0;
  	}
b298d289c   Srivatsa S. Bhat   PM / Sleep: Fix f...
523
  	read_lock_usermodehelper();
caca9510f   Linus Torvalds   firmware loader: ...
524
525
526
527
528
529
  	if (WARN_ON(usermodehelper_is_disabled())) {
  		dev_err(device, "firmware: %s will not be loaded
  ", name);
  		retval = -EBUSY;
  		goto out;
  	}
5658c7694   David Woodhouse   firmware: allow f...
530
  	if (uevent)
6f18ff91d   Rafael J. Wysocki   Driver core: Redu...
531
532
  		dev_dbg(device, "firmware: requesting %s
  ", name);
5658c7694   David Woodhouse   firmware: allow f...
533

f8a4bd345   Dmitry Torokhov   firmware loader: ...
534
535
536
537
538
  	fw_priv = fw_create_instance(firmware, name, device, uevent, nowait);
  	if (IS_ERR(fw_priv)) {
  		retval = PTR_ERR(fw_priv);
  		goto out;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
539

312c004d3   Kay Sievers   [PATCH] driver co...
540
  	if (uevent) {
f8a4bd345   Dmitry Torokhov   firmware loader: ...
541
542
543
544
545
546
547
548
549
  		if (loading_timeout > 0)
  			mod_timer(&fw_priv->timeout,
  				  round_jiffies_up(jiffies +
  						   loading_timeout * HZ));
  
  		kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD);
  	}
  
  	wait_for_completion(&fw_priv->completion);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
550

f8a4bd345   Dmitry Torokhov   firmware loader: ...
551
552
  	set_bit(FW_STATUS_DONE, &fw_priv->status);
  	del_timer_sync(&fw_priv->timeout);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553

cad1e55d4   Laura Garcia   [PATCH] firmware_...
554
  	mutex_lock(&fw_lock);
f8a4bd345   Dmitry Torokhov   firmware loader: ...
555
  	if (!fw_priv->fw->size || test_bit(FW_STATUS_ABORT, &fw_priv->status))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
556
  		retval = -ENOENT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
557
  	fw_priv->fw = NULL;
cad1e55d4   Laura Garcia   [PATCH] firmware_...
558
  	mutex_unlock(&fw_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
559

f8a4bd345   Dmitry Torokhov   firmware loader: ...
560
  	fw_destroy_instance(fw_priv);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
561
  out:
b298d289c   Srivatsa S. Bhat   PM / Sleep: Fix f...
562
  	read_unlock_usermodehelper();
f8a4bd345   Dmitry Torokhov   firmware loader: ...
563
564
  	if (retval) {
  		release_firmware(firmware);
f45f3c1f3   Johannes Berg   firmware_class: f...
565
  		*firmware_p = NULL;
f8a4bd345   Dmitry Torokhov   firmware loader: ...
566
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
567
568
569
570
  	return retval;
  }
  
  /**
312c004d3   Kay Sievers   [PATCH] driver co...
571
   * request_firmware: - send firmware request and wait for it
eb8e31799   Randy Dunlap   [PATCH] firmware:...
572
573
574
575
576
   * @firmware_p: pointer to firmware image
   * @name: name of firmware file
   * @device: device for which firmware is being loaded
   *
   *      @firmware_p will be used to return a firmware image by the name
6e3eaab02   Abhay Salunke   [PATCH] modified ...
577
578
579
580
   *      of @name for device @device.
   *
   *      Should be called from user context where sleeping is allowed.
   *
312c004d3   Kay Sievers   [PATCH] driver co...
581
   *      @name will be used as $FIRMWARE in the uevent environment and
6e3eaab02   Abhay Salunke   [PATCH] modified ...
582
583
584
585
586
587
588
   *      should be distinctive enough not to be confused with any other
   *      firmware image for this or any other device.
   **/
  int
  request_firmware(const struct firmware **firmware_p, const char *name,
                   struct device *device)
  {
072fc8f0a   Bob Liu   firmware_classs: ...
589
          return _request_firmware(firmware_p, name, device, true, false);
6e3eaab02   Abhay Salunke   [PATCH] modified ...
590
591
592
  }
  
  /**
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
593
   * release_firmware: - release the resource associated with a firmware image
eb8e31799   Randy Dunlap   [PATCH] firmware:...
594
   * @fw: firmware resource to release
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
595
   **/
bcb9bd18e   Dmitry Torokhov   firmware loader: ...
596
  void release_firmware(const struct firmware *fw)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
597
598
  {
  	if (fw) {
bcb9bd18e   Dmitry Torokhov   firmware loader: ...
599
600
  		if (!fw_is_builtin_firmware(fw))
  			firmware_free_data(fw);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
601
602
603
  		kfree(fw);
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
604
605
606
607
608
609
610
611
  /* Async support */
  struct firmware_work {
  	struct work_struct work;
  	struct module *module;
  	const char *name;
  	struct device *device;
  	void *context;
  	void (*cont)(const struct firmware *fw, void *context);
072fc8f0a   Bob Liu   firmware_classs: ...
612
  	bool uevent;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
613
  };
f8a4bd345   Dmitry Torokhov   firmware loader: ...
614
  static int request_firmware_work_func(void *arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
615
616
617
  {
  	struct firmware_work *fw_work = arg;
  	const struct firmware *fw;
113fab138   Matthieu CASTET   [PATCH] fix leaks...
618
  	int ret;
f8a4bd345   Dmitry Torokhov   firmware loader: ...
619

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
620
621
622
623
  	if (!arg) {
  		WARN_ON(1);
  		return 0;
  	}
9ebfbd45f   Johannes Berg   firmware_class: m...
624

f8a4bd345   Dmitry Torokhov   firmware loader: ...
625
626
  	ret = _request_firmware(&fw, fw_work->name, fw_work->device,
  				fw_work->uevent, true);
9ebfbd45f   Johannes Berg   firmware_class: m...
627
  	fw_work->cont(fw, fw_work->context);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
628
629
  	module_put(fw_work->module);
  	kfree(fw_work);
f8a4bd345   Dmitry Torokhov   firmware loader: ...
630

113fab138   Matthieu CASTET   [PATCH] fix leaks...
631
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
632
633
634
  }
  
  /**
3c31f07ad   Ben Hutchings   Driver core: Fix ...
635
   * request_firmware_nowait - asynchronous version of request_firmware
eb8e31799   Randy Dunlap   [PATCH] firmware:...
636
   * @module: module requesting the firmware
312c004d3   Kay Sievers   [PATCH] driver co...
637
   * @uevent: sends uevent to copy the firmware image if this flag
eb8e31799   Randy Dunlap   [PATCH] firmware:...
638
639
640
   *	is non-zero else the firmware copy must be done manually.
   * @name: name of firmware file
   * @device: device for which firmware is being loaded
9ebfbd45f   Johannes Berg   firmware_class: m...
641
   * @gfp: allocation flags
eb8e31799   Randy Dunlap   [PATCH] firmware:...
642
643
644
645
   * @context: will be passed over to @cont, and
   *	@fw may be %NULL if firmware request fails.
   * @cont: function will be called asynchronously when the firmware
   *	request is over.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
646
   *
7fcab0997   Ming Lei   driver core: fix ...
647
648
649
   *	Asynchronous variant of request_firmware() for user contexts where
   *	it is not possible to sleep for long time. It can't be called
   *	in atomic contexts.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
650
651
652
   **/
  int
  request_firmware_nowait(
072fc8f0a   Bob Liu   firmware_classs: ...
653
  	struct module *module, bool uevent,
9ebfbd45f   Johannes Berg   firmware_class: m...
654
  	const char *name, struct device *device, gfp_t gfp, void *context,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
655
656
  	void (*cont)(const struct firmware *fw, void *context))
  {
563d07570   Sukadev Bhattiprolu   [PATCH] kthread: ...
657
  	struct task_struct *task;
f8a4bd345   Dmitry Torokhov   firmware loader: ...
658
  	struct firmware_work *fw_work;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
659

f8a4bd345   Dmitry Torokhov   firmware loader: ...
660
  	fw_work = kzalloc(sizeof (struct firmware_work), gfp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
661
662
  	if (!fw_work)
  		return -ENOMEM;
f8a4bd345   Dmitry Torokhov   firmware loader: ...
663
664
665
666
667
668
669
  
  	fw_work->module = module;
  	fw_work->name = name;
  	fw_work->device = device;
  	fw_work->context = context;
  	fw_work->cont = cont;
  	fw_work->uevent = uevent;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
670
671
672
673
  	if (!try_module_get(module)) {
  		kfree(fw_work);
  		return -EFAULT;
  	}
563d07570   Sukadev Bhattiprolu   [PATCH] kthread: ...
674
675
  	task = kthread_run(request_firmware_work_func, fw_work,
  			    "firmware/%s", name);
563d07570   Sukadev Bhattiprolu   [PATCH] kthread: ...
676
  	if (IS_ERR(task)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
677
  		fw_work->cont(NULL, fw_work->context);
113fab138   Matthieu CASTET   [PATCH] fix leaks...
678
679
  		module_put(fw_work->module);
  		kfree(fw_work);
563d07570   Sukadev Bhattiprolu   [PATCH] kthread: ...
680
  		return PTR_ERR(task);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
681
  	}
f8a4bd345   Dmitry Torokhov   firmware loader: ...
682

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
683
684
  	return 0;
  }
673fae90d   Dmitry Torokhov   firmware loader: ...
685
  static int __init firmware_class_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
686
  {
673fae90d   Dmitry Torokhov   firmware loader: ...
687
  	return class_register(&firmware_class);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
688
  }
673fae90d   Dmitry Torokhov   firmware loader: ...
689
690
  
  static void __exit firmware_class_exit(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
691
692
693
  {
  	class_unregister(&firmware_class);
  }
a30a6a2cb   Shaohua Li   [PATCH] x86 micro...
694
  fs_initcall(firmware_class_init);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
695
696
697
698
699
  module_exit(firmware_class_exit);
  
  EXPORT_SYMBOL(release_firmware);
  EXPORT_SYMBOL(request_firmware);
  EXPORT_SYMBOL(request_firmware_nowait);