staging: speakup: avoid out-of-range access in synth_add()
[firefly-linux-kernel-4.4.55.git] / drivers / staging / gdm72xx / usb_boot.c
1 /*
2  * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
3  *
4  * This software is licensed under the terms of the GNU General Public
5  * License version 2, as published by the Free Software Foundation, and
6  * may be copied, distributed, and modified under those terms.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  */
13
14 #include <linux/uaccess.h>
15 #include <linux/module.h>
16 #include <linux/kernel.h>
17 #include <linux/mm.h>
18 #include <linux/usb.h>
19 #include <linux/unistd.h>
20 #include <linux/slab.h>
21 #include <linux/firmware.h>
22
23 #include <asm/byteorder.h>
24 #include "gdm_usb.h"
25 #include "usb_boot.h"
26
27 #define DN_KERNEL_MAGIC_NUMBER  0x10760001
28 #define DN_ROOTFS_MAGIC_NUMBER  0x10760002
29
30 #define DOWNLOAD_SIZE           1024
31
32 #define MAX_IMG_CNT             16
33 #define FW_DIR                  "gdm72xx/"
34 #define FW_UIMG                 "gdmuimg.bin"
35 #define FW_KERN                 "zImage"
36 #define FW_FS                   "ramdisk.jffs2"
37
38 struct dn_header {
39         u32     magic_num;
40         u32     file_size;
41 };
42
43 struct img_header {
44         u32     magic_code;
45         u32     count;
46         u32     len;
47         u32     offset[MAX_IMG_CNT];
48         char    hostname[32];
49         char    date[32];
50 };
51
52 struct fw_info {
53         u32     id;
54         u32     len;
55         u32     kernel_len;
56         u32     rootfs_len;
57         u32     kernel_offset;
58         u32     rootfs_offset;
59         u32     fw_ver;
60         u32     mac_ver;
61         char    hostname[32];
62         char    userid[16];
63         char    date[32];
64         char    user_desc[128];
65 };
66
67 static void array_le32_to_cpu(u32 *arr, int num)
68 {
69         int i;
70         for (i = 0; i < num; i++, arr++)
71                 *arr = __le32_to_cpu(*arr);
72 }
73
74 static u8 *tx_buf;
75
76 static int gdm_wibro_send(struct usb_device *usbdev, void *data, int len)
77 {
78         int ret;
79         int actual;
80
81         ret = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), data, len,
82                         &actual, 1000);
83
84         if (ret < 0) {
85                 dev_err(&usbdev->dev, "Error : usb_bulk_msg ( result = %d )\n",
86                         ret);
87                 return ret;
88         }
89         return 0;
90 }
91
92 static int gdm_wibro_recv(struct usb_device *usbdev, void *data, int len)
93 {
94         int ret;
95         int actual;
96
97         ret = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, 2), data, len,
98                         &actual, 5000);
99
100         if (ret < 0) {
101                 dev_err(&usbdev->dev,
102                         "Error : usb_bulk_msg(recv) ( result = %d )\n", ret);
103                 return ret;
104         }
105         return 0;
106 }
107
108 static int download_image(struct usb_device *usbdev,
109                                 const struct firmware *firm,
110                                 loff_t pos, u32 img_len, u32 magic_num)
111 {
112         struct dn_header h;
113         int ret = 0;
114         u32 size;
115
116         size = ALIGN(img_len, DOWNLOAD_SIZE);
117         h.magic_num = __cpu_to_be32(magic_num);
118         h.file_size = __cpu_to_be32(size);
119
120         ret = gdm_wibro_send(usbdev, &h, sizeof(h));
121         if (ret < 0)
122                 return ret;
123
124         while (img_len > 0) {
125                 if (img_len > DOWNLOAD_SIZE)
126                         size = DOWNLOAD_SIZE;
127                 else
128                         size = img_len; /* the last chunk of data */
129
130                 memcpy(tx_buf, firm->data + pos, size);
131                 ret = gdm_wibro_send(usbdev, tx_buf, size);
132
133                 if (ret < 0)
134                         return ret;
135
136                 img_len -= size;
137                 pos += size;
138         }
139
140         return ret;
141 }
142
143 int usb_boot(struct usb_device *usbdev, u16 pid)
144 {
145         int i, ret = 0;
146         struct img_header hdr;
147         struct fw_info fw_info;
148         loff_t pos = 0;
149         char *img_name = FW_DIR FW_UIMG;
150         const struct firmware *firm;
151
152         ret = request_firmware(&firm, img_name, &usbdev->dev);
153         if (ret < 0) {
154                 dev_err(&usbdev->dev,
155                         "requesting firmware %s failed with error %d\n",
156                         img_name, ret);
157                 return ret;
158         }
159
160         tx_buf = kmalloc(DOWNLOAD_SIZE, GFP_KERNEL);
161         if (tx_buf == NULL) {
162                 dev_err(&usbdev->dev, "Error: kmalloc\n");
163                 return -ENOMEM;
164         }
165
166         if (firm->size < sizeof(hdr)) {
167                 dev_err(&usbdev->dev, "Cannot read the image info.\n");
168                 ret = -EIO;
169                 goto out;
170         }
171         memcpy(&hdr, firm->data, sizeof(hdr));
172
173         array_le32_to_cpu((u32 *)&hdr, 19);
174 #if 0
175         if (hdr.magic_code != 0x10767fff) {
176                 dev_err(&usbdev->dev, "Invalid magic code 0x%08x\n",
177                         hdr.magic_code);
178                 ret = -EINVAL;
179                 goto out;
180         }
181 #endif
182         if (hdr.count > MAX_IMG_CNT) {
183                 dev_err(&usbdev->dev, "Too many images. %d\n", hdr.count);
184                 ret = -EINVAL;
185                 goto out;
186         }
187
188         for (i = 0; i < hdr.count; i++) {
189                 if (hdr.offset[i] > hdr.len) {
190                         dev_err(&usbdev->dev,
191                                 "Invalid offset. Entry = %d Offset = 0x%08x Image length = 0x%08x\n",
192                                 i, hdr.offset[i], hdr.len);
193                         ret = -EINVAL;
194                         goto out;
195                 }
196
197                 pos = hdr.offset[i];
198                 if (firm->size < sizeof(fw_info) + pos) {
199                         dev_err(&usbdev->dev, "Cannot read the FW info.\n");
200                         ret = -EIO;
201                         goto out;
202                 }
203                 memcpy(&fw_info, firm->data + pos, sizeof(fw_info));
204
205                 array_le32_to_cpu((u32 *)&fw_info, 8);
206 #if 0
207                 if ((fw_info.id & 0xfffff000) != 0x10767000) {
208                         dev_err(&usbdev->dev, "Invalid FW id. 0x%08x\n",
209                                 fw_info.id);
210                         ret = -EIO;
211                         goto out;
212                 }
213 #endif
214
215                 if ((fw_info.id & 0xffff) != pid)
216                         continue;
217
218                 pos = hdr.offset[i] + fw_info.kernel_offset;
219                 if (firm->size < fw_info.kernel_len + pos) {
220                         dev_err(&usbdev->dev, "Kernel FW is too small.\n");
221                         goto out;
222                 }
223
224                 ret = download_image(usbdev, firm, pos,
225                                 fw_info.kernel_len, DN_KERNEL_MAGIC_NUMBER);
226                 if (ret < 0)
227                         goto out;
228                 dev_info(&usbdev->dev, "GCT: Kernel download success.\n");
229
230                 pos = hdr.offset[i] + fw_info.rootfs_offset;
231                 if (firm->size < fw_info.rootfs_len + pos) {
232                         dev_err(&usbdev->dev, "Filesystem FW is too small.\n");
233                         goto out;
234                 }
235                 ret = download_image(usbdev, firm, pos, fw_info.rootfs_len,
236                                 DN_ROOTFS_MAGIC_NUMBER);
237                 if (ret < 0)
238                         goto out;
239                 dev_info(&usbdev->dev, "GCT: Filesystem download success.\n");
240
241                 break;
242         }
243
244         if (i == hdr.count) {
245                 dev_err(&usbdev->dev, "Firmware for gsk%x is not installed.\n",
246                         pid);
247                 ret = -EINVAL;
248         }
249 out:
250         release_firmware(firm);
251         kfree(tx_buf);
252         return ret;
253 }
254
255 /*#define GDM7205_PADDING               256 */
256 #define DOWNLOAD_CHUCK                  2048
257 #define KERNEL_TYPE_STRING              "linux"
258 #define FS_TYPE_STRING                  "rootfs"
259
260 static int em_wait_ack(struct usb_device *usbdev, int send_zlp)
261 {
262         int ack;
263         int ret = -1;
264
265         if (send_zlp) {
266                 /*Send ZLP*/
267                 ret = gdm_wibro_send(usbdev, NULL, 0);
268                 if (ret < 0)
269                         goto out;
270         }
271
272         /*Wait for ACK*/
273         ret = gdm_wibro_recv(usbdev, &ack, sizeof(ack));
274         if (ret < 0)
275                 goto out;
276 out:
277         return ret;
278 }
279
280 static int em_download_image(struct usb_device *usbdev, const char *img_name,
281                                 char *type_string)
282 {
283         char *buf = NULL;
284         loff_t pos = 0;
285         int ret = 0;
286         int len;
287         int img_len;
288         const struct firmware *firm;
289         #if defined(GDM7205_PADDING)
290         const int pad_size = GDM7205_PADDING;
291         #else
292         const int pad_size = 0;
293         #endif
294
295         ret = request_firmware(&firm, img_name, &usbdev->dev);
296         if (ret < 0) {
297                 dev_err(&usbdev->dev,
298                         "requesting firmware %s failed with error %d\n",
299                         img_name, ret);
300                 return ret;
301         }
302
303         buf = kmalloc(DOWNLOAD_CHUCK + pad_size, GFP_KERNEL);
304         if (buf == NULL) {
305                 dev_err(&usbdev->dev, "Error: kmalloc\n");
306                 return -ENOMEM;
307         }
308
309         strcpy(buf+pad_size, type_string);
310         ret = gdm_wibro_send(usbdev, buf, strlen(type_string)+pad_size);
311         if (ret < 0)
312                 goto out;
313
314         img_len = firm->size;
315
316         if (img_len <= 0) {
317                 ret = -1;
318                 goto out;
319         }
320
321         while (img_len > 0) {
322                 if (img_len > DOWNLOAD_CHUCK)
323                         len = DOWNLOAD_CHUCK;
324                 else
325                         len = img_len; /* the last chunk of data */
326
327                 memcpy(buf+pad_size, firm->data + pos, len);
328                 ret = gdm_wibro_send(usbdev, buf, len+pad_size);
329
330                 if (ret < 0)
331                         goto out;
332
333                 img_len -= DOWNLOAD_CHUCK;
334                 pos += DOWNLOAD_CHUCK;
335
336                 ret = em_wait_ack(usbdev, ((len+pad_size) % 512 == 0));
337                 if (ret < 0)
338                         goto out;
339         }
340
341         ret = em_wait_ack(usbdev, 1);
342         if (ret < 0)
343                 goto out;
344
345 out:
346         release_firmware(firm);
347         kfree(buf);
348
349         return ret;
350 }
351
352 static int em_fw_reset(struct usb_device *usbdev)
353 {
354         int ret;
355
356         /*Send ZLP*/
357         ret = gdm_wibro_send(usbdev, NULL, 0);
358         return ret;
359 }
360
361 int usb_emergency(struct usb_device *usbdev)
362 {
363         int ret;
364         const char *kern_name = FW_DIR FW_KERN;
365         const char *fs_name = FW_DIR FW_FS;
366
367         ret = em_download_image(usbdev, kern_name, KERNEL_TYPE_STRING);
368         if (ret < 0)
369                 return ret;
370         dev_err(&usbdev->dev, "GCT Emergency: Kernel download success.\n");
371
372         ret = em_download_image(usbdev, fs_name, FS_TYPE_STRING);
373         if (ret < 0)
374                 return ret;
375         dev_info(&usbdev->dev, "GCT Emergency: Filesystem download success.\n");
376
377         ret = em_fw_reset(usbdev);
378
379         return ret;
380 }