Commit | Line | Data |
---|---|---|
d9523678 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
6c54c28e AS |
2 | /* |
3 | * dell_rbu.c | |
4 | * Bios Update driver for Dell systems | |
5 | * Author: Dell Inc | |
6 | * Abhay Salunke <abhay_salunke@dell.com> | |
7 | * | |
8 | * Copyright (C) 2005 Dell Inc. | |
9 | * | |
10 | * Remote BIOS Update (rbu) driver is used for updating DELL BIOS by | |
11 | * creating entries in the /sys file systems on Linux 2.6 and higher | |
12 | * kernels. The driver supports two mechanism to update the BIOS namely | |
13 | * contiguous and packetized. Both these methods still require having some | |
14 | * application to set the CMOS bit indicating the BIOS to update itself | |
15 | * after a reboot. | |
16 | * | |
17 | * Contiguous method: | |
18 | * This driver writes the incoming data in a monolithic image by allocating | |
19 | * contiguous physical pages large enough to accommodate the incoming BIOS | |
20 | * image size. | |
21 | * | |
22 | * Packetized method: | |
23 | * The driver writes the incoming packet image by allocating a new packet | |
24 | * on every time the packet data is written. This driver requires an | |
25 | * application to break the BIOS image in to fixed sized packet chunks. | |
26 | * | |
d4300c4e | 27 | * See Documentation/admin-guide/dell_rbu.rst for more info. |
6c54c28e | 28 | */ |
e5e32572 AS |
29 | |
30 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
31 | ||
6c54c28e AS |
32 | #include <linux/init.h> |
33 | #include <linux/module.h> | |
5a0e3ad6 | 34 | #include <linux/slab.h> |
6c54c28e AS |
35 | #include <linux/string.h> |
36 | #include <linux/errno.h> | |
37 | #include <linux/blkdev.h> | |
d052d1be | 38 | #include <linux/platform_device.h> |
6c54c28e AS |
39 | #include <linux/spinlock.h> |
40 | #include <linux/moduleparam.h> | |
41 | #include <linux/firmware.h> | |
42 | #include <linux/dma-mapping.h> | |
6aecee6a | 43 | #include <asm/set_memory.h> |
6c54c28e AS |
44 | |
45 | MODULE_AUTHOR("Abhay Salunke <abhay_salunke@dell.com>"); | |
46 | MODULE_DESCRIPTION("Driver for updating BIOS image on DELL systems"); | |
47 | MODULE_LICENSE("GPL"); | |
2c560840 | 48 | MODULE_VERSION("3.2"); |
6c54c28e AS |
49 | |
50 | #define BIOS_SCAN_LIMIT 0xffffffff | |
51 | #define MAX_IMAGE_LENGTH 16 | |
52 | static struct _rbu_data { | |
53 | void *image_update_buffer; | |
54 | unsigned long image_update_buffer_size; | |
55 | unsigned long bios_image_size; | |
56 | int image_update_ordernum; | |
6c54c28e AS |
57 | spinlock_t lock; |
58 | unsigned long packet_read_count; | |
6c54c28e AS |
59 | unsigned long num_packets; |
60 | unsigned long packetsize; | |
ad6ce87e | 61 | unsigned long imagesize; |
e61c0e33 | 62 | int entry_created; |
6c54c28e AS |
63 | } rbu_data; |
64 | ||
e61c0e33 AS |
65 | static char image_type[MAX_IMAGE_LENGTH + 1] = "mono"; |
66 | module_param_string(image_type, image_type, sizeof (image_type), 0); | |
e5e32572 | 67 | MODULE_PARM_DESC(image_type, "BIOS image type. choose- mono or packet or init"); |
6c54c28e | 68 | |
274b6933 AS |
69 | static unsigned long allocation_floor = 0x100000; |
70 | module_param(allocation_floor, ulong, 0644); | |
e5e32572 | 71 | MODULE_PARM_DESC(allocation_floor, "Minimum address for allocations when using Packet mode"); |
274b6933 | 72 | |
6c54c28e AS |
73 | struct packet_data { |
74 | struct list_head list; | |
75 | size_t length; | |
76 | void *data; | |
77 | int ordernum; | |
78 | }; | |
79 | ||
80 | static struct packet_data packet_data_head; | |
81 | ||
82 | static struct platform_device *rbu_device; | |
83 | static int context; | |
6c54c28e | 84 | |
dda8577f | 85 | static void init_packet_head(void) |
6c54c28e AS |
86 | { |
87 | INIT_LIST_HEAD(&packet_data_head.list); | |
6c54c28e AS |
88 | rbu_data.packet_read_count = 0; |
89 | rbu_data.num_packets = 0; | |
90 | rbu_data.packetsize = 0; | |
ad6ce87e | 91 | rbu_data.imagesize = 0; |
6c54c28e AS |
92 | } |
93 | ||
ad6ce87e | 94 | static int create_packet(void *data, size_t length) |
6c54c28e AS |
95 | { |
96 | struct packet_data *newpacket; | |
97 | int ordernum = 0; | |
274b6933 AS |
98 | int retval = 0; |
99 | unsigned int packet_array_size = 0; | |
5ad9201b AV |
100 | void **invalid_addr_packet_array = NULL; |
101 | void *packet_data_temp_buf = NULL; | |
274b6933 | 102 | unsigned int idx = 0; |
6c54c28e | 103 | |
e5e32572 | 104 | pr_debug("entry\n"); |
6c54c28e AS |
105 | |
106 | if (!rbu_data.packetsize) { | |
e5e32572 | 107 | pr_debug("packetsize not specified\n"); |
274b6933 AS |
108 | retval = -EINVAL; |
109 | goto out_noalloc; | |
6c54c28e | 110 | } |
274b6933 | 111 | |
e61c0e33 | 112 | spin_unlock(&rbu_data.lock); |
274b6933 AS |
113 | |
114 | newpacket = kzalloc(sizeof (struct packet_data), GFP_KERNEL); | |
6c54c28e | 115 | |
6c54c28e | 116 | if (!newpacket) { |
e5e32572 | 117 | pr_warn("failed to allocate new packet\n"); |
274b6933 AS |
118 | retval = -ENOMEM; |
119 | spin_lock(&rbu_data.lock); | |
120 | goto out_noalloc; | |
6c54c28e AS |
121 | } |
122 | ||
123 | ordernum = get_order(length); | |
274b6933 | 124 | |
6c54c28e | 125 | /* |
274b6933 AS |
126 | * BIOS errata mean we cannot allocate packets below 1MB or they will |
127 | * be overwritten by BIOS. | |
128 | * | |
129 | * array to temporarily hold packets | |
130 | * that are below the allocation floor | |
131 | * | |
132 | * NOTE: very simplistic because we only need the floor to be at 1MB | |
133 | * due to BIOS errata. This shouldn't be used for higher floors | |
134 | * or you will run out of mem trying to allocate the array. | |
6c54c28e | 135 | */ |
682baa24 | 136 | packet_array_size = max_t(unsigned int, allocation_floor / rbu_data.packetsize, 1); |
6396bb22 | 137 | invalid_addr_packet_array = kcalloc(packet_array_size, sizeof(void *), |
274b6933 | 138 | GFP_KERNEL); |
6c54c28e | 139 | |
274b6933 | 140 | if (!invalid_addr_packet_array) { |
e5e32572 | 141 | pr_warn("failed to allocate invalid_addr_packet_array\n"); |
274b6933 AS |
142 | retval = -ENOMEM; |
143 | spin_lock(&rbu_data.lock); | |
144 | goto out_alloc_packet; | |
6c54c28e AS |
145 | } |
146 | ||
274b6933 AS |
147 | while (!packet_data_temp_buf) { |
148 | packet_data_temp_buf = (unsigned char *) | |
149 | __get_free_pages(GFP_KERNEL, ordernum); | |
150 | if (!packet_data_temp_buf) { | |
e5e32572 | 151 | pr_warn("failed to allocate new packet\n"); |
274b6933 AS |
152 | retval = -ENOMEM; |
153 | spin_lock(&rbu_data.lock); | |
154 | goto out_alloc_packet_array; | |
155 | } | |
156 | ||
157 | if ((unsigned long)virt_to_phys(packet_data_temp_buf) | |
158 | < allocation_floor) { | |
e5e32572 | 159 | pr_debug("packet 0x%lx below floor at 0x%lx\n", |
274b6933 AS |
160 | (unsigned long)virt_to_phys( |
161 | packet_data_temp_buf), | |
162 | allocation_floor); | |
163 | invalid_addr_packet_array[idx++] = packet_data_temp_buf; | |
5ad9201b | 164 | packet_data_temp_buf = NULL; |
274b6933 AS |
165 | } |
166 | } | |
6aecee6a SH |
167 | /* |
168 | * set to uncachable or it may never get written back before reboot | |
169 | */ | |
170 | set_memory_uc((unsigned long)packet_data_temp_buf, 1 << ordernum); | |
171 | ||
274b6933 AS |
172 | spin_lock(&rbu_data.lock); |
173 | ||
174 | newpacket->data = packet_data_temp_buf; | |
175 | ||
e5e32572 | 176 | pr_debug("newpacket at physical addr %lx\n", |
274b6933 AS |
177 | (unsigned long)virt_to_phys(newpacket->data)); |
178 | ||
179 | /* packets may not have fixed size */ | |
180 | newpacket->length = length; | |
6c54c28e AS |
181 | newpacket->ordernum = ordernum; |
182 | ++rbu_data.num_packets; | |
274b6933 AS |
183 | |
184 | /* initialize the newly created packet headers */ | |
6c54c28e AS |
185 | INIT_LIST_HEAD(&newpacket->list); |
186 | list_add_tail(&newpacket->list, &packet_data_head.list); | |
ad6ce87e AS |
187 | |
188 | memcpy(newpacket->data, data, length); | |
6c54c28e | 189 | |
e5e32572 | 190 | pr_debug("exit\n"); |
6c54c28e | 191 | |
274b6933 AS |
192 | out_alloc_packet_array: |
193 | /* always free packet array */ | |
45e21277 | 194 | while (idx--) { |
e5e32572 | 195 | pr_debug("freeing unused packet below floor 0x%lx\n", |
45e21277 AS |
196 | (unsigned long)virt_to_phys(invalid_addr_packet_array[idx])); |
197 | free_pages((unsigned long)invalid_addr_packet_array[idx], ordernum); | |
274b6933 AS |
198 | } |
199 | kfree(invalid_addr_packet_array); | |
200 | ||
201 | out_alloc_packet: | |
202 | /* if error, free data */ | |
203 | if (retval) | |
204 | kfree(newpacket); | |
205 | ||
206 | out_noalloc: | |
207 | return retval; | |
6c54c28e AS |
208 | } |
209 | ||
c6c1c94e | 210 | static int packetize_data(const u8 *data, size_t length) |
6c54c28e AS |
211 | { |
212 | int rc = 0; | |
ad6ce87e AS |
213 | int done = 0; |
214 | int packet_length; | |
215 | u8 *temp; | |
216 | u8 *end = (u8 *) data + length; | |
e5e32572 | 217 | pr_debug("data length %zd\n", length); |
ad6ce87e | 218 | if (!rbu_data.packetsize) { |
e5e32572 | 219 | pr_warn("packetsize not specified\n"); |
ad6ce87e AS |
220 | return -EIO; |
221 | } | |
6c54c28e | 222 | |
ad6ce87e AS |
223 | temp = (u8 *) data; |
224 | ||
225 | /* packetize the hunk */ | |
226 | while (!done) { | |
227 | if ((temp + rbu_data.packetsize) < end) | |
228 | packet_length = rbu_data.packetsize; | |
229 | else { | |
230 | /* this is the last packet */ | |
231 | packet_length = end - temp; | |
232 | done = 1; | |
233 | } | |
234 | ||
235 | if ((rc = create_packet(temp, packet_length))) | |
6c54c28e | 236 | return rc; |
ad6ce87e | 237 | |
9e42ef77 | 238 | pr_debug("%p:%td\n", temp, (end - temp)); |
ad6ce87e | 239 | temp += packet_length; |
6c54c28e | 240 | } |
ad6ce87e AS |
241 | |
242 | rbu_data.imagesize = length; | |
6c54c28e AS |
243 | |
244 | return rc; | |
245 | } | |
246 | ||
d19f359f | 247 | static int do_packet_read(char *data, struct packet_data *newpacket, |
e61c0e33 | 248 | int length, int bytes_read, int *list_read_count) |
6c54c28e AS |
249 | { |
250 | void *ptemp_buf; | |
6c54c28e AS |
251 | int bytes_copied = 0; |
252 | int j = 0; | |
253 | ||
6c54c28e AS |
254 | *list_read_count += newpacket->length; |
255 | ||
256 | if (*list_read_count > bytes_read) { | |
257 | /* point to the start of unread data */ | |
258 | j = newpacket->length - (*list_read_count - bytes_read); | |
259 | /* point to the offset in the packet buffer */ | |
260 | ptemp_buf = (u8 *) newpacket->data + j; | |
261 | /* | |
262 | * check if there is enough room in | |
263 | * * the incoming buffer | |
264 | */ | |
265 | if (length > (*list_read_count - bytes_read)) | |
266 | /* | |
267 | * copy what ever is there in this | |
268 | * packet and move on | |
269 | */ | |
270 | bytes_copied = (*list_read_count - bytes_read); | |
271 | else | |
272 | /* copy the remaining */ | |
273 | bytes_copied = length; | |
274 | memcpy(data, ptemp_buf, bytes_copied); | |
275 | } | |
276 | return bytes_copied; | |
277 | } | |
278 | ||
ad6ce87e | 279 | static int packet_read_list(char *data, size_t * pread_length) |
6c54c28e | 280 | { |
d19f359f | 281 | struct packet_data *newpacket; |
6c54c28e AS |
282 | int temp_count = 0; |
283 | int bytes_copied = 0; | |
284 | int bytes_read = 0; | |
285 | int remaining_bytes = 0; | |
286 | char *pdest = data; | |
287 | ||
288 | /* check if we have any packets */ | |
289 | if (0 == rbu_data.num_packets) | |
290 | return -ENOMEM; | |
291 | ||
292 | remaining_bytes = *pread_length; | |
293 | bytes_read = rbu_data.packet_read_count; | |
294 | ||
d19f359f AS |
295 | list_for_each_entry(newpacket, (&packet_data_head.list)->next, list) { |
296 | bytes_copied = do_packet_read(pdest, newpacket, | |
e61c0e33 | 297 | remaining_bytes, bytes_read, &temp_count); |
6c54c28e AS |
298 | remaining_bytes -= bytes_copied; |
299 | bytes_read += bytes_copied; | |
300 | pdest += bytes_copied; | |
301 | /* | |
302 | * check if we reached end of buffer before reaching the | |
303 | * last packet | |
304 | */ | |
305 | if (remaining_bytes == 0) | |
306 | break; | |
6c54c28e AS |
307 | } |
308 | /*finally set the bytes read */ | |
309 | *pread_length = bytes_read - rbu_data.packet_read_count; | |
310 | rbu_data.packet_read_count = bytes_read; | |
311 | return 0; | |
312 | } | |
313 | ||
dda8577f | 314 | static void packet_empty_list(void) |
6c54c28e | 315 | { |
d19f359f AS |
316 | struct packet_data *newpacket, *tmp; |
317 | ||
318 | list_for_each_entry_safe(newpacket, tmp, (&packet_data_head.list)->next, list) { | |
319 | list_del(&newpacket->list); | |
6c54c28e | 320 | |
6c54c28e AS |
321 | /* |
322 | * zero out the RBU packet memory before freeing | |
323 | * to make sure there are no stale RBU packets left in memory | |
324 | */ | |
325 | memset(newpacket->data, 0, rbu_data.packetsize); | |
6aecee6a SH |
326 | set_memory_wb((unsigned long)newpacket->data, |
327 | 1 << newpacket->ordernum); | |
e61c0e33 AS |
328 | free_pages((unsigned long) newpacket->data, |
329 | newpacket->ordernum); | |
6c54c28e AS |
330 | kfree(newpacket); |
331 | } | |
6c54c28e AS |
332 | rbu_data.packet_read_count = 0; |
333 | rbu_data.num_packets = 0; | |
ad6ce87e | 334 | rbu_data.imagesize = 0; |
6c54c28e AS |
335 | } |
336 | ||
337 | /* | |
338 | * img_update_free: Frees the buffer allocated for storing BIOS image | |
339 | * Always called with lock held and returned with lock held | |
340 | */ | |
dda8577f | 341 | static void img_update_free(void) |
6c54c28e AS |
342 | { |
343 | if (!rbu_data.image_update_buffer) | |
344 | return; | |
345 | /* | |
346 | * zero out this buffer before freeing it to get rid of any stale | |
347 | * BIOS image copied in memory. | |
348 | */ | |
349 | memset(rbu_data.image_update_buffer, 0, | |
e61c0e33 | 350 | rbu_data.image_update_buffer_size); |
fd47a36f CH |
351 | free_pages((unsigned long) rbu_data.image_update_buffer, |
352 | rbu_data.image_update_ordernum); | |
6c54c28e AS |
353 | |
354 | /* | |
355 | * Re-initialize the rbu_data variables after a free | |
356 | */ | |
357 | rbu_data.image_update_ordernum = -1; | |
358 | rbu_data.image_update_buffer = NULL; | |
359 | rbu_data.image_update_buffer_size = 0; | |
360 | rbu_data.bios_image_size = 0; | |
6c54c28e AS |
361 | } |
362 | ||
363 | /* | |
364 | * img_update_realloc: This function allocates the contiguous pages to | |
365 | * accommodate the requested size of data. The memory address and size | |
366 | * values are stored globally and on every call to this function the new | |
367 | * size is checked to see if more data is required than the existing size. | |
368 | * If true the previous memory is freed and new allocation is done to | |
369 | * accommodate the new size. If the incoming size is less then than the | |
370 | * already allocated size, then that memory is reused. This function is | |
371 | * called with lock held and returns with lock held. | |
372 | */ | |
dda8577f | 373 | static int img_update_realloc(unsigned long size) |
6c54c28e AS |
374 | { |
375 | unsigned char *image_update_buffer = NULL; | |
6c54c28e AS |
376 | unsigned long img_buf_phys_addr; |
377 | int ordernum; | |
6c54c28e AS |
378 | |
379 | /* | |
380 | * check if the buffer of sufficient size has been | |
381 | * already allocated | |
382 | */ | |
383 | if (rbu_data.image_update_buffer_size >= size) { | |
384 | /* | |
385 | * check for corruption | |
386 | */ | |
387 | if ((size != 0) && (rbu_data.image_update_buffer == NULL)) { | |
e5e32572 | 388 | pr_err("corruption check failed\n"); |
6c54c28e AS |
389 | return -EINVAL; |
390 | } | |
391 | /* | |
392 | * we have a valid pre-allocated buffer with | |
393 | * sufficient size | |
394 | */ | |
395 | return 0; | |
396 | } | |
397 | ||
398 | /* | |
399 | * free any previously allocated buffer | |
400 | */ | |
401 | img_update_free(); | |
402 | ||
403 | spin_unlock(&rbu_data.lock); | |
404 | ||
405 | ordernum = get_order(size); | |
406 | image_update_buffer = | |
fd47a36f | 407 | (unsigned char *)__get_free_pages(GFP_DMA32, ordernum); |
f27e1d18 | 408 | spin_lock(&rbu_data.lock); |
fd47a36f | 409 | if (!image_update_buffer) { |
e5e32572 | 410 | pr_debug("Not enough memory for image update: size = %ld\n", size); |
fd47a36f | 411 | return -ENOMEM; |
6c54c28e AS |
412 | } |
413 | ||
fd47a36f CH |
414 | img_buf_phys_addr = (unsigned long)virt_to_phys(image_update_buffer); |
415 | if (WARN_ON_ONCE(img_buf_phys_addr > BIOS_SCAN_LIMIT)) | |
416 | return -EINVAL; /* can't happen per definition */ | |
417 | ||
fd47a36f CH |
418 | rbu_data.image_update_buffer = image_update_buffer; |
419 | rbu_data.image_update_buffer_size = size; | |
420 | rbu_data.bios_image_size = rbu_data.image_update_buffer_size; | |
421 | rbu_data.image_update_ordernum = ordernum; | |
422 | return 0; | |
6c54c28e AS |
423 | } |
424 | ||
dda8577f | 425 | static ssize_t read_packet_data(char *buffer, loff_t pos, size_t count) |
6c54c28e AS |
426 | { |
427 | int retval; | |
428 | size_t bytes_left; | |
429 | size_t data_length; | |
430 | char *ptempBuf = buffer; | |
6c54c28e AS |
431 | |
432 | /* check to see if we have something to return */ | |
433 | if (rbu_data.num_packets == 0) { | |
e5e32572 | 434 | pr_debug("no packets written\n"); |
6c54c28e AS |
435 | retval = -ENOMEM; |
436 | goto read_rbu_data_exit; | |
437 | } | |
438 | ||
ad6ce87e | 439 | if (pos > rbu_data.imagesize) { |
6c54c28e | 440 | retval = 0; |
e5e32572 | 441 | pr_warn("data underrun\n"); |
6c54c28e AS |
442 | goto read_rbu_data_exit; |
443 | } | |
444 | ||
ad6ce87e | 445 | bytes_left = rbu_data.imagesize - pos; |
6c54c28e AS |
446 | data_length = min(bytes_left, count); |
447 | ||
448 | if ((retval = packet_read_list(ptempBuf, &data_length)) < 0) | |
449 | goto read_rbu_data_exit; | |
450 | ||
ad6ce87e | 451 | if ((pos + count) > rbu_data.imagesize) { |
6c54c28e AS |
452 | rbu_data.packet_read_count = 0; |
453 | /* this was the last copy */ | |
454 | retval = bytes_left; | |
455 | } else | |
456 | retval = count; | |
457 | ||
458 | read_rbu_data_exit: | |
459 | return retval; | |
460 | } | |
461 | ||
dda8577f | 462 | static ssize_t read_rbu_mono_data(char *buffer, loff_t pos, size_t count) |
6c54c28e | 463 | { |
6c54c28e AS |
464 | /* check to see if we have something to return */ |
465 | if ((rbu_data.image_update_buffer == NULL) || | |
e61c0e33 | 466 | (rbu_data.bios_image_size == 0)) { |
e5e32572 | 467 | pr_debug("image_update_buffer %p, bios_image_size %lu\n", |
e61c0e33 AS |
468 | rbu_data.image_update_buffer, |
469 | rbu_data.bios_image_size); | |
25377479 | 470 | return -ENOMEM; |
6c54c28e AS |
471 | } |
472 | ||
25377479 AM |
473 | return memory_read_from_buffer(buffer, count, &pos, |
474 | rbu_data.image_update_buffer, rbu_data.bios_image_size); | |
6c54c28e AS |
475 | } |
476 | ||
94ed3134 AS |
477 | static ssize_t data_read(struct file *filp, struct kobject *kobj, |
478 | struct bin_attribute *bin_attr, | |
479 | char *buffer, loff_t pos, size_t count) | |
6c54c28e AS |
480 | { |
481 | ssize_t ret_count = 0; | |
482 | ||
483 | spin_lock(&rbu_data.lock); | |
484 | ||
485 | if (!strcmp(image_type, "mono")) | |
486 | ret_count = read_rbu_mono_data(buffer, pos, count); | |
487 | else if (!strcmp(image_type, "packet")) | |
488 | ret_count = read_packet_data(buffer, pos, count); | |
489 | else | |
e5e32572 | 490 | pr_debug("invalid image type specified\n"); |
6c54c28e AS |
491 | |
492 | spin_unlock(&rbu_data.lock); | |
493 | return ret_count; | |
494 | } | |
94ed3134 | 495 | static BIN_ATTR_RO(data, 0); |
6c54c28e | 496 | |
dda8577f | 497 | static void callbackfn_rbu(const struct firmware *fw, void *context) |
e61c0e33 | 498 | { |
2c560840 | 499 | rbu_data.entry_created = 0; |
e61c0e33 | 500 | |
9ebfbd45 | 501 | if (!fw) |
e61c0e33 | 502 | return; |
e61c0e33 | 503 | |
9ebfbd45 JB |
504 | if (!fw->size) |
505 | goto out; | |
506 | ||
e61c0e33 AS |
507 | spin_lock(&rbu_data.lock); |
508 | if (!strcmp(image_type, "mono")) { | |
509 | if (!img_update_realloc(fw->size)) | |
510 | memcpy(rbu_data.image_update_buffer, | |
511 | fw->data, fw->size); | |
512 | } else if (!strcmp(image_type, "packet")) { | |
ad6ce87e AS |
513 | /* |
514 | * we need to free previous packets if a | |
515 | * new hunk of packets needs to be downloaded | |
516 | */ | |
517 | packet_empty_list(); | |
518 | if (packetize_data(fw->data, fw->size)) | |
519 | /* Incase something goes wrong when we are | |
520 | * in middle of packetizing the data, we | |
521 | * need to free up whatever packets might | |
522 | * have been created before we quit. | |
523 | */ | |
e61c0e33 | 524 | packet_empty_list(); |
e61c0e33 | 525 | } else |
e5e32572 | 526 | pr_debug("invalid image type specified\n"); |
e61c0e33 | 527 | spin_unlock(&rbu_data.lock); |
9ebfbd45 JB |
528 | out: |
529 | release_firmware(fw); | |
e61c0e33 AS |
530 | } |
531 | ||
94ed3134 AS |
532 | static ssize_t image_type_read(struct file *filp, struct kobject *kobj, |
533 | struct bin_attribute *bin_attr, | |
534 | char *buffer, loff_t pos, size_t count) | |
6c54c28e AS |
535 | { |
536 | int size = 0; | |
537 | if (!pos) | |
81156928 | 538 | size = scnprintf(buffer, count, "%s\n", image_type); |
6c54c28e AS |
539 | return size; |
540 | } | |
541 | ||
94ed3134 AS |
542 | static ssize_t image_type_write(struct file *filp, struct kobject *kobj, |
543 | struct bin_attribute *bin_attr, | |
544 | char *buffer, loff_t pos, size_t count) | |
6c54c28e AS |
545 | { |
546 | int rc = count; | |
e61c0e33 AS |
547 | int req_firm_rc = 0; |
548 | int i; | |
6c54c28e | 549 | spin_lock(&rbu_data.lock); |
e61c0e33 AS |
550 | /* |
551 | * Find the first newline or space | |
552 | */ | |
553 | for (i = 0; i < count; ++i) | |
554 | if (buffer[i] == '\n' || buffer[i] == ' ') { | |
555 | buffer[i] = '\0'; | |
556 | break; | |
557 | } | |
558 | if (i == count) | |
559 | buffer[count] = '\0'; | |
560 | ||
561 | if (strstr(buffer, "mono")) | |
562 | strcpy(image_type, "mono"); | |
563 | else if (strstr(buffer, "packet")) | |
564 | strcpy(image_type, "packet"); | |
565 | else if (strstr(buffer, "init")) { | |
566 | /* | |
567 | * If due to the user error the driver gets in a bad | |
568 | * state where even though it is loaded , the | |
569 | * /sys/class/firmware/dell_rbu entries are missing. | |
570 | * to cover this situation the user can recreate entries | |
571 | * by writing init to image_type. | |
572 | */ | |
573 | if (!rbu_data.entry_created) { | |
574 | spin_unlock(&rbu_data.lock); | |
575 | req_firm_rc = request_firmware_nowait(THIS_MODULE, | |
576 | FW_ACTION_NOHOTPLUG, "dell_rbu", | |
9ebfbd45 | 577 | &rbu_device->dev, GFP_KERNEL, &context, |
e61c0e33 AS |
578 | callbackfn_rbu); |
579 | if (req_firm_rc) { | |
e5e32572 | 580 | pr_err("request_firmware_nowait failed %d\n", rc); |
e61c0e33 AS |
581 | rc = -EIO; |
582 | } else | |
583 | rbu_data.entry_created = 1; | |
584 | ||
585 | spin_lock(&rbu_data.lock); | |
586 | } | |
587 | } else { | |
e5e32572 | 588 | pr_warn("image_type is invalid\n"); |
e61c0e33 AS |
589 | spin_unlock(&rbu_data.lock); |
590 | return -EINVAL; | |
591 | } | |
6c54c28e AS |
592 | |
593 | /* we must free all previous allocations */ | |
594 | packet_empty_list(); | |
595 | img_update_free(); | |
6c54c28e | 596 | spin_unlock(&rbu_data.lock); |
6c54c28e | 597 | |
e61c0e33 | 598 | return rc; |
6c54c28e | 599 | } |
94ed3134 | 600 | static BIN_ATTR_RW(image_type, 0); |
6c54c28e | 601 | |
94ed3134 AS |
602 | static ssize_t packet_size_read(struct file *filp, struct kobject *kobj, |
603 | struct bin_attribute *bin_attr, | |
604 | char *buffer, loff_t pos, size_t count) | |
ad6ce87e AS |
605 | { |
606 | int size = 0; | |
607 | if (!pos) { | |
608 | spin_lock(&rbu_data.lock); | |
81156928 | 609 | size = scnprintf(buffer, count, "%lu\n", rbu_data.packetsize); |
ad6ce87e AS |
610 | spin_unlock(&rbu_data.lock); |
611 | } | |
612 | return size; | |
613 | } | |
614 | ||
94ed3134 AS |
615 | static ssize_t packet_size_write(struct file *filp, struct kobject *kobj, |
616 | struct bin_attribute *bin_attr, | |
617 | char *buffer, loff_t pos, size_t count) | |
ad6ce87e AS |
618 | { |
619 | unsigned long temp; | |
620 | spin_lock(&rbu_data.lock); | |
621 | packet_empty_list(); | |
622 | sscanf(buffer, "%lu", &temp); | |
623 | if (temp < 0xffffffff) | |
624 | rbu_data.packetsize = temp; | |
625 | ||
626 | spin_unlock(&rbu_data.lock); | |
627 | return count; | |
628 | } | |
94ed3134 | 629 | static BIN_ATTR_RW(packet_size, 0); |
ad6ce87e | 630 | |
94ed3134 AS |
631 | static struct bin_attribute *rbu_bin_attrs[] = { |
632 | &bin_attr_data, | |
633 | &bin_attr_image_type, | |
634 | &bin_attr_packet_size, | |
635 | NULL | |
6c54c28e AS |
636 | }; |
637 | ||
94ed3134 AS |
638 | static const struct attribute_group rbu_group = { |
639 | .bin_attrs = rbu_bin_attrs, | |
ad6ce87e AS |
640 | }; |
641 | ||
dda8577f | 642 | static int __init dcdrbu_init(void) |
6c54c28e | 643 | { |
6897083a | 644 | int rc; |
6c54c28e AS |
645 | spin_lock_init(&rbu_data.lock); |
646 | ||
647 | init_packet_head(); | |
6897083a AM |
648 | rbu_device = platform_device_register_simple("dell_rbu", -1, NULL, 0); |
649 | if (IS_ERR(rbu_device)) { | |
e5e32572 | 650 | pr_err("platform_device_register_simple failed\n"); |
6897083a | 651 | return PTR_ERR(rbu_device); |
6c54c28e AS |
652 | } |
653 | ||
94ed3134 | 654 | rc = sysfs_create_group(&rbu_device->dev.kobj, &rbu_group); |
41bfcfd9 JG |
655 | if (rc) |
656 | goto out_devreg; | |
6c54c28e | 657 | |
2c560840 | 658 | rbu_data.entry_created = 0; |
41bfcfd9 | 659 | return 0; |
6c54c28e | 660 | |
41bfcfd9 JG |
661 | out_devreg: |
662 | platform_device_unregister(rbu_device); | |
663 | return rc; | |
6c54c28e AS |
664 | } |
665 | ||
dda8577f | 666 | static __exit void dcdrbu_exit(void) |
6c54c28e AS |
667 | { |
668 | spin_lock(&rbu_data.lock); | |
669 | packet_empty_list(); | |
670 | img_update_free(); | |
671 | spin_unlock(&rbu_data.lock); | |
94ed3134 | 672 | sysfs_remove_group(&rbu_device->dev.kobj, &rbu_group); |
6c54c28e AS |
673 | platform_device_unregister(rbu_device); |
674 | } | |
675 | ||
676 | module_exit(dcdrbu_exit); | |
677 | module_init(dcdrbu_init); |