mirror of
https://github.com/u-boot/u-boot.git
synced 2024-11-29 15:43:44 +08:00
Merge branch 'master' of git://git.denx.de/u-boot-usb
This commit is contained in:
commit
c9afa7cea8
22
README
22
README
@ -1579,6 +1579,28 @@ The following options need to be configured:
|
||||
entering dfuMANIFEST state. Host waits this timeout, before
|
||||
sending again an USB request to the device.
|
||||
|
||||
- USB Device Android Fastboot support:
|
||||
CONFIG_CMD_FASTBOOT
|
||||
This enables the command "fastboot" which enables the Android
|
||||
fastboot mode for the platform's USB device. Fastboot is a USB
|
||||
protocol for downloading images, flashing and device control
|
||||
used on Android devices.
|
||||
See doc/README.android-fastboot for more information.
|
||||
|
||||
CONFIG_ANDROID_BOOT_IMAGE
|
||||
This enables support for booting images which use the Android
|
||||
image format header.
|
||||
|
||||
CONFIG_USB_FASTBOOT_BUF_ADDR
|
||||
The fastboot protocol requires a large memory buffer for
|
||||
downloads. Define this to the starting RAM address to use for
|
||||
downloaded images.
|
||||
|
||||
CONFIG_USB_FASTBOOT_BUF_SIZE
|
||||
The fastboot protocol requires a large memory buffer for
|
||||
downloads. This buffer should be as large as possible for a
|
||||
platform. Define this to the size available RAM for fastboot.
|
||||
|
||||
- Journaling Flash filesystem support:
|
||||
CONFIG_JFFS2_NAND, CONFIG_JFFS2_NAND_OFF, CONFIG_JFFS2_NAND_SIZE,
|
||||
CONFIG_JFFS2_NAND_DEV
|
||||
|
@ -7,7 +7,6 @@
|
||||
|
||||
obj-$(CONFIG_SOFT_I2C_MULTI_BUS) += multi_i2c.o
|
||||
obj-$(CONFIG_THOR_FUNCTION) += thor.o
|
||||
obj-$(CONFIG_CMD_USB_MASS_STORAGE) += ums.o
|
||||
obj-$(CONFIG_MISC_COMMON) += misc.o
|
||||
|
||||
ifndef CONFIG_SPL_BUILD
|
||||
|
@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Samsung Electronics
|
||||
* Lukasz Majewski <l.majewski@samsung.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <usb_mass_storage.h>
|
||||
#include <part.h>
|
||||
|
||||
static int ums_read_sector(struct ums *ums_dev,
|
||||
ulong start, lbaint_t blkcnt, void *buf)
|
||||
{
|
||||
block_dev_desc_t *block_dev = &ums_dev->mmc->block_dev;
|
||||
lbaint_t blkstart = start + ums_dev->start_sector;
|
||||
int dev_num = block_dev->dev;
|
||||
|
||||
return block_dev->block_read(dev_num, blkstart, blkcnt, buf);
|
||||
}
|
||||
|
||||
static int ums_write_sector(struct ums *ums_dev,
|
||||
ulong start, lbaint_t blkcnt, const void *buf)
|
||||
{
|
||||
block_dev_desc_t *block_dev = &ums_dev->mmc->block_dev;
|
||||
lbaint_t blkstart = start + ums_dev->start_sector;
|
||||
int dev_num = block_dev->dev;
|
||||
|
||||
return block_dev->block_write(dev_num, blkstart, blkcnt, buf);
|
||||
}
|
||||
|
||||
static struct ums ums_dev = {
|
||||
.read_sector = ums_read_sector,
|
||||
.write_sector = ums_write_sector,
|
||||
.name = "UMS disk",
|
||||
};
|
||||
|
||||
static struct ums *ums_disk_init(struct mmc *mmc)
|
||||
{
|
||||
uint64_t mmc_end_sector = mmc->capacity / SECTOR_SIZE;
|
||||
uint64_t ums_end_sector = UMS_NUM_SECTORS + UMS_START_SECTOR;
|
||||
|
||||
if (!mmc_end_sector) {
|
||||
error("MMC capacity is not valid");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ums_dev.mmc = mmc;
|
||||
|
||||
if (ums_end_sector <= mmc_end_sector) {
|
||||
ums_dev.start_sector = UMS_START_SECTOR;
|
||||
if (UMS_NUM_SECTORS)
|
||||
ums_dev.num_sectors = UMS_NUM_SECTORS;
|
||||
else
|
||||
ums_dev.num_sectors = mmc_end_sector - UMS_START_SECTOR;
|
||||
} else {
|
||||
ums_dev.num_sectors = mmc_end_sector;
|
||||
puts("UMS: defined bad disk parameters. Using default.\n");
|
||||
}
|
||||
|
||||
printf("UMS: disk start sector: %#x, count: %#x\n",
|
||||
ums_dev.start_sector, ums_dev.num_sectors);
|
||||
|
||||
return &ums_dev;
|
||||
}
|
||||
|
||||
struct ums *ums_init(unsigned int dev_num)
|
||||
{
|
||||
struct mmc *mmc = find_mmc_device(dev_num);
|
||||
|
||||
if (!mmc || mmc_init(mmc))
|
||||
return NULL;
|
||||
return ums_disk_init(mmc);
|
||||
}
|
@ -168,6 +168,8 @@ obj-y += cmd_usb.o
|
||||
obj-y += usb.o usb_hub.o
|
||||
obj-$(CONFIG_USB_STORAGE) += usb_storage.o
|
||||
endif
|
||||
obj-$(CONFIG_CMD_FASTBOOT) += cmd_fastboot.o
|
||||
|
||||
obj-$(CONFIG_CMD_USB_MASS_STORAGE) += cmd_usb_mass_storage.o
|
||||
obj-$(CONFIG_CMD_THOR_DOWNLOAD) += cmd_thordown.o
|
||||
obj-$(CONFIG_CMD_XIMG) += cmd_ximg.o
|
||||
@ -237,6 +239,7 @@ obj-y += console.o
|
||||
obj-$(CONFIG_CROS_EC) += cros_ec.o
|
||||
obj-y += dlmalloc.o
|
||||
obj-y += image.o
|
||||
obj-$(CONFIG_ANDROID_BOOT_IMAGE) += image-android.o
|
||||
obj-$(CONFIG_OF_LIBFDT) += image-fdt.o
|
||||
obj-$(CONFIG_FIT) += image-fit.o
|
||||
obj-$(CONFIG_FIT_SIGNATURE) += image-sig.o
|
||||
|
@ -222,6 +222,7 @@ static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc,
|
||||
char * const argv[])
|
||||
{
|
||||
const void *os_hdr;
|
||||
bool ep_found = false;
|
||||
|
||||
/* get kernel image header, start address and length */
|
||||
os_hdr = boot_get_kernel(cmdtp, flag, argc, argv,
|
||||
@ -273,6 +274,18 @@ static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc,
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_ANDROID_BOOT_IMAGE
|
||||
case IMAGE_FORMAT_ANDROID:
|
||||
images.os.type = IH_TYPE_KERNEL;
|
||||
images.os.comp = IH_COMP_NONE;
|
||||
images.os.os = IH_OS_LINUX;
|
||||
images.ep = images.os.load;
|
||||
ep_found = true;
|
||||
|
||||
images.os.end = android_image_get_end(os_hdr);
|
||||
images.os.load = android_image_get_kload(os_hdr);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
puts("ERROR: unknown image format type!\n");
|
||||
@ -293,7 +306,7 @@ static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc,
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
} else if (!ep_found) {
|
||||
puts("Could not find kernel entry point!\n");
|
||||
return 1;
|
||||
}
|
||||
@ -1001,6 +1014,14 @@ static const void *boot_get_kernel(cmd_tbl_t *cmdtp, int flag, int argc,
|
||||
images->fit_uname_cfg = fit_uname_config;
|
||||
images->fit_noffset_os = os_noffset;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_ANDROID_BOOT_IMAGE
|
||||
case IMAGE_FORMAT_ANDROID:
|
||||
printf("## Booting Android Image at 0x%08lx ...\n", img_addr);
|
||||
if (android_image_get_kernel((void *)img_addr, images->verify,
|
||||
os_data, os_len))
|
||||
return NULL;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
printf("Wrong Image Format for %s command\n", cmdtp->name);
|
||||
|
36
common/cmd_fastboot.c
Normal file
36
common/cmd_fastboot.c
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2008 - 2009 Windriver, <www.windriver.com>
|
||||
* Author: Tom Rix <Tom.Rix@windriver.com>
|
||||
*
|
||||
* (C) Copyright 2014 Linaro, Ltd.
|
||||
* Rob Herring <robh@kernel.org>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <g_dnl.h>
|
||||
|
||||
static int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = g_dnl_register("usb_dnl_fastboot");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
while (1) {
|
||||
if (ctrlc())
|
||||
break;
|
||||
usb_gadget_handle_interrupts();
|
||||
}
|
||||
|
||||
g_dnl_unregister();
|
||||
return CMD_RET_SUCCESS;
|
||||
}
|
||||
|
||||
U_BOOT_CMD(
|
||||
fastboot, 1, 1, do_fastboot,
|
||||
"fastboot - enter USB Fastboot protocol",
|
||||
""
|
||||
);
|
@ -9,41 +9,107 @@
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <g_dnl.h>
|
||||
#include <part.h>
|
||||
#include <usb.h>
|
||||
#include <usb_mass_storage.h>
|
||||
|
||||
static int ums_read_sector(struct ums *ums_dev,
|
||||
ulong start, lbaint_t blkcnt, void *buf)
|
||||
{
|
||||
block_dev_desc_t *block_dev = ums_dev->block_dev;
|
||||
lbaint_t blkstart = start + ums_dev->start_sector;
|
||||
int dev_num = block_dev->dev;
|
||||
|
||||
return block_dev->block_read(dev_num, blkstart, blkcnt, buf);
|
||||
}
|
||||
|
||||
static int ums_write_sector(struct ums *ums_dev,
|
||||
ulong start, lbaint_t blkcnt, const void *buf)
|
||||
{
|
||||
block_dev_desc_t *block_dev = ums_dev->block_dev;
|
||||
lbaint_t blkstart = start + ums_dev->start_sector;
|
||||
int dev_num = block_dev->dev;
|
||||
|
||||
return block_dev->block_write(dev_num, blkstart, blkcnt, buf);
|
||||
}
|
||||
|
||||
static struct ums ums_dev = {
|
||||
.read_sector = ums_read_sector,
|
||||
.write_sector = ums_write_sector,
|
||||
.name = "UMS disk",
|
||||
};
|
||||
|
||||
struct ums *ums_init(const char *devtype, const char *devnum)
|
||||
{
|
||||
block_dev_desc_t *block_dev;
|
||||
int ret;
|
||||
|
||||
ret = get_device(devtype, devnum, &block_dev);
|
||||
if (ret < 0)
|
||||
return NULL;
|
||||
|
||||
/* f_mass_storage.c assumes SECTOR_SIZE sectors */
|
||||
if (block_dev->blksz != SECTOR_SIZE)
|
||||
return NULL;
|
||||
|
||||
ums_dev.block_dev = block_dev;
|
||||
ums_dev.start_sector = 0;
|
||||
ums_dev.num_sectors = block_dev->lba;
|
||||
|
||||
printf("UMS: disk start sector: %#x, count: %#x\n",
|
||||
ums_dev.start_sector, ums_dev.num_sectors);
|
||||
|
||||
return &ums_dev;
|
||||
}
|
||||
|
||||
int do_usb_mass_storage(cmd_tbl_t *cmdtp, int flag,
|
||||
int argc, char * const argv[])
|
||||
{
|
||||
const char *usb_controller;
|
||||
const char *devtype;
|
||||
const char *devnum;
|
||||
struct ums *ums;
|
||||
unsigned int controller_index;
|
||||
int rc;
|
||||
int cable_ready_timeout __maybe_unused;
|
||||
|
||||
if (argc < 3)
|
||||
return CMD_RET_USAGE;
|
||||
|
||||
const char *usb_controller = argv[1];
|
||||
const char *mmc_devstring = argv[2];
|
||||
usb_controller = argv[1];
|
||||
if (argc >= 4) {
|
||||
devtype = argv[2];
|
||||
devnum = argv[3];
|
||||
} else {
|
||||
devtype = "mmc";
|
||||
devnum = argv[2];
|
||||
}
|
||||
|
||||
unsigned int dev_num = simple_strtoul(mmc_devstring, NULL, 0);
|
||||
|
||||
struct ums *ums = ums_init(dev_num);
|
||||
ums = ums_init(devtype, devnum);
|
||||
if (!ums)
|
||||
return CMD_RET_FAILURE;
|
||||
|
||||
unsigned int controller_index = (unsigned int)(simple_strtoul(
|
||||
usb_controller, NULL, 0));
|
||||
controller_index = (unsigned int)(simple_strtoul(
|
||||
usb_controller, NULL, 0));
|
||||
if (board_usb_init(controller_index, USB_INIT_DEVICE)) {
|
||||
error("Couldn't init USB controller.");
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
|
||||
int rc = fsg_init(ums);
|
||||
rc = fsg_init(ums);
|
||||
if (rc) {
|
||||
error("fsg_init failed");
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
|
||||
g_dnl_register("usb_dnl_ums");
|
||||
rc = g_dnl_register("usb_dnl_ums");
|
||||
if (rc) {
|
||||
error("g_dnl_register failed");
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
|
||||
/* Timeout unit: seconds */
|
||||
int cable_ready_timeout = UMS_CABLE_READY_TIMEOUT;
|
||||
cable_ready_timeout = UMS_CABLE_READY_TIMEOUT;
|
||||
|
||||
if (!g_dnl_board_usb_cable_connected()) {
|
||||
/*
|
||||
@ -91,7 +157,8 @@ exit:
|
||||
return CMD_RET_SUCCESS;
|
||||
}
|
||||
|
||||
U_BOOT_CMD(ums, CONFIG_SYS_MAXARGS, 1, do_usb_mass_storage,
|
||||
U_BOOT_CMD(ums, 4, 1, do_usb_mass_storage,
|
||||
"Use the UMS [User Mass Storage]",
|
||||
"ums <USB_controller> <mmc_dev> e.g. ums 0 0"
|
||||
"ums <USB_controller> [<devtype>] <devnum> e.g. ums 0 mmc 0\n"
|
||||
" devtype defaults to mmc"
|
||||
);
|
||||
|
84
common/image-android.c
Normal file
84
common/image-android.c
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2011 Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <image.h>
|
||||
#include <android_image.h>
|
||||
|
||||
static char andr_tmp_str[ANDR_BOOT_ARGS_SIZE + 1];
|
||||
|
||||
int android_image_get_kernel(const struct andr_img_hdr *hdr, int verify,
|
||||
ulong *os_data, ulong *os_len)
|
||||
{
|
||||
/*
|
||||
* Not all Android tools use the id field for signing the image with
|
||||
* sha1 (or anything) so we don't check it. It is not obvious that the
|
||||
* string is null terminated so we take care of this.
|
||||
*/
|
||||
strncpy(andr_tmp_str, hdr->name, ANDR_BOOT_NAME_SIZE);
|
||||
andr_tmp_str[ANDR_BOOT_NAME_SIZE] = '\0';
|
||||
if (strlen(andr_tmp_str))
|
||||
printf("Android's image name: %s\n", andr_tmp_str);
|
||||
|
||||
printf("Kernel load addr 0x%08x size %u KiB\n",
|
||||
hdr->kernel_addr, DIV_ROUND_UP(hdr->kernel_size, 1024));
|
||||
strncpy(andr_tmp_str, hdr->cmdline, ANDR_BOOT_ARGS_SIZE);
|
||||
andr_tmp_str[ANDR_BOOT_ARGS_SIZE] = '\0';
|
||||
if (strlen(andr_tmp_str)) {
|
||||
printf("Kernel command line: %s\n", andr_tmp_str);
|
||||
setenv("bootargs", andr_tmp_str);
|
||||
}
|
||||
if (hdr->ramdisk_size)
|
||||
printf("RAM disk load addr 0x%08x size %u KiB\n",
|
||||
hdr->ramdisk_addr,
|
||||
DIV_ROUND_UP(hdr->ramdisk_size, 1024));
|
||||
|
||||
if (os_data) {
|
||||
*os_data = (ulong)hdr;
|
||||
*os_data += hdr->page_size;
|
||||
}
|
||||
if (os_len)
|
||||
*os_len = hdr->kernel_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int android_image_check_header(const struct andr_img_hdr *hdr)
|
||||
{
|
||||
return memcmp(ANDR_BOOT_MAGIC, hdr->magic, ANDR_BOOT_MAGIC_SIZE);
|
||||
}
|
||||
|
||||
ulong android_image_get_end(const struct andr_img_hdr *hdr)
|
||||
{
|
||||
u32 size = 0;
|
||||
/*
|
||||
* The header takes a full page, the remaining components are aligned
|
||||
* on page boundary
|
||||
*/
|
||||
size += hdr->page_size;
|
||||
size += ALIGN(hdr->kernel_size, hdr->page_size);
|
||||
size += ALIGN(hdr->ramdisk_size, hdr->page_size);
|
||||
size += ALIGN(hdr->second_size, hdr->page_size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
ulong android_image_get_kload(const struct andr_img_hdr *hdr)
|
||||
{
|
||||
return hdr->kernel_addr;
|
||||
}
|
||||
|
||||
int android_image_get_ramdisk(const struct andr_img_hdr *hdr,
|
||||
ulong *rd_data, ulong *rd_len)
|
||||
{
|
||||
if (!hdr->ramdisk_size)
|
||||
return -1;
|
||||
*rd_data = (unsigned long)hdr;
|
||||
*rd_data += hdr->page_size;
|
||||
*rd_data += ALIGN(hdr->kernel_size, hdr->page_size);
|
||||
|
||||
*rd_len = hdr->ramdisk_size;
|
||||
return 0;
|
||||
}
|
@ -660,10 +660,12 @@ int genimg_get_format(const void *img_addr)
|
||||
if (image_check_magic(hdr))
|
||||
format = IMAGE_FORMAT_LEGACY;
|
||||
#if defined(CONFIG_FIT) || defined(CONFIG_OF_LIBFDT)
|
||||
else {
|
||||
if (fdt_check_header(img_addr) == 0)
|
||||
format = IMAGE_FORMAT_FIT;
|
||||
}
|
||||
else if (fdt_check_header(img_addr) == 0)
|
||||
format = IMAGE_FORMAT_FIT;
|
||||
#endif
|
||||
#ifdef CONFIG_ANDROID_BOOT_IMAGE
|
||||
else if (android_image_check_header(img_addr) == 0)
|
||||
format = IMAGE_FORMAT_ANDROID;
|
||||
#endif
|
||||
|
||||
return format;
|
||||
@ -933,7 +935,15 @@ int boot_get_ramdisk(int argc, char * const argv[], bootm_headers_t *images,
|
||||
(ulong)images->legacy_hdr_os);
|
||||
|
||||
image_multi_getimg(images->legacy_hdr_os, 1, &rd_data, &rd_len);
|
||||
} else {
|
||||
}
|
||||
#ifdef CONFIG_ANDROID_BOOT_IMAGE
|
||||
else if ((genimg_get_format(images) == IMAGE_FORMAT_ANDROID) &&
|
||||
(!android_image_get_ramdisk((void *)images->os.start,
|
||||
&rd_data, &rd_len))) {
|
||||
/* empty */
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
/*
|
||||
* no initrd image
|
||||
*/
|
||||
|
91
doc/README.android-fastboot
Normal file
91
doc/README.android-fastboot
Normal file
@ -0,0 +1,91 @@
|
||||
Android Fastboot
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Overview
|
||||
========
|
||||
The protocol that is used over USB is described in
|
||||
README.android-fastboot-protocol in same directory.
|
||||
|
||||
The current implementation does not yet support the flash and erase
|
||||
commands.
|
||||
|
||||
Client installation
|
||||
===================
|
||||
The counterpart to this gadget is the fastboot client which can
|
||||
be found in Android's platform/system/core repository in the fastboot
|
||||
folder. It runs on Windows, Linux and even OSX. Linux user are lucky since
|
||||
they only need libusb.
|
||||
Windows users need to bring some time until they have Android SDK (currently
|
||||
http://dl.google.com/android/installer_r12-windows.exe) installed. You
|
||||
need to install ADB package which contains the required glue libraries for
|
||||
accessing USB. Also you need "Google USB driver package" and "SDK platform
|
||||
tools". Once installed the usb driver is placed in your SDK folder under
|
||||
extras\google\usb_driver. The android_winusb.inf needs a line like
|
||||
|
||||
%SingleBootLoaderInterface% = USB_Install, USB\VID_0451&PID_D022
|
||||
|
||||
either in the [Google.NTx86] section for 32bit Windows or [Google.NTamd64]
|
||||
for 64bit Windows. VID and PID should match whatever the fastboot is
|
||||
advertising.
|
||||
|
||||
Board specific
|
||||
==============
|
||||
The fastboot gadget relies on the USB download gadget, so the following
|
||||
options must be configured:
|
||||
|
||||
CONFIG_USBDOWNLOAD_GADGET
|
||||
CONFIG_G_DNL_VENDOR_NUM
|
||||
CONFIG_G_DNL_PRODUCT_NUM
|
||||
CONFIG_G_DNL_MANUFACTURER
|
||||
|
||||
The fastboot function is enabled by defining CONFIG_CMD_FASTBOOT and
|
||||
CONFIG_ANDROID_BOOT_IMAGE.
|
||||
|
||||
The fastboot protocol requires a large memory buffer for downloads. This
|
||||
buffer should be as large as possible for a platform. The location of the
|
||||
buffer and size are set with CONFIG_USB_FASTBOOT_BUF_ADDR and
|
||||
CONFIG_USB_FASTBOOT_BUF_SIZE.
|
||||
|
||||
In Action
|
||||
=========
|
||||
Enter into fastboot by executing the fastboot command in u-boot and you
|
||||
should see:
|
||||
|GADGET DRIVER: usb_dnl_fastboot
|
||||
|
||||
On the client side you can fetch the bootloader version for instance:
|
||||
|>fastboot getvar bootloader-version
|
||||
|bootloader-version: U-Boot 2014.04-00005-gd24cabc
|
||||
|finished. total time: 0.000s
|
||||
|
||||
or initiate a reboot:
|
||||
|>fastboot reboot
|
||||
|
||||
and once the client comes back, the board should reset.
|
||||
|
||||
You can also specify a kernel image to boot. You have to either specify
|
||||
the an image in Android format _or_ pass a binary kernel and let the
|
||||
fastboot client wrap the Android suite around it. On OMAP for instance you
|
||||
take zImage kernel and pass it to the fastboot client:
|
||||
|
||||
|>fastboot -b 0x80000000 -c "console=ttyO2 earlyprintk root=/dev/ram0
|
||||
| mem=128M" boot zImage
|
||||
|creating boot image...
|
||||
|creating boot image - 1847296 bytes
|
||||
|downloading 'boot.img'...
|
||||
|OKAY [ 2.766s]
|
||||
|booting...
|
||||
|OKAY [ -0.000s]
|
||||
|finished. total time: 2.766s
|
||||
|
||||
and on the gadget side you should see:
|
||||
|Starting download of 1847296 bytes
|
||||
|........................................................
|
||||
|downloading of 1847296 bytes finished
|
||||
|Booting kernel..
|
||||
|## Booting Android Image at 0x81000000 ...
|
||||
|Kernel load addr 0x80008000 size 1801 KiB
|
||||
|Kernel command line: console=ttyO2 earlyprintk root=/dev/ram0 mem=128M
|
||||
| Loading Kernel Image ... OK
|
||||
|OK
|
||||
|
|
||||
|Starting kernel ...
|
170
doc/README.android-fastboot-protocol
Normal file
170
doc/README.android-fastboot-protocol
Normal file
@ -0,0 +1,170 @@
|
||||
FastBoot Version 0.4
|
||||
----------------------
|
||||
|
||||
The fastboot protocol is a mechanism for communicating with bootloaders
|
||||
over USB. It is designed to be very straightforward to implement, to
|
||||
allow it to be used across a wide range of devices and from hosts running
|
||||
Linux, Windows, or OSX.
|
||||
|
||||
|
||||
Basic Requirements
|
||||
------------------
|
||||
|
||||
* Two bulk endpoints (in, out) are required
|
||||
* Max packet size must be 64 bytes for full-speed and 512 bytes for
|
||||
high-speed USB
|
||||
* The protocol is entirely host-driven and synchronous (unlike the
|
||||
multi-channel, bi-directional, asynchronous ADB protocol)
|
||||
|
||||
|
||||
Transport and Framing
|
||||
---------------------
|
||||
|
||||
1. Host sends a command, which is an ascii string in a single
|
||||
packet no greater than 64 bytes.
|
||||
|
||||
2. Client response with a single packet no greater than 64 bytes.
|
||||
The first four bytes of the response are "OKAY", "FAIL", "DATA",
|
||||
or "INFO". Additional bytes may contain an (ascii) informative
|
||||
message.
|
||||
|
||||
a. INFO -> the remaining 60 bytes are an informative message
|
||||
(providing progress or diagnostic messages). They should
|
||||
be displayed and then step #2 repeats
|
||||
|
||||
b. FAIL -> the requested command failed. The remaining 60 bytes
|
||||
of the response (if present) provide a textual failure message
|
||||
to present to the user. Stop.
|
||||
|
||||
c. OKAY -> the requested command completed successfully. Go to #5
|
||||
|
||||
d. DATA -> the requested command is ready for the data phase.
|
||||
A DATA response packet will be 12 bytes long, in the form of
|
||||
DATA00000000 where the 8 digit hexidecimal number represents
|
||||
the total data size to transfer.
|
||||
|
||||
3. Data phase. Depending on the command, the host or client will
|
||||
send the indicated amount of data. Short packets are always
|
||||
acceptable and zero-length packets are ignored. This phase continues
|
||||
until the client has sent or received the number of bytes indicated
|
||||
in the "DATA" response above.
|
||||
|
||||
4. Client responds with a single packet no greater than 64 bytes.
|
||||
The first four bytes of the response are "OKAY", "FAIL", or "INFO".
|
||||
Similar to #2:
|
||||
|
||||
a. INFO -> display the remaining 60 bytes and return to #4
|
||||
|
||||
b. FAIL -> display the remaining 60 bytes (if present) as a failure
|
||||
reason and consider the command failed. Stop.
|
||||
|
||||
c. OKAY -> success. Go to #5
|
||||
|
||||
5. Success. Stop.
|
||||
|
||||
|
||||
Example Session
|
||||
---------------
|
||||
|
||||
Host: "getvar:version" request version variable
|
||||
|
||||
Client: "OKAY0.4" return version "0.4"
|
||||
|
||||
Host: "getvar:nonexistant" request some undefined variable
|
||||
|
||||
Client: "OKAY" return value ""
|
||||
|
||||
Host: "download:00001234" request to send 0x1234 bytes of data
|
||||
|
||||
Client: "DATA00001234" ready to accept data
|
||||
|
||||
Host: < 0x1234 bytes > send data
|
||||
|
||||
Client: "OKAY" success
|
||||
|
||||
Host: "flash:bootloader" request to flash the data to the bootloader
|
||||
|
||||
Client: "INFOerasing flash" indicate status / progress
|
||||
"INFOwriting flash"
|
||||
"OKAY" indicate success
|
||||
|
||||
Host: "powerdown" send a command
|
||||
|
||||
Client: "FAILunknown command" indicate failure
|
||||
|
||||
|
||||
Command Reference
|
||||
-----------------
|
||||
|
||||
* Command parameters are indicated by printf-style escape sequences.
|
||||
|
||||
* Commands are ascii strings and sent without the quotes (which are
|
||||
for illustration only here) and without a trailing 0 byte.
|
||||
|
||||
* Commands that begin with a lowercase letter are reserved for this
|
||||
specification. OEM-specific commands should not begin with a
|
||||
lowercase letter, to prevent incompatibilities with future specs.
|
||||
|
||||
"getvar:%s" Read a config/version variable from the bootloader.
|
||||
The variable contents will be returned after the
|
||||
OKAY response.
|
||||
|
||||
"download:%08x" Write data to memory which will be later used
|
||||
by "boot", "ramdisk", "flash", etc. The client
|
||||
will reply with "DATA%08x" if it has enough
|
||||
space in RAM or "FAIL" if not. The size of
|
||||
the download is remembered.
|
||||
|
||||
"verify:%08x" Send a digital signature to verify the downloaded
|
||||
data. Required if the bootloader is "secure"
|
||||
otherwise "flash" and "boot" will be ignored.
|
||||
|
||||
"flash:%s" Write the previously downloaded image to the
|
||||
named partition (if possible).
|
||||
|
||||
"erase:%s" Erase the indicated partition (clear to 0xFFs)
|
||||
|
||||
"boot" The previously downloaded data is a boot.img
|
||||
and should be booted according to the normal
|
||||
procedure for a boot.img
|
||||
|
||||
"continue" Continue booting as normal (if possible)
|
||||
|
||||
"reboot" Reboot the device.
|
||||
|
||||
"reboot-bootloader" Reboot back into the bootloader.
|
||||
Useful for upgrade processes that require upgrading
|
||||
the bootloader and then upgrading other partitions
|
||||
using the new bootloader.
|
||||
|
||||
"powerdown" Power off the device.
|
||||
|
||||
|
||||
|
||||
Client Variables
|
||||
----------------
|
||||
|
||||
The "getvar:%s" command is used to read client variables which
|
||||
represent various information about the device and the software
|
||||
on it.
|
||||
|
||||
The various currently defined names are:
|
||||
|
||||
version Version of FastBoot protocol supported.
|
||||
It should be "0.3" for this document.
|
||||
|
||||
version-bootloader Version string for the Bootloader.
|
||||
|
||||
version-baseband Version string of the Baseband Software
|
||||
|
||||
product Name of the product
|
||||
|
||||
serialno Product serial number
|
||||
|
||||
secure If the value is "yes", this is a secure
|
||||
bootloader requiring a signature before
|
||||
it will install or boot images.
|
||||
|
||||
Names starting with a lowercase character are reserved by this
|
||||
specification. OEM-specific names should not start with lowercase
|
||||
characters.
|
@ -163,6 +163,18 @@ static int dfu_flush_medium_nand(struct dfu_entity *dfu)
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned int dfu_polltimeout_nand(struct dfu_entity *dfu)
|
||||
{
|
||||
/*
|
||||
* Currently, Poll Timeout != 0 is only needed on nand
|
||||
* ubi partition, as the not used sectors need an erase
|
||||
*/
|
||||
if (dfu->data.nand.ubi)
|
||||
return DFU_MANIFEST_POLL_TIMEOUT;
|
||||
|
||||
return DFU_DEFAULT_POLL_TIMEOUT;
|
||||
}
|
||||
|
||||
int dfu_fill_entity_nand(struct dfu_entity *dfu, char *s)
|
||||
{
|
||||
char *st;
|
||||
@ -211,6 +223,7 @@ int dfu_fill_entity_nand(struct dfu_entity *dfu, char *s)
|
||||
dfu->read_medium = dfu_read_medium_nand;
|
||||
dfu->write_medium = dfu_write_medium_nand;
|
||||
dfu->flush_medium = dfu_flush_medium_nand;
|
||||
dfu->poll_timeout = dfu_polltimeout_nand;
|
||||
|
||||
/* initial state */
|
||||
dfu->inited = 0;
|
||||
|
@ -18,6 +18,7 @@ obj-$(CONFIG_THOR_FUNCTION) += f_thor.o
|
||||
obj-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o
|
||||
obj-$(CONFIG_DFU_FUNCTION) += f_dfu.o
|
||||
obj-$(CONFIG_USB_GADGET_MASS_STORAGE) += f_mass_storage.o
|
||||
obj-$(CONFIG_CMD_FASTBOOT) += f_fastboot.o
|
||||
endif
|
||||
ifdef CONFIG_USB_ETHER
|
||||
obj-y += ether.o
|
||||
|
@ -205,13 +205,26 @@ static void ci_invalidate_qtd(int ep_num)
|
||||
static struct usb_request *
|
||||
ci_ep_alloc_request(struct usb_ep *ep, unsigned int gfp_flags)
|
||||
{
|
||||
struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep);
|
||||
return &ci_ep->req;
|
||||
struct ci_req *ci_req;
|
||||
|
||||
ci_req = memalign(ARCH_DMA_MINALIGN, sizeof(*ci_req));
|
||||
if (!ci_req)
|
||||
return NULL;
|
||||
|
||||
INIT_LIST_HEAD(&ci_req->queue);
|
||||
ci_req->b_buf = 0;
|
||||
|
||||
return &ci_req->req;
|
||||
}
|
||||
|
||||
static void ci_ep_free_request(struct usb_ep *ep, struct usb_request *_req)
|
||||
static void ci_ep_free_request(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
return;
|
||||
struct ci_req *ci_req;
|
||||
|
||||
ci_req = container_of(req, struct ci_req, req);
|
||||
if (ci_req->b_buf)
|
||||
free(ci_req->b_buf);
|
||||
free(ci_req);
|
||||
}
|
||||
|
||||
static void ep_enable(int num, int in, int maxpacket)
|
||||
@ -267,99 +280,102 @@ static int ci_ep_disable(struct usb_ep *ep)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ci_bounce(struct ci_ep *ep, int in)
|
||||
static int ci_bounce(struct ci_req *ci_req, int in)
|
||||
{
|
||||
uint32_t addr = (uint32_t)ep->req.buf;
|
||||
uint32_t ba;
|
||||
struct usb_request *req = &ci_req->req;
|
||||
uint32_t addr = (uint32_t)req->buf;
|
||||
uint32_t hwaddr;
|
||||
uint32_t aligned_used_len;
|
||||
|
||||
/* Input buffer address is not aligned. */
|
||||
if (addr & (ARCH_DMA_MINALIGN - 1))
|
||||
goto align;
|
||||
|
||||
/* Input buffer length is not aligned. */
|
||||
if (ep->req.length & (ARCH_DMA_MINALIGN - 1))
|
||||
if (req->length & (ARCH_DMA_MINALIGN - 1))
|
||||
goto align;
|
||||
|
||||
/* The buffer is well aligned, only flush cache. */
|
||||
ep->b_len = ep->req.length;
|
||||
ep->b_buf = ep->req.buf;
|
||||
ci_req->hw_len = req->length;
|
||||
ci_req->hw_buf = req->buf;
|
||||
goto flush;
|
||||
|
||||
align:
|
||||
/* Use internal buffer for small payloads. */
|
||||
if (ep->req.length <= 64) {
|
||||
ep->b_len = 64;
|
||||
ep->b_buf = ep->b_fast;
|
||||
} else {
|
||||
ep->b_len = roundup(ep->req.length, ARCH_DMA_MINALIGN);
|
||||
ep->b_buf = memalign(ARCH_DMA_MINALIGN, ep->b_len);
|
||||
if (!ep->b_buf)
|
||||
if (ci_req->b_buf && req->length > ci_req->b_len) {
|
||||
free(ci_req->b_buf);
|
||||
ci_req->b_buf = 0;
|
||||
}
|
||||
if (!ci_req->b_buf) {
|
||||
ci_req->b_len = roundup(req->length, ARCH_DMA_MINALIGN);
|
||||
ci_req->b_buf = memalign(ARCH_DMA_MINALIGN, ci_req->b_len);
|
||||
if (!ci_req->b_buf)
|
||||
return -ENOMEM;
|
||||
}
|
||||
ci_req->hw_len = ci_req->b_len;
|
||||
ci_req->hw_buf = ci_req->b_buf;
|
||||
|
||||
if (in)
|
||||
memcpy(ep->b_buf, ep->req.buf, ep->req.length);
|
||||
memcpy(ci_req->hw_buf, req->buf, req->length);
|
||||
|
||||
flush:
|
||||
ba = (uint32_t)ep->b_buf;
|
||||
flush_dcache_range(ba, ba + ep->b_len);
|
||||
hwaddr = (uint32_t)ci_req->hw_buf;
|
||||
aligned_used_len = roundup(req->length, ARCH_DMA_MINALIGN);
|
||||
flush_dcache_range(hwaddr, hwaddr + aligned_used_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ci_debounce(struct ci_ep *ep, int in)
|
||||
static void ci_debounce(struct ci_req *ci_req, int in)
|
||||
{
|
||||
uint32_t addr = (uint32_t)ep->req.buf;
|
||||
uint32_t ba = (uint32_t)ep->b_buf;
|
||||
struct usb_request *req = &ci_req->req;
|
||||
uint32_t addr = (uint32_t)req->buf;
|
||||
uint32_t hwaddr = (uint32_t)ci_req->hw_buf;
|
||||
uint32_t aligned_used_len;
|
||||
|
||||
if (in) {
|
||||
if (addr == ba)
|
||||
return; /* not a bounce */
|
||||
goto free;
|
||||
}
|
||||
invalidate_dcache_range(ba, ba + ep->b_len);
|
||||
if (in)
|
||||
return;
|
||||
|
||||
if (addr == ba)
|
||||
return; /* not a bounce */
|
||||
aligned_used_len = roundup(req->actual, ARCH_DMA_MINALIGN);
|
||||
invalidate_dcache_range(hwaddr, hwaddr + aligned_used_len);
|
||||
|
||||
memcpy(ep->req.buf, ep->b_buf, ep->req.actual);
|
||||
free:
|
||||
/* Large payloads use allocated buffer, free it. */
|
||||
if (ep->b_buf != ep->b_fast)
|
||||
free(ep->b_buf);
|
||||
if (addr == hwaddr)
|
||||
return; /* not a bounce */
|
||||
|
||||
memcpy(req->buf, ci_req->hw_buf, req->actual);
|
||||
}
|
||||
|
||||
static int ci_ep_queue(struct usb_ep *ep,
|
||||
struct usb_request *req, gfp_t gfp_flags)
|
||||
static void ci_ep_submit_next_request(struct ci_ep *ci_ep)
|
||||
{
|
||||
struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep);
|
||||
struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
|
||||
struct ept_queue_item *item;
|
||||
struct ept_queue_head *head;
|
||||
int bit, num, len, in, ret;
|
||||
int bit, num, len, in;
|
||||
struct ci_req *ci_req;
|
||||
|
||||
ci_ep->req_primed = true;
|
||||
|
||||
num = ci_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
|
||||
in = (ci_ep->desc->bEndpointAddress & USB_DIR_IN) != 0;
|
||||
item = ci_get_qtd(num, in);
|
||||
head = ci_get_qh(num, in);
|
||||
len = req->length;
|
||||
|
||||
ret = ci_bounce(ci_ep, in);
|
||||
if (ret)
|
||||
return ret;
|
||||
ci_req = list_first_entry(&ci_ep->queue, struct ci_req, queue);
|
||||
len = ci_req->req.length;
|
||||
|
||||
item->next = TERMINATE;
|
||||
item->info = INFO_BYTES(len) | INFO_IOC | INFO_ACTIVE;
|
||||
item->page0 = (uint32_t)ci_ep->b_buf;
|
||||
item->page1 = ((uint32_t)ci_ep->b_buf & 0xfffff000) + 0x1000;
|
||||
item->page2 = ((uint32_t)ci_ep->b_buf & 0xfffff000) + 0x2000;
|
||||
item->page3 = ((uint32_t)ci_ep->b_buf & 0xfffff000) + 0x3000;
|
||||
item->page4 = ((uint32_t)ci_ep->b_buf & 0xfffff000) + 0x4000;
|
||||
item->page0 = (uint32_t)ci_req->hw_buf;
|
||||
item->page1 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x1000;
|
||||
item->page2 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x2000;
|
||||
item->page3 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x3000;
|
||||
item->page4 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x4000;
|
||||
ci_flush_qtd(num);
|
||||
|
||||
head->next = (unsigned) item;
|
||||
head->info = 0;
|
||||
|
||||
DBG("ept%d %s queue len %x, buffer %p\n",
|
||||
num, in ? "in" : "out", len, ci_ep->b_buf);
|
||||
DBG("ept%d %s queue len %x, req %p, buffer %p\n",
|
||||
num, in ? "in" : "out", len, ci_req, ci_req->hw_buf);
|
||||
ci_flush_qh(num);
|
||||
|
||||
if (in)
|
||||
@ -368,6 +384,29 @@ static int ci_ep_queue(struct usb_ep *ep,
|
||||
bit = EPT_RX(num);
|
||||
|
||||
writel(bit, &udc->epprime);
|
||||
}
|
||||
|
||||
static int ci_ep_queue(struct usb_ep *ep,
|
||||
struct usb_request *req, gfp_t gfp_flags)
|
||||
{
|
||||
struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep);
|
||||
struct ci_req *ci_req = container_of(req, struct ci_req, req);
|
||||
int in, ret;
|
||||
int __maybe_unused num;
|
||||
|
||||
num = ci_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
|
||||
in = (ci_ep->desc->bEndpointAddress & USB_DIR_IN) != 0;
|
||||
|
||||
ret = ci_bounce(ci_req, in);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
DBG("ept%d %s pre-queue req %p, buffer %p\n",
|
||||
num, in ? "in" : "out", ci_req, ci_req->hw_buf);
|
||||
list_add_tail(&ci_req->queue, &ci_ep->queue);
|
||||
|
||||
if (!ci_ep->req_primed)
|
||||
ci_ep_submit_next_request(ci_ep);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -376,6 +415,8 @@ static void handle_ep_complete(struct ci_ep *ep)
|
||||
{
|
||||
struct ept_queue_item *item;
|
||||
int num, in, len;
|
||||
struct ci_req *ci_req;
|
||||
|
||||
num = ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
|
||||
in = (ep->desc->bEndpointAddress & USB_DIR_IN) != 0;
|
||||
if (num == 0)
|
||||
@ -387,16 +428,23 @@ static void handle_ep_complete(struct ci_ep *ep)
|
||||
printf("EP%d/%s FAIL info=%x pg0=%x\n",
|
||||
num, in ? "in" : "out", item->info, item->page0);
|
||||
|
||||
len = (item->info >> 16) & 0x7fff;
|
||||
ep->req.actual = ep->req.length - len;
|
||||
ci_debounce(ep, in);
|
||||
ci_req = list_first_entry(&ep->queue, struct ci_req, queue);
|
||||
list_del_init(&ci_req->queue);
|
||||
ep->req_primed = false;
|
||||
|
||||
DBG("ept%d %s complete %x\n",
|
||||
num, in ? "in" : "out", len);
|
||||
ep->req.complete(&ep->ep, &ep->req);
|
||||
if (!list_empty(&ep->queue))
|
||||
ci_ep_submit_next_request(ep);
|
||||
|
||||
len = (item->info >> 16) & 0x7fff;
|
||||
ci_req->req.actual = ci_req->req.length - len;
|
||||
ci_debounce(ci_req, in);
|
||||
|
||||
DBG("ept%d %s req %p, complete %x\n",
|
||||
num, in ? "in" : "out", ci_req, len);
|
||||
ci_req->req.complete(&ep->ep, &ci_req->req);
|
||||
if (num == 0) {
|
||||
ep->req.length = 0;
|
||||
usb_ep_queue(&ep->ep, &ep->req, 0);
|
||||
ci_req->req.length = 0;
|
||||
usb_ep_queue(&ep->ep, &ci_req->req, 0);
|
||||
ep->desc = &ep0_in_desc;
|
||||
}
|
||||
}
|
||||
@ -405,13 +453,18 @@ static void handle_ep_complete(struct ci_ep *ep)
|
||||
|
||||
static void handle_setup(void)
|
||||
{
|
||||
struct usb_request *req = &controller.ep[0].req;
|
||||
struct ci_ep *ci_ep = &controller.ep[0];
|
||||
struct ci_req *ci_req;
|
||||
struct usb_request *req;
|
||||
struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
|
||||
struct ept_queue_head *head;
|
||||
struct usb_ctrlrequest r;
|
||||
int status = 0;
|
||||
int num, in, _num, _in, i;
|
||||
char *buf;
|
||||
|
||||
ci_req = list_first_entry(&ci_ep->queue, struct ci_req, queue);
|
||||
req = &ci_req->req;
|
||||
head = ci_get_qh(0, 0); /* EP0 OUT */
|
||||
|
||||
ci_invalidate_qh(0);
|
||||
@ -424,6 +477,9 @@ static void handle_setup(void)
|
||||
DBG("handle setup %s, %x, %x index %x value %x\n", reqname(r.bRequest),
|
||||
r.bRequestType, r.bRequest, r.wIndex, r.wValue);
|
||||
|
||||
list_del_init(&ci_req->queue);
|
||||
ci_ep->req_primed = false;
|
||||
|
||||
switch (SETUP(r.bRequestType, r.bRequest)) {
|
||||
case SETUP(USB_RECIP_ENDPOINT, USB_REQ_CLEAR_FEATURE):
|
||||
_num = r.wIndex & 15;
|
||||
@ -701,6 +757,8 @@ static int ci_udc_probe(void)
|
||||
/* Init EP 0 */
|
||||
memcpy(&controller.ep[0].ep, &ci_ep_init[0], sizeof(*ci_ep_init));
|
||||
controller.ep[0].desc = &ep0_in_desc;
|
||||
INIT_LIST_HEAD(&controller.ep[0].queue);
|
||||
controller.ep[0].req_primed = false;
|
||||
controller.gadget.ep0 = &controller.ep[0].ep;
|
||||
INIT_LIST_HEAD(&controller.gadget.ep0->ep_list);
|
||||
|
||||
@ -708,6 +766,8 @@ static int ci_udc_probe(void)
|
||||
for (i = 1; i < NUM_ENDPOINTS; i++) {
|
||||
memcpy(&controller.ep[i].ep, &ci_ep_init[1],
|
||||
sizeof(*ci_ep_init));
|
||||
INIT_LIST_HEAD(&controller.ep[i].queue);
|
||||
controller.ep[i].req_primed = false;
|
||||
list_add_tail(&controller.ep[i].ep.ep_list,
|
||||
&controller.gadget.ep_list);
|
||||
}
|
||||
|
@ -77,15 +77,22 @@ struct ci_udc {
|
||||
#define CTRL_TXT_BULK (2 << 18)
|
||||
#define CTRL_RXT_BULK (2 << 2)
|
||||
|
||||
struct ci_req {
|
||||
struct usb_request req;
|
||||
struct list_head queue;
|
||||
/* Bounce buffer allocated if needed to align the transfer */
|
||||
uint8_t *b_buf;
|
||||
uint32_t b_len;
|
||||
/* Buffer for the current transfer. Either req.buf/len or b_buf/len */
|
||||
uint8_t *hw_buf;
|
||||
uint32_t hw_len;
|
||||
};
|
||||
|
||||
struct ci_ep {
|
||||
struct usb_ep ep;
|
||||
struct list_head queue;
|
||||
bool req_primed;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
|
||||
struct usb_request req;
|
||||
uint8_t *b_buf;
|
||||
uint32_t b_len;
|
||||
uint8_t b_fast[64] __aligned(ARCH_DMA_MINALIGN);
|
||||
};
|
||||
|
||||
struct ci_drv {
|
||||
|
@ -175,10 +175,17 @@ static void dnload_request_flush(struct usb_ep *ep, struct usb_request *req)
|
||||
req->length, f_dfu->blk_seq_num);
|
||||
}
|
||||
|
||||
static inline int dfu_get_manifest_timeout(struct dfu_entity *dfu)
|
||||
{
|
||||
return dfu->poll_timeout ? dfu->poll_timeout(dfu) :
|
||||
DFU_MANIFEST_POLL_TIMEOUT;
|
||||
}
|
||||
|
||||
static void handle_getstatus(struct usb_request *req)
|
||||
{
|
||||
struct dfu_status *dstat = (struct dfu_status *)req->buf;
|
||||
struct f_dfu *f_dfu = req->context;
|
||||
struct dfu_entity *dfu = dfu_get_entity(f_dfu->altsetting);
|
||||
|
||||
dfu_set_poll_timeout(dstat, 0);
|
||||
|
||||
@ -191,7 +198,8 @@ static void handle_getstatus(struct usb_request *req)
|
||||
f_dfu->dfu_state = DFU_STATE_dfuMANIFEST;
|
||||
break;
|
||||
case DFU_STATE_dfuMANIFEST:
|
||||
dfu_set_poll_timeout(dstat, DFU_MANIFEST_POLL_TIMEOUT);
|
||||
dfu_set_poll_timeout(dstat, dfu_get_manifest_timeout(dfu));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
513
drivers/usb/gadget/f_fastboot.c
Normal file
513
drivers/usb/gadget/f_fastboot.c
Normal file
@ -0,0 +1,513 @@
|
||||
/*
|
||||
* (C) Copyright 2008 - 2009
|
||||
* Windriver, <www.windriver.com>
|
||||
* Tom Rix <Tom.Rix@windriver.com>
|
||||
*
|
||||
* Copyright 2011 Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
*
|
||||
* Copyright 2014 Linaro, Ltd.
|
||||
* Rob Herring <robh@kernel.org>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <errno.h>
|
||||
#include <malloc.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/composite.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <version.h>
|
||||
#include <g_dnl.h>
|
||||
|
||||
#define FASTBOOT_VERSION "0.4"
|
||||
|
||||
#define FASTBOOT_INTERFACE_CLASS 0xff
|
||||
#define FASTBOOT_INTERFACE_SUB_CLASS 0x42
|
||||
#define FASTBOOT_INTERFACE_PROTOCOL 0x03
|
||||
|
||||
#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0 (0x0200)
|
||||
#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1 (0x0040)
|
||||
#define TX_ENDPOINT_MAXIMUM_PACKET_SIZE (0x0040)
|
||||
|
||||
/* The 64 defined bytes plus \0 */
|
||||
#define RESPONSE_LEN (64 + 1)
|
||||
|
||||
#define EP_BUFFER_SIZE 4096
|
||||
|
||||
struct f_fastboot {
|
||||
struct usb_function usb_function;
|
||||
|
||||
/* IN/OUT EP's and correspoinding requests */
|
||||
struct usb_ep *in_ep, *out_ep;
|
||||
struct usb_request *in_req, *out_req;
|
||||
};
|
||||
|
||||
static inline struct f_fastboot *func_to_fastboot(struct usb_function *f)
|
||||
{
|
||||
return container_of(f, struct f_fastboot, usb_function);
|
||||
}
|
||||
|
||||
static struct f_fastboot *fastboot_func;
|
||||
static unsigned int download_size;
|
||||
static unsigned int download_bytes;
|
||||
|
||||
static struct usb_endpoint_descriptor fs_ep_in = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = TX_ENDPOINT_MAXIMUM_PACKET_SIZE,
|
||||
.bInterval = 0x00,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor fs_ep_out = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_OUT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1,
|
||||
.bInterval = 0x00,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor hs_ep_out = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_OUT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0,
|
||||
.bInterval = 0x00,
|
||||
};
|
||||
|
||||
static struct usb_interface_descriptor interface_desc = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bInterfaceNumber = 0x00,
|
||||
.bAlternateSetting = 0x00,
|
||||
.bNumEndpoints = 0x02,
|
||||
.bInterfaceClass = FASTBOOT_INTERFACE_CLASS,
|
||||
.bInterfaceSubClass = FASTBOOT_INTERFACE_SUB_CLASS,
|
||||
.bInterfaceProtocol = FASTBOOT_INTERFACE_PROTOCOL,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *fb_runtime_descs[] = {
|
||||
(struct usb_descriptor_header *)&interface_desc,
|
||||
(struct usb_descriptor_header *)&fs_ep_in,
|
||||
(struct usb_descriptor_header *)&hs_ep_out,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/*
|
||||
* static strings, in UTF-8
|
||||
*/
|
||||
static const char fastboot_name[] = "Android Fastboot";
|
||||
|
||||
static struct usb_string fastboot_string_defs[] = {
|
||||
[0].s = fastboot_name,
|
||||
{ } /* end of list */
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings stringtab_fastboot = {
|
||||
.language = 0x0409, /* en-us */
|
||||
.strings = fastboot_string_defs,
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings *fastboot_strings[] = {
|
||||
&stringtab_fastboot,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void rx_handler_command(struct usb_ep *ep, struct usb_request *req);
|
||||
|
||||
static void fastboot_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
int status = req->status;
|
||||
if (!status)
|
||||
return;
|
||||
printf("status: %d ep '%s' trans: %d\n", status, ep->name, req->actual);
|
||||
}
|
||||
|
||||
static int fastboot_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
int id;
|
||||
struct usb_gadget *gadget = c->cdev->gadget;
|
||||
struct f_fastboot *f_fb = func_to_fastboot(f);
|
||||
|
||||
/* DYNAMIC interface numbers assignments */
|
||||
id = usb_interface_id(c, f);
|
||||
if (id < 0)
|
||||
return id;
|
||||
interface_desc.bInterfaceNumber = id;
|
||||
|
||||
id = usb_string_id(c->cdev);
|
||||
if (id < 0)
|
||||
return id;
|
||||
fastboot_string_defs[0].id = id;
|
||||
interface_desc.iInterface = id;
|
||||
|
||||
f_fb->in_ep = usb_ep_autoconfig(gadget, &fs_ep_in);
|
||||
if (!f_fb->in_ep)
|
||||
return -ENODEV;
|
||||
f_fb->in_ep->driver_data = c->cdev;
|
||||
|
||||
f_fb->out_ep = usb_ep_autoconfig(gadget, &fs_ep_out);
|
||||
if (!f_fb->out_ep)
|
||||
return -ENODEV;
|
||||
f_fb->out_ep->driver_data = c->cdev;
|
||||
|
||||
hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fastboot_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
memset(fastboot_func, 0, sizeof(*fastboot_func));
|
||||
}
|
||||
|
||||
static void fastboot_disable(struct usb_function *f)
|
||||
{
|
||||
struct f_fastboot *f_fb = func_to_fastboot(f);
|
||||
|
||||
usb_ep_disable(f_fb->out_ep);
|
||||
usb_ep_disable(f_fb->in_ep);
|
||||
|
||||
if (f_fb->out_req) {
|
||||
free(f_fb->out_req->buf);
|
||||
usb_ep_free_request(f_fb->out_ep, f_fb->out_req);
|
||||
f_fb->out_req = NULL;
|
||||
}
|
||||
if (f_fb->in_req) {
|
||||
free(f_fb->in_req->buf);
|
||||
usb_ep_free_request(f_fb->in_ep, f_fb->in_req);
|
||||
f_fb->in_req = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static struct usb_request *fastboot_start_ep(struct usb_ep *ep)
|
||||
{
|
||||
struct usb_request *req;
|
||||
|
||||
req = usb_ep_alloc_request(ep, 0);
|
||||
if (!req)
|
||||
return NULL;
|
||||
|
||||
req->length = EP_BUFFER_SIZE;
|
||||
req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, EP_BUFFER_SIZE);
|
||||
if (!req->buf) {
|
||||
usb_ep_free_request(ep, req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(req->buf, 0, req->length);
|
||||
return req;
|
||||
}
|
||||
|
||||
static int fastboot_set_alt(struct usb_function *f,
|
||||
unsigned interface, unsigned alt)
|
||||
{
|
||||
int ret;
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
struct f_fastboot *f_fb = func_to_fastboot(f);
|
||||
|
||||
debug("%s: func: %s intf: %d alt: %d\n",
|
||||
__func__, f->name, interface, alt);
|
||||
|
||||
/* make sure we don't enable the ep twice */
|
||||
if (gadget->speed == USB_SPEED_HIGH)
|
||||
ret = usb_ep_enable(f_fb->out_ep, &hs_ep_out);
|
||||
else
|
||||
ret = usb_ep_enable(f_fb->out_ep, &fs_ep_out);
|
||||
if (ret) {
|
||||
puts("failed to enable out ep\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
f_fb->out_req = fastboot_start_ep(f_fb->out_ep);
|
||||
if (!f_fb->out_req) {
|
||||
puts("failed to alloc out req\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
f_fb->out_req->complete = rx_handler_command;
|
||||
|
||||
ret = usb_ep_enable(f_fb->in_ep, &fs_ep_in);
|
||||
if (ret) {
|
||||
puts("failed to enable in ep\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
f_fb->in_req = fastboot_start_ep(f_fb->in_ep);
|
||||
if (!f_fb->in_req) {
|
||||
puts("failed alloc req in\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
f_fb->in_req->complete = fastboot_complete;
|
||||
|
||||
ret = usb_ep_queue(f_fb->out_ep, f_fb->out_req, 0);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
fastboot_disable(f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fastboot_add(struct usb_configuration *c)
|
||||
{
|
||||
struct f_fastboot *f_fb = fastboot_func;
|
||||
int status;
|
||||
|
||||
debug("%s: cdev: 0x%p\n", __func__, c->cdev);
|
||||
|
||||
if (!f_fb) {
|
||||
f_fb = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*f_fb));
|
||||
if (!f_fb)
|
||||
return -ENOMEM;
|
||||
|
||||
fastboot_func = f_fb;
|
||||
memset(f_fb, 0, sizeof(*f_fb));
|
||||
}
|
||||
|
||||
f_fb->usb_function.name = "f_fastboot";
|
||||
f_fb->usb_function.hs_descriptors = fb_runtime_descs;
|
||||
f_fb->usb_function.bind = fastboot_bind;
|
||||
f_fb->usb_function.unbind = fastboot_unbind;
|
||||
f_fb->usb_function.set_alt = fastboot_set_alt;
|
||||
f_fb->usb_function.disable = fastboot_disable;
|
||||
f_fb->usb_function.strings = fastboot_strings;
|
||||
|
||||
status = usb_add_function(c, &f_fb->usb_function);
|
||||
if (status) {
|
||||
free(f_fb);
|
||||
fastboot_func = f_fb;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
DECLARE_GADGET_BIND_CALLBACK(usb_dnl_fastboot, fastboot_add);
|
||||
|
||||
int fastboot_tx_write(const char *buffer, unsigned int buffer_size)
|
||||
{
|
||||
struct usb_request *in_req = fastboot_func->in_req;
|
||||
int ret;
|
||||
|
||||
memcpy(in_req->buf, buffer, buffer_size);
|
||||
in_req->length = buffer_size;
|
||||
ret = usb_ep_queue(fastboot_func->in_ep, in_req, 0);
|
||||
if (ret)
|
||||
printf("Error %d on queue\n", ret);
|
||||
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)
|
||||
{
|
||||
fastboot_func->in_req->complete = compl_do_reset;
|
||||
fastboot_tx_write_str("OKAY");
|
||||
}
|
||||
|
||||
static int strcmp_l1(const char *s1, const char *s2)
|
||||
{
|
||||
if (!s1 || !s2)
|
||||
return -1;
|
||||
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("bootloader-version", cmd)) {
|
||||
strncat(response, U_BOOT_VERSION, sizeof(response));
|
||||
} else if (!strcmp_l1("downloadsize", cmd)) {
|
||||
char str_num[12];
|
||||
|
||||
sprintf(str_num, "%08x", CONFIG_USB_FASTBOOT_BUF_SIZE);
|
||||
strncat(response, str_num, sizeof(response));
|
||||
} else if (!strcmp_l1("serialno", cmd)) {
|
||||
s = getenv("serial#");
|
||||
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 = download_size - 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 0x20000
|
||||
static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
char response[RESPONSE_LEN];
|
||||
unsigned int transfer_size = download_size - download_bytes;
|
||||
const unsigned char *buffer = req->buf;
|
||||
unsigned int buffer_size = req->actual;
|
||||
|
||||
if (req->status != 0) {
|
||||
printf("Bad status: %d\n", req->status);
|
||||
return;
|
||||
}
|
||||
|
||||
if (buffer_size < transfer_size)
|
||||
transfer_size = buffer_size;
|
||||
|
||||
memcpy((void *)CONFIG_USB_FASTBOOT_BUF_ADDR + download_bytes,
|
||||
buffer, transfer_size);
|
||||
|
||||
download_bytes += transfer_size;
|
||||
|
||||
/* Check if transfer is done */
|
||||
if (download_bytes >= download_size) {
|
||||
/*
|
||||
* Reset global transfer variable, keep download_bytes because
|
||||
* it will be used in the next possible flashing command
|
||||
*/
|
||||
download_size = 0;
|
||||
req->complete = rx_handler_command;
|
||||
req->length = EP_BUFFER_SIZE;
|
||||
|
||||
sprintf(response, "OKAY");
|
||||
fastboot_tx_write_str(response);
|
||||
|
||||
printf("\ndownloading of %d bytes finished\n", download_bytes);
|
||||
} else {
|
||||
req->length = rx_bytes_expected();
|
||||
if (req->length < ep->maxpacket)
|
||||
req->length = ep->maxpacket;
|
||||
}
|
||||
|
||||
if (download_bytes && !(download_bytes % BYTES_PER_DOT)) {
|
||||
putc('.');
|
||||
if (!(download_bytes % (74 * BYTES_PER_DOT)))
|
||||
putc('\n');
|
||||
}
|
||||
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, ":");
|
||||
download_size = simple_strtoul(cmd, NULL, 16);
|
||||
download_bytes = 0;
|
||||
|
||||
printf("Starting download of %d bytes\n", download_size);
|
||||
|
||||
if (0 == download_size) {
|
||||
sprintf(response, "FAILdata invalid size");
|
||||
} else if (download_size > CONFIG_USB_FASTBOOT_BUF_SIZE) {
|
||||
download_size = 0;
|
||||
sprintf(response, "FAILdata too large");
|
||||
} else {
|
||||
sprintf(response, "DATA%08x", download_size);
|
||||
req->complete = rx_handler_dl_image;
|
||||
req->length = rx_bytes_expected();
|
||||
if (req->length < ep->maxpacket)
|
||||
req->length = ep->maxpacket;
|
||||
}
|
||||
fastboot_tx_write_str(response);
|
||||
}
|
||||
|
||||
static void do_bootm_on_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
char boot_addr_start[12];
|
||||
char *bootm_args[] = { "bootm", boot_addr_start, NULL };
|
||||
|
||||
puts("Booting kernel..\n");
|
||||
|
||||
sprintf(boot_addr_start, "0x%lx", load_addr);
|
||||
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)
|
||||
{
|
||||
fastboot_func->in_req->complete = do_bootm_on_complete;
|
||||
fastboot_tx_write_str("OKAY");
|
||||
}
|
||||
|
||||
struct cmd_dispatch_info {
|
||||
char *cmd;
|
||||
void (*cb)(struct usb_ep *ep, struct usb_request *req);
|
||||
};
|
||||
|
||||
static const struct cmd_dispatch_info cmd_dispatch_info[] = {
|
||||
{
|
||||
.cmd = "reboot",
|
||||
.cb = cb_reboot,
|
||||
}, {
|
||||
.cmd = "getvar:",
|
||||
.cb = cb_getvar,
|
||||
}, {
|
||||
.cmd = "download:",
|
||||
.cb = cb_download,
|
||||
}, {
|
||||
.cmd = "boot",
|
||||
.cb = cb_boot,
|
||||
},
|
||||
};
|
||||
|
||||
static void rx_handler_command(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
char *cmdbuf = req->buf;
|
||||
void (*func_cb)(struct usb_ep *ep, struct usb_request *req) = NULL;
|
||||
int i;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@ -311,11 +311,7 @@ static struct fsg_lun *fsg_lun_from_dev(struct device *dev)
|
||||
#define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */
|
||||
|
||||
/* Number of buffers we will use. 2 is enough for double-buffering */
|
||||
#ifndef CONFIG_CI_UDC
|
||||
#define FSG_NUM_BUFFERS 2
|
||||
#else
|
||||
#define FSG_NUM_BUFFERS 1 /* ci_udc only allows 1 req per ep at present */
|
||||
#endif
|
||||
|
||||
/* Default size of buffer length. */
|
||||
#define FSG_BUFLEN ((u32)16384)
|
||||
|
@ -576,6 +576,10 @@ static void ep0_txstate(struct musb *musb)
|
||||
} else
|
||||
request = NULL;
|
||||
|
||||
/* send it out, triggering a "txpktrdy cleared" irq */
|
||||
musb_ep_select(musb->mregs, 0);
|
||||
musb_writew(regs, MUSB_CSR0, csr);
|
||||
|
||||
/* report completions as soon as the fifo's loaded; there's no
|
||||
* win in waiting till this last packet gets acked. (other than
|
||||
* very precise fault reporting, needed by USB TMC; possible with
|
||||
@ -588,10 +592,6 @@ static void ep0_txstate(struct musb *musb)
|
||||
return;
|
||||
musb->ackpend = 0;
|
||||
}
|
||||
|
||||
/* send it out, triggering a "txpktrdy cleared" irq */
|
||||
musb_ep_select(musb->mregs, 0);
|
||||
musb_writew(regs, MUSB_CSR0, csr);
|
||||
}
|
||||
|
||||
/*
|
||||
|
69
include/android_image.h
Normal file
69
include/android_image.h
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* This is from the Android Project,
|
||||
* Repository: https://android.googlesource.com/platform/bootable/bootloader/legacy
|
||||
* File: include/boot/bootimg.h
|
||||
* Commit: 4205b865141ff2e255fe1d3bd16de18e217ef06a
|
||||
*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#ifndef _ANDROID_IMAGE_H_
|
||||
#define _ANDROID_IMAGE_H_
|
||||
|
||||
#define ANDR_BOOT_MAGIC "ANDROID!"
|
||||
#define ANDR_BOOT_MAGIC_SIZE 8
|
||||
#define ANDR_BOOT_NAME_SIZE 16
|
||||
#define ANDR_BOOT_ARGS_SIZE 512
|
||||
|
||||
struct andr_img_hdr {
|
||||
char magic[ANDR_BOOT_MAGIC_SIZE];
|
||||
|
||||
u32 kernel_size; /* size in bytes */
|
||||
u32 kernel_addr; /* physical load addr */
|
||||
|
||||
u32 ramdisk_size; /* size in bytes */
|
||||
u32 ramdisk_addr; /* physical load addr */
|
||||
|
||||
u32 second_size; /* size in bytes */
|
||||
u32 second_addr; /* physical load addr */
|
||||
|
||||
u32 tags_addr; /* physical addr for kernel tags */
|
||||
u32 page_size; /* flash page size we assume */
|
||||
u32 unused[2]; /* future expansion: should be 0 */
|
||||
|
||||
char name[ANDR_BOOT_NAME_SIZE]; /* asciiz product name */
|
||||
|
||||
char cmdline[ANDR_BOOT_ARGS_SIZE];
|
||||
|
||||
u32 id[8]; /* timestamp / checksum / sha1 / etc */
|
||||
};
|
||||
|
||||
/*
|
||||
* +-----------------+
|
||||
* | boot header | 1 page
|
||||
* +-----------------+
|
||||
* | kernel | n pages
|
||||
* +-----------------+
|
||||
* | ramdisk | m pages
|
||||
* +-----------------+
|
||||
* | second stage | o pages
|
||||
* +-----------------+
|
||||
*
|
||||
* n = (kernel_size + page_size - 1) / page_size
|
||||
* m = (ramdisk_size + page_size - 1) / page_size
|
||||
* o = (second_size + page_size - 1) / page_size
|
||||
*
|
||||
* 0. all entities are page_size aligned in flash
|
||||
* 1. kernel and ramdisk are required (size != 0)
|
||||
* 2. second is optional (second_size == 0 -> no second)
|
||||
* 3. load each element (kernel, ramdisk, second) at
|
||||
* the specified physical address (kernel_addr, etc)
|
||||
* 4. prepare tags at tag_addr. kernel_args[] is
|
||||
* appended to the kernel commandline in the tags.
|
||||
* 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
|
||||
* 6. if second_size != 0: jump to second_addr
|
||||
* else: jump to kernel_addr
|
||||
*/
|
||||
#endif
|
@ -66,6 +66,16 @@
|
||||
#define CONFIG_TWL4030_USB 1
|
||||
#define CONFIG_USB_ETHER
|
||||
#define CONFIG_USB_ETHER_RNDIS
|
||||
#define CONFIG_USB_GADGET
|
||||
#define CONFIG_USB_GADGET_VBUS_DRAW 0
|
||||
#define CONFIG_USBDOWNLOAD_GADGET
|
||||
#define CONFIG_G_DNL_VENDOR_NUM 0x0451
|
||||
#define CONFIG_G_DNL_PRODUCT_NUM 0xd022
|
||||
#define CONFIG_G_DNL_MANUFACTURER "TI"
|
||||
#define CONFIG_CMD_FASTBOOT
|
||||
#define CONFIG_ANDROID_BOOT_IMAGE
|
||||
#define CONFIG_USB_FASTBOOT_BUF_ADDR CONFIG_SYS_LOAD_ADDR
|
||||
#define CONFIG_USB_FASTBOOT_BUF_SIZE 0x07000000
|
||||
|
||||
/* USB EHCI */
|
||||
#define CONFIG_CMD_USB
|
||||
|
@ -100,6 +100,7 @@ struct dfu_entity {
|
||||
u64 offset, void *buf, long *len);
|
||||
|
||||
int (*flush_medium)(struct dfu_entity *dfu);
|
||||
unsigned int (*poll_timeout)(struct dfu_entity *dfu);
|
||||
|
||||
struct list_head list;
|
||||
|
||||
|
@ -413,6 +413,7 @@ enum fit_load_op {
|
||||
#define IMAGE_FORMAT_INVALID 0x00
|
||||
#define IMAGE_FORMAT_LEGACY 0x01 /* legacy image_header based format */
|
||||
#define IMAGE_FORMAT_FIT 0x02 /* new, libfdt based format */
|
||||
#define IMAGE_FORMAT_ANDROID 0x03 /* Android boot image */
|
||||
|
||||
int genimg_get_format(const void *img_addr);
|
||||
int genimg_has_config(bootm_headers_t *images);
|
||||
@ -1031,4 +1032,16 @@ static inline int fit_image_check_target_arch(const void *fdt, int node)
|
||||
#endif /* CONFIG_FIT_VERBOSE */
|
||||
#endif /* CONFIG_FIT */
|
||||
|
||||
#if defined(CONFIG_ANDROID_BOOT_IMAGE)
|
||||
struct andr_img_hdr;
|
||||
int android_image_check_header(const struct andr_img_hdr *hdr);
|
||||
int android_image_get_kernel(const struct andr_img_hdr *hdr, int verify,
|
||||
ulong *os_data, ulong *os_len);
|
||||
int android_image_get_ramdisk(const struct andr_img_hdr *hdr,
|
||||
ulong *rd_data, ulong *rd_len);
|
||||
ulong android_image_get_end(const struct andr_img_hdr *hdr);
|
||||
ulong android_image_get_kload(const struct andr_img_hdr *hdr);
|
||||
|
||||
#endif /* CONFIG_ANDROID_BOOT_IMAGE */
|
||||
|
||||
#endif /* __IMAGE_H__ */
|
||||
|
@ -9,17 +9,9 @@
|
||||
#define __USB_MASS_STORAGE_H__
|
||||
|
||||
#define SECTOR_SIZE 0x200
|
||||
#include <mmc.h>
|
||||
#include <part.h>
|
||||
#include <linux/usb/composite.h>
|
||||
|
||||
#ifndef UMS_START_SECTOR
|
||||
#define UMS_START_SECTOR 0
|
||||
#endif
|
||||
|
||||
#ifndef UMS_NUM_SECTORS
|
||||
#define UMS_NUM_SECTORS 0
|
||||
#endif
|
||||
|
||||
/* Wait at maximum 60 seconds for cable connection */
|
||||
#define UMS_CABLE_READY_TIMEOUT 60
|
||||
|
||||
@ -31,14 +23,13 @@ struct ums {
|
||||
unsigned int start_sector;
|
||||
unsigned int num_sectors;
|
||||
const char *name;
|
||||
struct mmc *mmc;
|
||||
block_dev_desc_t *block_dev;
|
||||
};
|
||||
|
||||
extern struct ums *ums;
|
||||
|
||||
int fsg_init(struct ums *);
|
||||
void fsg_cleanup(void);
|
||||
struct ums *ums_init(unsigned int);
|
||||
int fsg_main_thread(void *);
|
||||
int fsg_add(struct usb_configuration *c);
|
||||
#endif /* __USB_MASS_STORAGE_H__ */
|
||||
|
Loading…
Reference in New Issue
Block a user