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
16static const char as102_st_fw1[] = "as102_data1_st.hex";
17static const char as102_st_fw2[] = "as102_data2_st.hex";
18static const char as102_dt_fw1[] = "as102_data1_dt.hex";
19static const char as102_dt_fw2[] = "as102_data2_dt.hex";
20
21static 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 */
40static 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
91static 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 }
150error:
151 kfree(objp: fw_pkt);
152 return (errno == 0) ? total_read_bytes : errno;
153}
154
155int 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);
221error:
222 kfree(objp: cmd_buf);
223 release_firmware(fw: firmware);
224
225 return errno;
226}
227

source code of linux/drivers/media/usb/as102/as102_fw.c