1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> |
4 | Copyright (C) 2004 - 2009 Gertjan van Wingerde <gwingerde@gmail.com> |
5 | <http://rt2x00.serialmonkey.com> |
6 | |
7 | */ |
8 | |
9 | /* |
10 | Module: rt2x00lib |
11 | Abstract: rt2x00 firmware loading routines. |
12 | */ |
13 | |
14 | #include <linux/kernel.h> |
15 | #include <linux/module.h> |
16 | |
17 | #include "rt2x00.h" |
18 | #include "rt2x00lib.h" |
19 | |
20 | static int rt2x00lib_request_firmware(struct rt2x00_dev *rt2x00dev) |
21 | { |
22 | struct device *device = wiphy_dev(wiphy: rt2x00dev->hw->wiphy); |
23 | const struct firmware *fw; |
24 | char *fw_name; |
25 | int retval; |
26 | |
27 | /* |
28 | * Read correct firmware from harddisk. |
29 | */ |
30 | fw_name = rt2x00dev->ops->lib->get_firmware_name(rt2x00dev); |
31 | if (!fw_name) { |
32 | rt2x00_err(rt2x00dev, |
33 | "Invalid firmware filename\n" |
34 | "Please file bug report to %s\n" , DRV_PROJECT); |
35 | return -EINVAL; |
36 | } |
37 | |
38 | rt2x00_info(rt2x00dev, "Loading firmware file '%s'\n" , fw_name); |
39 | |
40 | retval = request_firmware(fw: &fw, name: fw_name, device); |
41 | if (retval) { |
42 | rt2x00_err(rt2x00dev, "Failed to request Firmware\n" ); |
43 | return retval; |
44 | } |
45 | |
46 | if (!fw || !fw->size || !fw->data) { |
47 | rt2x00_err(rt2x00dev, "Failed to read Firmware\n" ); |
48 | release_firmware(fw); |
49 | return -ENOENT; |
50 | } |
51 | |
52 | rt2x00_info(rt2x00dev, "Firmware detected - version: %d.%d\n" , |
53 | fw->data[fw->size - 4], fw->data[fw->size - 3]); |
54 | snprintf(buf: rt2x00dev->hw->wiphy->fw_version, |
55 | size: sizeof(rt2x00dev->hw->wiphy->fw_version), fmt: "%d.%d" , |
56 | fw->data[fw->size - 4], fw->data[fw->size - 3]); |
57 | |
58 | retval = rt2x00dev->ops->lib->check_firmware(rt2x00dev, fw->data, fw->size); |
59 | switch (retval) { |
60 | case FW_OK: |
61 | break; |
62 | case FW_BAD_CRC: |
63 | rt2x00_err(rt2x00dev, "Firmware checksum error\n" ); |
64 | goto exit; |
65 | case FW_BAD_LENGTH: |
66 | rt2x00_err(rt2x00dev, "Invalid firmware file length (len=%zu)\n" , |
67 | fw->size); |
68 | goto exit; |
69 | case FW_BAD_VERSION: |
70 | rt2x00_err(rt2x00dev, "Current firmware does not support detected chipset\n" ); |
71 | goto exit; |
72 | } |
73 | |
74 | rt2x00dev->fw = fw; |
75 | |
76 | return 0; |
77 | |
78 | exit: |
79 | release_firmware(fw); |
80 | |
81 | return -ENOENT; |
82 | } |
83 | |
84 | int rt2x00lib_load_firmware(struct rt2x00_dev *rt2x00dev) |
85 | { |
86 | int retval; |
87 | |
88 | if (!rt2x00_has_cap_flag(rt2x00dev, cap_flag: REQUIRE_FIRMWARE)) |
89 | return 0; |
90 | |
91 | if (!rt2x00dev->fw) { |
92 | retval = rt2x00lib_request_firmware(rt2x00dev); |
93 | if (retval) |
94 | return retval; |
95 | } |
96 | |
97 | /* |
98 | * Send firmware to the device. |
99 | */ |
100 | retval = rt2x00dev->ops->lib->load_firmware(rt2x00dev, |
101 | rt2x00dev->fw->data, |
102 | rt2x00dev->fw->size); |
103 | |
104 | /* |
105 | * When the firmware is uploaded to the hardware the LED |
106 | * association status might have been triggered, for correct |
107 | * LED handling it should now be reset. |
108 | */ |
109 | rt2x00leds_led_assoc(rt2x00dev, enabled: false); |
110 | |
111 | return retval; |
112 | } |
113 | |
114 | void rt2x00lib_free_firmware(struct rt2x00_dev *rt2x00dev) |
115 | { |
116 | release_firmware(fw: rt2x00dev->fw); |
117 | rt2x00dev->fw = NULL; |
118 | } |
119 | |