blob: 9f5e392124a043bf8bed64b29c5c3738247ac2c6
1 | /* |
2 | * drivers/amlogic/media/common/firmware/firmware.c |
3 | * |
4 | * Copyright (C) 2016 Amlogic, Inc. All rights reserved. |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation; either version 2 of the License, or |
9 | * (at your option) any later version. |
10 | * |
11 | * This program is distributed in the hope that it will be useful, but WITHOUT |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
14 | * more details. |
15 | * |
16 | */ |
17 | |
18 | #include <linux/kernel.h> |
19 | #include <linux/module.h> |
20 | #include <linux/types.h> |
21 | #include <linux/fs.h> |
22 | #include <linux/init.h> |
23 | #include <linux/device.h> |
24 | #include <linux/vmalloc.h> |
25 | #include <linux/mm.h> |
26 | #include <linux/slab.h> |
27 | |
28 | #include <linux/amlogic/media/utils/vformat.h> |
29 | #include <linux/amlogic/cpu_version.h> |
30 | #include "../../stream_input/amports/amports_priv.h" |
31 | #include "../../frame_provider/decoder/utils/vdec.h" |
32 | #include "firmware_priv.h" |
33 | #include "../chips/chips.h" |
34 | #include <linux/string.h> |
35 | #include <linux/amlogic/media/utils/log.h> |
36 | #include <linux/firmware.h> |
37 | #include <linux/amlogic/tee.h> |
38 | #include <linux/amlogic/major.h> |
39 | #include <linux/cdev.h> |
40 | #include <linux/crc32.h> |
41 | |
42 | #define CLASS_NAME "firmware_codec" |
43 | #define DEV_NAME "firmware_vdec" |
44 | #define DIR "video" |
45 | #define FRIMWARE_SIZE (128 * 1024) /*128k*/ |
46 | #define BUFF_SIZE (1024 * 1024) |
47 | |
48 | #define FW_LOAD_FORCE (0x1) |
49 | #define FW_LOAD_TRY (0X2) |
50 | |
51 | /*the first 256 bytes are signature data*/ |
52 | #define SEC_OFFSET (256) |
53 | |
54 | #define PACK ('P' << 24 | 'A' << 16 | 'C' << 8 | 'K') |
55 | #define CODE ('C' << 24 | 'O' << 16 | 'D' << 8 | 'E') |
56 | |
57 | static DEFINE_MUTEX(mutex); |
58 | |
59 | static struct ucode_info_s ucode_info[] = { |
60 | #include "firmware_cfg.h" |
61 | }; |
62 | |
63 | static const struct file_operations firmware_fops = { |
64 | .owner = THIS_MODULE |
65 | }; |
66 | |
67 | struct firmware_mgr_s *g_mgr; |
68 | struct firmware_dev_s *g_dev; |
69 | |
70 | static u32 debug; |
71 | |
72 | int get_firmware_data(enum firmware_type_e type, char *buf) |
73 | { |
74 | int data_len, ret = -1; |
75 | struct firmware_mgr_s *mgr = g_mgr; |
76 | struct firmware_info_s *info; |
77 | |
78 | if (tee_enabled()) { |
79 | pr_info ("tee load firmware type= %d\n",(u32)type); |
80 | ret = tee_load_video_fw((u32)type); |
81 | if (ret == 0) |
82 | ret = 1; |
83 | else |
84 | ret = -1; |
85 | return ret; |
86 | } |
87 | |
88 | mutex_lock(&mutex); |
89 | |
90 | if (list_empty(&mgr->head)) { |
91 | pr_info("the info list is empty.\n"); |
92 | goto out; |
93 | } |
94 | |
95 | list_for_each_entry(info, &mgr->head, node) { |
96 | if (type != info->type) |
97 | continue; |
98 | |
99 | data_len = info->data->header.data_size; |
100 | memcpy(buf, info->data->data, data_len); |
101 | ret = data_len; |
102 | |
103 | break; |
104 | } |
105 | out: |
106 | mutex_unlock(&mutex); |
107 | |
108 | return ret; |
109 | } |
110 | EXPORT_SYMBOL(get_firmware_data); |
111 | |
112 | int get_data_from_name(const char *name, char *buf) |
113 | { |
114 | int data_len, ret = -1; |
115 | struct firmware_mgr_s *mgr = g_mgr; |
116 | struct firmware_info_s *info; |
117 | char *firmware_name = __getname(); |
118 | |
119 | if (IS_ERR_OR_NULL(firmware_name)) |
120 | return -ENOMEM; |
121 | |
122 | strcat(firmware_name, name); |
123 | strcat(firmware_name, ".bin"); |
124 | |
125 | mutex_lock(&mutex); |
126 | |
127 | if (list_empty(&mgr->head)) { |
128 | pr_info("the info list is empty.\n"); |
129 | goto out; |
130 | } |
131 | |
132 | list_for_each_entry(info, &mgr->head, node) { |
133 | if (strcmp(firmware_name, info->name)) |
134 | continue; |
135 | |
136 | data_len = info->data->header.data_size; |
137 | memcpy(buf, info->data->data, data_len); |
138 | ret = data_len; |
139 | |
140 | break; |
141 | } |
142 | out: |
143 | mutex_unlock(&mutex); |
144 | |
145 | __putname(firmware_name); |
146 | |
147 | return ret; |
148 | } |
149 | EXPORT_SYMBOL(get_data_from_name); |
150 | |
151 | static int firmware_probe(char *buf) |
152 | { |
153 | int magic = 0; |
154 | |
155 | memcpy(&magic, buf, sizeof(int)); |
156 | return magic; |
157 | } |
158 | |
159 | static int request_firmware_from_sys(const char *file_name, |
160 | char *buf, int size) |
161 | { |
162 | int ret = -1; |
163 | const struct firmware *firmware; |
164 | int magic, offset = 0; |
165 | |
166 | pr_info("Try load %s ...\n", file_name); |
167 | |
168 | ret = request_firmware(&firmware, file_name, g_dev->dev); |
169 | if (ret < 0) { |
170 | pr_info("Error : %d can't load the %s.\n", ret, file_name); |
171 | goto err; |
172 | } |
173 | |
174 | if (firmware->size > size) { |
175 | pr_info("Not enough memory size for ucode.\n"); |
176 | ret = -ENOMEM; |
177 | goto release; |
178 | } |
179 | |
180 | magic = firmware_probe((char *)firmware->data); |
181 | if (magic != PACK && magic != CODE) { |
182 | if (firmware->size < SEC_OFFSET) { |
183 | pr_info("This is an invalid firmware file.\n"); |
184 | goto release; |
185 | } |
186 | |
187 | magic = firmware_probe((char *)firmware->data + SEC_OFFSET); |
188 | if (magic != PACK) { |
189 | pr_info("The firmware file is not packet.\n"); |
190 | goto release; |
191 | } |
192 | |
193 | offset = SEC_OFFSET; |
194 | } |
195 | |
196 | memcpy(buf, (char *)firmware->data + offset, firmware->size - offset); |
197 | |
198 | pr_info("load firmware size : %zd, Name : %s.\n", |
199 | firmware->size, file_name); |
200 | ret = firmware->size; |
201 | release: |
202 | release_firmware(firmware); |
203 | err: |
204 | return ret; |
205 | } |
206 | |
207 | int request_decoder_firmware_on_sys(enum vformat_e type, |
208 | const char *file_name, char *buf, int size) |
209 | { |
210 | int ret; |
211 | |
212 | ret = get_data_from_name(file_name, buf); |
213 | if (ret < 0) |
214 | pr_info("Get firmware fail.\n"); |
215 | |
216 | if (ret > size) { |
217 | pr_info("Not enough memory.\n"); |
218 | return -ENOMEM; |
219 | } |
220 | |
221 | return ret; |
222 | } |
223 | int get_decoder_firmware_data(enum vformat_e type, |
224 | const char *file_name, char *buf, int size) |
225 | { |
226 | int ret; |
227 | |
228 | ret = request_decoder_firmware_on_sys(type, file_name, buf, size); |
229 | if (ret < 0) |
230 | pr_info("get_decoder_firmware_data %s for format %d failed!\n", |
231 | file_name, type); |
232 | |
233 | return ret; |
234 | } |
235 | EXPORT_SYMBOL(get_decoder_firmware_data); |
236 | |
237 | static unsigned long firmware_mgr_lock(struct firmware_mgr_s *mgr) |
238 | { |
239 | unsigned long flags; |
240 | |
241 | spin_lock_irqsave(&mgr->lock, flags); |
242 | return flags; |
243 | } |
244 | |
245 | static void firmware_mgr_unlock(struct firmware_mgr_s *mgr, unsigned long flags) |
246 | { |
247 | spin_unlock_irqrestore(&mgr->lock, flags); |
248 | } |
249 | |
250 | static void add_info(struct firmware_info_s *info) |
251 | { |
252 | unsigned long flags; |
253 | struct firmware_mgr_s *mgr = g_mgr; |
254 | |
255 | flags = firmware_mgr_lock(mgr); |
256 | list_add(&info->node, &mgr->head); |
257 | firmware_mgr_unlock(mgr, flags); |
258 | } |
259 | |
260 | static void del_info(struct firmware_info_s *info) |
261 | { |
262 | unsigned long flags; |
263 | struct firmware_mgr_s *mgr = g_mgr; |
264 | |
265 | flags = firmware_mgr_lock(mgr); |
266 | list_del(&info->node); |
267 | firmware_mgr_unlock(mgr, flags); |
268 | } |
269 | |
270 | static void walk_firmware_info(void) |
271 | { |
272 | struct firmware_mgr_s *mgr = g_mgr; |
273 | struct firmware_info_s *info; |
274 | |
275 | mutex_lock(&mutex); |
276 | |
277 | if (list_empty(&mgr->head)) { |
278 | pr_info("the info list is empty.\n"); |
279 | return; |
280 | } |
281 | |
282 | list_for_each_entry(info, &mgr->head, node) { |
283 | if (IS_ERR_OR_NULL(info->data)) |
284 | continue; |
285 | |
286 | pr_info("path : %s.\n", info->path); |
287 | pr_info("name : %s.\n", info->name); |
288 | pr_info("version : %s.\n", |
289 | info->data->header.version); |
290 | pr_info("checksum : 0x%x.\n", |
291 | info->data->header.checksum); |
292 | pr_info("data size : %d.\n", |
293 | info->data->header.data_size); |
294 | pr_info("author : %s.\n", |
295 | info->data->header.author); |
296 | pr_info("date : %s.\n", |
297 | info->data->header.date); |
298 | pr_info("commit : %s.\n\n", |
299 | info->data->header.commit); |
300 | } |
301 | |
302 | mutex_unlock(&mutex); |
303 | } |
304 | |
305 | static ssize_t info_show(struct class *class, |
306 | struct class_attribute *attr, char *buf) |
307 | { |
308 | char *pbuf = buf; |
309 | struct firmware_mgr_s *mgr = g_mgr; |
310 | struct firmware_info_s *info; |
311 | |
312 | mutex_lock(&mutex); |
313 | |
314 | if (list_empty(&mgr->head)) { |
315 | pbuf += sprintf(pbuf, "No firmware.\n"); |
316 | goto out; |
317 | } |
318 | |
319 | list_for_each_entry(info, &mgr->head, node) { |
320 | if (IS_ERR_OR_NULL(info->data)) |
321 | continue; |
322 | |
323 | pr_info("%10s : %s\n", "name", info->name); |
324 | pr_info("%10s : %d\n", "size", |
325 | info->data->header.data_size); |
326 | pr_info("%10s : %s\n", "ver", |
327 | info->data->header.version); |
328 | pr_info("%10s : 0x%x\n", "sum", |
329 | info->data->header.checksum); |
330 | pr_info("%10s : %s\n", "commit", |
331 | info->data->header.commit); |
332 | pr_info("%10s : %s\n", "author", |
333 | info->data->header.author); |
334 | pr_info("%10s : %s\n\n", "date", |
335 | info->data->header.date); |
336 | } |
337 | out: |
338 | mutex_unlock(&mutex); |
339 | |
340 | return pbuf - buf; |
341 | } |
342 | |
343 | static int set_firmware_info(void) |
344 | { |
345 | int ret = 0, i, len; |
346 | struct firmware_info_s *info; |
347 | int info_size = ARRAY_SIZE(ucode_info); |
348 | int cpu = get_cpu_type(); |
349 | char *path = __getname(); |
350 | const char *name; |
351 | |
352 | if (IS_ERR_OR_NULL(path)) |
353 | return -ENOMEM; |
354 | |
355 | for (i = 0; i < info_size; i++) { |
356 | if (cpu != ucode_info[i].cpu) |
357 | continue; |
358 | |
359 | name = ucode_info[i].name; |
360 | if (IS_ERR_OR_NULL(name)) |
361 | break; |
362 | |
363 | len = snprintf(path, PATH_MAX, "%s/%s", DIR, |
364 | ucode_info[i].name); |
365 | if (len >= PATH_MAX) |
366 | continue; |
367 | |
368 | info = kzalloc(sizeof(struct firmware_info_s), GFP_KERNEL); |
369 | if (IS_ERR_OR_NULL(info)) { |
370 | __putname(path); |
371 | return -ENOMEM; |
372 | } |
373 | |
374 | strcpy(info->path, path); |
375 | strcpy(info->name, name); |
376 | info->type = ucode_info[i].type; |
377 | info->data = NULL; |
378 | |
379 | add_info(info); |
380 | } |
381 | |
382 | __putname(path); |
383 | |
384 | return ret; |
385 | } |
386 | |
387 | static int checksum(struct firmware_s *firmware) |
388 | { |
389 | unsigned int crc; |
390 | |
391 | crc = crc32_le(~0U, firmware->data, firmware->header.data_size); |
392 | |
393 | if (debug) |
394 | pr_info("firmware crc result : 0x%x\n", crc ^ ~0U); |
395 | |
396 | return firmware->header.checksum != (crc ^ ~0U) ? 0 : 1; |
397 | } |
398 | |
399 | static int check_repeat(struct firmware_s *data, enum firmware_type_e type) |
400 | { |
401 | struct firmware_mgr_s *mgr = g_mgr; |
402 | struct firmware_info_s *info; |
403 | |
404 | if (list_empty(&mgr->head)) { |
405 | pr_info("the info list is empty.\n"); |
406 | return -1; |
407 | } |
408 | |
409 | list_for_each_entry(info, &mgr->head, node) { |
410 | if (info->type != type) |
411 | continue; |
412 | |
413 | if (IS_ERR_OR_NULL(info->data)) |
414 | info->data = data; |
415 | |
416 | return 1; |
417 | } |
418 | |
419 | return 0; |
420 | } |
421 | |
422 | static int firmware_parse_package(char *buf, int size) |
423 | { |
424 | int ret = 0; |
425 | struct package_info_s *pack_info; |
426 | struct firmware_info_s *info; |
427 | struct firmware_s *data; |
428 | char *pack_data; |
429 | int info_len, len; |
430 | int try_cnt = 100; |
431 | char *path = __getname(); |
432 | |
433 | if (IS_ERR_OR_NULL(path)) |
434 | return -ENOMEM; |
435 | |
436 | pack_data = ((struct package_s *)buf)->data; |
437 | pack_info = (struct package_info_s *)pack_data; |
438 | info_len = sizeof(struct package_info_s); |
439 | |
440 | do { |
441 | if (!pack_info->header.length) |
442 | break; |
443 | |
444 | len = snprintf(path, PATH_MAX, "%s/%s", DIR, |
445 | pack_info->header.name); |
446 | if (len >= PATH_MAX) |
447 | continue; |
448 | |
449 | info = kzalloc(sizeof(struct firmware_info_s), GFP_KERNEL); |
450 | if (IS_ERR_OR_NULL(info)) { |
451 | ret = -ENOMEM; |
452 | goto out; |
453 | } |
454 | |
455 | data = kzalloc(FRIMWARE_SIZE, GFP_KERNEL); |
456 | if (IS_ERR_OR_NULL(data)) { |
457 | kfree(info); |
458 | ret = -ENOMEM; |
459 | goto out; |
460 | } |
461 | |
462 | strcpy(info->path, path); |
463 | strcpy(info->name, pack_info->header.name); |
464 | info->type = get_firmware_type(pack_info->header.format); |
465 | |
466 | len = pack_info->header.length; |
467 | memcpy(data, pack_info->data, len); |
468 | |
469 | pack_data += (pack_info->header.length + info_len); |
470 | pack_info = (struct package_info_s *)pack_data; |
471 | |
472 | ret = checksum(data); |
473 | if (!ret) { |
474 | pr_info("check sum fail !\n"); |
475 | kfree(data); |
476 | kfree(info); |
477 | goto out; |
478 | } |
479 | |
480 | ret = check_repeat(data, info->type); |
481 | if (ret < 0) { |
482 | kfree(data); |
483 | kfree(info); |
484 | goto out; |
485 | } |
486 | |
487 | if (ret) { |
488 | kfree(info); |
489 | continue; |
490 | } |
491 | |
492 | info->data = data; |
493 | add_info(info); |
494 | } while (try_cnt--); |
495 | out: |
496 | __putname(path); |
497 | |
498 | return ret; |
499 | } |
500 | |
501 | static int firmware_parse_code(struct firmware_info_s *info, |
502 | char *buf, int size) |
503 | { |
504 | if (!IS_ERR_OR_NULL(info->data)) |
505 | kfree(info->data); |
506 | |
507 | info->data = kzalloc(FRIMWARE_SIZE, GFP_KERNEL); |
508 | if (IS_ERR_OR_NULL(info->data)) |
509 | return -ENOMEM; |
510 | |
511 | memcpy(info->data, buf, size); |
512 | |
513 | if (!checksum(info->data)) { |
514 | pr_info("check sum fail !\n"); |
515 | kfree(info->data); |
516 | return -1; |
517 | } |
518 | |
519 | return 0; |
520 | } |
521 | |
522 | static int get_firmware_from_sys(const char *path, |
523 | char *buf, int size) |
524 | { |
525 | int len = 0; |
526 | |
527 | len = request_firmware_from_sys(path, buf, size); |
528 | if (len < 0) |
529 | pr_info("get data from fsys fail.\n"); |
530 | |
531 | return len; |
532 | } |
533 | |
534 | static int set_firmware_data(void) |
535 | { |
536 | int ret = 0, magic = 0; |
537 | struct firmware_mgr_s *mgr = g_mgr; |
538 | struct firmware_info_s *info, *temp; |
539 | char *buf = NULL; |
540 | int size; |
541 | |
542 | if (list_empty(&mgr->head)) { |
543 | pr_info("the info list is empty.\n"); |
544 | return 0; |
545 | } |
546 | |
547 | buf = vmalloc(BUFF_SIZE); |
548 | if (IS_ERR_OR_NULL(buf)) |
549 | return -ENOMEM; |
550 | |
551 | memset(buf, 0, BUFF_SIZE); |
552 | |
553 | list_for_each_entry_safe(info, temp, &mgr->head, node) { |
554 | size = get_firmware_from_sys(info->path, buf, BUFF_SIZE); |
555 | magic = firmware_probe(buf); |
556 | |
557 | switch (magic) { |
558 | case PACK: |
559 | ret = firmware_parse_package(buf, size); |
560 | |
561 | del_info(info); |
562 | kfree(info); |
563 | break; |
564 | |
565 | case CODE: |
566 | ret = firmware_parse_code(info, buf, size); |
567 | break; |
568 | |
569 | default: |
570 | del_info(info); |
571 | kfree(info); |
572 | pr_info("invaild type.\n"); |
573 | } |
574 | |
575 | memset(buf, 0, BUFF_SIZE); |
576 | } |
577 | |
578 | if (debug) |
579 | walk_firmware_info(); |
580 | |
581 | vfree(buf); |
582 | |
583 | return ret; |
584 | } |
585 | |
586 | static int firmware_pre_load(void) |
587 | { |
588 | int ret = -1; |
589 | |
590 | ret = set_firmware_info(); |
591 | if (ret < 0) { |
592 | pr_info("Get path fail.\n"); |
593 | goto err; |
594 | } |
595 | |
596 | ret = set_firmware_data(); |
597 | if (ret < 0) { |
598 | pr_info("Set data fail.\n"); |
599 | goto err; |
600 | } |
601 | err: |
602 | return ret; |
603 | } |
604 | |
605 | static int firmware_mgr_init(void) |
606 | { |
607 | g_mgr = kzalloc(sizeof(struct firmware_mgr_s), GFP_KERNEL); |
608 | if (IS_ERR_OR_NULL(g_mgr)) |
609 | return -ENOMEM; |
610 | |
611 | INIT_LIST_HEAD(&g_mgr->head); |
612 | spin_lock_init(&g_mgr->lock); |
613 | |
614 | return 0; |
615 | } |
616 | |
617 | static ssize_t reload_show(struct class *class, |
618 | struct class_attribute *attr, char *buf) |
619 | { |
620 | char *pbuf = buf; |
621 | |
622 | pbuf += sprintf(pbuf, "The fw reload usage.\n"); |
623 | pbuf += sprintf(pbuf, "> set 1 means that the fw is forced to update\n"); |
624 | pbuf += sprintf(pbuf, "> set 2 means that the fw is try to reload\n"); |
625 | |
626 | return pbuf - buf; |
627 | } |
628 | |
629 | int firmware_reload(int mode) |
630 | { |
631 | int ret = 0; |
632 | struct firmware_mgr_s *mgr = g_mgr; |
633 | struct firmware_info_s *info = NULL; |
634 | |
635 | if (tee_enabled()) |
636 | return 0; |
637 | |
638 | mutex_lock(&mutex); |
639 | |
640 | if (mode & FW_LOAD_FORCE) { |
641 | while (!list_empty(&mgr->head)) { |
642 | info = list_entry(mgr->head.next, |
643 | struct firmware_info_s, node); |
644 | list_del(&info->node); |
645 | kfree(info->data); |
646 | kfree(info); |
647 | } |
648 | |
649 | ret = firmware_pre_load(); |
650 | if (ret < 0) |
651 | pr_err("The fw reload fail.\n"); |
652 | } else if (mode & FW_LOAD_TRY) { |
653 | if (!list_empty(&mgr->head)) { |
654 | pr_info("The fw has been loaded.\n"); |
655 | goto out; |
656 | } |
657 | |
658 | ret = firmware_pre_load(); |
659 | if (ret < 0) |
660 | pr_err("The fw try to reload fail.\n"); |
661 | } |
662 | out: |
663 | mutex_unlock(&mutex); |
664 | |
665 | return ret; |
666 | } |
667 | EXPORT_SYMBOL(firmware_reload); |
668 | |
669 | static ssize_t reload_store(struct class *class, |
670 | struct class_attribute *attr, |
671 | const char *buf, size_t size) |
672 | { |
673 | int ret = -1; |
674 | unsigned int val; |
675 | |
676 | ret = kstrtoint(buf, 0, &val); |
677 | if (ret != 0) |
678 | return -EINVAL; |
679 | |
680 | ret = firmware_reload(val); |
681 | if (ret < 0) |
682 | pr_err("fw reload fail.\n"); |
683 | |
684 | return size; |
685 | } |
686 | |
687 | static struct class_attribute firmware_class_attrs[] = { |
688 | __ATTR_RO(info), |
689 | __ATTR(reload, 0664, reload_show, reload_store), |
690 | __ATTR_NULL |
691 | }; |
692 | |
693 | static struct class firmware_class = { |
694 | .name = CLASS_NAME, |
695 | .class_attrs = firmware_class_attrs, |
696 | }; |
697 | |
698 | static int firmware_driver_init(void) |
699 | { |
700 | int ret = -1; |
701 | |
702 | g_dev = kzalloc(sizeof(struct firmware_dev_s), GFP_KERNEL); |
703 | if (IS_ERR_OR_NULL(g_dev)) |
704 | return -ENOMEM; |
705 | |
706 | g_dev->dev_no = MKDEV(AMSTREAM_MAJOR, 100); |
707 | |
708 | ret = register_chrdev_region(g_dev->dev_no, 1, DEV_NAME); |
709 | if (ret < 0) { |
710 | pr_info("Can't get major number %d.\n", AMSTREAM_MAJOR); |
711 | goto err; |
712 | } |
713 | |
714 | cdev_init(&g_dev->cdev, &firmware_fops); |
715 | g_dev->cdev.owner = THIS_MODULE; |
716 | |
717 | ret = cdev_add(&g_dev->cdev, g_dev->dev_no, 1); |
718 | if (ret) { |
719 | pr_info("Error %d adding cdev fail.\n", ret); |
720 | goto err; |
721 | } |
722 | |
723 | ret = class_register(&firmware_class); |
724 | if (ret < 0) { |
725 | pr_info("Failed in creating class.\n"); |
726 | goto err; |
727 | } |
728 | |
729 | g_dev->dev = device_create(&firmware_class, NULL, |
730 | g_dev->dev_no, NULL, DEV_NAME); |
731 | if (IS_ERR_OR_NULL(g_dev->dev)) { |
732 | pr_info("Create device failed.\n"); |
733 | ret = -ENODEV; |
734 | goto err; |
735 | } |
736 | |
737 | pr_info("Registered firmware driver success.\n"); |
738 | err: |
739 | return ret; |
740 | } |
741 | |
742 | static void firmware_info_clean(void) |
743 | { |
744 | struct firmware_mgr_s *mgr = g_mgr; |
745 | struct firmware_info_s *info; |
746 | unsigned long flags; |
747 | |
748 | flags = firmware_mgr_lock(mgr); |
749 | while (!list_empty(&mgr->head)) { |
750 | info = list_entry(mgr->head.next, |
751 | struct firmware_info_s, node); |
752 | list_del(&info->node); |
753 | kfree(info->data); |
754 | kfree(info); |
755 | } |
756 | firmware_mgr_unlock(mgr, flags); |
757 | |
758 | kfree(g_mgr); |
759 | } |
760 | |
761 | static void firmware_driver_exit(void) |
762 | { |
763 | cdev_del(&g_dev->cdev); |
764 | device_destroy(&firmware_class, g_dev->dev_no); |
765 | class_unregister(&firmware_class); |
766 | unregister_chrdev_region(g_dev->dev_no, 1); |
767 | kfree(g_dev); |
768 | } |
769 | |
770 | static int __init firmware_module_init(void) |
771 | { |
772 | int ret = -1; |
773 | |
774 | ret = firmware_driver_init(); |
775 | if (ret) { |
776 | pr_info("Error %d firmware driver init fail.\n", ret); |
777 | goto err; |
778 | } |
779 | |
780 | ret = firmware_mgr_init(); |
781 | if (ret) { |
782 | pr_info("Error %d firmware mgr init fail.\n", ret); |
783 | goto err; |
784 | } |
785 | |
786 | ret = firmware_pre_load(); |
787 | if (ret) { |
788 | pr_info("Error %d firmware pre load fail.\n", ret); |
789 | goto err; |
790 | } |
791 | err: |
792 | return ret; |
793 | } |
794 | |
795 | static void __exit firmware_module_exit(void) |
796 | { |
797 | firmware_info_clean(); |
798 | firmware_driver_exit(); |
799 | pr_info("Firmware driver cleaned up.\n"); |
800 | } |
801 | |
802 | module_param(debug, uint, 0664); |
803 | |
804 | module_init(firmware_module_init); |
805 | module_exit(firmware_module_exit); |
806 | |
807 | MODULE_LICENSE("GPL"); |
808 | MODULE_AUTHOR("Nanxin Qin <nanxin.qin@amlogic.com>"); |
809 |