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