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