1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Abilis Systems Single DVB-T Receiver |
4 | * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com> |
5 | * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com> |
6 | */ |
7 | #include <linux/kernel.h> |
8 | #include <linux/errno.h> |
9 | #include <linux/ctype.h> |
10 | #include <linux/delay.h> |
11 | #include <linux/firmware.h> |
12 | |
13 | #include "as102_drv.h" |
14 | #include "as102_fw.h" |
15 | |
16 | static const char as102_st_fw1[] = "as102_data1_st.hex" ; |
17 | static const char as102_st_fw2[] = "as102_data2_st.hex" ; |
18 | static const char as102_dt_fw1[] = "as102_data1_dt.hex" ; |
19 | static const char as102_dt_fw2[] = "as102_data2_dt.hex" ; |
20 | |
21 | static unsigned char atohx(unsigned char *dst, char *src) |
22 | { |
23 | unsigned char value = 0; |
24 | |
25 | char msb = tolower(*src) - '0'; |
26 | char lsb = tolower(*(src + 1)) - '0'; |
27 | |
28 | if (msb > 9) |
29 | msb -= 7; |
30 | if (lsb > 9) |
31 | lsb -= 7; |
32 | |
33 | *dst = value = ((msb & 0xF) << 4) | (lsb & 0xF); |
34 | return value; |
35 | } |
36 | |
37 | /* |
38 | * Parse INTEL HEX firmware file to extract address and data. |
39 | */ |
40 | static int parse_hex_line(unsigned char *fw_data, unsigned char *addr, |
41 | unsigned char *data, int *dataLength, |
42 | unsigned char *addr_has_changed) { |
43 | |
44 | int count = 0; |
45 | unsigned char *src, dst; |
46 | |
47 | if (*fw_data++ != ':') { |
48 | pr_err("invalid firmware file\n" ); |
49 | return -EFAULT; |
50 | } |
51 | |
52 | /* locate end of line */ |
53 | for (src = fw_data; *src != '\n'; src += 2) { |
54 | atohx(dst: &dst, src); |
55 | /* parse line to split addr / data */ |
56 | switch (count) { |
57 | case 0: |
58 | *dataLength = dst; |
59 | break; |
60 | case 1: |
61 | addr[2] = dst; |
62 | break; |
63 | case 2: |
64 | addr[3] = dst; |
65 | break; |
66 | case 3: |
67 | /* check if data is an address */ |
68 | if (dst == 0x04) |
69 | *addr_has_changed = 1; |
70 | else |
71 | *addr_has_changed = 0; |
72 | break; |
73 | case 4: |
74 | case 5: |
75 | if (*addr_has_changed) |
76 | addr[(count - 4)] = dst; |
77 | else |
78 | data[(count - 4)] = dst; |
79 | break; |
80 | default: |
81 | data[(count - 4)] = dst; |
82 | break; |
83 | } |
84 | count++; |
85 | } |
86 | |
87 | /* return read value + ':' + '\n' */ |
88 | return (count * 2) + 2; |
89 | } |
90 | |
91 | static int as102_firmware_upload(struct as10x_bus_adapter_t *bus_adap, |
92 | unsigned char *cmd, |
93 | const struct firmware *firmware) { |
94 | |
95 | struct as10x_fw_pkt_t *fw_pkt; |
96 | int total_read_bytes = 0, errno = 0; |
97 | unsigned char addr_has_changed = 0; |
98 | |
99 | fw_pkt = kmalloc(size: sizeof(*fw_pkt), GFP_KERNEL); |
100 | if (!fw_pkt) |
101 | return -ENOMEM; |
102 | |
103 | |
104 | for (total_read_bytes = 0; total_read_bytes < firmware->size; ) { |
105 | int read_bytes = 0, data_len = 0; |
106 | |
107 | /* parse intel hex line */ |
108 | read_bytes = parse_hex_line( |
109 | fw_data: (u8 *) (firmware->data + total_read_bytes), |
110 | addr: fw_pkt->raw.address, |
111 | data: fw_pkt->raw.data, |
112 | dataLength: &data_len, |
113 | addr_has_changed: &addr_has_changed); |
114 | |
115 | if (read_bytes <= 0) |
116 | goto error; |
117 | |
118 | /* detect the end of file */ |
119 | total_read_bytes += read_bytes; |
120 | if (total_read_bytes == firmware->size) { |
121 | fw_pkt->u.request[0] = 0x00; |
122 | fw_pkt->u.request[1] = 0x03; |
123 | |
124 | /* send EOF command */ |
125 | errno = bus_adap->ops->upload_fw_pkt(bus_adap, |
126 | (uint8_t *) |
127 | fw_pkt, 2, 0); |
128 | if (errno < 0) |
129 | goto error; |
130 | } else { |
131 | if (!addr_has_changed) { |
132 | /* prepare command to send */ |
133 | fw_pkt->u.request[0] = 0x00; |
134 | fw_pkt->u.request[1] = 0x01; |
135 | |
136 | data_len += sizeof(fw_pkt->u.request); |
137 | data_len += sizeof(fw_pkt->raw.address); |
138 | |
139 | /* send cmd to device */ |
140 | errno = bus_adap->ops->upload_fw_pkt(bus_adap, |
141 | (uint8_t *) |
142 | fw_pkt, |
143 | data_len, |
144 | 0); |
145 | if (errno < 0) |
146 | goto error; |
147 | } |
148 | } |
149 | } |
150 | error: |
151 | kfree(objp: fw_pkt); |
152 | return (errno == 0) ? total_read_bytes : errno; |
153 | } |
154 | |
155 | int as102_fw_upload(struct as10x_bus_adapter_t *bus_adap) |
156 | { |
157 | int errno = -EFAULT; |
158 | const struct firmware *firmware = NULL; |
159 | unsigned char *cmd_buf = NULL; |
160 | const char *fw1, *fw2; |
161 | struct usb_device *dev = bus_adap->usb_dev; |
162 | |
163 | /* select fw file to upload */ |
164 | if (dual_tuner) { |
165 | fw1 = as102_dt_fw1; |
166 | fw2 = as102_dt_fw2; |
167 | } else { |
168 | fw1 = as102_st_fw1; |
169 | fw2 = as102_st_fw2; |
170 | } |
171 | |
172 | /* allocate buffer to store firmware upload command and data */ |
173 | cmd_buf = kzalloc(MAX_FW_PKT_SIZE, GFP_KERNEL); |
174 | if (cmd_buf == NULL) { |
175 | errno = -ENOMEM; |
176 | goto error; |
177 | } |
178 | |
179 | /* request kernel to locate firmware file: part1 */ |
180 | errno = request_firmware(fw: &firmware, name: fw1, device: &dev->dev); |
181 | if (errno < 0) { |
182 | pr_err("%s: unable to locate firmware file: %s\n" , |
183 | DRIVER_NAME, fw1); |
184 | goto error; |
185 | } |
186 | |
187 | /* initiate firmware upload */ |
188 | errno = as102_firmware_upload(bus_adap, cmd: cmd_buf, firmware); |
189 | if (errno < 0) { |
190 | pr_err("%s: error during firmware upload part1\n" , |
191 | DRIVER_NAME); |
192 | goto error; |
193 | } |
194 | |
195 | pr_info("%s: firmware: %s loaded with success\n" , |
196 | DRIVER_NAME, fw1); |
197 | release_firmware(fw: firmware); |
198 | firmware = NULL; |
199 | |
200 | /* wait for boot to complete */ |
201 | mdelay(100); |
202 | |
203 | /* request kernel to locate firmware file: part2 */ |
204 | errno = request_firmware(fw: &firmware, name: fw2, device: &dev->dev); |
205 | if (errno < 0) { |
206 | pr_err("%s: unable to locate firmware file: %s\n" , |
207 | DRIVER_NAME, fw2); |
208 | goto error; |
209 | } |
210 | |
211 | /* initiate firmware upload */ |
212 | errno = as102_firmware_upload(bus_adap, cmd: cmd_buf, firmware); |
213 | if (errno < 0) { |
214 | pr_err("%s: error during firmware upload part2\n" , |
215 | DRIVER_NAME); |
216 | goto error; |
217 | } |
218 | |
219 | pr_info("%s: firmware: %s loaded with success\n" , |
220 | DRIVER_NAME, fw2); |
221 | error: |
222 | kfree(objp: cmd_buf); |
223 | release_firmware(fw: firmware); |
224 | |
225 | return errno; |
226 | } |
227 | |