summaryrefslogtreecommitdiff
path: root/drivers/usb/gadget/u_fastboot.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/gadget/u_fastboot.c')
-rw-r--r--drivers/usb/gadget/u_fastboot.c416
1 files changed, 416 insertions, 0 deletions
diff --git a/drivers/usb/gadget/u_fastboot.c b/drivers/usb/gadget/u_fastboot.c
new file mode 100644
index 000000000..b0b6e0805
--- /dev/null
+++ b/drivers/usb/gadget/u_fastboot.c
@@ -0,0 +1,416 @@
+/*
+ * (C) Copyright 2008 - 2009
+ * Windriver, <www.windriver.com>
+ * Tom Rix <Tom.Rix at windriver.com>
+ *
+ * Copyright (c) 2011 Sebastian Andrzej Siewior <bigeasy at linutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * Part of the rx_handler were copied from the Android project.
+ * Specifically rx command parsing in the usb_rx_data_complete
+ * function of the file bootable/bootloader/legacy/usbloader/usbloader.c
+ *
+ * The logical naming of flash comes from the Android project
+ * Thse structures and functions that look like fastboot_flash_*
+ * They come from bootable/bootloader/legacy/libboot/flash.c
+ *
+ * This is their Copyright:
+ *
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <common.h>
+#include <command.h>
+#include <usb/fastboot.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include "g_fastboot.h"
+#include <environment.h>
+
+#include <olioh1_reboot.h>
+
+/* The 64 defined bytes plus \0 */
+#define RESPONSE_LEN (64 + 1)
+
+static struct fastboot_config *fb_cfg;
+
+/* To support the Android-style naming of flash */
+#define MAX_PTN 16
+static fastboot_ptentry ptable[MAX_PTN];
+static unsigned int pcount;
+static int static_pcount = -1;
+
+void set_fb_config (struct fastboot_config *cfg)
+{
+ fb_cfg = cfg;
+}
+
+
+/*
+ * Android style flash utilties */
+void fastboot_flash_reset_ptn(void)
+{
+ FBTINFO("fastboot flash reset partition..!!");
+ pcount = 0;
+}
+
+void fastboot_flash_add_ptn(fastboot_ptentry *ptn)
+{
+ if(pcount < MAX_PTN){
+ memcpy(ptable + pcount, ptn, sizeof(*ptn));
+ pcount++;
+ }
+}
+
+void fastboot_flash_dump_ptn(void)
+{
+ unsigned int n;
+ for(n = 0; n < pcount; n++) {
+ fastboot_ptentry *ptn = ptable + n;
+ FBTINFO("ptn %d name='%s' start=%d len=%d\n",
+ n, ptn->name, ptn->start, ptn->length);
+ printf("ptn %d name='%s' start=%d len=%d\n",
+ n, ptn->name, ptn->start, ptn->length);
+ }
+}
+
+fastboot_ptentry *fastboot_flash_find_ptn(const char *name)
+{
+ unsigned int n;
+
+ for(n = 0; n < pcount; n++) {
+ /* Make sure a substring is not accepted */
+ if (strlen(name) == strlen(ptable[n].name))
+ {
+ if(0 == strcmp(ptable[n].name, name))
+ return ptable + n;
+ }
+ }
+ return 0;
+}
+
+static int fastboot_tx_write_str(const char *buffer)
+{
+ return fastboot_tx_write(buffer, strlen(buffer));
+}
+
+static void compl_do_reset(struct usb_ep *ep, struct usb_request *req)
+{
+ do_reset(NULL, 0, 0, NULL);
+}
+
+static void cb_reboot(struct usb_ep *ep, struct usb_request *req)
+{
+ *(unsigned int *)REBOOT_REASON_PA = REBOOT_FLAG_NORMAL;
+ req_in->complete = compl_do_reset;
+ fastboot_tx_write_str("OKAY");
+}
+
+static void cb_reboot_bootloader(struct usb_ep *ep, struct usb_request *req)
+{
+ *(unsigned int *)REBOOT_REASON_PA = REBOOT_FLAG_FASTBOOT;
+ req_in->complete = compl_do_reset;
+ fastboot_tx_write_str("OKAY");
+}
+static int strcmp_l1(const char *s1, const char *s2)
+{
+ return strncmp(s1, s2, strlen(s1));
+}
+
+static void cb_getvar(struct usb_ep *ep, struct usb_request *req)
+{
+ char *cmd = req->buf;
+ char response[RESPONSE_LEN];
+ const char *s;
+
+ strcpy(response, "OKAY");
+ strsep(&cmd, ":");
+ if (!cmd) {
+ fastboot_tx_write_str("FAILmissing var");
+ return;
+ }
+
+ if (!strcmp_l1("version", cmd)) {
+ strncat(response, FASTBOOT_VERSION, sizeof(response));
+
+ } else if (!strcmp_l1("downloadsize", cmd)) {
+ char str_num[12];
+
+ sprintf(str_num, "%08x", fb_cfg->transfer_buffer_size);
+ strncat(response, str_num, sizeof(response));
+
+ } else if (!strcmp_l1("product", cmd)) {
+
+ s = fb_find_usb_string(FB_STR_PRODUCT_IDX);
+ if (s)
+ strncat(response, s, sizeof(response));
+ else
+ strcpy(response, "FAILValue not set");
+
+ } else if (!strcmp_l1("serialno", cmd)) {
+
+ s = fb_find_usb_string(FB_STR_SERIAL_IDX);
+ if (s)
+ strncat(response, s, sizeof(response));
+ else
+ strcpy(response, "FAILValue not set");
+
+ } else if (!strcmp_l1("cpurev", cmd)) {
+
+ s = fb_find_usb_string(FB_STR_PROC_REV_IDX);
+ if (s)
+ strncat(response, s, sizeof(response));
+ else
+ strcpy(response, "FAILValue not set");
+ } else if (!strcmp_l1("secure", cmd)) {
+
+ s = fb_find_usb_string(FB_STR_PROC_TYPE_IDX);
+ if (s)
+ strncat(response, s, sizeof(response));
+ else
+ strcpy(response, "FAILValue not set");
+ } else {
+ strcpy(response, "FAILVariable not implemented");
+ }
+ fastboot_tx_write_str(response);
+}
+
+static unsigned int rx_bytes_expected(void)
+{
+ int rx_remain = fb_cfg->download_size - fb_cfg->download_bytes;
+ if (rx_remain < 0)
+ return 0;
+ if (rx_remain > EP_BUFFER_SIZE)
+ return EP_BUFFER_SIZE;
+ return rx_remain;
+}
+
+#define BYTES_PER_DOT 1048576
+static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req)
+{
+ char response[RESPONSE_LEN];
+ unsigned int transfer_size = fb_cfg->download_size - fb_cfg->download_bytes;
+ const unsigned char *buffer = req->buf;
+ unsigned int buffer_size = req->actual;
+ int dnl_complete = 0;
+
+ if (req->status != 0) {
+ printf("Bad status: %d\n", req->status);
+ return;
+ }
+
+ if (buffer_size < transfer_size)
+ transfer_size = buffer_size;
+
+ memcpy(fb_cfg->transfer_buffer + fb_cfg->download_bytes,
+ buffer, transfer_size);
+
+ fb_cfg->download_bytes += transfer_size;
+
+ /* Check if transfer is done */
+ if (fb_cfg->download_bytes >= fb_cfg->download_size) {
+ /*
+ * Reset global transfer variable, keep fb_cfg->download_bytes because
+ * it will be used in the next possible flashing command
+ */
+ fb_cfg->download_size = 0;
+ req->complete = rx_handler_command;
+ req->length = EP_BUFFER_SIZE;
+ dnl_complete = 1;
+ printf("\ndownloading of %d bytes finished\n",
+ fb_cfg->download_bytes);
+ } else
+ req->length = rx_bytes_expected();
+
+ if (fb_cfg->download_bytes && !(fb_cfg->download_bytes % BYTES_PER_DOT)) {
+ printf(".");
+ if (!(fb_cfg->download_bytes % (74 * BYTES_PER_DOT)))
+ printf("\n");
+
+ }
+ if (dnl_complete)
+ {
+ fastboot_tx_write_str("OKAY");
+ }
+ req->actual = 0;
+ usb_ep_queue(ep, req, 0);
+}
+
+static void cb_download(struct usb_ep *ep, struct usb_request *req)
+{
+ char *cmd = req->buf;
+ char response[RESPONSE_LEN];
+
+ strsep(&cmd, ":");
+ fb_cfg->download_size = simple_strtoul(cmd, NULL, 16);
+ fb_cfg->download_bytes = 0;
+
+ printf("Starting download of %d bytes\n",
+ fb_cfg->download_size);
+
+ if (0 == fb_cfg->download_size) {
+ sprintf(response, "FAILdata invalid size");
+ } else if (fb_cfg->download_size >
+ fb_cfg->transfer_buffer_size) {
+ fb_cfg->download_size = 0;
+ sprintf(response, "FAILdata too large");
+ } else {
+ sprintf(response, "DATA%08x", fb_cfg->download_size);
+ req->complete = rx_handler_dl_image;
+ req->length = rx_bytes_expected();
+ }
+ fastboot_tx_write_str(response);
+}
+
+static char boot_addr_start[32];
+static char *bootm_args[] = { "bootm", boot_addr_start, NULL };
+
+static void do_bootm_on_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ req->complete = NULL;
+ fastboot_shutdown();
+ printf("Booting kernel..\n");
+
+ do_bootm(NULL, 0, 2, bootm_args);
+
+ /* This only happens if image is somehow faulty so we start over */
+ do_reset(NULL, 0, 0, NULL);
+}
+
+static void cb_boot(struct usb_ep *ep, struct usb_request *req)
+{
+ sprintf(boot_addr_start, "0x%p", fb_cfg->transfer_buffer);
+
+ req_in->complete = do_bootm_on_complete;
+ fastboot_tx_write_str("OKAY");
+ return;
+}
+
+
+int fastboot_oem(const char *cmd)
+{
+ printf("fastboot_oem:%s", cmd);
+ if (!strcmp(cmd, "format"))
+ return do_format();
+ return -1;
+}
+
+
+static void cb_oem(struct usb_ep *ep, struct usb_request *req)
+{
+ char *cmd = req->buf;
+
+ printf ("calling fastboot oem!! : %s\n", cmd);
+ int r = fastboot_oem(cmd + 4);
+ if (r < 0) {
+ fastboot_tx_write_str("FAIL");
+ } else {
+ fastboot_tx_write_str("OKAY");
+ }
+}
+
+static void cb_flash(struct usb_ep *ep, struct usb_request *req)
+{
+ char *cmdbuf = req->buf;
+ char response[32];
+ char part_name[20]={0,};
+ strncpy (part_name, cmdbuf + 6, req->actual - 6);
+ handle_flash(part_name, response);
+ fastboot_tx_write_str(response);
+}
+struct cmd_dispatch_info {
+ char *cmd;
+ void (*cb)(struct usb_ep *ep, struct usb_request *req);
+};
+
+static struct cmd_dispatch_info cmd_dispatch_info[] = {
+ {
+ .cmd = "reboot-bootloader",
+ .cb = cb_reboot_bootloader,
+ }, {
+ .cmd = "reboot",
+ .cb = cb_reboot,
+ }, {
+ .cmd = "getvar:",
+ .cb = cb_getvar,
+ }, {
+ .cmd = "download:",
+ .cb = cb_download,
+ }, {
+ .cmd = "boot",
+ .cb = cb_boot,
+ }, {
+ .cmd = "oem",
+ .cb = cb_oem,
+ },{
+ .cmd = "flash:",
+ .cb = cb_flash,
+ },
+};
+
+void rx_handler_command(struct usb_ep *ep, struct usb_request *req)
+{
+ char response[RESPONSE_LEN];
+ char *cmdbuf = req->buf;
+ void (*func_cb)(struct usb_ep *ep, struct usb_request *req) = NULL;
+ int i;
+ sprintf(response, "FAIL");
+
+ *(cmdbuf + req->actual) = '\0';
+ FBTINFO ("Recieved command : %s : req len : %d \n", cmdbuf, req->actual);
+
+ for (i = 0; i < ARRAY_SIZE(cmd_dispatch_info); i++) {
+ if (!strcmp_l1(cmd_dispatch_info[i].cmd, cmdbuf)) {
+ func_cb = cmd_dispatch_info[i].cb;
+ break;
+ }
+ }
+
+ if (!func_cb)
+ fastboot_tx_write_str("FAILunknown command");
+ else
+ func_cb(ep, req);
+
+ if (req->status == 0) {
+ *cmdbuf = '\0';
+ req->actual = 0;
+ usb_ep_queue(ep, req, 0);
+ }
+}