summaryrefslogtreecommitdiff
path: root/drivers/usb/gadget/f_fastboot.c
diff options
context:
space:
mode:
authormattis fjallstrom <mattis@acm.org>2015-09-06 11:09:39 -0700
committermattis fjallstrom <mattis@acm.org>2015-09-06 11:09:39 -0700
commitee87ec4d2c955c929a4c27ce8a56f918444cf955 (patch)
treec18141755ad893eddd5b504e4cf096ad4e7062b9 /drivers/usb/gadget/f_fastboot.c
parent2c25de1ed5c6f8f6bba3b5ec506f430d8a883a83 (diff)
downloadolio-uboot-2014.01-ee87ec4d2c955c929a4c27ce8a56f918444cf955.tar.xz
olio-uboot-2014.01-ee87ec4d2c955c929a4c27ce8a56f918444cf955.zip
First fastboot commit, MLO built here wont work so be careful.
Change-Id: Ic8d65a92da82896282eee71cf0d0515f64c939bc
Diffstat (limited to 'drivers/usb/gadget/f_fastboot.c')
-rw-r--r--drivers/usb/gadget/f_fastboot.c609
1 files changed, 609 insertions, 0 deletions
diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c
new file mode 100644
index 000000000..fb341d399
--- /dev/null
+++ b/drivers/usb/gadget/f_fastboot.c
@@ -0,0 +1,609 @@
+/*
+ * The file is based on content which was
+ *
+ * (C) Copyright 2008 - 2009
+ * Windriver, <www.windriver.com>
+ * Tom Rix <Tom.Rix at windriver.com>
+ *
+ * Copyright (c) 2011 Sebastian Andrzej Siewior <bigeasy at linutronix.de>
+ *
+ * Modified by Vishveshwar Bhat <vishevshwar.bhat@ti.com>
+ *
+ * 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
+ *
+ * Some part of this code is based on ideas from Android AOSP project
+ * bootable/bootloader/legacy/
+ * 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
+ * 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 <errno.h>
+#include <usb/fastboot.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/compiler.h>
+
+#include "g_fastboot.h"
+
+#ifdef CONFIG_USB_GADGET_DUALSPEED
+#define DEVSPEED USB_SPEED_HIGH
+#else
+#define DEVSPEED USB_SPEED_FULL
+#endif
+
+#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 usb_gadget_connect(gadget);
+err:
+ return -1;
+}
+
+static void fastboot_unbind(struct usb_gadget *gadget)
+{
+ usb_gadget_disconnect(gadget);
+ usb_ep_free_request(g->ep0, ep0_req);
+ ep_in->driver_data = NULL;
+ ep_out->driver_data = NULL;
+}
+
+struct usb_device_descriptor fb_descriptor = {
+ .bLength = sizeof(fb_descriptor),
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = 0x200,
+ .bMaxPacketSize0 = 0x40,
+ .idVendor = FASTBOOT_DEVICE_VENDOR_ID,
+ .idProduct = FASTBOOT_DEVICE_PRODUCT_ID,
+ .bcdDevice = FASTBOOT_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;
+
+ FBTINFO("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 = {
+ .speed = DEVSPEED,
+ .bind = fastboot_bind,
+ .unbind = fastboot_unbind,
+ .setup = fastboot_setup,
+ .disconnect = fastboot_disconnect,
+};
+
+/* FIXME: move this board-specific code somewhere else */
+#define BEAGLEBONEBLACK_U0_LED_GPIO 53
+static void turn_fastboot_led_on(void)
+{
+ printf("olio fastboot enter\n");
+ /*
+ if (!gpio_request(BEAGLEBONEBLACK_U0_LED_GPIO, "fastboot")) {
+ gpio_direction_output(BEAGLEBONEBLACK_U0_LED_GPIO, 0);
+ gpio_set_value(BEAGLEBONEBLACK_U0_LED_GPIO, 1);
+ } else {
+ printf("gpio_request %d failed\n", BEAGLEBONEBLACK_U0_LED_GPIO);
+ }
+ */
+}
+
+static void turn_fastboot_led_off(void)
+{
+ printf("olio fastboot exit\n");
+ //gpio_set_value(BEAGLEBONEBLACK_U0_LED_GPIO, 0);
+}
+
+int fastboot_init(void)
+{
+ int ret;
+
+ printf("func: fastboot_init \n");
+
+ ret = fastboot_board_init(&fastboot_cfg, &vendor_fb_strings);
+ if (ret)
+ return ret;
+ if (!vendor_fb_strings)
+ return -EINVAL;
+
+ set_fb_config(&fastboot_cfg);
+ turn_fastboot_led_on();
+ 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)
+{
+ turn_fastboot_led_off();
+}
+
+int fastboot_tx_write(const char *buffer, unsigned int buffer_size)
+{
+ int ret = 0;
+
+ 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 ret;
+}