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
25static const char as102_st_fw1[] = "as102_data1_st.hex";
26static const char as102_st_fw2[] = "as102_data2_st.hex";
27static const char as102_dt_fw1[] = "as102_data1_dt.hex";
28static const char as102_dt_fw2[] = "as102_data2_dt.hex";
29
30static 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 */
49static 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
100static 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	}
154error:
155	return (errno == 0) ? total_read_bytes : errno;
156}
157
158int 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);
223error:
224	kfree(cmd_buf);
225	release_firmware(firmware);
226
227	return errno;
228}
229