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