usb_boot.c revision d67030d215ac1ec13cab16467904c2a7265e1fbd
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 22#include <asm/byteorder.h> 23#include "gdm_usb.h" 24#include "usb_boot.h" 25 26#define DN_KERNEL_MAGIC_NUMBER 0x10760001 27#define DN_ROOTFS_MAGIC_NUMBER 0x10760002 28 29#define DOWNLOAD_SIZE 1024 30 31#define DH2B(x) __cpu_to_be32(x) 32#define DL2H(x) __le32_to_cpu(x) 33 34#define MIN(a, b) ((a) > (b) ? (b) : (a)) 35 36#define MAX_IMG_CNT 16 37#define UIMG_PATH "/lib/firmware/gdm72xx/gdmuimg.bin" 38#define KERN_PATH "/lib/firmware/gdm72xx/zImage" 39#define FS_PATH "/lib/firmware/gdm72xx/ramdisk.jffs2" 40 41struct dn_header { 42 u32 magic_num; 43 u32 file_size; 44}; 45 46struct img_header { 47 u32 magic_code; 48 u32 count; 49 u32 len; 50 u32 offset[MAX_IMG_CNT]; 51 char hostname[32]; 52 char date[32]; 53}; 54 55struct fw_info { 56 u32 id; 57 u32 len; 58 u32 kernel_len; 59 u32 rootfs_len; 60 u32 kernel_offset; 61 u32 rootfs_offset; 62 u32 fw_ver; 63 u32 mac_ver; 64 char hostname[32]; 65 char userid[16]; 66 char date[32]; 67 char user_desc[128]; 68}; 69 70static void array_le32_to_cpu(u32 *arr, int num) 71{ 72 int i; 73 for (i = 0; i < num; i++, arr++) 74 *arr = DL2H(*arr); 75} 76 77static u8 *tx_buf; 78 79static int gdm_wibro_send(struct usb_device *usbdev, void *data, int len) 80{ 81 int ret; 82 int actual; 83 84 ret = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), data, len, 85 &actual, 1000); 86 87 if (ret < 0) { 88 printk(KERN_ERR "Error : usb_bulk_msg ( result = %d )\n", ret); 89 return ret; 90 } 91 return 0; 92} 93 94static int gdm_wibro_recv(struct usb_device *usbdev, void *data, int len) 95{ 96 int ret; 97 int actual; 98 99 ret = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, 2), data, len, 100 &actual, 5000); 101 102 if (ret < 0) { 103 printk(KERN_ERR "Error : usb_bulk_msg(recv) ( result = %d )\n", 104 ret); 105 return ret; 106 } 107 return 0; 108} 109 110static int download_image(struct usb_device *usbdev, struct file *filp, 111 loff_t *pos, u32 img_len, u32 magic_num) 112{ 113 struct dn_header h; 114 int ret = 0; 115 u32 size; 116 int len, readn; 117 118 size = (img_len + DOWNLOAD_SIZE - 1) & ~(DOWNLOAD_SIZE - 1); 119 h.magic_num = DH2B(magic_num); 120 h.file_size = DH2B(size); 121 122 ret = gdm_wibro_send(usbdev, &h, sizeof(h)); 123 if (ret < 0) 124 goto out; 125 126 readn = 0; 127 while ((len = filp->f_op->read(filp, tx_buf, DOWNLOAD_SIZE, pos))) { 128 129 if (len < 0) { 130 ret = -1; 131 goto out; 132 } 133 readn += len; 134 135 ret = gdm_wibro_send(usbdev, tx_buf, DOWNLOAD_SIZE); 136 if (ret < 0) 137 goto out; 138 if (readn >= img_len) 139 break; 140 } 141 142 if (readn < img_len) { 143 printk(KERN_ERR "gdmwm: Cannot read to the requested size. " 144 "Read = %d Requested = %d\n", readn, img_len); 145 ret = -EIO; 146 } 147out: 148 149 return ret; 150} 151 152int usb_boot(struct usb_device *usbdev, u16 pid) 153{ 154 int i, ret = 0; 155 struct file *filp = NULL; 156 struct inode *inode = NULL; 157 static mm_segment_t fs; 158 struct img_header hdr; 159 struct fw_info fw_info; 160 loff_t pos = 0; 161 char *img_name = UIMG_PATH; 162 int len; 163 164 tx_buf = kmalloc(DOWNLOAD_SIZE, GFP_KERNEL); 165 if (tx_buf == NULL) { 166 printk(KERN_ERR "Error: kmalloc\n"); 167 return -ENOMEM; 168 } 169 170 fs = get_fs(); 171 set_fs(get_ds()); 172 173 filp = filp_open(img_name, O_RDONLY | O_LARGEFILE, 0); 174 if (IS_ERR(filp)) { 175 printk(KERN_ERR "Can't find %s.\n", img_name); 176 set_fs(fs); 177 ret = PTR_ERR(filp); 178 goto restore_fs; 179 } 180 181 if (filp->f_dentry) 182 inode = filp->f_dentry->d_inode; 183 if (!inode || !S_ISREG(inode->i_mode)) { 184 printk(KERN_ERR "Invalid file type: %s\n", img_name); 185 ret = -EINVAL; 186 goto out; 187 } 188 189 len = filp->f_op->read(filp, (u8 *)&hdr, sizeof(hdr), &pos); 190 if (len != sizeof(hdr)) { 191 printk(KERN_ERR "gdmwm: Cannot read the image info.\n"); 192 ret = -EIO; 193 goto out; 194 } 195 196 array_le32_to_cpu((u32 *)&hdr, 19); 197#if 0 198 if (hdr.magic_code != 0x10767fff) { 199 printk(KERN_ERR "gdmwm: Invalid magic code 0x%08x\n", 200 hdr.magic_code); 201 ret = -EINVAL; 202 goto out; 203 } 204#endif 205 if (hdr.count > MAX_IMG_CNT) { 206 printk(KERN_ERR "gdmwm: Too many images. %d\n", hdr.count); 207 ret = -EINVAL; 208 goto out; 209 } 210 211 for (i = 0; i < hdr.count; i++) { 212 if (hdr.offset[i] > hdr.len) { 213 printk(KERN_ERR "gdmwm: Invalid offset. " 214 "Entry = %d Offset = 0x%08x " 215 "Image length = 0x%08x\n", 216 i, hdr.offset[i], hdr.len); 217 ret = -EINVAL; 218 goto out; 219 } 220 221 pos = hdr.offset[i]; 222 len = filp->f_op->read(filp, (u8 *)&fw_info, sizeof(fw_info), 223 &pos); 224 if (len != sizeof(fw_info)) { 225 printk(KERN_ERR "gdmwm: Cannot read the FW info.\n"); 226 ret = -EIO; 227 goto out; 228 } 229 230 array_le32_to_cpu((u32 *)&fw_info, 8); 231#if 0 232 if ((fw_info.id & 0xfffff000) != 0x10767000) { 233 printk(KERN_ERR "gdmwm: Invalid FW id. 0x%08x\n", 234 fw_info.id); 235 ret = -EIO; 236 goto out; 237 } 238#endif 239 240 if ((fw_info.id & 0xffff) != pid) 241 continue; 242 243 pos = hdr.offset[i] + fw_info.kernel_offset; 244 ret = download_image(usbdev, filp, &pos, fw_info.kernel_len, 245 DN_KERNEL_MAGIC_NUMBER); 246 if (ret < 0) 247 goto out; 248 printk(KERN_INFO "GCT: Kernel download success.\n"); 249 250 pos = hdr.offset[i] + fw_info.rootfs_offset; 251 ret = download_image(usbdev, filp, &pos, fw_info.rootfs_len, 252 DN_ROOTFS_MAGIC_NUMBER); 253 if (ret < 0) 254 goto out; 255 printk(KERN_INFO "GCT: Filesystem download success.\n"); 256 257 break; 258 } 259 260 if (i == hdr.count) { 261 printk(KERN_ERR "Firmware for gsk%x is not installed.\n", pid); 262 ret = -EINVAL; 263 } 264out: 265 filp_close(filp, current->files); 266 267restore_fs: 268 set_fs(fs); 269 kfree(tx_buf); 270 return ret; 271} 272 273/*#define GDM7205_PADDING 256 */ 274#define DOWNLOAD_CHUCK 2048 275#define KERNEL_TYPE_STRING "linux" 276#define FS_TYPE_STRING "rootfs" 277 278static int em_wait_ack(struct usb_device *usbdev, int send_zlp) 279{ 280 int ack; 281 int ret = -1; 282 283 if (send_zlp) { 284 /*Send ZLP*/ 285 ret = gdm_wibro_send(usbdev, NULL, 0); 286 if (ret < 0) 287 goto out; 288 } 289 290 /*Wait for ACK*/ 291 ret = gdm_wibro_recv(usbdev, &ack, sizeof(ack)); 292 if (ret < 0) 293 goto out; 294out: 295 return ret; 296} 297 298static int em_download_image(struct usb_device *usbdev, char *path, 299 char *type_string) 300{ 301 struct file *filp; 302 struct inode *inode; 303 static mm_segment_t fs; 304 char *buf = NULL; 305 loff_t pos = 0; 306 int ret = 0; 307 int len, readn = 0; 308 #if defined(GDM7205_PADDING) 309 const int pad_size = GDM7205_PADDING; 310 #else 311 const int pad_size = 0; 312 #endif 313 314 fs = get_fs(); 315 set_fs(get_ds()); 316 317 filp = filp_open(path, O_RDONLY | O_LARGEFILE, 0); 318 if (IS_ERR(filp)) { 319 printk(KERN_ERR "Can't find %s.\n", path); 320 set_fs(fs); 321 ret = -ENOENT; 322 goto restore_fs; 323 } 324 325 if (filp->f_dentry) { 326 inode = filp->f_dentry->d_inode; 327 if (!inode || !S_ISREG(inode->i_mode)) { 328 printk(KERN_ERR "Invalid file type: %s\n", path); 329 ret = -EINVAL; 330 goto out; 331 } 332 } 333 334 buf = kmalloc(DOWNLOAD_CHUCK + pad_size, GFP_KERNEL); 335 if (buf == NULL) { 336 printk(KERN_ERR "Error: kmalloc\n"); 337 return -ENOMEM; 338 } 339 340 strcpy(buf+pad_size, type_string); 341 ret = gdm_wibro_send(usbdev, buf, strlen(type_string)+pad_size); 342 if (ret < 0) 343 goto out; 344 345 while ((len = filp->f_op->read(filp, buf+pad_size, DOWNLOAD_CHUCK, 346 &pos))) { 347 if (len < 0) { 348 ret = -1; 349 goto out; 350 } 351 readn += len; 352 353 ret = gdm_wibro_send(usbdev, buf, len+pad_size); 354 if (ret < 0) 355 goto out; 356 357 ret = em_wait_ack(usbdev, ((len+pad_size) % 512 == 0)); 358 if (ret < 0) 359 goto out; 360 } 361 362 ret = em_wait_ack(usbdev, 1); 363 if (ret < 0) 364 goto out; 365 366out: 367 filp_close(filp, current->files); 368 369restore_fs: 370 set_fs(fs); 371 372 kfree(buf); 373 374 return ret; 375} 376 377static int em_fw_reset(struct usb_device *usbdev) 378{ 379 int ret; 380 381 /*Send ZLP*/ 382 ret = gdm_wibro_send(usbdev, NULL, 0); 383 return ret; 384} 385 386int usb_emergency(struct usb_device *usbdev) 387{ 388 int ret; 389 390 ret = em_download_image(usbdev, KERN_PATH, KERNEL_TYPE_STRING); 391 if (ret < 0) 392 goto out; 393 printk(KERN_INFO "GCT Emergency: Kernel download success.\n"); 394 395 ret = em_download_image(usbdev, FS_PATH, FS_TYPE_STRING); 396 if (ret < 0) 397 goto out; 398 printk(KERN_INFO "GCT Emergency: Filesystem download success.\n"); 399 400 ret = em_fw_reset(usbdev); 401out: 402 return ret; 403} 404