linux/drivers/staging/gdm72xx/usb_boot.c
<<
>>
Prefs
   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        __be32  magic_num;
  40        __be32  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
  71        for (i = 0; i < num; i++, arr++)
  72                le32_to_cpus(arr);
  73}
  74
  75static u8 *tx_buf;
  76
  77static 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,
  83                           &actual, 1000);
  84
  85        if (ret < 0) {
  86                dev_err(&usbdev->dev, "Error : usb_bulk_msg ( result = %d )\n",
  87                        ret);
  88                return ret;
  89        }
  90        return 0;
  91}
  92
  93static 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,
  99                           &actual, 5000);
 100
 101        if (ret < 0) {
 102                dev_err(&usbdev->dev,
 103                        "Error : usb_bulk_msg(recv) ( result = %d )\n", ret);
 104                return ret;
 105        }
 106        return 0;
 107}
 108
 109static int download_image(struct usb_device *usbdev,
 110                          const struct firmware *firm,
 111                          loff_t pos, u32 img_len, u32 magic_num)
 112{
 113        struct dn_header h;
 114        int ret = 0;
 115        u32 size;
 116
 117        size = ALIGN(img_len, DOWNLOAD_SIZE);
 118        h.magic_num = cpu_to_be32(magic_num);
 119        h.file_size = cpu_to_be32(size);
 120
 121        ret = gdm_wibro_send(usbdev, &h, sizeof(h));
 122        if (ret < 0)
 123                return ret;
 124
 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 */
 130
 131                memcpy(tx_buf, firm->data + pos, size);
 132                ret = gdm_wibro_send(usbdev, tx_buf, size);
 133
 134                if (ret < 0)
 135                        return ret;
 136
 137                img_len -= size;
 138                pos += size;
 139        }
 140
 141        return ret;
 142}
 143
 144int usb_boot(struct usb_device *usbdev, u16 pid)
 145{
 146        int i, ret = 0;
 147        struct img_header hdr;
 148        struct fw_info fw_info;
 149        loff_t pos = 0;
 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) {
 155                dev_err(&usbdev->dev,
 156                        "requesting firmware %s failed with error %d\n",
 157                        img_name, ret);
 158                return ret;
 159        }
 160
 161        tx_buf = kmalloc(DOWNLOAD_SIZE, GFP_KERNEL);
 162        if (!tx_buf) {
 163                release_firmware(firm);
 164                return -ENOMEM;
 165        }
 166
 167        if (firm->size < sizeof(hdr)) {
 168                dev_err(&usbdev->dev, "Cannot read the image info.\n");
 169                ret = -EIO;
 170                goto out;
 171        }
 172        memcpy(&hdr, firm->data, sizeof(hdr));
 173
 174        array_le32_to_cpu((u32 *)&hdr, 19);
 175
 176        if (hdr.count > MAX_IMG_CNT) {
 177                dev_err(&usbdev->dev, "Too many images. %d\n", hdr.count);
 178                ret = -EINVAL;
 179                goto out;
 180        }
 181
 182        for (i = 0; i < hdr.count; i++) {
 183                if (hdr.offset[i] > hdr.len) {
 184                        dev_err(&usbdev->dev,
 185                                "Invalid offset. Entry = %d Offset = 0x%08x Image length = 0x%08x\n",
 186                                i, hdr.offset[i], hdr.len);
 187                        ret = -EINVAL;
 188                        goto out;
 189                }
 190
 191                pos = hdr.offset[i];
 192                if (firm->size < sizeof(fw_info) + pos) {
 193                        dev_err(&usbdev->dev, "Cannot read the FW info.\n");
 194                        ret = -EIO;
 195                        goto out;
 196                }
 197                memcpy(&fw_info, firm->data + pos, sizeof(fw_info));
 198
 199                array_le32_to_cpu((u32 *)&fw_info, 8);
 200
 201                if ((fw_info.id & 0xffff) != pid)
 202                        continue;
 203
 204                pos = hdr.offset[i] + fw_info.kernel_offset;
 205                if (firm->size < fw_info.kernel_len + pos) {
 206                        dev_err(&usbdev->dev, "Kernel FW is too small.\n");
 207                        goto out;
 208                }
 209
 210                ret = download_image(usbdev, firm, pos, fw_info.kernel_len,
 211                                     DN_KERNEL_MAGIC_NUMBER);
 212                if (ret < 0)
 213                        goto out;
 214                dev_info(&usbdev->dev, "GCT: Kernel download success.\n");
 215
 216                pos = hdr.offset[i] + fw_info.rootfs_offset;
 217                if (firm->size < fw_info.rootfs_len + pos) {
 218                        dev_err(&usbdev->dev, "Filesystem FW is too small.\n");
 219                        goto out;
 220                }
 221                ret = download_image(usbdev, firm, pos, fw_info.rootfs_len,
 222                                     DN_ROOTFS_MAGIC_NUMBER);
 223                if (ret < 0)
 224                        goto out;
 225                dev_info(&usbdev->dev, "GCT: Filesystem download success.\n");
 226
 227                break;
 228        }
 229
 230        if (i == hdr.count) {
 231                dev_err(&usbdev->dev, "Firmware for gsk%x is not installed.\n",
 232                        pid);
 233                ret = -EINVAL;
 234        }
 235out:
 236        release_firmware(firm);
 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
 246static 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;
 262out:
 263        return ret;
 264}
 265
 266static int em_download_image(struct usb_device *usbdev, const char *img_name,
 267                             char *type_string)
 268{
 269        char *buf = NULL;
 270        loff_t pos = 0;
 271        int ret = 0;
 272        int len;
 273        int img_len;
 274        const struct firmware *firm;
 275        #if defined(GDM7205_PADDING)
 276        const int pad_size = GDM7205_PADDING;
 277        #else
 278        const int pad_size = 0;
 279        #endif
 280
 281        ret = request_firmware(&firm, img_name, &usbdev->dev);
 282        if (ret < 0) {
 283                dev_err(&usbdev->dev,
 284                        "requesting firmware %s failed with error %d\n",
 285                        img_name, ret);
 286                return ret;
 287        }
 288
 289        buf = kmalloc(DOWNLOAD_CHUCK + pad_size, GFP_KERNEL);
 290        if (!buf) {
 291                release_firmware(firm);
 292                return -ENOMEM;
 293        }
 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
 300        img_len = firm->size;
 301
 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);
 314                ret = gdm_wibro_send(usbdev, buf, len+pad_size);
 315
 316                if (ret < 0)
 317                        goto out;
 318
 319                img_len -= DOWNLOAD_CHUCK;
 320                pos += DOWNLOAD_CHUCK;
 321
 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
 331out:
 332        release_firmware(firm);
 333        kfree(buf);
 334
 335        return ret;
 336}
 337
 338static int em_fw_reset(struct usb_device *usbdev)
 339{
 340        /*Send ZLP*/
 341        return gdm_wibro_send(usbdev, NULL, 0);
 342}
 343
 344int usb_emergency(struct usb_device *usbdev)
 345{
 346        int ret;
 347        const char *kern_name = FW_DIR FW_KERN;
 348        const char *fs_name = FW_DIR FW_FS;
 349
 350        ret = em_download_image(usbdev, kern_name, KERNEL_TYPE_STRING);
 351        if (ret < 0)
 352                return ret;
 353        dev_err(&usbdev->dev, "GCT Emergency: Kernel download success.\n");
 354
 355        ret = em_download_image(usbdev, fs_name, FS_TYPE_STRING);
 356        if (ret < 0)
 357                return ret;
 358        dev_info(&usbdev->dev, "GCT Emergency: Filesystem download success.\n");
 359
 360        ret = em_fw_reset(usbdev);
 361
 362        return ret;
 363}
 364