Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs
[deliverable/linux.git] / drivers / staging / gdm72xx / usb_boot.c
CommitLineData
247e9cff
SA
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>
247e9cff
SA
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>
1839c7eb 21#include <linux/firmware.h>
247e9cff
SA
22
23#include <asm/byteorder.h>
24#include "gdm_usb.h"
25#include "usb_boot.h"
26
443242d2
ML
27#define DN_KERNEL_MAGIC_NUMBER 0x10760001
28#define DN_ROOTFS_MAGIC_NUMBER 0x10760002
247e9cff 29
443242d2 30#define DOWNLOAD_SIZE 1024
247e9cff 31
247e9cff 32#define MAX_IMG_CNT 16
1839c7eb
ML
33#define FW_DIR "gdm72xx/"
34#define FW_UIMG "gdmuimg.bin"
3afcb91c
ML
35#define FW_KERN "zImage"
36#define FW_FS "ramdisk.jffs2"
247e9cff
SA
37
38struct dn_header {
39 u32 magic_num;
40 u32 file_size;
41};
42
43struct img_header {
443242d2
ML
44 u32 magic_code;
45 u32 count;
46 u32 len;
47 u32 offset[MAX_IMG_CNT];
247e9cff
SA
48 char hostname[32];
49 char date[32];
50};
51
52struct fw_info {
443242d2
ML
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;
247e9cff
SA
61 char hostname[32];
62 char userid[16];
63 char date[32];
64 char user_desc[128];
65};
66
67static void array_le32_to_cpu(u32 *arr, int num)
68{
69 int i;
70 for (i = 0; i < num; i++, arr++)
1839c7eb 71 *arr = __le32_to_cpu(*arr);
247e9cff
SA
72}
73
74static u8 *tx_buf;
75
76static 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 printk(KERN_ERR "Error : usb_bulk_msg ( result = %d )\n", ret);
86 return ret;
87 }
88 return 0;
89}
90
91static int gdm_wibro_recv(struct usb_device *usbdev, void *data, int len)
92{
93 int ret;
94 int actual;
95
96 ret = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, 2), data, len,
97 &actual, 5000);
98
99 if (ret < 0) {
100 printk(KERN_ERR "Error : usb_bulk_msg(recv) ( result = %d )\n",
101 ret);
102 return ret;
103 }
104 return 0;
105}
106
1839c7eb
ML
107static int download_image(struct usb_device *usbdev,
108 const struct firmware *firm,
109 loff_t pos, u32 img_len, u32 magic_num)
247e9cff
SA
110{
111 struct dn_header h;
112 int ret = 0;
113 u32 size;
247e9cff 114
1839c7eb
ML
115 size = ALIGN(img_len, DOWNLOAD_SIZE);
116 h.magic_num = __cpu_to_be32(magic_num);
117 h.file_size = __cpu_to_be32(size);
247e9cff
SA
118
119 ret = gdm_wibro_send(usbdev, &h, sizeof(h));
120 if (ret < 0)
1839c7eb 121 return ret;
247e9cff 122
1839c7eb
ML
123 while (img_len > 0) {
124 if (img_len > DOWNLOAD_SIZE)
125 size = DOWNLOAD_SIZE;
126 else
127 size = img_len; /* the last chunk of data */
247e9cff 128
1839c7eb
ML
129 memcpy(tx_buf, firm->data + pos, size);
130 ret = gdm_wibro_send(usbdev, tx_buf, size);
247e9cff 131
247e9cff 132 if (ret < 0)
1839c7eb 133 return ret;
247e9cff 134
1839c7eb
ML
135 img_len -= size;
136 pos += size;
247e9cff 137 }
247e9cff
SA
138
139 return ret;
140}
141
142int usb_boot(struct usb_device *usbdev, u16 pid)
143{
144 int i, ret = 0;
247e9cff
SA
145 struct img_header hdr;
146 struct fw_info fw_info;
147 loff_t pos = 0;
1839c7eb
ML
148 char *img_name = FW_DIR FW_UIMG;
149 const struct firmware *firm;
150
151 ret = request_firmware(&firm, img_name, &usbdev->dev);
152 if (ret < 0) {
153 printk(KERN_ERR
154 "requesting firmware %s failed with error %d\n",
155 img_name, ret);
156 return ret;
157 }
247e9cff
SA
158
159 tx_buf = kmalloc(DOWNLOAD_SIZE, GFP_KERNEL);
160 if (tx_buf == NULL) {
161 printk(KERN_ERR "Error: kmalloc\n");
162 return -ENOMEM;
163 }
164
1839c7eb 165 if (firm->size < sizeof(hdr)) {
247e9cff
SA
166 printk(KERN_ERR "gdmwm: Cannot read the image info.\n");
167 ret = -EIO;
168 goto out;
169 }
1839c7eb 170 memcpy(&hdr, firm->data, sizeof(hdr));
247e9cff
SA
171
172 array_le32_to_cpu((u32 *)&hdr, 19);
173#if 0
174 if (hdr.magic_code != 0x10767fff) {
175 printk(KERN_ERR "gdmwm: Invalid magic code 0x%08x\n",
176 hdr.magic_code);
177 ret = -EINVAL;
178 goto out;
179 }
180#endif
181 if (hdr.count > MAX_IMG_CNT) {
182 printk(KERN_ERR "gdmwm: Too many images. %d\n", hdr.count);
183 ret = -EINVAL;
184 goto out;
185 }
186
187 for (i = 0; i < hdr.count; i++) {
188 if (hdr.offset[i] > hdr.len) {
189 printk(KERN_ERR "gdmwm: Invalid offset. "
190 "Entry = %d Offset = 0x%08x "
191 "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];
1839c7eb 198 if (firm->size < sizeof(fw_info) + pos) {
247e9cff
SA
199 printk(KERN_ERR "gdmwm: Cannot read the FW info.\n");
200 ret = -EIO;
201 goto out;
202 }
1839c7eb 203 memcpy(&fw_info, firm->data + pos, sizeof(fw_info));
247e9cff
SA
204
205 array_le32_to_cpu((u32 *)&fw_info, 8);
206#if 0
207 if ((fw_info.id & 0xfffff000) != 0x10767000) {
208 printk(KERN_ERR "gdmwm: 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;
1839c7eb
ML
219 if (firm->size < fw_info.kernel_len + pos) {
220 printk(KERN_ERR "gdmwm: 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);
247e9cff
SA
226 if (ret < 0)
227 goto out;
228 printk(KERN_INFO "GCT: Kernel download success.\n");
229
230 pos = hdr.offset[i] + fw_info.rootfs_offset;
1839c7eb
ML
231 if (firm->size < fw_info.rootfs_len + pos) {
232 printk(KERN_ERR "gdmwm: Filesystem FW is too small.\n");
233 goto out;
234 }
235 ret = download_image(usbdev, firm, pos, fw_info.rootfs_len,
247e9cff
SA
236 DN_ROOTFS_MAGIC_NUMBER);
237 if (ret < 0)
238 goto out;
239 printk(KERN_INFO "GCT: Filesystem download success.\n");
240
241 break;
242 }
243
244 if (i == hdr.count) {
245 printk(KERN_ERR "Firmware for gsk%x is not installed.\n", pid);
246 ret = -EINVAL;
247 }
248out:
1839c7eb 249 release_firmware(firm);
247e9cff
SA
250 kfree(tx_buf);
251 return ret;
252}
253
254/*#define GDM7205_PADDING 256 */
255#define DOWNLOAD_CHUCK 2048
256#define KERNEL_TYPE_STRING "linux"
257#define FS_TYPE_STRING "rootfs"
258
259static int em_wait_ack(struct usb_device *usbdev, int send_zlp)
260{
261 int ack;
262 int ret = -1;
263
264 if (send_zlp) {
265 /*Send ZLP*/
266 ret = gdm_wibro_send(usbdev, NULL, 0);
267 if (ret < 0)
268 goto out;
269 }
270
271 /*Wait for ACK*/
272 ret = gdm_wibro_recv(usbdev, &ack, sizeof(ack));
273 if (ret < 0)
274 goto out;
275out:
276 return ret;
277}
278
3afcb91c 279static int em_download_image(struct usb_device *usbdev, const char *img_name,
247e9cff
SA
280 char *type_string)
281{
247e9cff
SA
282 char *buf = NULL;
283 loff_t pos = 0;
284 int ret = 0;
3afcb91c
ML
285 int len;
286 int img_len;
287 const struct firmware *firm;
247e9cff
SA
288 #if defined(GDM7205_PADDING)
289 const int pad_size = GDM7205_PADDING;
290 #else
291 const int pad_size = 0;
292 #endif
293
3afcb91c
ML
294 ret = request_firmware(&firm, img_name, &usbdev->dev);
295 if (ret < 0) {
296 printk(KERN_ERR
297 "requesting firmware %s failed with error %d\n",
298 img_name, ret);
299 return ret;
247e9cff
SA
300 }
301
302 buf = kmalloc(DOWNLOAD_CHUCK + pad_size, GFP_KERNEL);
303 if (buf == NULL) {
304 printk(KERN_ERR "Error: kmalloc\n");
305 return -ENOMEM;
306 }
307
308 strcpy(buf+pad_size, type_string);
309 ret = gdm_wibro_send(usbdev, buf, strlen(type_string)+pad_size);
310 if (ret < 0)
311 goto out;
312
3afcb91c 313 img_len = firm->size;
247e9cff 314
3afcb91c
ML
315 if (img_len <= 0) {
316 ret = -1;
317 goto out;
318 }
319
320 while (img_len > 0) {
321 if (img_len > DOWNLOAD_CHUCK)
322 len = DOWNLOAD_CHUCK;
323 else
324 len = img_len; /* the last chunk of data */
325
326 memcpy(buf+pad_size, firm->data + pos, len);
247e9cff 327 ret = gdm_wibro_send(usbdev, buf, len+pad_size);
3afcb91c 328
247e9cff
SA
329 if (ret < 0)
330 goto out;
331
3afcb91c
ML
332 img_len -= DOWNLOAD_CHUCK;
333 pos += DOWNLOAD_CHUCK;
334
247e9cff
SA
335 ret = em_wait_ack(usbdev, ((len+pad_size) % 512 == 0));
336 if (ret < 0)
337 goto out;
338 }
339
340 ret = em_wait_ack(usbdev, 1);
341 if (ret < 0)
342 goto out;
343
344out:
3afcb91c 345 release_firmware(firm);
247e9cff
SA
346 kfree(buf);
347
348 return ret;
349}
350
351static int em_fw_reset(struct usb_device *usbdev)
352{
353 int ret;
354
355 /*Send ZLP*/
356 ret = gdm_wibro_send(usbdev, NULL, 0);
357 return ret;
358}
359
360int usb_emergency(struct usb_device *usbdev)
361{
362 int ret;
3afcb91c
ML
363 const char *kern_name = FW_DIR FW_KERN;
364 const char *fs_name = FW_DIR FW_FS;
247e9cff 365
3afcb91c 366 ret = em_download_image(usbdev, kern_name, KERNEL_TYPE_STRING);
247e9cff 367 if (ret < 0)
3afcb91c 368 return ret;
247e9cff
SA
369 printk(KERN_INFO "GCT Emergency: Kernel download success.\n");
370
3afcb91c 371 ret = em_download_image(usbdev, fs_name, FS_TYPE_STRING);
247e9cff 372 if (ret < 0)
3afcb91c 373 return ret;
247e9cff
SA
374 printk(KERN_INFO "GCT Emergency: Filesystem download success.\n");
375
376 ret = em_fw_reset(usbdev);
3afcb91c 377
247e9cff
SA
378 return ret;
379}
This page took 0.105123 seconds and 5 git commands to generate.