1 | // SPDX-License-Identifier: GPL-2.0-only |
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 | * |
27 | * See Documentation/admin-guide/dell_rbu.rst for more info. |
28 | */ |
29 | |
30 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
31 | |
32 | #include <linux/init.h> |
33 | #include <linux/module.h> |
34 | #include <linux/slab.h> |
35 | #include <linux/string.h> |
36 | #include <linux/errno.h> |
37 | #include <linux/blkdev.h> |
38 | #include <linux/platform_device.h> |
39 | #include <linux/spinlock.h> |
40 | #include <linux/moduleparam.h> |
41 | #include <linux/firmware.h> |
42 | #include <linux/dma-mapping.h> |
43 | #include <asm/set_memory.h> |
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" ); |
48 | MODULE_VERSION("3.2" ); |
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; |
57 | spinlock_t lock; |
58 | unsigned long packet_read_count; |
59 | unsigned long num_packets; |
60 | unsigned long packetsize; |
61 | unsigned long imagesize; |
62 | int entry_created; |
63 | } rbu_data; |
64 | |
65 | static char image_type[MAX_IMAGE_LENGTH + 1] = "mono" ; |
66 | module_param_string(image_type, image_type, sizeof (image_type), 0); |
67 | MODULE_PARM_DESC(image_type, "BIOS image type. choose- mono or packet or init" ); |
68 | |
69 | static unsigned long allocation_floor = 0x100000; |
70 | module_param(allocation_floor, ulong, 0644); |
71 | MODULE_PARM_DESC(allocation_floor, "Minimum address for allocations when using Packet mode" ); |
72 | |
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; |
84 | |
85 | static void init_packet_head(void) |
86 | { |
87 | INIT_LIST_HEAD(list: &packet_data_head.list); |
88 | rbu_data.packet_read_count = 0; |
89 | rbu_data.num_packets = 0; |
90 | rbu_data.packetsize = 0; |
91 | rbu_data.imagesize = 0; |
92 | } |
93 | |
94 | static int create_packet(void *data, size_t length) |
95 | { |
96 | struct packet_data *newpacket; |
97 | int ordernum = 0; |
98 | int retval = 0; |
99 | unsigned int packet_array_size = 0; |
100 | void **invalid_addr_packet_array = NULL; |
101 | void *packet_data_temp_buf = NULL; |
102 | unsigned int idx = 0; |
103 | |
104 | pr_debug("entry\n" ); |
105 | |
106 | if (!rbu_data.packetsize) { |
107 | pr_debug("packetsize not specified\n" ); |
108 | retval = -EINVAL; |
109 | goto out_noalloc; |
110 | } |
111 | |
112 | spin_unlock(lock: &rbu_data.lock); |
113 | |
114 | newpacket = kzalloc(size: sizeof (struct packet_data), GFP_KERNEL); |
115 | |
116 | if (!newpacket) { |
117 | pr_warn("failed to allocate new packet\n" ); |
118 | retval = -ENOMEM; |
119 | spin_lock(lock: &rbu_data.lock); |
120 | goto out_noalloc; |
121 | } |
122 | |
123 | ordernum = get_order(size: length); |
124 | |
125 | /* |
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. |
135 | */ |
136 | packet_array_size = max_t(unsigned int, allocation_floor / rbu_data.packetsize, 1); |
137 | invalid_addr_packet_array = kcalloc(n: packet_array_size, size: sizeof(void *), |
138 | GFP_KERNEL); |
139 | |
140 | if (!invalid_addr_packet_array) { |
141 | pr_warn("failed to allocate invalid_addr_packet_array\n" ); |
142 | retval = -ENOMEM; |
143 | spin_lock(lock: &rbu_data.lock); |
144 | goto out_alloc_packet; |
145 | } |
146 | |
147 | while (!packet_data_temp_buf) { |
148 | packet_data_temp_buf = (unsigned char *) |
149 | __get_free_pages(GFP_KERNEL, order: ordernum); |
150 | if (!packet_data_temp_buf) { |
151 | pr_warn("failed to allocate new packet\n" ); |
152 | retval = -ENOMEM; |
153 | spin_lock(lock: &rbu_data.lock); |
154 | goto out_alloc_packet_array; |
155 | } |
156 | |
157 | if ((unsigned long)virt_to_phys(address: packet_data_temp_buf) |
158 | < allocation_floor) { |
159 | pr_debug("packet 0x%lx below floor at 0x%lx\n" , |
160 | (unsigned long)virt_to_phys( |
161 | packet_data_temp_buf), |
162 | allocation_floor); |
163 | invalid_addr_packet_array[idx++] = packet_data_temp_buf; |
164 | packet_data_temp_buf = NULL; |
165 | } |
166 | } |
167 | /* |
168 | * set to uncachable or it may never get written back before reboot |
169 | */ |
170 | set_memory_uc(addr: (unsigned long)packet_data_temp_buf, numpages: 1 << ordernum); |
171 | |
172 | spin_lock(lock: &rbu_data.lock); |
173 | |
174 | newpacket->data = packet_data_temp_buf; |
175 | |
176 | pr_debug("newpacket at physical addr %lx\n" , |
177 | (unsigned long)virt_to_phys(newpacket->data)); |
178 | |
179 | /* packets may not have fixed size */ |
180 | newpacket->length = length; |
181 | newpacket->ordernum = ordernum; |
182 | ++rbu_data.num_packets; |
183 | |
184 | /* initialize the newly created packet headers */ |
185 | INIT_LIST_HEAD(list: &newpacket->list); |
186 | list_add_tail(new: &newpacket->list, head: &packet_data_head.list); |
187 | |
188 | memcpy(newpacket->data, data, length); |
189 | |
190 | pr_debug("exit\n" ); |
191 | |
192 | out_alloc_packet_array: |
193 | /* always free packet array */ |
194 | while (idx--) { |
195 | pr_debug("freeing unused packet below floor 0x%lx\n" , |
196 | (unsigned long)virt_to_phys(invalid_addr_packet_array[idx])); |
197 | free_pages(addr: (unsigned long)invalid_addr_packet_array[idx], order: ordernum); |
198 | } |
199 | kfree(objp: invalid_addr_packet_array); |
200 | |
201 | out_alloc_packet: |
202 | /* if error, free data */ |
203 | if (retval) |
204 | kfree(objp: newpacket); |
205 | |
206 | out_noalloc: |
207 | return retval; |
208 | } |
209 | |
210 | static int packetize_data(const u8 *data, size_t length) |
211 | { |
212 | int rc = 0; |
213 | int done = 0; |
214 | int packet_length; |
215 | u8 *temp; |
216 | u8 *end = (u8 *) data + length; |
217 | pr_debug("data length %zd\n" , length); |
218 | if (!rbu_data.packetsize) { |
219 | pr_warn("packetsize not specified\n" ); |
220 | return -EIO; |
221 | } |
222 | |
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(data: temp, length: packet_length))) |
236 | return rc; |
237 | |
238 | pr_debug("%p:%td\n" , temp, (end - temp)); |
239 | temp += packet_length; |
240 | } |
241 | |
242 | rbu_data.imagesize = length; |
243 | |
244 | return rc; |
245 | } |
246 | |
247 | static int do_packet_read(char *data, struct packet_data *newpacket, |
248 | int length, int bytes_read, int *list_read_count) |
249 | { |
250 | void *ptemp_buf; |
251 | int bytes_copied = 0; |
252 | int j = 0; |
253 | |
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 | |
279 | static int packet_read_list(char *data, size_t * pread_length) |
280 | { |
281 | struct packet_data *newpacket; |
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 | |
295 | list_for_each_entry(newpacket, (&packet_data_head.list)->next, list) { |
296 | bytes_copied = do_packet_read(data: pdest, newpacket, |
297 | length: remaining_bytes, bytes_read, list_read_count: &temp_count); |
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; |
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 | |
314 | static void packet_empty_list(void) |
315 | { |
316 | struct packet_data *newpacket, *tmp; |
317 | |
318 | list_for_each_entry_safe(newpacket, tmp, (&packet_data_head.list)->next, list) { |
319 | list_del(entry: &newpacket->list); |
320 | |
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); |
326 | set_memory_wb(addr: (unsigned long)newpacket->data, |
327 | numpages: 1 << newpacket->ordernum); |
328 | free_pages(addr: (unsigned long) newpacket->data, |
329 | order: newpacket->ordernum); |
330 | kfree(objp: newpacket); |
331 | } |
332 | rbu_data.packet_read_count = 0; |
333 | rbu_data.num_packets = 0; |
334 | rbu_data.imagesize = 0; |
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 | */ |
341 | static void img_update_free(void) |
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, |
350 | rbu_data.image_update_buffer_size); |
351 | free_pages(addr: (unsigned long) rbu_data.image_update_buffer, |
352 | order: rbu_data.image_update_ordernum); |
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; |
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 | */ |
373 | static int img_update_realloc(unsigned long size) |
374 | { |
375 | unsigned char *image_update_buffer = NULL; |
376 | unsigned long img_buf_phys_addr; |
377 | int ordernum; |
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)) { |
388 | pr_err("corruption check failed\n" ); |
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(lock: &rbu_data.lock); |
404 | |
405 | ordernum = get_order(size); |
406 | image_update_buffer = |
407 | (unsigned char *)__get_free_pages(GFP_DMA32, order: ordernum); |
408 | spin_lock(lock: &rbu_data.lock); |
409 | if (!image_update_buffer) { |
410 | pr_debug("Not enough memory for image update: size = %ld\n" , size); |
411 | return -ENOMEM; |
412 | } |
413 | |
414 | img_buf_phys_addr = (unsigned long)virt_to_phys(address: image_update_buffer); |
415 | if (WARN_ON_ONCE(img_buf_phys_addr > BIOS_SCAN_LIMIT)) |
416 | return -EINVAL; /* can't happen per definition */ |
417 | |
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; |
423 | } |
424 | |
425 | static ssize_t read_packet_data(char *buffer, loff_t pos, size_t count) |
426 | { |
427 | int retval; |
428 | size_t bytes_left; |
429 | size_t data_length; |
430 | char *ptempBuf = buffer; |
431 | |
432 | /* check to see if we have something to return */ |
433 | if (rbu_data.num_packets == 0) { |
434 | pr_debug("no packets written\n" ); |
435 | retval = -ENOMEM; |
436 | goto read_rbu_data_exit; |
437 | } |
438 | |
439 | if (pos > rbu_data.imagesize) { |
440 | retval = 0; |
441 | pr_warn("data underrun\n" ); |
442 | goto read_rbu_data_exit; |
443 | } |
444 | |
445 | bytes_left = rbu_data.imagesize - pos; |
446 | data_length = min(bytes_left, count); |
447 | |
448 | if ((retval = packet_read_list(data: ptempBuf, pread_length: &data_length)) < 0) |
449 | goto read_rbu_data_exit; |
450 | |
451 | if ((pos + count) > rbu_data.imagesize) { |
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 | |
462 | static ssize_t read_rbu_mono_data(char *buffer, loff_t pos, size_t count) |
463 | { |
464 | /* check to see if we have something to return */ |
465 | if ((rbu_data.image_update_buffer == NULL) || |
466 | (rbu_data.bios_image_size == 0)) { |
467 | pr_debug("image_update_buffer %p, bios_image_size %lu\n" , |
468 | rbu_data.image_update_buffer, |
469 | rbu_data.bios_image_size); |
470 | return -ENOMEM; |
471 | } |
472 | |
473 | return memory_read_from_buffer(to: buffer, count, ppos: &pos, |
474 | from: rbu_data.image_update_buffer, available: rbu_data.bios_image_size); |
475 | } |
476 | |
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) |
480 | { |
481 | ssize_t ret_count = 0; |
482 | |
483 | spin_lock(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 |
490 | pr_debug("invalid image type specified\n" ); |
491 | |
492 | spin_unlock(lock: &rbu_data.lock); |
493 | return ret_count; |
494 | } |
495 | static BIN_ATTR_RO(data, 0); |
496 | |
497 | static void callbackfn_rbu(const struct firmware *fw, void *context) |
498 | { |
499 | rbu_data.entry_created = 0; |
500 | |
501 | if (!fw) |
502 | return; |
503 | |
504 | if (!fw->size) |
505 | goto out; |
506 | |
507 | spin_lock(lock: &rbu_data.lock); |
508 | if (!strcmp(image_type, "mono" )) { |
509 | if (!img_update_realloc(size: fw->size)) |
510 | memcpy(rbu_data.image_update_buffer, |
511 | fw->data, fw->size); |
512 | } else if (!strcmp(image_type, "packet" )) { |
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(data: fw->data, length: 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 | */ |
524 | packet_empty_list(); |
525 | } else |
526 | pr_debug("invalid image type specified\n" ); |
527 | spin_unlock(lock: &rbu_data.lock); |
528 | out: |
529 | release_firmware(fw); |
530 | } |
531 | |
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) |
535 | { |
536 | int size = 0; |
537 | if (!pos) |
538 | size = scnprintf(buf: buffer, size: count, fmt: "%s\n" , image_type); |
539 | return size; |
540 | } |
541 | |
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) |
545 | { |
546 | int rc = count; |
547 | int req_firm_rc = 0; |
548 | int i; |
549 | spin_lock(lock: &rbu_data.lock); |
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(p: image_type, q: "mono" ); |
563 | else if (strstr(buffer, "packet" )) |
564 | strcpy(p: image_type, q: "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(lock: &rbu_data.lock); |
575 | req_firm_rc = request_firmware_nowait(THIS_MODULE, |
576 | FW_ACTION_NOUEVENT, name: "dell_rbu" , |
577 | device: &rbu_device->dev, GFP_KERNEL, context: &context, |
578 | cont: callbackfn_rbu); |
579 | if (req_firm_rc) { |
580 | pr_err("request_firmware_nowait failed %d\n" , rc); |
581 | rc = -EIO; |
582 | } else |
583 | rbu_data.entry_created = 1; |
584 | |
585 | spin_lock(lock: &rbu_data.lock); |
586 | } |
587 | } else { |
588 | pr_warn("image_type is invalid\n" ); |
589 | spin_unlock(lock: &rbu_data.lock); |
590 | return -EINVAL; |
591 | } |
592 | |
593 | /* we must free all previous allocations */ |
594 | packet_empty_list(); |
595 | img_update_free(); |
596 | spin_unlock(lock: &rbu_data.lock); |
597 | |
598 | return rc; |
599 | } |
600 | static BIN_ATTR_RW(image_type, 0); |
601 | |
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) |
605 | { |
606 | int size = 0; |
607 | if (!pos) { |
608 | spin_lock(lock: &rbu_data.lock); |
609 | size = scnprintf(buf: buffer, size: count, fmt: "%lu\n" , rbu_data.packetsize); |
610 | spin_unlock(lock: &rbu_data.lock); |
611 | } |
612 | return size; |
613 | } |
614 | |
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) |
618 | { |
619 | unsigned long temp; |
620 | spin_lock(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(lock: &rbu_data.lock); |
627 | return count; |
628 | } |
629 | static BIN_ATTR_RW(packet_size, 0); |
630 | |
631 | static struct bin_attribute *rbu_bin_attrs[] = { |
632 | &bin_attr_data, |
633 | &bin_attr_image_type, |
634 | &bin_attr_packet_size, |
635 | NULL |
636 | }; |
637 | |
638 | static const struct attribute_group rbu_group = { |
639 | .bin_attrs = rbu_bin_attrs, |
640 | }; |
641 | |
642 | static int __init dcdrbu_init(void) |
643 | { |
644 | int rc; |
645 | spin_lock_init(&rbu_data.lock); |
646 | |
647 | init_packet_head(); |
648 | rbu_device = platform_device_register_simple(name: "dell_rbu" , PLATFORM_DEVID_NONE, NULL, num: 0); |
649 | if (IS_ERR(ptr: rbu_device)) { |
650 | pr_err("platform_device_register_simple failed\n" ); |
651 | return PTR_ERR(ptr: rbu_device); |
652 | } |
653 | |
654 | rc = sysfs_create_group(kobj: &rbu_device->dev.kobj, grp: &rbu_group); |
655 | if (rc) |
656 | goto out_devreg; |
657 | |
658 | rbu_data.entry_created = 0; |
659 | return 0; |
660 | |
661 | out_devreg: |
662 | platform_device_unregister(rbu_device); |
663 | return rc; |
664 | } |
665 | |
666 | static __exit void dcdrbu_exit(void) |
667 | { |
668 | spin_lock(lock: &rbu_data.lock); |
669 | packet_empty_list(); |
670 | img_update_free(); |
671 | spin_unlock(lock: &rbu_data.lock); |
672 | sysfs_remove_group(kobj: &rbu_device->dev.kobj, grp: &rbu_group); |
673 | platform_device_unregister(rbu_device); |
674 | } |
675 | |
676 | module_exit(dcdrbu_exit); |
677 | module_init(dcdrbu_init); |
678 | |