Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 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> |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 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> |
Macpaul Lin | 1839c7eb | 2012-09-13 18:11:55 +0800 | [diff] [blame] | 21 | #include <linux/firmware.h> |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 22 | |
| 23 | #include <asm/byteorder.h> |
| 24 | #include "gdm_usb.h" |
| 25 | #include "usb_boot.h" |
| 26 | |
Macpaul Lin | 443242d | 2012-09-13 18:11:54 +0800 | [diff] [blame] | 27 | #define DN_KERNEL_MAGIC_NUMBER 0x10760001 |
| 28 | #define DN_ROOTFS_MAGIC_NUMBER 0x10760002 |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 29 | |
Macpaul Lin | 443242d | 2012-09-13 18:11:54 +0800 | [diff] [blame] | 30 | #define DOWNLOAD_SIZE 1024 |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 31 | |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 32 | #define MAX_IMG_CNT 16 |
Macpaul Lin | 1839c7eb | 2012-09-13 18:11:55 +0800 | [diff] [blame] | 33 | #define FW_DIR "gdm72xx/" |
| 34 | #define FW_UIMG "gdmuimg.bin" |
Macpaul Lin | 3afcb91 | 2012-09-14 14:02:04 +0800 | [diff] [blame] | 35 | #define FW_KERN "zImage" |
| 36 | #define FW_FS "ramdisk.jffs2" |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 37 | |
| 38 | struct dn_header { |
Ben Chan | a8a175d | 2014-06-30 23:32:27 -0700 | [diff] [blame] | 39 | __be32 magic_num; |
| 40 | __be32 file_size; |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 41 | }; |
| 42 | |
| 43 | struct img_header { |
Macpaul Lin | 443242d | 2012-09-13 18:11:54 +0800 | [diff] [blame] | 44 | u32 magic_code; |
| 45 | u32 count; |
| 46 | u32 len; |
| 47 | u32 offset[MAX_IMG_CNT]; |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 48 | char hostname[32]; |
| 49 | char date[32]; |
| 50 | }; |
| 51 | |
| 52 | struct fw_info { |
Macpaul Lin | 443242d | 2012-09-13 18:11:54 +0800 | [diff] [blame] | 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; |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 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; |
Michalis Pappas | 39c511f | 2014-05-09 18:08:27 +0800 | [diff] [blame] | 70 | |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 71 | for (i = 0; i < num; i++, arr++) |
Ben Chan | a8a175d | 2014-06-30 23:32:27 -0700 | [diff] [blame] | 72 | le32_to_cpus(arr); |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 73 | } |
| 74 | |
| 75 | static u8 *tx_buf; |
| 76 | |
| 77 | static int gdm_wibro_send(struct usb_device *usbdev, void *data, int len) |
| 78 | { |
| 79 | int ret; |
| 80 | int actual; |
| 81 | |
| 82 | ret = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), data, len, |
Michalis Pappas | 39c511f | 2014-05-09 18:08:27 +0800 | [diff] [blame] | 83 | &actual, 1000); |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 84 | |
| 85 | if (ret < 0) { |
YAMANE Toshiaki | 3800178 | 2012-10-29 20:05:57 +0900 | [diff] [blame] | 86 | dev_err(&usbdev->dev, "Error : usb_bulk_msg ( result = %d )\n", |
| 87 | ret); |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 88 | return ret; |
| 89 | } |
| 90 | return 0; |
| 91 | } |
| 92 | |
| 93 | static int gdm_wibro_recv(struct usb_device *usbdev, void *data, int len) |
| 94 | { |
| 95 | int ret; |
| 96 | int actual; |
| 97 | |
| 98 | ret = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, 2), data, len, |
Michalis Pappas | 39c511f | 2014-05-09 18:08:27 +0800 | [diff] [blame] | 99 | &actual, 5000); |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 100 | |
| 101 | if (ret < 0) { |
YAMANE Toshiaki | 3800178 | 2012-10-29 20:05:57 +0900 | [diff] [blame] | 102 | dev_err(&usbdev->dev, |
| 103 | "Error : usb_bulk_msg(recv) ( result = %d )\n", ret); |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 104 | return ret; |
| 105 | } |
| 106 | return 0; |
| 107 | } |
| 108 | |
Davide Gianforte | a3709f7 | 2014-05-23 22:06:44 +0200 | [diff] [blame] | 109 | static int download_image(struct usb_device *usbdev, |
| 110 | const struct firmware *firm, |
Michalis Pappas | 39c511f | 2014-05-09 18:08:27 +0800 | [diff] [blame] | 111 | loff_t pos, u32 img_len, u32 magic_num) |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 112 | { |
| 113 | struct dn_header h; |
| 114 | int ret = 0; |
| 115 | u32 size; |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 116 | |
Macpaul Lin | 1839c7eb | 2012-09-13 18:11:55 +0800 | [diff] [blame] | 117 | size = ALIGN(img_len, DOWNLOAD_SIZE); |
Ben Chan | a8a175d | 2014-06-30 23:32:27 -0700 | [diff] [blame] | 118 | h.magic_num = cpu_to_be32(magic_num); |
| 119 | h.file_size = cpu_to_be32(size); |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 120 | |
| 121 | ret = gdm_wibro_send(usbdev, &h, sizeof(h)); |
| 122 | if (ret < 0) |
Macpaul Lin | 1839c7eb | 2012-09-13 18:11:55 +0800 | [diff] [blame] | 123 | return ret; |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 124 | |
Macpaul Lin | 1839c7eb | 2012-09-13 18:11:55 +0800 | [diff] [blame] | 125 | while (img_len > 0) { |
| 126 | if (img_len > DOWNLOAD_SIZE) |
| 127 | size = DOWNLOAD_SIZE; |
| 128 | else |
| 129 | size = img_len; /* the last chunk of data */ |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 130 | |
Macpaul Lin | 1839c7eb | 2012-09-13 18:11:55 +0800 | [diff] [blame] | 131 | memcpy(tx_buf, firm->data + pos, size); |
| 132 | ret = gdm_wibro_send(usbdev, tx_buf, size); |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 133 | |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 134 | if (ret < 0) |
Macpaul Lin | 1839c7eb | 2012-09-13 18:11:55 +0800 | [diff] [blame] | 135 | return ret; |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 136 | |
Macpaul Lin | 1839c7eb | 2012-09-13 18:11:55 +0800 | [diff] [blame] | 137 | img_len -= size; |
| 138 | pos += size; |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 139 | } |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 140 | |
| 141 | return ret; |
| 142 | } |
| 143 | |
| 144 | int usb_boot(struct usb_device *usbdev, u16 pid) |
| 145 | { |
| 146 | int i, ret = 0; |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 147 | struct img_header hdr; |
| 148 | struct fw_info fw_info; |
| 149 | loff_t pos = 0; |
Macpaul Lin | 1839c7eb | 2012-09-13 18:11:55 +0800 | [diff] [blame] | 150 | char *img_name = FW_DIR FW_UIMG; |
| 151 | const struct firmware *firm; |
| 152 | |
| 153 | ret = request_firmware(&firm, img_name, &usbdev->dev); |
| 154 | if (ret < 0) { |
YAMANE Toshiaki | 3800178 | 2012-10-29 20:05:57 +0900 | [diff] [blame] | 155 | dev_err(&usbdev->dev, |
| 156 | "requesting firmware %s failed with error %d\n", |
Macpaul Lin | 1839c7eb | 2012-09-13 18:11:55 +0800 | [diff] [blame] | 157 | img_name, ret); |
| 158 | return ret; |
| 159 | } |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 160 | |
| 161 | tx_buf = kmalloc(DOWNLOAD_SIZE, GFP_KERNEL); |
Sudip Mukherjee | c32bb25 | 2015-09-07 18:08:22 +0530 | [diff] [blame] | 162 | if (!tx_buf) { |
Sudip Mukherjee | ea022df1 | 2015-09-07 18:08:21 +0530 | [diff] [blame] | 163 | release_firmware(firm); |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 164 | return -ENOMEM; |
Sudip Mukherjee | ea022df1 | 2015-09-07 18:08:21 +0530 | [diff] [blame] | 165 | } |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 166 | |
Macpaul Lin | 1839c7eb | 2012-09-13 18:11:55 +0800 | [diff] [blame] | 167 | if (firm->size < sizeof(hdr)) { |
YAMANE Toshiaki | 3800178 | 2012-10-29 20:05:57 +0900 | [diff] [blame] | 168 | dev_err(&usbdev->dev, "Cannot read the image info.\n"); |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 169 | ret = -EIO; |
| 170 | goto out; |
| 171 | } |
Macpaul Lin | 1839c7eb | 2012-09-13 18:11:55 +0800 | [diff] [blame] | 172 | memcpy(&hdr, firm->data, sizeof(hdr)); |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 173 | |
| 174 | array_le32_to_cpu((u32 *)&hdr, 19); |
Michalis Pappas | b268666 | 2014-05-09 18:08:26 +0800 | [diff] [blame] | 175 | |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 176 | if (hdr.count > MAX_IMG_CNT) { |
YAMANE Toshiaki | 3800178 | 2012-10-29 20:05:57 +0900 | [diff] [blame] | 177 | dev_err(&usbdev->dev, "Too many images. %d\n", hdr.count); |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 178 | ret = -EINVAL; |
| 179 | goto out; |
| 180 | } |
| 181 | |
| 182 | for (i = 0; i < hdr.count; i++) { |
| 183 | if (hdr.offset[i] > hdr.len) { |
YAMANE Toshiaki | 3800178 | 2012-10-29 20:05:57 +0900 | [diff] [blame] | 184 | dev_err(&usbdev->dev, |
| 185 | "Invalid offset. Entry = %d Offset = 0x%08x Image length = 0x%08x\n", |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 186 | i, hdr.offset[i], hdr.len); |
| 187 | ret = -EINVAL; |
| 188 | goto out; |
| 189 | } |
| 190 | |
| 191 | pos = hdr.offset[i]; |
Macpaul Lin | 1839c7eb | 2012-09-13 18:11:55 +0800 | [diff] [blame] | 192 | if (firm->size < sizeof(fw_info) + pos) { |
YAMANE Toshiaki | 3800178 | 2012-10-29 20:05:57 +0900 | [diff] [blame] | 193 | dev_err(&usbdev->dev, "Cannot read the FW info.\n"); |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 194 | ret = -EIO; |
| 195 | goto out; |
| 196 | } |
Macpaul Lin | 1839c7eb | 2012-09-13 18:11:55 +0800 | [diff] [blame] | 197 | memcpy(&fw_info, firm->data + pos, sizeof(fw_info)); |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 198 | |
| 199 | array_le32_to_cpu((u32 *)&fw_info, 8); |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 200 | |
| 201 | if ((fw_info.id & 0xffff) != pid) |
| 202 | continue; |
| 203 | |
| 204 | pos = hdr.offset[i] + fw_info.kernel_offset; |
Macpaul Lin | 1839c7eb | 2012-09-13 18:11:55 +0800 | [diff] [blame] | 205 | if (firm->size < fw_info.kernel_len + pos) { |
YAMANE Toshiaki | 3800178 | 2012-10-29 20:05:57 +0900 | [diff] [blame] | 206 | dev_err(&usbdev->dev, "Kernel FW is too small.\n"); |
Macpaul Lin | 1839c7eb | 2012-09-13 18:11:55 +0800 | [diff] [blame] | 207 | goto out; |
| 208 | } |
| 209 | |
Michalis Pappas | 39c511f | 2014-05-09 18:08:27 +0800 | [diff] [blame] | 210 | ret = download_image(usbdev, firm, pos, fw_info.kernel_len, |
| 211 | DN_KERNEL_MAGIC_NUMBER); |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 212 | if (ret < 0) |
| 213 | goto out; |
YAMANE Toshiaki | 3800178 | 2012-10-29 20:05:57 +0900 | [diff] [blame] | 214 | dev_info(&usbdev->dev, "GCT: Kernel download success.\n"); |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 215 | |
| 216 | pos = hdr.offset[i] + fw_info.rootfs_offset; |
Macpaul Lin | 1839c7eb | 2012-09-13 18:11:55 +0800 | [diff] [blame] | 217 | if (firm->size < fw_info.rootfs_len + pos) { |
YAMANE Toshiaki | 3800178 | 2012-10-29 20:05:57 +0900 | [diff] [blame] | 218 | dev_err(&usbdev->dev, "Filesystem FW is too small.\n"); |
Macpaul Lin | 1839c7eb | 2012-09-13 18:11:55 +0800 | [diff] [blame] | 219 | goto out; |
| 220 | } |
| 221 | ret = download_image(usbdev, firm, pos, fw_info.rootfs_len, |
Michalis Pappas | 39c511f | 2014-05-09 18:08:27 +0800 | [diff] [blame] | 222 | DN_ROOTFS_MAGIC_NUMBER); |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 223 | if (ret < 0) |
| 224 | goto out; |
YAMANE Toshiaki | 3800178 | 2012-10-29 20:05:57 +0900 | [diff] [blame] | 225 | dev_info(&usbdev->dev, "GCT: Filesystem download success.\n"); |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 226 | |
| 227 | break; |
| 228 | } |
| 229 | |
| 230 | if (i == hdr.count) { |
YAMANE Toshiaki | 3800178 | 2012-10-29 20:05:57 +0900 | [diff] [blame] | 231 | dev_err(&usbdev->dev, "Firmware for gsk%x is not installed.\n", |
| 232 | pid); |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 233 | ret = -EINVAL; |
| 234 | } |
| 235 | out: |
Macpaul Lin | 1839c7eb | 2012-09-13 18:11:55 +0800 | [diff] [blame] | 236 | release_firmware(firm); |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 237 | kfree(tx_buf); |
| 238 | return ret; |
| 239 | } |
| 240 | |
| 241 | /*#define GDM7205_PADDING 256 */ |
| 242 | #define DOWNLOAD_CHUCK 2048 |
| 243 | #define KERNEL_TYPE_STRING "linux" |
| 244 | #define FS_TYPE_STRING "rootfs" |
| 245 | |
| 246 | static int em_wait_ack(struct usb_device *usbdev, int send_zlp) |
| 247 | { |
| 248 | int ack; |
| 249 | int ret = -1; |
| 250 | |
| 251 | if (send_zlp) { |
| 252 | /*Send ZLP*/ |
| 253 | ret = gdm_wibro_send(usbdev, NULL, 0); |
| 254 | if (ret < 0) |
| 255 | goto out; |
| 256 | } |
| 257 | |
| 258 | /*Wait for ACK*/ |
| 259 | ret = gdm_wibro_recv(usbdev, &ack, sizeof(ack)); |
| 260 | if (ret < 0) |
| 261 | goto out; |
| 262 | out: |
| 263 | return ret; |
| 264 | } |
| 265 | |
Macpaul Lin | 3afcb91 | 2012-09-14 14:02:04 +0800 | [diff] [blame] | 266 | static int em_download_image(struct usb_device *usbdev, const char *img_name, |
Michalis Pappas | 39c511f | 2014-05-09 18:08:27 +0800 | [diff] [blame] | 267 | char *type_string) |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 268 | { |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 269 | char *buf = NULL; |
| 270 | loff_t pos = 0; |
| 271 | int ret = 0; |
Macpaul Lin | 3afcb91 | 2012-09-14 14:02:04 +0800 | [diff] [blame] | 272 | int len; |
| 273 | int img_len; |
| 274 | const struct firmware *firm; |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 275 | #if defined(GDM7205_PADDING) |
| 276 | const int pad_size = GDM7205_PADDING; |
| 277 | #else |
| 278 | const int pad_size = 0; |
| 279 | #endif |
| 280 | |
Macpaul Lin | 3afcb91 | 2012-09-14 14:02:04 +0800 | [diff] [blame] | 281 | ret = request_firmware(&firm, img_name, &usbdev->dev); |
| 282 | if (ret < 0) { |
YAMANE Toshiaki | 3800178 | 2012-10-29 20:05:57 +0900 | [diff] [blame] | 283 | dev_err(&usbdev->dev, |
| 284 | "requesting firmware %s failed with error %d\n", |
Macpaul Lin | 3afcb91 | 2012-09-14 14:02:04 +0800 | [diff] [blame] | 285 | img_name, ret); |
| 286 | return ret; |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 287 | } |
| 288 | |
| 289 | buf = kmalloc(DOWNLOAD_CHUCK + pad_size, GFP_KERNEL); |
Sudip Mukherjee | c32bb25 | 2015-09-07 18:08:22 +0530 | [diff] [blame] | 290 | if (!buf) { |
Sudip Mukherjee | ea022df1 | 2015-09-07 18:08:21 +0530 | [diff] [blame] | 291 | release_firmware(firm); |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 292 | return -ENOMEM; |
Sudip Mukherjee | ea022df1 | 2015-09-07 18:08:21 +0530 | [diff] [blame] | 293 | } |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 294 | |
| 295 | strcpy(buf+pad_size, type_string); |
| 296 | ret = gdm_wibro_send(usbdev, buf, strlen(type_string)+pad_size); |
| 297 | if (ret < 0) |
| 298 | goto out; |
| 299 | |
Macpaul Lin | 3afcb91 | 2012-09-14 14:02:04 +0800 | [diff] [blame] | 300 | img_len = firm->size; |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 301 | |
Macpaul Lin | 3afcb91 | 2012-09-14 14:02:04 +0800 | [diff] [blame] | 302 | if (img_len <= 0) { |
| 303 | ret = -1; |
| 304 | goto out; |
| 305 | } |
| 306 | |
| 307 | while (img_len > 0) { |
| 308 | if (img_len > DOWNLOAD_CHUCK) |
| 309 | len = DOWNLOAD_CHUCK; |
| 310 | else |
| 311 | len = img_len; /* the last chunk of data */ |
| 312 | |
| 313 | memcpy(buf+pad_size, firm->data + pos, len); |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 314 | ret = gdm_wibro_send(usbdev, buf, len+pad_size); |
Macpaul Lin | 3afcb91 | 2012-09-14 14:02:04 +0800 | [diff] [blame] | 315 | |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 316 | if (ret < 0) |
| 317 | goto out; |
| 318 | |
Macpaul Lin | 3afcb91 | 2012-09-14 14:02:04 +0800 | [diff] [blame] | 319 | img_len -= DOWNLOAD_CHUCK; |
| 320 | pos += DOWNLOAD_CHUCK; |
| 321 | |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 322 | ret = em_wait_ack(usbdev, ((len+pad_size) % 512 == 0)); |
| 323 | if (ret < 0) |
| 324 | goto out; |
| 325 | } |
| 326 | |
| 327 | ret = em_wait_ack(usbdev, 1); |
| 328 | if (ret < 0) |
| 329 | goto out; |
| 330 | |
| 331 | out: |
Macpaul Lin | 3afcb91 | 2012-09-14 14:02:04 +0800 | [diff] [blame] | 332 | release_firmware(firm); |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 333 | kfree(buf); |
| 334 | |
| 335 | return ret; |
| 336 | } |
| 337 | |
| 338 | static int em_fw_reset(struct usb_device *usbdev) |
| 339 | { |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 340 | /*Send ZLP*/ |
Gengis Dave | 8a5e7b0 | 2014-05-23 01:07:13 +0200 | [diff] [blame] | 341 | return gdm_wibro_send(usbdev, NULL, 0); |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 342 | } |
| 343 | |
| 344 | int usb_emergency(struct usb_device *usbdev) |
| 345 | { |
| 346 | int ret; |
Macpaul Lin | 3afcb91 | 2012-09-14 14:02:04 +0800 | [diff] [blame] | 347 | const char *kern_name = FW_DIR FW_KERN; |
| 348 | const char *fs_name = FW_DIR FW_FS; |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 349 | |
Macpaul Lin | 3afcb91 | 2012-09-14 14:02:04 +0800 | [diff] [blame] | 350 | ret = em_download_image(usbdev, kern_name, KERNEL_TYPE_STRING); |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 351 | if (ret < 0) |
Macpaul Lin | 3afcb91 | 2012-09-14 14:02:04 +0800 | [diff] [blame] | 352 | return ret; |
YAMANE Toshiaki | 3800178 | 2012-10-29 20:05:57 +0900 | [diff] [blame] | 353 | dev_err(&usbdev->dev, "GCT Emergency: Kernel download success.\n"); |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 354 | |
Macpaul Lin | 3afcb91 | 2012-09-14 14:02:04 +0800 | [diff] [blame] | 355 | ret = em_download_image(usbdev, fs_name, FS_TYPE_STRING); |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 356 | if (ret < 0) |
Macpaul Lin | 3afcb91 | 2012-09-14 14:02:04 +0800 | [diff] [blame] | 357 | return ret; |
YAMANE Toshiaki | 3800178 | 2012-10-29 20:05:57 +0900 | [diff] [blame] | 358 | dev_info(&usbdev->dev, "GCT Emergency: Filesystem download success.\n"); |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 359 | |
| 360 | ret = em_fw_reset(usbdev); |
Macpaul Lin | 3afcb91 | 2012-09-14 14:02:04 +0800 | [diff] [blame] | 361 | |
Sage Ahn | 247e9cf | 2012-05-15 13:20:36 +0900 | [diff] [blame] | 362 | return ret; |
| 363 | } |