This patch contains an implementation of the fastboot protocol on the device side and a little of documentation. The gadget expects the new-style gadget framework. The gadget implements the getvar, reboot, download and reboot commands. What is missing is the flash handling i.e. writting the image to media.
Signed-off-by: Sebastian Andrzej Siewior <bige...@linutronix.de> --- common/Makefile | 2 + common/cmd_fastboot.c | 28 ++ doc/README.android-fastboot | 86 ++++++ doc/README.android-fastboot-protocol | 170 +++++++++++ drivers/usb/gadget/Makefile | 5 + drivers/usb/gadget/f_fastboot.c | 549 ++++++++++++++++++++++++++++++++++ drivers/usb/gadget/g_fastboot.h | 20 ++ drivers/usb/gadget/u_fastboot.c | 308 +++++++++++++++++++ include/usb/fastboot.h | 95 ++++++ 9 files changed, 1263 insertions(+), 0 deletions(-) create mode 100644 common/cmd_fastboot.c create mode 100644 doc/README.android-fastboot create mode 100644 doc/README.android-fastboot-protocol create mode 100644 drivers/usb/gadget/f_fastboot.c create mode 100644 drivers/usb/gadget/g_fastboot.h create mode 100644 drivers/usb/gadget/u_fastboot.c create mode 100644 include/usb/fastboot.h diff --git a/common/Makefile b/common/Makefile index d662468..da40d64 100644 --- a/common/Makefile +++ b/common/Makefile @@ -157,6 +157,8 @@ COBJS-y += cmd_usb.o COBJS-y += usb.o COBJS-$(CONFIG_USB_STORAGE) += usb_storage.o endif +COBJS-$(CONFIG_CMD_FASTBOOT) += cmd_fastboot.o + COBJS-$(CONFIG_CMD_XIMG) += cmd_ximg.o COBJS-$(CONFIG_YAFFS2) += cmd_yaffs2.o diff --git a/common/cmd_fastboot.c b/common/cmd_fastboot.c new file mode 100644 index 0000000..9a656dd --- /dev/null +++ b/common/cmd_fastboot.c @@ -0,0 +1,28 @@ +#include <common.h> +#include <command.h> +#include <usb/fastboot.h> + +static int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + int ret = 1; + + if (!fastboot_init()) { + printf("Fastboot entered...\n"); + + ret = 0; + + while (1) { + if (fastboot_poll()) + break; + } + } + + fastboot_shutdown(); + return ret; +} + +U_BOOT_CMD( + fastboot, 1, 1, do_fastboot, + "fastboot- use USB Fastboot protocol\n", + "" +); diff --git a/doc/README.android-fastboot b/doc/README.android-fastboot new file mode 100644 index 0000000..4b2a9aa --- /dev/null +++ b/doc/README.android-fastboot @@ -0,0 +1,86 @@ +Android Fastboot +~~~~~~~~~~~~~~~~ + +Overview +======== +The protocol that is used over USB is described in +README.android-fastboot-protocol in same folder. + +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 gadget calls at probe time the function fastboot_board_init() which +should be provided by the board to setup its specific configuration. +It is possible here to overwrite specific strings like Vendor or Serial +number. Strings which are not specified here will return a default value. +This init function must also provide a memory area for the +"transfer_buffer" and its size. This buffer should be large enough to hold +whatever the download commands is willing to send or it will fail. This +can be a kernel image for booting which could be around two MiB or a flash +partition which could be slightly larger :) + +In Action +========= +Enter into fastboot by executing the fastboot command in u-boot and you +should see: +|Fastboot entered... + +The gadget terminates once the is unplugged. On the client side you can +fetch the product name for instance: +|>fastboot getvar product +|product: Default Product +|finished. total time: 0.016s + +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 ... diff --git a/doc/README.android-fastboot-protocol b/doc/README.android-fastboot-protocol new file mode 100644 index 0000000..e9e7166 --- /dev/null +++ b/doc/README.android-fastboot-protocol @@ -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. diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 7d5b504..75d4a12 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -42,6 +42,11 @@ COBJS-$(CONFIG_SPEARUDC) += spr_udc.o endif endif +ifdef CONFIG_CMD_FASTBOOT +COBJS-y += f_fastboot.o u_fastboot.o +COBJS-y += epautoconf.o usbstring.o +endif + COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) OBJS := $(addprefix $(obj),$(COBJS)) diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c new file mode 100644 index 0000000..6c3cae5 --- /dev/null +++ b/drivers/usb/gadget/f_fastboot.c @@ -0,0 +1,549 @@ +/* + * The file is based on content which was + * (C) Copyright 2008 - 2009 + * Windriver, <www.windriver.com> + * Tom Rix <tom....@windriver.com> + * + * but after the chainsaw attack by + * Copyright (c) 2011 Sebastian Andrzej Siewior <bige...@linutronix.de> + * + * there isn't much left. The license is in both cases GPLv2 as published + * by the FSF + */ +#include <common.h> +#include <errno.h> +#include <usb/fastboot.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/compiler.h> + +#include "g_fastboot.h" + +#define CONFIGURATION_NORMAL 1 +#define BULK_ENDPOINT 1 +#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) + +static struct usb_string def_usb_fb_strings[] = { + { FB_STR_PRODUCT_IDX, "Default Product" }, + { FB_STR_SERIAL_IDX, "1234567890" }, + { FB_STR_CONFIG_IDX, "Android Fastboot" }, + { FB_STR_INTERFACE_IDX, "Android Fastboot" }, + { FB_STR_MANUFACTURER_IDX, "Default Manufacturer" }, + { FB_STR_PROC_REV_IDX, "Default 1.0" }, + { FB_STR_PROC_TYPE_IDX, "Emulator" }, + { } +}; + +static struct usb_gadget_strings def_fb_strings = { + .language = 0x0409, /* en-us */ + .strings = def_usb_fb_strings, +}; + +static struct usb_gadget_strings *vendor_fb_strings; + +static unsigned int gadget_is_connected; + +static u8 ep0_buffer[512]; +static u8 ep_out_buffer[EP_BUFFER_SIZE]; +static u8 ep_in_buffer[EP_BUFFER_SIZE]; +static int current_config; + +/* e1 */ +static struct usb_endpoint_descriptor fs_ep_in = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, /* IN */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = TX_ENDPOINT_MAXIMUM_PACKET_SIZE, + .bInterval = 0x00, +}; + +/* e2 */ +static struct usb_endpoint_descriptor fs_ep_out = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, /* 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, /* OUT */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0, + .bInterval = 0x00, +}; + +const char *fb_find_usb_string(unsigned int id) +{ + struct usb_string *s; + + for (s = vendor_fb_strings->strings; s && s->s; s++) { + if (s->id == id) + break; + } + if (!s || !s->s) { + for (s = def_fb_strings.strings; s && s->s; s++) { + if (s->id == id) + break; + } + } + if (!s) + return NULL; + return s->s; +} + +static struct usb_gadget *g; +static struct usb_request *ep0_req; + +struct usb_ep *ep_in; +struct usb_request *req_in; + +struct usb_ep *ep_out; +struct usb_request *req_out; + +static void fastboot_ep0_complete(struct usb_ep *ep, struct usb_request *req) +{ + int status = req->status; + + if (!status) + return; + printf("ep0 status %d\n", status); +} + +static int fastboot_bind(struct usb_gadget *gadget) +{ + + g = gadget; + ep0_req = usb_ep_alloc_request(g->ep0, 0); + if (!ep0_req) + goto err; + ep0_req->buf = ep0_buffer; + ep0_req->complete = fastboot_ep0_complete; + + ep_in = usb_ep_autoconfig(gadget, &fs_ep_in); + if (!ep_in) + goto err; + ep_in->driver_data = ep_in; + + ep_out = usb_ep_autoconfig(gadget, &fs_ep_out); + if (!ep_out) + goto err; + ep_out->driver_data = ep_out; + + hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress; + return 0; +err: + return -1; +} + +static void fastboot_unbind(struct usb_gadget *gadget) +{ + usb_ep_free_request(g->ep0, ep0_req); + ep_in->driver_data = NULL; + ep_out->driver_data = NULL; +} + +/* This is the TI USB vendor id a product ID from TI's internal tree */ +#define DEVICE_VENDOR_ID 0x0451 +#define DEVICE_PRODUCT_ID 0xd022 +#define DEVICE_BCD 0x0100 + +struct usb_device_descriptor fb_descriptor = { + .bLength = sizeof(fb_descriptor), + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = 0x200, + .bMaxPacketSize0 = 0x40, + .idVendor = DEVICE_VENDOR_ID, + .idProduct = DEVICE_PRODUCT_ID, + .bcdDevice = DEVICE_BCD, + .iManufacturer = FB_STR_MANUFACTURER_IDX, + .iProduct = FB_STR_PRODUCT_IDX, + .iSerialNumber = FB_STR_SERIAL_IDX, + .bNumConfigurations = 1, +}; + +#define TOT_CFG_DESC_LEN (USB_DT_CONFIG_SIZE + USB_DT_INTERFACE_SIZE + \ + USB_DT_ENDPOINT_SIZE + USB_DT_ENDPOINT_SIZE) + +static struct usb_config_descriptor config_desc = { + .bLength = USB_DT_CONFIG_SIZE, + .bDescriptorType = USB_DT_CONFIG, + .wTotalLength = cpu_to_le16(TOT_CFG_DESC_LEN), + .bNumInterfaces = 1, + .bConfigurationValue = CONFIGURATION_NORMAL, + .iConfiguration = FB_STR_CONFIG_IDX, + .bmAttributes = 0xc0, + .bMaxPower = 0x32, +}; + +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, + .iInterface = FB_STR_INTERFACE_IDX, +}; + +static struct usb_qualifier_descriptor qual_desc = { + .bLength = sizeof(qual_desc), + .bDescriptorType = USB_DT_DEVICE_QUALIFIER, + .bcdUSB = 0x200, + .bMaxPacketSize0 = 0x40, + .bNumConfigurations = 1, +}; + +static int fastboot_setup_get_descr(struct usb_gadget *gadget, + const struct usb_ctrlrequest *ctrl) +{ + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + u16 val; + int ret; + u32 bytes_remaining; + u32 bytes_total; + u32 this_inc; + + val = w_value >> 8; + + switch (val) { + case USB_DT_DEVICE: + + memcpy(ep0_buffer, &fb_descriptor, sizeof(fb_descriptor)); + ep0_req->length = min(w_length, sizeof(fb_descriptor)); + ret = usb_ep_queue(gadget->ep0, ep0_req, 0); + break; + + case USB_DT_CONFIG: + + bytes_remaining = min(w_length, sizeof(ep0_buffer)); + bytes_total = 0; + + /* config */ + this_inc = min(bytes_remaining, USB_DT_CONFIG_SIZE); + bytes_remaining -= this_inc; + memcpy(ep0_buffer + bytes_total, &config_desc, this_inc); + bytes_total += this_inc; + + /* interface */ + this_inc = min(bytes_remaining, USB_DT_INTERFACE_SIZE); + bytes_remaining -= this_inc; + memcpy(ep0_buffer + bytes_total, &interface_desc, this_inc); + bytes_total += this_inc; + + /* ep in */ + this_inc = min(bytes_remaining, USB_DT_ENDPOINT_SIZE); + bytes_remaining -= this_inc; + memcpy(ep0_buffer + bytes_total, &fs_ep_in, this_inc); + bytes_total += this_inc; + + /* ep out */ + this_inc = min(bytes_remaining, USB_DT_ENDPOINT_SIZE); + + if (gadget->speed == USB_SPEED_HIGH) + memcpy(ep0_buffer + bytes_total, &hs_ep_out, + this_inc); + else + memcpy(ep0_buffer + bytes_total, &fs_ep_out, + this_inc); + bytes_total += this_inc; + + ep0_req->length = bytes_total; + ret = usb_ep_queue(gadget->ep0, ep0_req, 0); + break; + + case USB_DT_STRING: + + ret = usb_gadget_get_string(vendor_fb_strings, + w_value & 0xff, ep0_buffer); + if (ret < 0) + ret = usb_gadget_get_string(&def_fb_strings, + w_value & 0xff, ep0_buffer); + if (ret < 0) + break; + + ep0_req->length = ret; + ret = usb_ep_queue(gadget->ep0, ep0_req, 0); + break; + + case USB_DT_DEVICE_QUALIFIER: + + memcpy(ep0_buffer, &qual_desc, sizeof(qual_desc)); + ep0_req->length = min(w_length, sizeof(qual_desc)); + ret = usb_ep_queue(gadget->ep0, ep0_req, 0); + break; + default: + ret = -EINVAL; + } + return ret; +} + +static int fastboot_setup_get_conf(struct usb_gadget *gadget, + const struct usb_ctrlrequest *ctrl) +{ + u16 w_length = le16_to_cpu(ctrl->wLength); + + if (w_length == 0) + return -1; + + ep0_buffer[0] = current_config; + ep0_req->length = 1; + return usb_ep_queue(gadget->ep0, ep0_req, 0); +} + +static void fastboot_complete_in(struct usb_ep *ep, struct usb_request *req) +{ + int status = req->status; + + if (status) + printf("status: %d ep_in trans: %d\n", + status, + req->actual); +} + +static int fastboot_disable_ep(struct usb_gadget *gadget) +{ + if (req_out) { + usb_ep_free_request(ep_out, req_out); + req_out = NULL; + } + if (req_in) { + usb_ep_free_request(ep_in, req_in); + req_in = NULL; + } + usb_ep_disable(ep_out); + usb_ep_disable(ep_in); + + return 0; +} + +static int fastboot_enable_ep(struct usb_gadget *gadget) +{ + int ret; + + /* make sure we don't enable the ep twice */ + if (gadget->speed == USB_SPEED_HIGH) + ret = usb_ep_enable(ep_out, &hs_ep_out); + else + ret = usb_ep_enable(ep_out, &fs_ep_out); + if (ret) { + printf("failed to enable out ep\n"); + goto err; + } + + req_out = usb_ep_alloc_request(ep_out, 0); + if (!req_out) { + printf("failed to alloc out req\n"); + goto err; + } + + ret = usb_ep_enable(ep_in, &fs_ep_in); + if (ret) { + printf("failed to enable in ep\n"); + goto err; + } + req_in = usb_ep_alloc_request(ep_in, 0); + if (!req_in) { + printf("failed alloc req in\n"); + goto err; + } + + req_out->complete = rx_handler_command; + req_out->buf = ep_out_buffer; + req_out->length = sizeof(ep_out_buffer); + + req_in->buf = ep_in_buffer; + req_in->length = sizeof(ep_in_buffer); + + ret = usb_ep_queue(ep_out, req_out, 0); + if (ret) + goto err; + + return 0; +err: + fastboot_disable_ep(gadget); + return -1; +} + +static int fastboot_set_interface(struct usb_gadget *gadget, u32 enable) +{ + if (enable && req_out) + return 0; + if (!enable && !req_out) + return 0; + + if (enable) + return fastboot_enable_ep(gadget); + else + return fastboot_disable_ep(gadget); +} + +static int fastboot_setup_out_req(struct usb_gadget *gadget, + const struct usb_ctrlrequest *req) +{ + switch (req->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + switch (req->bRequest) { + case USB_REQ_SET_CONFIGURATION: + + ep0_req->length = 0; + if (req->wValue == CONFIGURATION_NORMAL) { + current_config = CONFIGURATION_NORMAL; + fastboot_set_interface(gadget, 1); + return usb_ep_queue(gadget->ep0, + ep0_req, 0); + } + if (req->wValue == 0) { + current_config = 0; + fastboot_set_interface(gadget, 0); + return usb_ep_queue(gadget->ep0, + ep0_req, 0); + } + return -1; + break; + default: + return -1; + }; + + case USB_RECIP_INTERFACE: + switch (req->bRequest) { + case USB_REQ_SET_INTERFACE: + + ep0_req->length = 0; + if (!fastboot_set_interface(gadget, 1)) + return usb_ep_queue(gadget->ep0, + ep0_req, 0); + return -1; + break; + default: + return -1; + } + + case USB_RECIP_ENDPOINT: + switch (req->bRequest) { + case USB_REQ_CLEAR_FEATURE: + + return usb_ep_queue(gadget->ep0, ep0_req, 0); + break; + default: + return -1; + } + } + return -1; +} + +static int fastboot_setup(struct usb_gadget *gadget, + const struct usb_ctrlrequest *req) +{ + if ((req->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) + return -1; + + if ((req->bRequestType & USB_DIR_IN) == 0) + /* host-to-device */ + return fastboot_setup_out_req(gadget, req); + + /* device-to-host */ + if ((req->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) { + switch (req->bRequest) { + case USB_REQ_GET_DESCRIPTOR: + return fastboot_setup_get_descr(gadget, req); + break; + + case USB_REQ_GET_CONFIGURATION: + return fastboot_setup_get_conf(gadget, req); + break; + default: + return -1; + } + } + return -1; +} + +static void fastboot_disconnect(struct usb_gadget *gadget) +{ + fastboot_disable_ep(gadget); + gadget_is_connected = 0; +} + +struct usb_gadget_driver fast_gadget = { + .bind = fastboot_bind, + .unbind = fastboot_unbind, + .setup = fastboot_setup, + .disconnect = fastboot_disconnect, +}; + +static int udc_is_probbed; + +int fastboot_init(void) +{ + int ret; + + ret = fastboot_board_init(&fb_cfg, &vendor_fb_strings); + if (ret) + return ret; + if (!vendor_fb_strings) + return -EINVAL; + + ret = usb_gadget_init_udc(); + if (ret) { + printf("gadget probe failed\n"); + return 1; + } + udc_is_probbed = 1; + + ret = usb_gadget_register_driver(&fast_gadget); + if (ret) { + printf("Add gadget failed\n"); + goto err; + } + + gadget_is_connected = 1; + usb_gadget_handle_interrupts(); + return 0; + +err: + fastboot_shutdown(); + return 1; +} + +int fastboot_poll(void) +{ + usb_gadget_handle_interrupts(); + + if (gadget_is_connected) + return 0; + else + return 1; +} + +void fastboot_shutdown(void) +{ + if (!udc_is_probbed) + return; + udc_is_probbed = 0; + usb_gadget_exit_udc(); +} + +int fastboot_tx_write(const char *buffer, unsigned int buffer_size) +{ + int ret; + + if (req_in->complete == NULL) + req_in->complete = fastboot_complete_in; + + memcpy(req_in->buf, buffer, buffer_size); + req_in->length = buffer_size; + ret = usb_ep_queue(ep_in, req_in, 0); + if (ret) + printf("Error %d on queue\n", ret); + return 0; +} diff --git a/drivers/usb/gadget/g_fastboot.h b/drivers/usb/gadget/g_fastboot.h new file mode 100644 index 0000000..ff2621c --- /dev/null +++ b/drivers/usb/gadget/g_fastboot.h @@ -0,0 +1,20 @@ +#ifndef _G_FASTBOOT_H_ +#define _G_FASTBOOT_H_ + +#define EP_BUFFER_SIZE 4096 +#define FASTBOOT_INTERFACE_CLASS 0xff +#define FASTBOOT_INTERFACE_SUB_CLASS 0x42 +#define FASTBOOT_INTERFACE_PROTOCOL 0x03 +#define FASTBOOT_VERSION "0.4" + +extern struct fastboot_config fb_cfg; +extern struct usb_ep *ep_in; +extern struct usb_request *req_in; +extern struct usb_ep *ep_out; +extern struct usb_request *req_out; + +void rx_handler_command(struct usb_ep *ep, struct usb_request *req); +int fastboot_tx_write(const char *buffer, unsigned int buffer_size); +const char *fb_find_usb_string(unsigned int id); + +#endif diff --git a/drivers/usb/gadget/u_fastboot.c b/drivers/usb/gadget/u_fastboot.c new file mode 100644 index 0000000..00762aa --- /dev/null +++ b/drivers/usb/gadget/u_fastboot.c @@ -0,0 +1,308 @@ +/* + * (C) Copyright 2008 - 2009 + * Windriver, <www.windriver.com> + * Tom Rix <tom....@windriver.com> + * + * Copyright (c) 2011 Sebastian Andrzej Siewior <bige...@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 <usb/fastboot.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include "g_fastboot.h" + +/* The 64 defined bytes plus \0 */ +#define RESPONSE_LEN (64 + 1) + +struct fastboot_config fb_cfg; + +static unsigned int download_size; +static unsigned int download_bytes; + +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) +{ + 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 = 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 32768 +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(fb_cfg.transfer_buffer + 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 (download_bytes && !(download_bytes % BYTES_PER_DOT)) { + printf("."); + if (!(download_bytes % (74 * BYTES_PER_DOT))) + printf("\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 > + fb_cfg.transfer_buffer_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(); + } + 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; +} + +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", + .cb = cb_reboot, + }, { + .cmd = "getvar:", + .cb = cb_getvar, + }, { + .cmd = "download:", + .cb = cb_download, + }, { + .cmd = "boot", + .cb = cb_boot, + }, +}; + +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"); + + 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); + } +} diff --git a/include/usb/fastboot.h b/include/usb/fastboot.h new file mode 100644 index 0000000..bc2fd2d --- /dev/null +++ b/include/usb/fastboot.h @@ -0,0 +1,95 @@ +/* + * (C) Copyright 2008 - 2009 + * Windriver, <www.windriver.com> + * Tom Rix <tom....@windriver.com> + * + * Copyright (c) 2011 Sebastian Andrzej Siewior <bige...@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. + * + * The logical naming of flash comes from the Android project + * Thse structures and functions that look like fastboot_flash_* + * They come from bootloader/legacy/include/boot/flash.h + * + * The boot_img_hdr structure and associated magic numbers also + * come from the Android project. They are from + * bootloader/legacy/include/boot/bootimg.h + * + * Here are their copyrights + * + * 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. + * + */ + +#ifndef FASTBOOT_H +#define FASTBOOT_H + +#include <common.h> +#include <command.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <android_image.h> + +struct fastboot_config { + + /* + * Transfer buffer for storing data sent by the client. It should be + * able to hold a kernel image and flash partitions. Care should be + * take so it does not overrun bootloader memory + */ + unsigned char *transfer_buffer; + + /* Size of the buffer mentioned above */ + unsigned int transfer_buffer_size; +}; + +#define FB_STR_PRODUCT_IDX 1 +#define FB_STR_SERIAL_IDX 2 +#define FB_STR_CONFIG_IDX 3 +#define FB_STR_INTERFACE_IDX 4 +#define FB_STR_MANUFACTURER_IDX 5 +#define FB_STR_PROC_REV_IDX 6 +#define FB_STR_PROC_TYPE_IDX 7 + +#if (CONFIG_CMD_FASTBOOT) + +int fastboot_init(void); +void fastboot_shutdown(void); +int fastboot_poll(void); + +int fastboot_board_init(struct fastboot_config *interface, + struct usb_gadget_strings **str); +#endif +#endif -- 1.7.5.4 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot