usb_boot.c revision 3afcb91c418a9e50cfb79d7b6e28907d782e69d9
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 38struct dn_header { 39 u32 magic_num; 40 u32 file_size; 41}; 42 43struct 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 52struct 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 67static 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 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 107static int download_image(struct usb_device *usbdev, 108 const struct firmware *firm, 109 loff_t pos, u32 img_len, u32 magic_num) 110{ 111 struct dn_header h; 112 int ret = 0; 113 u32 size; 114 115 size = ALIGN(img_len, DOWNLOAD_SIZE); 116 h.magic_num = __cpu_to_be32(magic_num); 117 h.file_size = __cpu_to_be32(size); 118 119 ret = gdm_wibro_send(usbdev, &h, sizeof(h)); 120 if (ret < 0) 121 return ret; 122 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 */ 128 129 memcpy(tx_buf, firm->data + pos, size); 130 ret = gdm_wibro_send(usbdev, tx_buf, size); 131 132 if (ret < 0) 133 return ret; 134 135 img_len -= size; 136 pos += size; 137 } 138 139 return ret; 140} 141 142int usb_boot(struct usb_device *usbdev, u16 pid) 143{ 144 int i, ret = 0; 145 struct img_header hdr; 146 struct fw_info fw_info; 147 loff_t pos = 0; 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 } 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 165 if (firm->size < sizeof(hdr)) { 166 printk(KERN_ERR "gdmwm: Cannot read the image info.\n"); 167 ret = -EIO; 168 goto out; 169 } 170 memcpy(&hdr, firm->data, sizeof(hdr)); 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]; 198 if (firm->size < sizeof(fw_info) + pos) { 199 printk(KERN_ERR "gdmwm: 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 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; 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); 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; 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, 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: 249 release_firmware(firm); 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 279static int em_download_image(struct usb_device *usbdev, const char *img_name, 280 char *type_string) 281{ 282 char *buf = NULL; 283 loff_t pos = 0; 284 int ret = 0; 285 int len; 286 int img_len; 287 const struct firmware *firm; 288 #if defined(GDM7205_PADDING) 289 const int pad_size = GDM7205_PADDING; 290 #else 291 const int pad_size = 0; 292 #endif 293 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; 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 313 img_len = firm->size; 314 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); 327 ret = gdm_wibro_send(usbdev, buf, len+pad_size); 328 329 if (ret < 0) 330 goto out; 331 332 img_len -= DOWNLOAD_CHUCK; 333 pos += DOWNLOAD_CHUCK; 334 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: 345 release_firmware(firm); 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; 363 const char *kern_name = FW_DIR FW_KERN; 364 const char *fs_name = FW_DIR FW_FS; 365 366 ret = em_download_image(usbdev, kern_name, KERNEL_TYPE_STRING); 367 if (ret < 0) 368 return ret; 369 printk(KERN_INFO "GCT Emergency: Kernel download success.\n"); 370 371 ret = em_download_image(usbdev, fs_name, FS_TYPE_STRING); 372 if (ret < 0) 373 return ret; 374 printk(KERN_INFO "GCT Emergency: Filesystem download success.\n"); 375 376 ret = em_fw_reset(usbdev); 377 378 return ret; 379} 380