From 2813006fecdab740c3745b2dcbb06777cdd51dff Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 5 May 2014 17:48:11 -0600 Subject: usb: ci_udc: allow multiple buffer allocs per ep Modify ci_ep_alloc_request() to return a dynamically allocated request object, rather than a singleton that's part of the endpoint. This requires moving various state from the endpoint structure to the request structure, since we need one copy per request. The "fast bounce buffer" b_fast is removed by this change rather than moved to the request object. Instead, we enhance the bounce buffer logic in ci_bounce()/ci_debounce() to keep the bounce buffer around between request submissions. This avoids the need to allocate an arbitrarily- sized bounce buffer up-front, yet avoids incurring the allocation overhead each time a request is submitted. A future enhancement would be to actually submit multiple requests to HW at once. The Linux driver shows that this is possible. That might improve throughput (depending on the USB protocol in use), since USB could be performing a transfer to one HW buffer in parallel with whatever SW actions U-Boot performs on another buffer. However, I have not made this change as part of this patch, in order to keep SW changes related to buffer management separate from any change in the way the HW is programmed. Signed-off-by: Stephen Warren diff --git a/drivers/usb/gadget/ci_udc.c b/drivers/usb/gadget/ci_udc.c index 02d3fda..290863d 100644 --- a/drivers/usb/gadget/ci_udc.c +++ b/drivers/usb/gadget/ci_udc.c @@ -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; + + aligned_used_len = roundup(req->actual, ARCH_DMA_MINALIGN); + invalidate_dcache_range(hwaddr, hwaddr + aligned_used_len); - if (addr == ba) - return; /* not a bounce */ + if (addr == hwaddr) + return; /* not a bounce */ - 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); + 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); + ci_req = list_first_entry(&ep->queue, struct ci_req, queue); + list_del_init(&ci_req->queue); + ep->req_primed = false; + + if (!list_empty(&ep->queue)) + ci_ep_submit_next_request(ep); + len = (item->info >> 16) & 0x7fff; - ep->req.actual = ep->req.length - len; - ci_debounce(ep, in); + ci_req->req.actual = ci_req->req.length - len; + ci_debounce(ci_req, in); - DBG("ept%d %s complete %x\n", - num, in ? "in" : "out", len); - ep->req.complete(&ep->ep, &ep->req); + 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); } diff --git a/drivers/usb/gadget/ci_udc.h b/drivers/usb/gadget/ci_udc.h index 4425fd9..23cff56 100644 --- a/drivers/usb/gadget/ci_udc.h +++ b/drivers/usb/gadget/ci_udc.h @@ -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 { -- cgit v0.10.2 From 340ed422d2a58c4858ba8e110c233e8ae92c04e0 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 5 May 2014 17:48:12 -0600 Subject: usb: ums: remove ci_udc special case Now that the ci_udc driver supports allocating multiple requests per endpoint, we can revert the special-case added by a022c1e13c01 "usb: ums: use only 1 buffer for CI_UDC". Signed-off-by: Stephen Warren diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index 7430074..02803df 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -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) -- cgit v0.10.2 From 66b88b07cdab041ac0cf6a9bf5b131aedc248089 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Thu, 1 May 2014 15:42:10 -0600 Subject: usb: ums: add error handling for failed registration Without this, if g_dnl_register() fails, the UMS code continues on blindly and crashes. This fix makes it simply print an error message instead. Signed-off-by: Stephen Warren diff --git a/common/cmd_usb_mass_storage.c b/common/cmd_usb_mass_storage.c index d8d9efd..31b7eb0 100644 --- a/common/cmd_usb_mass_storage.c +++ b/common/cmd_usb_mass_storage.c @@ -40,7 +40,11 @@ int do_usb_mass_storage(cmd_tbl_t *cmdtp, int flag, 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; -- cgit v0.10.2 From ce19d974018273c8f69d98b7139e7fef822cc02a Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 5 May 2014 10:40:11 -0600 Subject: ums: support block devices not MMC devices The USB Mass Storage function could equally well support a SATA device as support an MMC device. Update struct ums to contain a block device descriptor, not an MMC device descriptor. Cc: Lukasz Majewski Signed-off-by: Stephen Warren Acked-by: Przemyslaw Marczak diff --git a/board/samsung/common/ums.c b/board/samsung/common/ums.c index cebabe9..ffe6368 100644 --- a/board/samsung/common/ums.c +++ b/board/samsung/common/ums.c @@ -7,12 +7,13 @@ #include #include +#include #include 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; + block_dev_desc_t *block_dev = ums_dev->block_dev; lbaint_t blkstart = start + ums_dev->start_sector; int dev_num = block_dev->dev; @@ -22,7 +23,7 @@ static int ums_read_sector(struct ums *ums_dev, 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; + block_dev_desc_t *block_dev = ums_dev->block_dev; lbaint_t blkstart = start + ums_dev->start_sector; int dev_num = block_dev->dev; @@ -45,7 +46,7 @@ static struct ums *ums_disk_init(struct mmc *mmc) return NULL; } - ums_dev.mmc = mmc; + ums_dev.block_dev = &mmc->block_dev; if (ums_end_sector <= mmc_end_sector) { ums_dev.start_sector = UMS_START_SECTOR; diff --git a/include/usb_mass_storage.h b/include/usb_mass_storage.h index ed46064..e3eb1eb 100644 --- a/include/usb_mass_storage.h +++ b/include/usb_mass_storage.h @@ -9,7 +9,7 @@ #define __USB_MASS_STORAGE_H__ #define SECTOR_SIZE 0x200 -#include +#include #include #ifndef UMS_START_SECTOR @@ -31,7 +31,7 @@ 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; -- cgit v0.10.2 From c2474d9c718dea4f26067e85d356644a3aed26c2 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 5 May 2014 10:40:12 -0600 Subject: ums: remove UMS_{NUM,START}_SECTORS + UMS_START_SECTOR These values aren't set anywhere at present, and hence have no effect. The concept of a single global offset/number of sectors to expose through USB Mass Storage doesn't even make sense in the face of multiple storage devices. Remove these defines to simplify the code. Cc: Lukasz Majewski Signed-off-by: Stephen Warren Acked-by: Przemyslaw Marczak diff --git a/board/samsung/common/ums.c b/board/samsung/common/ums.c index ffe6368..1f7dda4 100644 --- a/board/samsung/common/ums.c +++ b/board/samsung/common/ums.c @@ -39,7 +39,6 @@ static struct ums ums_dev = { 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"); @@ -47,17 +46,8 @@ static struct ums *ums_disk_init(struct mmc *mmc) } ums_dev.block_dev = &mmc->block_dev; - - 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"); - } + ums_dev.start_sector = 0; + ums_dev.num_sectors = mmc_end_sector; printf("UMS: disk start sector: %#x, count: %#x\n", ums_dev.start_sector, ums_dev.num_sectors); diff --git a/include/usb_mass_storage.h b/include/usb_mass_storage.h index e3eb1eb..2b959ec 100644 --- a/include/usb_mass_storage.h +++ b/include/usb_mass_storage.h @@ -12,14 +12,6 @@ #include #include -#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 -- cgit v0.10.2 From dc1e746c42d440fcd70aaaf02a1bbce5a4b2a5e6 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 5 May 2014 10:40:13 -0600 Subject: ums: remove error-checking of MMC device size There's no reason to believe that an MMC device will incorrectly report its capacity. Remove error checking of this value from ums_disk_init() to simplify it. Cc: Lukasz Majewski Signed-off-by: Stephen Warren Acked-by: Przemyslaw Marczak diff --git a/board/samsung/common/ums.c b/board/samsung/common/ums.c index 1f7dda4..42a4901 100644 --- a/board/samsung/common/ums.c +++ b/board/samsung/common/ums.c @@ -38,16 +38,9 @@ static struct ums ums_dev = { static struct ums *ums_disk_init(struct mmc *mmc) { - uint64_t mmc_end_sector = mmc->capacity / SECTOR_SIZE; - - if (!mmc_end_sector) { - error("MMC capacity is not valid"); - return NULL; - } - ums_dev.block_dev = &mmc->block_dev; ums_dev.start_sector = 0; - ums_dev.num_sectors = mmc_end_sector; + ums_dev.num_sectors = mmc->capacity / SECTOR_SIZE; printf("UMS: disk start sector: %#x, count: %#x\n", ums_dev.start_sector, ums_dev.num_sectors); -- cgit v0.10.2 From 36ff05c4f968bf71713644413d64850e48b34145 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 5 May 2014 10:40:14 -0600 Subject: ums: remove ums_disk_init() Now that ums_disk_init() is so simple, there's no need for it to be a separate function. Instead, just add it to the tail end of ums_init(). Cc: Lukasz Majewski Signed-off-by: Stephen Warren Acked-by: Przemyslaw Marczak diff --git a/board/samsung/common/ums.c b/board/samsung/common/ums.c index 42a4901..b35670d 100644 --- a/board/samsung/common/ums.c +++ b/board/samsung/common/ums.c @@ -36,8 +36,14 @@ static struct ums ums_dev = { .name = "UMS disk", }; -static struct ums *ums_disk_init(struct mmc *mmc) +struct ums *ums_init(unsigned int dev_num) { + struct mmc *mmc = NULL; + + mmc = find_mmc_device(dev_num); + if (!mmc || mmc_init(mmc)) + return NULL; + ums_dev.block_dev = &mmc->block_dev; ums_dev.start_sector = 0; ums_dev.num_sectors = mmc->capacity / SECTOR_SIZE; @@ -47,12 +53,3 @@ static struct ums *ums_disk_init(struct mmc *mmc) 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); -} -- cgit v0.10.2 From abfe8afe88d6d8219602659f44981411e1daeb1d Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 5 May 2014 10:40:15 -0600 Subject: ums: move IO support code to common location There's nothing Samsung-/board-specfic about the implementation of ums_init(). Move the code into cmd_usb_mass_storage.c, so that it can be shared by any user of that command. Cc: Lukasz Majewski Signed-off-by: Stephen Warren Acked-by: Przemyslaw Marczak diff --git a/board/samsung/common/Makefile b/board/samsung/common/Makefile index 7d2bb8c..41d0cc3 100644 --- a/board/samsung/common/Makefile +++ b/board/samsung/common/Makefile @@ -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 diff --git a/board/samsung/common/ums.c b/board/samsung/common/ums.c deleted file mode 100644 index b35670d..0000000 --- a/board/samsung/common/ums.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2013 Samsung Electronics - * Lukasz Majewski - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include -#include -#include - -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(unsigned int dev_num) -{ - struct mmc *mmc = NULL; - - mmc = find_mmc_device(dev_num); - if (!mmc || mmc_init(mmc)) - return NULL; - - ums_dev.block_dev = &mmc->block_dev; - ums_dev.start_sector = 0; - ums_dev.num_sectors = mmc->capacity / SECTOR_SIZE; - - printf("UMS: disk start sector: %#x, count: %#x\n", - ums_dev.start_sector, ums_dev.num_sectors); - - return &ums_dev; -} diff --git a/common/cmd_usb_mass_storage.c b/common/cmd_usb_mass_storage.c index 31b7eb0..9888ed4 100644 --- a/common/cmd_usb_mass_storage.c +++ b/common/cmd_usb_mass_storage.c @@ -9,9 +9,55 @@ #include #include #include +#include +#include #include #include +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(unsigned int dev_num) +{ + struct mmc *mmc = NULL; + + mmc = find_mmc_device(dev_num); + if (!mmc || mmc_init(mmc)) + return NULL; + + ums_dev.block_dev = &mmc->block_dev; + ums_dev.start_sector = 0; + ums_dev.num_sectors = mmc->capacity / SECTOR_SIZE; + + 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[]) { diff --git a/include/usb_mass_storage.h b/include/usb_mass_storage.h index 2b959ec..69b80cd 100644 --- a/include/usb_mass_storage.h +++ b/include/usb_mass_storage.h @@ -30,7 +30,6 @@ 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__ */ -- cgit v0.10.2 From d0cc456dc3561d473926e60fb10ab643790a9ac7 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 5 May 2014 10:40:16 -0600 Subject: ums: use get_device() not find_mmc_device(); get_device() is a generic routine that will support any type of block device. Use this instead of the type-specific find_mmc_device(), for future flexibility. Cc: Lukasz Majewski Signed-off-by: Stephen Warren Acked-by: Przemyslaw Marczak diff --git a/common/cmd_usb_mass_storage.c b/common/cmd_usb_mass_storage.c index 9888ed4..b5954fc 100644 --- a/common/cmd_usb_mass_storage.c +++ b/common/cmd_usb_mass_storage.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -40,17 +39,22 @@ static struct ums ums_dev = { .name = "UMS disk", }; -struct ums *ums_init(unsigned int dev_num) +struct ums *ums_init(const char *devtype, const char *devnum) { - struct mmc *mmc = NULL; + block_dev_desc_t *block_dev; + int ret; - mmc = find_mmc_device(dev_num); - if (!mmc || mmc_init(mmc)) + ret = get_device(devtype, devnum, &block_dev); + if (ret < 0) return NULL; - ums_dev.block_dev = &mmc->block_dev; + /* 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 = mmc->capacity / SECTOR_SIZE; + ums_dev.num_sectors = block_dev->lba; printf("UMS: disk start sector: %#x, count: %#x\n", ums_dev.start_sector, ums_dev.num_sectors); @@ -65,11 +69,10 @@ int do_usb_mass_storage(cmd_tbl_t *cmdtp, int flag, return CMD_RET_USAGE; const char *usb_controller = argv[1]; - const char *mmc_devstring = argv[2]; - - unsigned int dev_num = simple_strtoul(mmc_devstring, NULL, 0); + const char *devtype = "mmc"; + const char *devnum = argv[2]; - struct ums *ums = ums_init(dev_num); + struct ums *ums = ums_init(devtype, devnum); if (!ums) return CMD_RET_FAILURE; -- cgit v0.10.2 From 1725f12882eca6424d70dcdc962ca4e5c2de798a Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 5 May 2014 10:40:17 -0600 Subject: ums: move all variable declarations to the start of the block It's easier to assign values to the variables inside an if statement body if the assignment and declaration are separate. Cc: Lukasz Majewski Signed-off-by: Stephen Warren Acked-by: Przemyslaw Marczak diff --git a/common/cmd_usb_mass_storage.c b/common/cmd_usb_mass_storage.c index b5954fc..5e02116 100644 --- a/common/cmd_usb_mass_storage.c +++ b/common/cmd_usb_mass_storage.c @@ -65,25 +65,33 @@ struct ums *ums_init(const char *devtype, const char *devnum) 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 *devtype = "mmc"; - const char *devnum = argv[2]; + usb_controller = argv[1]; + devtype = "mmc"; + devnum = argv[2]; - struct ums *ums = ums_init(devtype, devnum); + 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; @@ -96,7 +104,7 @@ int do_usb_mass_storage(cmd_tbl_t *cmdtp, int flag, } /* 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()) { /* -- cgit v0.10.2 From 8c6004568f7f0cb98c35fb86a193b502278f650f Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 5 May 2014 10:40:18 -0600 Subject: ums: allow the user to specify the device type Allow an optional devtype parameter to the ums command, which specifies the type of the device to be exported. This could allow exporting a SATA or even another USB device. Cc: Lukasz Majewski Signed-off-by: Stephen Warren Acked-by: Przemyslaw Marczak diff --git a/common/cmd_usb_mass_storage.c b/common/cmd_usb_mass_storage.c index 5e02116..2c879ea 100644 --- a/common/cmd_usb_mass_storage.c +++ b/common/cmd_usb_mass_storage.c @@ -77,8 +77,13 @@ int do_usb_mass_storage(cmd_tbl_t *cmdtp, int flag, return CMD_RET_USAGE; usb_controller = argv[1]; - devtype = "mmc"; - devnum = argv[2]; + if (argc >= 4) { + devtype = argv[2]; + devnum = argv[3]; + } else { + devtype = "mmc"; + devnum = argv[2]; + } ums = ums_init(devtype, devnum); if (!ums) @@ -152,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 e.g. ums 0 0" + "ums [] e.g. ums 0 mmc 0\n" + " devtype defaults to mmc" ); -- cgit v0.10.2 From 9ace3fc81427f6a4036718c2daff9d6f8ee7b69a Mon Sep 17 00:00:00 2001 From: Sebastian Siewior Date: Mon, 5 May 2014 15:08:09 -0500 Subject: image: add support for Android's boot image format This patch adds support for the Android boot-image format. The header file is from the Android project and got slightly alterted so the struct + its defines are not generic but have something like a namespace. The header file is from bootloader/legacy/include/boot/bootimg.h. The header parsing has been written from scratch and I looked at bootloader/legacy/usbloader/usbloader.c for some details. The image contains the physical address (load address) of the kernel and ramdisk. This address is considered only for the kernel image. The "second image" defined in the image header is currently not supported. I haven't found anything that is creating this. v3 (Rob Herring): This is based on http://patchwork.ozlabs.org/patch/126797/ with the following changes: - Rebased to current mainline - Moved android image handling to separate functions in common/image-android.c - s/u8/char/ in header to fix string function warnings - Use SPDX identifiers for licenses - Cleaned-up file source information: android_image.h is from file include/boot/bootimg.h in repository: https://android.googlesource.com/platform/bootable/bootloader/legacy The git commit hash is 4205b865141ff2e255fe1d3bd16de18e217ef06a usbloader.c would be from the same commit, but it does not appear to have been used for any actual code. v4: - s/andriod/android/ - Use a separate flag ep_found to track if the entry point has been set rather than using a magic value. Cc: Wolfgang Denk Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Rob Herring Reviewed-by: Tom Rini Reviewed-by: Lukasz Majewski diff --git a/common/Makefile b/common/Makefile index 7c853ae..bfcb466 100644 --- a/common/Makefile +++ b/common/Makefile @@ -237,6 +237,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 diff --git a/common/cmd_bootm.c b/common/cmd_bootm.c index c243a5b..993b906 100644 --- a/common/cmd_bootm.c +++ b/common/cmd_bootm.c @@ -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, @@ -274,6 +275,18 @@ static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc, } 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"); return 1; @@ -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; } @@ -1002,6 +1015,14 @@ static const void *boot_get_kernel(cmd_tbl_t *cmdtp, int flag, int argc, 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); bootstage_error(BOOTSTAGE_ID_FIT_KERNEL_INFO); diff --git a/common/image-android.c b/common/image-android.c new file mode 100644 index 0000000..6ded7e2 --- /dev/null +++ b/common/image-android.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2011 Sebastian Andrzej Siewior + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +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; +} diff --git a/common/image.c b/common/image.c index 9c6bec5..7ff27d7 100644 --- a/common/image.c +++ b/common/image.c @@ -659,10 +659,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; @@ -932,7 +934,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 */ diff --git a/include/android_image.h b/include/android_image.h new file mode 100644 index 0000000..094d60a --- /dev/null +++ b/include/android_image.h @@ -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 diff --git a/include/image.h b/include/image.h index 2508d7d..e1f9297 100644 --- a/include/image.h +++ b/include/image.h @@ -412,6 +412,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); @@ -1030,4 +1031,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__ */ -- cgit v0.10.2 From 3aab70afc531d1eea0e6fcfcf49001163ad13137 Mon Sep 17 00:00:00 2001 From: Sebastian Siewior Date: Mon, 5 May 2014 15:08:10 -0500 Subject: usb/gadget: add the fastboot gadget This patch contains an implementation of the fastboot protocol on the device side and documentation. This is based on USB download gadget infrastructure. The fastboot function implements the getvar, reboot, download and reboot commands. What is missing is the flash handling i.e. writting the image to media. v3 (Rob Herring): This is based on http://patchwork.ozlabs.org/patch/126798/ with the following changes: - Rebase to current mainline and updates for current gadget API - Use SPDX identifiers for licenses - Traced the history and added missing copyright to cmd_fastboot.c - Use load_addr/load_size for transfer buffer - Allow vendor strings to be optional - Set vendor/product ID from config defines - Allow Ctrl-C to exit fastboot mode v4: - Major re-write to use the USB download gadget. Consolidated function code to a single file. - Moved globals into single struct. - Use puts and putc as appropriate. - Added CONFIG_USB_FASTBOOT_BUF_ADDR and CONFIG_USB_FASTBOOT_BUF_SIZE to set the fastboot transfer buffer. v5: - Add CONFIG option documentation to README - Rebase using new downloader registration Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Rob Herring diff --git a/README b/README index b973344..8114f79 100644 --- a/README +++ b/README @@ -1558,6 +1558,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 diff --git a/common/Makefile b/common/Makefile index bfcb466..219cb51 100644 --- a/common/Makefile +++ b/common/Makefile @@ -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 diff --git a/common/cmd_fastboot.c b/common/cmd_fastboot.c new file mode 100644 index 0000000..83fa7bd --- /dev/null +++ b/common/cmd_fastboot.c @@ -0,0 +1,36 @@ +/* + * Copyright 2008 - 2009 Windriver, + * Author: Tom Rix + * + * (C) Copyright 2014 Linaro, Ltd. + * Rob Herring + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include + +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", + "" +); diff --git a/doc/README.android-fastboot b/doc/README.android-fastboot new file mode 100644 index 0000000..f1d128c --- /dev/null +++ b/doc/README.android-fastboot @@ -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 ... 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 896c8d4..66becdc 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -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 diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c new file mode 100644 index 0000000..9dd85b6 --- /dev/null +++ b/drivers/usb/gadget/f_fastboot.c @@ -0,0 +1,513 @@ +/* + * (C) Copyright 2008 - 2009 + * Windriver, + * Tom Rix + * + * Copyright 2011 Sebastian Andrzej Siewior + * + * Copyright 2014 Linaro, Ltd. + * Rob Herring + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); + } +} -- cgit v0.10.2 From dd5b68fba74371b40963f02d4c237163baa94865 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 5 May 2014 15:08:11 -0500 Subject: arm: beagle: enable Android fastboot support Enable Android Fastboot support on omap3_beagle board. Signed-off-by: Rob Herring diff --git a/include/configs/omap3_beagle.h b/include/configs/omap3_beagle.h index 0b57421..be39b7c 100644 --- a/include/configs/omap3_beagle.h +++ b/include/configs/omap3_beagle.h @@ -110,6 +110,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 -- cgit v0.10.2 From ab2f5c12f7ca27b0191ebae90664182510330cd6 Mon Sep 17 00:00:00 2001 From: Heiko Schocher Date: Thu, 10 Apr 2014 07:08:05 +0200 Subject: musb-new, dfu: first send request answer then call completions comment in ep0_txstate() states: "report completions as soon as the fifo's loaded; there's no win in waiting till this last packet gets acked". This is wrong for using dfu. In the dfu usecase we must send a PollTimeout to the host, so the host can wait until the U-Boot Code is ready for answering new usb requests. So the answer which contains the PollTimeout must send *before* U-Boot calls req->complete. The req->complete is used in the dfu case for flushing the medium, when entering DFU_STATE_dfuMANIFEST_SYNC state. Change-Id: Ib2941119c72761e48e15fedbdad1ecce07ae0b3d Signed-off-by: Heiko Schocher Cc: Kyungmin Park Cc: Marek Vasut Cc: Pantelis Antoniou Acked-by: Marek Vasut Acked-by: Lukasz Majewski diff --git a/drivers/usb/musb-new/musb_gadget_ep0.c b/drivers/usb/musb-new/musb_gadget_ep0.c index 6599d38..8c3b0a1 100644 --- a/drivers/usb/musb-new/musb_gadget_ep0.c +++ b/drivers/usb/musb-new/musb_gadget_ep0.c @@ -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); } /* -- cgit v0.10.2 From fc25fa27e5f439705e9ca42182014e2d75d9f0ae Mon Sep 17 00:00:00 2001 From: Heiko Schocher Date: Fri, 11 Apr 2014 07:59:47 +0200 Subject: dfu, nand: add medium specific polltimeout function add a possibility to add a medium specific polltimeout function. So it is possible to define different poll timeouts. Used on nand medium, for setting the DFU_MANIFEST_POLL_TIMEOUT only on nand ubi partitions, which is currently the only usecase. Change-Id: If1db5f49b32d93fefa7481e8dfe5b7ccc0e65af4 Signed-off-by: Heiko Schocher Cc: Kyungmin Park Cc: Marek Vasut Cc: Pantelis Antoniou Reviewed-by: Marek Vasut Acked-by: Lukasz Majewski diff --git a/drivers/dfu/dfu_nand.c b/drivers/dfu/dfu_nand.c index 2d07097..ccdbef6 100644 --- a/drivers/dfu/dfu_nand.c +++ b/drivers/dfu/dfu_nand.c @@ -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; diff --git a/drivers/usb/gadget/f_dfu.c b/drivers/usb/gadget/f_dfu.c index 1b1e179..859fe82 100644 --- a/drivers/usb/gadget/f_dfu.c +++ b/drivers/usb/gadget/f_dfu.c @@ -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; } diff --git a/include/dfu.h b/include/dfu.h index 2409168..986598e 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -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; -- cgit v0.10.2