123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- /*
- * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #include <linux/uaccess.h>
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/mm.h>
- #include <linux/usb.h>
- #include <linux/unistd.h>
- #include <linux/slab.h>
- #include <linux/firmware.h>
- #include <asm/byteorder.h>
- #include "gdm_usb.h"
- #include "usb_boot.h"
- #define DN_KERNEL_MAGIC_NUMBER 0x10760001
- #define DN_ROOTFS_MAGIC_NUMBER 0x10760002
- #define DOWNLOAD_SIZE 1024
- #define MAX_IMG_CNT 16
- #define FW_DIR "gdm72xx/"
- #define FW_UIMG "gdmuimg.bin"
- #define FW_KERN "zImage"
- #define FW_FS "ramdisk.jffs2"
- struct dn_header {
- __be32 magic_num;
- __be32 file_size;
- };
- struct img_header {
- u32 magic_code;
- u32 count;
- u32 len;
- u32 offset[MAX_IMG_CNT];
- char hostname[32];
- char date[32];
- };
- struct fw_info {
- u32 id;
- u32 len;
- u32 kernel_len;
- u32 rootfs_len;
- u32 kernel_offset;
- u32 rootfs_offset;
- u32 fw_ver;
- u32 mac_ver;
- char hostname[32];
- char userid[16];
- char date[32];
- char user_desc[128];
- };
- static void array_le32_to_cpu(u32 *arr, int num)
- {
- int i;
- for (i = 0; i < num; i++, arr++)
- le32_to_cpus(arr);
- }
- static u8 *tx_buf;
- static int gdm_wibro_send(struct usb_device *usbdev, void *data, int len)
- {
- int ret;
- int actual;
- ret = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), data, len,
- &actual, 1000);
- if (ret < 0) {
- dev_err(&usbdev->dev, "Error : usb_bulk_msg ( result = %d )\n",
- ret);
- return ret;
- }
- return 0;
- }
- static int gdm_wibro_recv(struct usb_device *usbdev, void *data, int len)
- {
- int ret;
- int actual;
- ret = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, 2), data, len,
- &actual, 5000);
- if (ret < 0) {
- dev_err(&usbdev->dev,
- "Error : usb_bulk_msg(recv) ( result = %d )\n", ret);
- return ret;
- }
- return 0;
- }
- static int download_image(struct usb_device *usbdev,
- const struct firmware *firm,
- loff_t pos, u32 img_len, u32 magic_num)
- {
- struct dn_header h;
- int ret = 0;
- u32 size;
- size = ALIGN(img_len, DOWNLOAD_SIZE);
- h.magic_num = cpu_to_be32(magic_num);
- h.file_size = cpu_to_be32(size);
- ret = gdm_wibro_send(usbdev, &h, sizeof(h));
- if (ret < 0)
- return ret;
- while (img_len > 0) {
- if (img_len > DOWNLOAD_SIZE)
- size = DOWNLOAD_SIZE;
- else
- size = img_len; /* the last chunk of data */
- memcpy(tx_buf, firm->data + pos, size);
- ret = gdm_wibro_send(usbdev, tx_buf, size);
- if (ret < 0)
- return ret;
- img_len -= size;
- pos += size;
- }
- return ret;
- }
- int usb_boot(struct usb_device *usbdev, u16 pid)
- {
- int i, ret = 0;
- struct img_header hdr;
- struct fw_info fw_info;
- loff_t pos = 0;
- char *img_name = FW_DIR FW_UIMG;
- const struct firmware *firm;
- ret = request_firmware(&firm, img_name, &usbdev->dev);
- if (ret < 0) {
- dev_err(&usbdev->dev,
- "requesting firmware %s failed with error %d\n",
- img_name, ret);
- return ret;
- }
- tx_buf = kmalloc(DOWNLOAD_SIZE, GFP_KERNEL);
- if (!tx_buf) {
- release_firmware(firm);
- return -ENOMEM;
- }
- if (firm->size < sizeof(hdr)) {
- dev_err(&usbdev->dev, "Cannot read the image info.\n");
- ret = -EIO;
- goto out;
- }
- memcpy(&hdr, firm->data, sizeof(hdr));
- array_le32_to_cpu((u32 *)&hdr, 19);
- if (hdr.count > MAX_IMG_CNT) {
- dev_err(&usbdev->dev, "Too many images. %d\n", hdr.count);
- ret = -EINVAL;
- goto out;
- }
- for (i = 0; i < hdr.count; i++) {
- if (hdr.offset[i] > hdr.len) {
- dev_err(&usbdev->dev,
- "Invalid offset. Entry = %d Offset = 0x%08x Image length = 0x%08x\n",
- i, hdr.offset[i], hdr.len);
- ret = -EINVAL;
- goto out;
- }
- pos = hdr.offset[i];
- if (firm->size < sizeof(fw_info) + pos) {
- dev_err(&usbdev->dev, "Cannot read the FW info.\n");
- ret = -EIO;
- goto out;
- }
- memcpy(&fw_info, firm->data + pos, sizeof(fw_info));
- array_le32_to_cpu((u32 *)&fw_info, 8);
- if ((fw_info.id & 0xffff) != pid)
- continue;
- pos = hdr.offset[i] + fw_info.kernel_offset;
- if (firm->size < fw_info.kernel_len + pos) {
- dev_err(&usbdev->dev, "Kernel FW is too small.\n");
- goto out;
- }
- ret = download_image(usbdev, firm, pos, fw_info.kernel_len,
- DN_KERNEL_MAGIC_NUMBER);
- if (ret < 0)
- goto out;
- dev_info(&usbdev->dev, "GCT: Kernel download success.\n");
- pos = hdr.offset[i] + fw_info.rootfs_offset;
- if (firm->size < fw_info.rootfs_len + pos) {
- dev_err(&usbdev->dev, "Filesystem FW is too small.\n");
- goto out;
- }
- ret = download_image(usbdev, firm, pos, fw_info.rootfs_len,
- DN_ROOTFS_MAGIC_NUMBER);
- if (ret < 0)
- goto out;
- dev_info(&usbdev->dev, "GCT: Filesystem download success.\n");
- break;
- }
- if (i == hdr.count) {
- dev_err(&usbdev->dev, "Firmware for gsk%x is not installed.\n",
- pid);
- ret = -EINVAL;
- }
- out:
- release_firmware(firm);
- kfree(tx_buf);
- return ret;
- }
- /*#define GDM7205_PADDING 256 */
- #define DOWNLOAD_CHUCK 2048
- #define KERNEL_TYPE_STRING "linux"
- #define FS_TYPE_STRING "rootfs"
- static int em_wait_ack(struct usb_device *usbdev, int send_zlp)
- {
- int ack;
- int ret = -1;
- if (send_zlp) {
- /*Send ZLP*/
- ret = gdm_wibro_send(usbdev, NULL, 0);
- if (ret < 0)
- goto out;
- }
- /*Wait for ACK*/
- ret = gdm_wibro_recv(usbdev, &ack, sizeof(ack));
- if (ret < 0)
- goto out;
- out:
- return ret;
- }
- static int em_download_image(struct usb_device *usbdev, const char *img_name,
- char *type_string)
- {
- char *buf = NULL;
- loff_t pos = 0;
- int ret = 0;
- int len;
- int img_len;
- const struct firmware *firm;
- #if defined(GDM7205_PADDING)
- const int pad_size = GDM7205_PADDING;
- #else
- const int pad_size = 0;
- #endif
- ret = request_firmware(&firm, img_name, &usbdev->dev);
- if (ret < 0) {
- dev_err(&usbdev->dev,
- "requesting firmware %s failed with error %d\n",
- img_name, ret);
- return ret;
- }
- buf = kmalloc(DOWNLOAD_CHUCK + pad_size, GFP_KERNEL);
- if (!buf) {
- release_firmware(firm);
- return -ENOMEM;
- }
- strcpy(buf+pad_size, type_string);
- ret = gdm_wibro_send(usbdev, buf, strlen(type_string)+pad_size);
- if (ret < 0)
- goto out;
- img_len = firm->size;
- if (img_len <= 0) {
- ret = -1;
- goto out;
- }
- while (img_len > 0) {
- if (img_len > DOWNLOAD_CHUCK)
- len = DOWNLOAD_CHUCK;
- else
- len = img_len; /* the last chunk of data */
- memcpy(buf+pad_size, firm->data + pos, len);
- ret = gdm_wibro_send(usbdev, buf, len+pad_size);
- if (ret < 0)
- goto out;
- img_len -= DOWNLOAD_CHUCK;
- pos += DOWNLOAD_CHUCK;
- ret = em_wait_ack(usbdev, ((len+pad_size) % 512 == 0));
- if (ret < 0)
- goto out;
- }
- ret = em_wait_ack(usbdev, 1);
- if (ret < 0)
- goto out;
- out:
- release_firmware(firm);
- kfree(buf);
- return ret;
- }
- static int em_fw_reset(struct usb_device *usbdev)
- {
- /*Send ZLP*/
- return gdm_wibro_send(usbdev, NULL, 0);
- }
- int usb_emergency(struct usb_device *usbdev)
- {
- int ret;
- const char *kern_name = FW_DIR FW_KERN;
- const char *fs_name = FW_DIR FW_FS;
- ret = em_download_image(usbdev, kern_name, KERNEL_TYPE_STRING);
- if (ret < 0)
- return ret;
- dev_err(&usbdev->dev, "GCT Emergency: Kernel download success.\n");
- ret = em_download_image(usbdev, fs_name, FS_TYPE_STRING);
- if (ret < 0)
- return ret;
- dev_info(&usbdev->dev, "GCT Emergency: Filesystem download success.\n");
- ret = em_fw_reset(usbdev);
- return ret;
- }
|