Merge tag 'ecryptfs-3.10-rc5-msync' of git://git.kernel.org/pub/scm/linux/kernel...
[firefly-linux-kernel-4.4.55.git] / drivers / staging / media / as102 / as102_fw.c
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  * 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
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
34 static unsigned char atohx(unsigned char *dst, char *src)
35 {
36         unsigned char value = 0;
37
38         char msb = tolower(*src) - '0';
39         char lsb = tolower(*(src + 1)) - '0';
40
41         if (msb > 9)
42                 msb -= 7;
43         if (lsb > 9)
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++ != ':') {
61                 pr_err("invalid firmware file\n");
62                 return -EFAULT;
63         }
64
65         /* locate end of line */
66         for (src = fw_data; *src != '\n'; src += 2) {
67                 atohx(&dst, src);
68                 /* parse line to split addr / data */
69                 switch (count) {
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
91                                 data[(count - 4)] = dst;
92                         break;
93                 default:
94                         data[(count - 4)] = dst;
95                         break;
96                 }
97                 count++;
98         }
99
100         /* return read value + ':' + '\n' */
101         return (count * 2) + 2;
102 }
103
104 static int as102_firmware_upload(struct as10x_bus_adapter_t *bus_adap,
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
125                 if (read_bytes <= 0)
126                         goto error;
127
128                 /* detect the end of file */
129                 total_read_bytes += read_bytes;
130                 if (total_read_bytes == firmware->size) {
131                         fw_pkt.u.request[0] = 0x00;
132                         fw_pkt.u.request[1] = 0x03;
133
134                         /* send EOF command */
135                         errno = bus_adap->ops->upload_fw_pkt(bus_adap,
136                                                              (uint8_t *)
137                                                              &fw_pkt, 2, 0);
138                         if (errno < 0)
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 */
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)
156                                         goto error;
157                         }
158                 }
159         }
160 error:
161         LEAVE();
162         return (errno == 0) ? total_read_bytes : errno;
163 }
164
165 int as102_fw_upload(struct as10x_bus_adapter_t *bus_adap)
166 {
167         int errno = -EFAULT;
168         const struct firmware *firmware = NULL;
169         unsigned char *cmd_buf = NULL;
170         char *fw1, *fw2;
171         struct usb_device *dev = bus_adap->usb_dev;
172
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
184         /* allocate buffer to store firmware upload command and data */
185         cmd_buf = kzalloc(MAX_FW_PKT_SIZE, GFP_KERNEL);
186         if (cmd_buf == NULL) {
187                 errno = -ENOMEM;
188                 goto error;
189         }
190
191         /* request kernel to locate firmware file: part1 */
192         errno = request_firmware(&firmware, fw1, &dev->dev);
193         if (errno < 0) {
194                 pr_err("%s: unable to locate firmware file: %s\n",
195                        DRIVER_NAME, fw1);
196                 goto error;
197         }
198
199         /* initiate firmware upload */
200         errno = as102_firmware_upload(bus_adap, cmd_buf, firmware);
201         if (errno < 0) {
202                 pr_err("%s: error during firmware upload part1\n",
203                        DRIVER_NAME);
204                 goto error;
205         }
206
207         pr_info("%s: firmware: %s loaded with success\n",
208                 DRIVER_NAME, fw1);
209         release_firmware(firmware);
210
211         /* wait for boot to complete */
212         mdelay(100);
213
214         /* request kernel to locate firmware file: part2 */
215         errno = request_firmware(&firmware, fw2, &dev->dev);
216         if (errno < 0) {
217                 pr_err("%s: unable to locate firmware file: %s\n",
218                        DRIVER_NAME, fw2);
219                 goto error;
220         }
221
222         /* initiate firmware upload */
223         errno = as102_firmware_upload(bus_adap, cmd_buf, firmware);
224         if (errno < 0) {
225                 pr_err("%s: error during firmware upload part2\n",
226                        DRIVER_NAME);
227                 goto error;
228         }
229
230         pr_info("%s: firmware: %s loaded with success\n",
231                 DRIVER_NAME, fw2);
232 error:
233         kfree(cmd_buf);
234         release_firmware(firmware);
235
236         LEAVE();
237         return errno;
238 }