xref: /openbmc/linux/drivers/media/usb/as102/as102_fw.c (revision a8da474e)
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 	for (total_read_bytes = 0; total_read_bytes < firmware->size; ) {
109 		int read_bytes = 0, data_len = 0;
110 
111 		/* parse intel hex line */
112 		read_bytes = parse_hex_line(
113 				(u8 *) (firmware->data + total_read_bytes),
114 				fw_pkt.raw.address,
115 				fw_pkt.raw.data,
116 				&data_len,
117 				&addr_has_changed);
118 
119 		if (read_bytes <= 0)
120 			goto error;
121 
122 		/* detect the end of file */
123 		total_read_bytes += read_bytes;
124 		if (total_read_bytes == firmware->size) {
125 			fw_pkt.u.request[0] = 0x00;
126 			fw_pkt.u.request[1] = 0x03;
127 
128 			/* send EOF command */
129 			errno = bus_adap->ops->upload_fw_pkt(bus_adap,
130 							     (uint8_t *)
131 							     &fw_pkt, 2, 0);
132 			if (errno < 0)
133 				goto error;
134 		} else {
135 			if (!addr_has_changed) {
136 				/* prepare command to send */
137 				fw_pkt.u.request[0] = 0x00;
138 				fw_pkt.u.request[1] = 0x01;
139 
140 				data_len += sizeof(fw_pkt.u.request);
141 				data_len += sizeof(fw_pkt.raw.address);
142 
143 				/* send cmd to device */
144 				errno = bus_adap->ops->upload_fw_pkt(bus_adap,
145 								     (uint8_t *)
146 								     &fw_pkt,
147 								     data_len,
148 								     0);
149 				if (errno < 0)
150 					goto error;
151 			}
152 		}
153 	}
154 error:
155 	return (errno == 0) ? total_read_bytes : errno;
156 }
157 
158 int as102_fw_upload(struct as10x_bus_adapter_t *bus_adap)
159 {
160 	int errno = -EFAULT;
161 	const struct firmware *firmware = NULL;
162 	unsigned char *cmd_buf = NULL;
163 	const char *fw1, *fw2;
164 	struct usb_device *dev = bus_adap->usb_dev;
165 
166 	/* select fw file to upload */
167 	if (dual_tuner) {
168 		fw1 = as102_dt_fw1;
169 		fw2 = as102_dt_fw2;
170 	} else {
171 		fw1 = as102_st_fw1;
172 		fw2 = as102_st_fw2;
173 	}
174 
175 	/* allocate buffer to store firmware upload command and data */
176 	cmd_buf = kzalloc(MAX_FW_PKT_SIZE, GFP_KERNEL);
177 	if (cmd_buf == NULL) {
178 		errno = -ENOMEM;
179 		goto error;
180 	}
181 
182 	/* request kernel to locate firmware file: part1 */
183 	errno = request_firmware(&firmware, fw1, &dev->dev);
184 	if (errno < 0) {
185 		pr_err("%s: unable to locate firmware file: %s\n",
186 		       DRIVER_NAME, fw1);
187 		goto error;
188 	}
189 
190 	/* initiate firmware upload */
191 	errno = as102_firmware_upload(bus_adap, cmd_buf, firmware);
192 	if (errno < 0) {
193 		pr_err("%s: error during firmware upload part1\n",
194 		       DRIVER_NAME);
195 		goto error;
196 	}
197 
198 	pr_info("%s: firmware: %s loaded with success\n",
199 		DRIVER_NAME, fw1);
200 	release_firmware(firmware);
201 
202 	/* wait for boot to complete */
203 	mdelay(100);
204 
205 	/* request kernel to locate firmware file: part2 */
206 	errno = request_firmware(&firmware, fw2, &dev->dev);
207 	if (errno < 0) {
208 		pr_err("%s: unable to locate firmware file: %s\n",
209 		       DRIVER_NAME, fw2);
210 		goto error;
211 	}
212 
213 	/* initiate firmware upload */
214 	errno = as102_firmware_upload(bus_adap, cmd_buf, firmware);
215 	if (errno < 0) {
216 		pr_err("%s: error during firmware upload part2\n",
217 		       DRIVER_NAME);
218 		goto error;
219 	}
220 
221 	pr_info("%s: firmware: %s loaded with success\n",
222 		DRIVER_NAME, fw2);
223 error:
224 	kfree(cmd_buf);
225 	release_firmware(firmware);
226 
227 	return errno;
228 }
229