diff options
Diffstat (limited to 'drivers/fastboot')
-rw-r--r-- | drivers/fastboot/Makefile | 8 | ||||
-rw-r--r-- | drivers/fastboot/fastboot.c | 1127 |
2 files changed, 1135 insertions, 0 deletions
diff --git a/drivers/fastboot/Makefile b/drivers/fastboot/Makefile new file mode 100644 index 0000000..251120d --- /dev/null +++ b/drivers/fastboot/Makefile @@ -0,0 +1,8 @@ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_FASTBOOT) += fastboot.o diff --git a/drivers/fastboot/fastboot.c b/drivers/fastboot/fastboot.c new file mode 100644 index 0000000..4c911f0 --- /dev/null +++ b/drivers/fastboot/fastboot.c @@ -0,0 +1,1127 @@ +/* + * Copyright (C) 2010-2014 Freescale Semiconductor, Inc. All Rights Reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <config.h> +#include <malloc.h> +#include <fastboot.h> +#include <usb/imx_udc.h> +#include <asm/io.h> +#include <usbdevice.h> +#include <mmc.h> +#include <sata.h> +#ifdef CONFIG_ANDROID_RECOVERY +#include <recovery.h> +#endif + +/* + * Defines + */ +#define NUM_ENDPOINTS 2 + +#define CONFIG_USBD_OUT_PKTSIZE 0x200 +#define CONFIG_USBD_IN_PKTSIZE 0x200 +#define MAX_BUFFER_SIZE 0x200 + +/* + * imx family android layout + * mbr - 0 ~ 0x3FF byte + * bootloader - 0x400 ~ 0xFFFFF byte + * kernel - 0x100000 ~ 5FFFFF byte + * uramedisk - 0x600000 ~ 0x6FFFFF supposing 1M temporarily + * SYSTEM partition - /dev/mmcblk0p2 or /dev/sda2 + * RECOVERY parittion - dev/mmcblk0p4 or /dev/sda4 + */ +#define ANDROID_MBR_OFFSET 0 +#define ANDROID_MBR_SIZE 0x200 +#define ANDROID_BOOTLOADER_OFFSET 0x400 +#define ANDROID_BOOTLOADER_SIZE 0xFFC00 +#define ANDROID_KERNEL_OFFSET 0x100000 +#define ANDROID_KERNEL_SIZE 0x500000 +#define ANDROID_URAMDISK_OFFSET 0x600000 +#define ANDROID_URAMDISK_SIZE 0x100000 + +#define STR_LANG_INDEX 0x00 +#define STR_MANUFACTURER_INDEX 0x01 +#define STR_PRODUCT_INDEX 0x02 +#define STR_SERIAL_INDEX 0x03 +#define STR_CONFIG_INDEX 0x04 +#define STR_DATA_INTERFACE_INDEX 0x05 +#define STR_CTRL_INTERFACE_INDEX 0x06 +#define STR_COUNT 0x07 + +#define FASTBOOT_FBPARTS_ENV_MAX_LEN 1024 +/* To support the Android-style naming of flash */ +#define MAX_PTN 16 + + +/*pentry index internally*/ +enum { + PTN_MBR_INDEX = 0, + PTN_BOOTLOADER_INDEX, + PTN_KERNEL_INDEX, + PTN_URAMDISK_INDEX, + PTN_SYSTEM_INDEX, + PTN_RECOVERY_INDEX +}; + +struct fastboot_device_info fastboot_devinfo; + +/* defined and used by gadget/ep0.c */ +extern struct usb_string_descriptor **usb_strings; + +static struct usb_device_instance device_instance[1]; +static struct usb_bus_instance bus_instance[1]; +static struct usb_configuration_instance config_instance[1]; +static struct usb_interface_instance interface_instance[1]; +static struct usb_alternate_instance alternate_instance[1]; +/* one extra for control endpoint */ +static struct usb_endpoint_instance endpoint_instance[NUM_ENDPOINTS+1]; + +static struct cmd_fastboot_interface *fastboot_interface; +static int fastboot_configured_flag; +static int usb_disconnected; + +/* Indicies, References */ +static u8 rx_endpoint; +static u8 tx_endpoint; +static struct usb_string_descriptor *fastboot_string_table[STR_COUNT]; + +/* USB Descriptor Strings */ +static u8 wstrLang[4] = {4, USB_DT_STRING, 0x9, 0x4}; +static u8 wstrManufacturer[2 * (sizeof(CONFIG_FASTBOOT_MANUFACTURER_STR))]; +static u8 wstrProduct[2 * (sizeof(CONFIG_FASTBOOT_PRODUCT_NAME_STR))]; +static u8 wstrSerial[2*(sizeof(CONFIG_FASTBOOT_SERIAL_NUM))]; +static u8 wstrConfiguration[2 * (sizeof(CONFIG_FASTBOOT_CONFIGURATION_STR))]; +static u8 wstrDataInterface[2 * (sizeof(CONFIG_FASTBOOT_INTERFACE_STR))]; + +/* Standard USB Data Structures */ +static struct usb_interface_descriptor interface_descriptors[1]; +static struct usb_endpoint_descriptor *ep_descriptor_ptrs[NUM_ENDPOINTS]; +static struct usb_configuration_descriptor *configuration_descriptor; +static struct usb_device_descriptor device_descriptor = { + .bLength = sizeof(struct usb_device_descriptor), + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = cpu_to_le16(USB_BCD_VERSION), + .bDeviceClass = 0xff, + .bDeviceSubClass = 0xff, + .bDeviceProtocol = 0xff, + .bMaxPacketSize0 = 0x40, + .idVendor = cpu_to_le16(CONFIG_FASTBOOT_VENDOR_ID), + .idProduct = cpu_to_le16(CONFIG_FASTBOOT_PRODUCT_ID), + .bcdDevice = cpu_to_le16(CONFIG_FASTBOOT_BCD_DEVICE), + .iManufacturer = STR_MANUFACTURER_INDEX, + .iProduct = STR_PRODUCT_INDEX, + .iSerialNumber = STR_SERIAL_INDEX, + .bNumConfigurations = 1 +}; + +/* + * Static Generic Serial specific data + */ + +struct fastboot_config_desc { + struct usb_configuration_descriptor configuration_desc; + struct usb_interface_descriptor interface_desc[1]; + struct usb_endpoint_descriptor data_endpoints[NUM_ENDPOINTS]; +}; + +static struct fastboot_config_desc +fastboot_configuration_descriptors[1] = { + { + .configuration_desc = { + .bLength = sizeof(struct usb_configuration_descriptor), + .bDescriptorType = USB_DT_CONFIG, + .wTotalLength = + cpu_to_le16(sizeof(struct fastboot_config_desc)), + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = STR_CONFIG_INDEX, + .bmAttributes = + BMATTRIBUTE_SELF_POWERED|BMATTRIBUTE_RESERVED, + .bMaxPower = 0x32 + }, + .interface_desc = { + { + .bLength = + sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = NUM_ENDPOINTS, + .bInterfaceClass = + FASTBOOT_INTERFACE_CLASS, + .bInterfaceSubClass = + FASTBOOT_INTERFACE_SUB_CLASS, + .bInterfaceProtocol = + FASTBOOT_INTERFACE_PROTOCOL, + .iInterface = STR_DATA_INTERFACE_INDEX + }, + }, + .data_endpoints = { + { + .bLength = + sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = UDC_OUT_ENDPOINT | + USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = + cpu_to_le16(CONFIG_USBD_OUT_PKTSIZE), + .bInterval = 0x00, + }, + { + .bLength = + sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = UDC_IN_ENDPOINT | + USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = + cpu_to_le16(CONFIG_USBD_IN_PKTSIZE), + .bInterval = 0x00, + }, + }, + }, +}; + + + +static struct fastboot_ptentry ptable[MAX_PTN]; +static unsigned int pcount; + + +/* Static Function Prototypes */ +static void _fastboot_init_strings(void); +static void _fastboot_init_instances(void); +static void _fastboot_init_endpoints(void); +static void _fastboot_event_handler(struct usb_device_instance *device, + usb_device_event_t event, int data); +static int _fastboot_cdc_setup(struct usb_device_request *request, + struct urb *urb); +static int _fastboot_usb_configured(void); +#if defined(CONFIG_FASTBOOT_STORAGE_SATA) \ + || defined(CONFIG_FASTBOOT_STORAGE_MMC) +static int _fastboot_parts_load_from_ptable(void); +#endif +#if defined(CONFIG_FASTBOOT_STORAGE_NAND) +static int _fastboot_parts_load_from_env(void); +#endif +static int _fastboot_setup_dev(void); +static void _fastboot_load_partitions(void); + +/* utility function for converting char* to wide string used by USB */ +static void str2wide(char *str, u16 * wide) +{ + int i; + for (i = 0; i < strlen(str) && str[i]; i++) { + #if defined(__LITTLE_ENDIAN) + wide[i] = (u16) str[i]; + #elif defined(__BIG_ENDIAN) + wide[i] = ((u16)(str[i])<<8); + #else + #error "__LITTLE_ENDIAN or __BIG_ENDIAN undefined" + #endif + } +} + +/* + Get mmc control number from passed string, eg, "mmc1" mean device 1. Only + support "mmc0" to "mmc9" currently. It will be treated as device 0 for + other string. +*/ +static int _fastboot_get_mmc_no(char *env_str) +{ + int digit = 0; + unsigned char a; + + if (env_str && (strlen(env_str) >= 4) && + !strncmp(env_str, "mmc", 3)) { + a = env_str[3]; + if (a >= '0' && a <= '9') + digit = a - '0'; + } + + return digit; +} + +static int _fastboot_setup_dev(void) +{ + char *fastboot_env; + fastboot_env = getenv("fastboot_dev"); + + if (fastboot_env) { + if (!strcmp(fastboot_env, "sata")) { + fastboot_devinfo.type = DEV_SATA; + fastboot_devinfo.dev_id = 0; + } else if (!strcmp(fastboot_env, "nand")) { + fastboot_devinfo.type = DEV_NAND; + fastboot_devinfo.dev_id = 0; + } else if (!strncmp(fastboot_env, "mmc", 3)) { + fastboot_devinfo.type = DEV_MMC; + fastboot_devinfo.dev_id = _fastboot_get_mmc_no(fastboot_env); + } + } else { + return 1; + } + + return 0; +} + + +/* + * Initialize fastboot + */ +int fastboot_init(struct cmd_fastboot_interface *interface) +{ + printf("fastboot is in init......"); + + fastboot_interface = interface; + fastboot_interface->product_name = CONFIG_FASTBOOT_PRODUCT_NAME_STR; + fastboot_interface->serial_no = CONFIG_FASTBOOT_SERIAL_NUM; + fastboot_interface->nand_block_size = 4096; + fastboot_interface->transfer_buffer = + (unsigned char *)CONFIG_FASTBOOT_TRANSFER_BUF; + fastboot_interface->transfer_buffer_size = + CONFIG_FASTBOOT_TRANSFER_BUF_SIZE; + + _fastboot_init_strings(); + /* Basic USB initialization */ + udc_init(); + + _fastboot_init_instances(); + + udc_startup_events(device_instance); + udc_connect(); /* Enable pullup for host detection */ + + return 0; +} + +static void _fastboot_init_strings(void) +{ + struct usb_string_descriptor *string; + + fastboot_string_table[STR_LANG_INDEX] = + (struct usb_string_descriptor *)wstrLang; + + string = (struct usb_string_descriptor *)wstrManufacturer; + string->bLength = sizeof(wstrManufacturer); + string->bDescriptorType = USB_DT_STRING; + str2wide(CONFIG_FASTBOOT_MANUFACTURER_STR, string->wData); + fastboot_string_table[STR_MANUFACTURER_INDEX] = string; + + string = (struct usb_string_descriptor *)wstrProduct; + string->bLength = sizeof(wstrProduct); + string->bDescriptorType = USB_DT_STRING; + str2wide(CONFIG_FASTBOOT_PRODUCT_NAME_STR, string->wData); + fastboot_string_table[STR_PRODUCT_INDEX] = string; + + string = (struct usb_string_descriptor *)wstrSerial; + string->bLength = sizeof(wstrSerial); + string->bDescriptorType = USB_DT_STRING; + str2wide(CONFIG_FASTBOOT_SERIAL_NUM, string->wData); + fastboot_string_table[STR_SERIAL_INDEX] = string; + + string = (struct usb_string_descriptor *)wstrConfiguration; + string->bLength = sizeof(wstrConfiguration); + string->bDescriptorType = USB_DT_STRING; + str2wide(CONFIG_FASTBOOT_CONFIGURATION_STR, string->wData); + fastboot_string_table[STR_CONFIG_INDEX] = string; + + string = (struct usb_string_descriptor *) wstrDataInterface; + string->bLength = sizeof(wstrDataInterface); + string->bDescriptorType = USB_DT_STRING; + str2wide(CONFIG_FASTBOOT_INTERFACE_STR, string->wData); + fastboot_string_table[STR_DATA_INTERFACE_INDEX] = string; + + /* Now, initialize the string table for ep0 handling */ + usb_strings = fastboot_string_table; +} + +static void _fastboot_init_instances(void) +{ + int i; + u16 temp; + + /* Assign endpoint descriptors */ + ep_descriptor_ptrs[0] = + &fastboot_configuration_descriptors[0].data_endpoints[0]; + ep_descriptor_ptrs[1] = + &fastboot_configuration_descriptors[0].data_endpoints[1]; + + /* Configuration Descriptor */ + configuration_descriptor = + (struct usb_configuration_descriptor *) + &fastboot_configuration_descriptors; + + fastboot_configured_flag = 0; + + /* initialize device instance */ + memset(device_instance, 0, sizeof(struct usb_device_instance)); + device_instance->device_state = STATE_INIT; + device_instance->device_descriptor = &device_descriptor; + device_instance->event = _fastboot_event_handler; + device_instance->cdc_recv_setup = _fastboot_cdc_setup; + device_instance->bus = bus_instance; + device_instance->configurations = 1; + device_instance->configuration_instance_array = config_instance; + + /* initialize bus instance */ + memset(bus_instance, 0, sizeof(struct usb_bus_instance)); + bus_instance->device = device_instance; + bus_instance->endpoint_array = endpoint_instance; + bus_instance->max_endpoints = NUM_ENDPOINTS + 1; + bus_instance->maxpacketsize = 0xFF; + bus_instance->serial_number_str = CONFIG_FASTBOOT_SERIAL_NUM; + + /* configuration instance */ + memset(config_instance, 0, + sizeof(struct usb_configuration_instance)); + config_instance->interfaces = 1; + config_instance->configuration_descriptor = configuration_descriptor; + config_instance->interface_instance_array = interface_instance; + + /* interface instance */ + memset(interface_instance, 0, + sizeof(struct usb_interface_instance)); + interface_instance->alternates = 1; + interface_instance->alternates_instance_array = alternate_instance; + + /* alternates instance */ + memset(alternate_instance, 0, + sizeof(struct usb_alternate_instance)); + alternate_instance->interface_descriptor = interface_descriptors; + alternate_instance->endpoints = NUM_ENDPOINTS; + alternate_instance->endpoints_descriptor_array = ep_descriptor_ptrs; + + /* endpoint instances */ + memset(&endpoint_instance[0], 0, + sizeof(struct usb_endpoint_instance)); + endpoint_instance[0].endpoint_address = 0; + endpoint_instance[0].rcv_packetSize = EP0_MAX_PACKET_SIZE; + endpoint_instance[0].rcv_attributes = USB_ENDPOINT_XFER_CONTROL; + endpoint_instance[0].tx_packetSize = EP0_MAX_PACKET_SIZE; + endpoint_instance[0].tx_attributes = USB_ENDPOINT_XFER_CONTROL; + udc_setup_ep(device_instance, 0, &endpoint_instance[0]); + + for (i = 1; i <= NUM_ENDPOINTS; i++) { + memset(&endpoint_instance[i], 0, + sizeof(struct usb_endpoint_instance)); + + endpoint_instance[i].endpoint_address = + ep_descriptor_ptrs[i - 1]->bEndpointAddress; + + endpoint_instance[i].rcv_attributes = + ep_descriptor_ptrs[i - 1]->bmAttributes; + + /*fix the abort caused by unalignment*/ + temp = *(u8 *)&ep_descriptor_ptrs[i - 1]->wMaxPacketSize; + temp |= + (*(((u8 *)&ep_descriptor_ptrs[i - 1]->wMaxPacketSize) + 1) << 8); + + endpoint_instance[i].rcv_packetSize = + le16_to_cpu(temp); + + endpoint_instance[i].tx_attributes = + ep_descriptor_ptrs[i - 1]->bmAttributes; + + endpoint_instance[i].tx_packetSize = + le16_to_cpu(temp); + + endpoint_instance[i].tx_attributes = + ep_descriptor_ptrs[i - 1]->bmAttributes; + + urb_link_init(&endpoint_instance[i].rcv); + urb_link_init(&endpoint_instance[i].rdy); + urb_link_init(&endpoint_instance[i].tx); + urb_link_init(&endpoint_instance[i].done); + + if (endpoint_instance[i].endpoint_address & USB_DIR_IN) { + tx_endpoint = i; + endpoint_instance[i].tx_urb = + usbd_alloc_urb(device_instance, + &endpoint_instance[i]); + } else { + rx_endpoint = i; + endpoint_instance[i].rcv_urb = + usbd_alloc_urb(device_instance, + &endpoint_instance[i]); + } + } +} + +static void _fastboot_init_endpoints(void) +{ + int i; + + bus_instance->max_endpoints = NUM_ENDPOINTS + 1; + for (i = 1; i <= NUM_ENDPOINTS; i++) + udc_setup_ep(device_instance, i, &endpoint_instance[i]); +} + +static void _fastboot_destroy_endpoints(void) +{ + int i; + struct urb *tx_urb; + + for (i = 1; i <= NUM_ENDPOINTS; i++) { + /*dealloc urb*/ + if (endpoint_instance[i].endpoint_address & USB_DIR_IN) { + if (endpoint_instance[i].tx_urb) + usbd_dealloc_urb(endpoint_instance[i].tx_urb); + + while (endpoint_instance[i].tx_queue) { + tx_urb = first_urb_detached(&endpoint_instance[i].tx); + if (tx_urb) { + usbd_dealloc_urb(tx_urb); + endpoint_instance[i].tx_queue--; + } else { + break; + } + } + endpoint_instance[i].tx_queue = 0; + + do { + tx_urb = first_urb_detached(&endpoint_instance[i].done); + if (tx_urb) + usbd_dealloc_urb(tx_urb); + } while (tx_urb); + + } else { + if (endpoint_instance[i].rcv_urb) + usbd_dealloc_urb(endpoint_instance[i].rcv_urb); + } + + udc_destroy_ep(device_instance, &endpoint_instance[i]); + } +} + + +static int _fill_buffer(u8 *buf) +{ + struct usb_endpoint_instance *endpoint = + &endpoint_instance[rx_endpoint]; + + if (endpoint->rcv_urb && endpoint->rcv_urb->actual_length) { + unsigned int nb = 0; + char *src = (char *)endpoint->rcv_urb->buffer; + unsigned int rx_avail = MAX_BUFFER_SIZE; + + if (rx_avail >= endpoint->rcv_urb->actual_length) { + nb = endpoint->rcv_urb->actual_length; + memcpy(buf, src, nb); + endpoint->rcv_urb->actual_length = 0; + } + return nb; + } + return 0; +} + +static struct urb *_next_urb(struct usb_device_instance *device, + struct usb_endpoint_instance *endpoint) +{ + struct urb *current_urb = NULL; + int space; + + /* If there's a queue, then we should add to the last urb */ + if (!endpoint->tx_queue) + current_urb = endpoint->tx_urb; + else + /* Last urb from tx chain */ + current_urb = + p2surround(struct urb, link, endpoint->tx.prev); + + /* Make sure this one has enough room */ + space = current_urb->buffer_length - current_urb->actual_length; + if (space > 0) + return current_urb; + else { /* No space here */ + /* First look at done list */ + current_urb = first_urb_detached(&endpoint->done); + if (!current_urb) + current_urb = usbd_alloc_urb(device, endpoint); + + urb_append(&endpoint->tx, current_urb); + endpoint->tx_queue++; + } + return current_urb; +} + +static int _fastboot_usb_configured(void) +{ + return fastboot_configured_flag; +} + +static void _fastboot_event_handler(struct usb_device_instance *device, + usb_device_event_t event, int data) +{ + switch (event) { + case DEVICE_RESET: + case DEVICE_BUS_INACTIVE: + fastboot_configured_flag = 0; + break; + case DEVICE_CONFIGURED: + fastboot_configured_flag = 1; + _fastboot_init_endpoints(); + break; + case DEVICE_ADDRESS_ASSIGNED: + default: + break; + } +} + +static int _fastboot_cdc_setup(struct usb_device_request *request, + struct urb *urb) +{ + return 0; +} + + +/*! + * Function to receive data from host through channel + * + * @buf buffer to fill in + * @count read data size + * + * @return 0 + */ +int fastboot_usb_recv(u8 *buf, int count) +{ + int len = 0; + + while (!_fastboot_usb_configured()) + udc_irq(); + + /* update rxqueue to wait new data */ + mxc_udc_rxqueue_update(2, count); + + while (!len) { + if (is_usb_disconnected()) { + /*it will not unconfigure when disconnect + from host, so here needs manual unconfigure + anyway, it's just a workaround*/ + fastboot_configured_flag = 0; + usb_disconnected = 1; + return 0; + } + udc_irq(); + if (_fastboot_usb_configured()) + len = _fill_buffer(buf); + } + return len; +} + +int fastboot_getvar(const char *rx_buffer, char *tx_buffer) +{ + /* Place board specific variables here */ + return 0; +} + +int fastboot_poll() +{ + u8 buffer[MAX_BUFFER_SIZE]; + int length = 0; + + memset(buffer, 0, MAX_BUFFER_SIZE); + + length = fastboot_usb_recv(buffer, MAX_BUFFER_SIZE); + + /* If usb disconnected, blocked here to wait */ + if (usb_disconnected) { + udc_disconnect(); + udc_connect(); + /*the udc_connect will be blocked until connect to host + so, the usb_disconnect should be 0 after udc_connect, + and should be set manually. Anyway, it's just a workaround*/ + usb_disconnected = 0; + } + + if (!length) + return FASTBOOT_INACTIVE; + + /* Pass this up to the interface's handler */ + if (fastboot_interface && fastboot_interface->rx_handler) { + if (!fastboot_interface->rx_handler(buffer, length)) + return FASTBOOT_OK; + } + return FASTBOOT_OK; +} + +int fastboot_tx(unsigned char *buffer, unsigned int buffer_size) +{ + /* Not realized yet */ + return 0; +} + +static int _fastboot_write_buffer(const char *buffer, + unsigned int buffer_size) +{ + struct usb_endpoint_instance *endpoint = + (struct usb_endpoint_instance *)&endpoint_instance[tx_endpoint]; + struct urb *current_urb = NULL; + + if (!_fastboot_usb_configured()) + return 0; + + current_urb = _next_urb(device_instance, endpoint); + if (buffer_size) { + char *dest; + int space_avail, popnum, count, total = 0; + + /* Break buffer into urb sized pieces, + * and link each to the endpoint + */ + count = buffer_size; + while (count > 0) { + if (!current_urb) { + printf("current_urb is NULL, buffer_size %d\n", + buffer_size); + return total; + } + + dest = (char *)current_urb->buffer + + current_urb->actual_length; + + space_avail = current_urb->buffer_length - + current_urb->actual_length; + popnum = MIN(space_avail, count); + if (popnum == 0) + break; + + memcpy(dest, buffer + total, popnum); + printf("send: %s\n", (char *)buffer); + + current_urb->actual_length += popnum; + total += popnum; + + if (udc_endpoint_write(endpoint)) + /* Write pre-empted by RX */ + return 0; + count -= popnum; + } /* end while */ + return total; + } + return 0; +} + +int fastboot_tx_status(const char *buffer, unsigned int buffer_size) +{ + int len = 0; + + while (buffer_size > 0) { + len = _fastboot_write_buffer(buffer + len, buffer_size); + buffer_size -= len; + + udc_irq(); + } + udc_irq(); + + return 0; +} + +void fastboot_shutdown(void) +{ + usb_shutdown(); + + /* Reset interface*/ + if (fastboot_interface && + fastboot_interface->reset_handler) { + fastboot_interface->reset_handler(); + } + + /* Reset some globals */ + _fastboot_destroy_endpoints(); + fastboot_interface = NULL; + fastboot_configured_flag = 0; + usb_disconnected = 0; + + /*free memory*/ + udc_destroy(); +} + +/* + * CPU and board-specific fastboot initializations. Aliased function + * signals caller to move on + */ +static void __def_fastboot_setup(void) +{ + /*do nothing here*/ +} +void board_fastboot_setup(void) \ + __attribute__((weak, alias("__def_fastboot_setup"))); + + +void fastboot_setup(void) +{ + /*execute board relevant initilizations for preparing fastboot */ + board_fastboot_setup(); + + /*get the fastboot dev*/ + _fastboot_setup_dev(); + + /*check if we need to setup recovery*/ +#ifdef CONFIG_ANDROID_RECOVERY + check_recovery_mode(); +#endif + + /*load partitions information for the fastboot dev*/ + _fastboot_load_partitions(); +} + +/* export to lib_arm/board.c */ +void check_fastboot(void) +{ + if (fastboot_check_and_clean_flag()) + do_fastboot(NULL, 0, 0, 0); +} + +#if defined(CONFIG_FASTBOOT_STORAGE_SATA) \ + || defined(CONFIG_FASTBOOT_STORAGE_MMC) +/** + @mmc_dos_partition_index: the partition index in mbr. + @mmc_partition_index: the boot partition or user partition index, + not related to the partition table. + */ +static int _fastboot_parts_add_ptable_entry(int ptable_index, + int mmc_dos_partition_index, + int mmc_partition_index, + const char *name, + block_dev_desc_t *dev_desc, + struct fastboot_ptentry *ptable) +{ + disk_partition_t info; + strcpy(ptable[ptable_index].name, name); + + if (get_partition_info(dev_desc, + mmc_dos_partition_index, &info)) { + printf("Bad partition index:%d for partition:%s\n", + mmc_dos_partition_index, name); + return -1; + } else { + ptable[ptable_index].start = info.start; + ptable[ptable_index].length = info.size; + ptable[ptable_index].partition_id = mmc_partition_index; + } + return 0; +} + +static int _fastboot_parts_load_from_ptable(void) +{ + int i; +#ifdef CONFIG_CMD_SATA + int sata_device_no; +#endif + + /* mmc boot partition: -1 means no partition, 0 user part., 1 boot part. + * default is no partition, for emmc default user part, except emmc*/ + int boot_partition = FASTBOOT_MMC_NONE_PARTITION_ID; + int user_partition = FASTBOOT_MMC_NONE_PARTITION_ID; + + struct mmc *mmc; + block_dev_desc_t *dev_desc; + struct fastboot_ptentry ptable[PTN_RECOVERY_INDEX + 1]; + + /* sata case in env */ + if (fastboot_devinfo.type == DEV_SATA) { +#ifdef CONFIG_CMD_SATA + puts("flash target is SATA\n"); + if (sata_initialize()) + return -1; + sata_device_no = CONFIG_FASTBOOT_SATA_NO; + if (sata_device_no >= CONFIG_SYS_SATA_MAX_DEVICE) { + printf("Unknown SATA(%d) device for fastboot\n", + sata_device_no); + return -1; + } + dev_desc = sata_get_dev(sata_device_no); +#else /*! CONFIG_CMD_SATA*/ + puts("SATA isn't buildin\n"); + return -1; +#endif /*! CONFIG_CMD_SATA*/ + } else if (fastboot_devinfo.type == DEV_MMC) { + int mmc_no = 0; + mmc_no = fastboot_devinfo.dev_id; + + printf("flash target is MMC:%d\n", mmc_no); + mmc = find_mmc_device(mmc_no); + if (mmc && mmc_init(mmc)) + printf("MMC card init failed!\n"); + + dev_desc = get_dev("mmc", mmc_no); + if (NULL == dev_desc) { + printf("** Block device MMC %d not supported\n", + mmc_no); + return -1; + } + + /* multiple boot paritions for eMMC 4.3 later */ + if (mmc->part_config != MMCPART_NOAVAILABLE) { + boot_partition = FASTBOOT_MMC_BOOT_PARTITION_ID; + user_partition = FASTBOOT_MMC_USER_PARTITION_ID; + } + } else { + printf("Can't setup partition table on this device %d\n", + fastboot_devinfo.type); + return -1; + } + + memset((char *)ptable, 0, + sizeof(struct fastboot_ptentry) * (PTN_RECOVERY_INDEX + 1)); + /* MBR */ + strcpy(ptable[PTN_MBR_INDEX].name, "mbr"); + ptable[PTN_MBR_INDEX].start = ANDROID_MBR_OFFSET / dev_desc->blksz; + ptable[PTN_MBR_INDEX].length = ANDROID_MBR_SIZE / dev_desc->blksz; + ptable[PTN_MBR_INDEX].partition_id = user_partition; + /* Bootloader */ + strcpy(ptable[PTN_BOOTLOADER_INDEX].name, "bootloader"); + ptable[PTN_BOOTLOADER_INDEX].start = + ANDROID_BOOTLOADER_OFFSET / dev_desc->blksz; + ptable[PTN_BOOTLOADER_INDEX].length = + ANDROID_BOOTLOADER_SIZE / dev_desc->blksz; + ptable[PTN_BOOTLOADER_INDEX].partition_id = boot_partition; + + _fastboot_parts_add_ptable_entry(PTN_KERNEL_INDEX, + CONFIG_ANDROID_BOOT_PARTITION_MMC, + user_partition, "boot", dev_desc, ptable); + _fastboot_parts_add_ptable_entry(PTN_RECOVERY_INDEX, + CONFIG_ANDROID_RECOVERY_PARTITION_MMC, + user_partition, + "recovery", dev_desc, ptable); + _fastboot_parts_add_ptable_entry(PTN_SYSTEM_INDEX, + CONFIG_ANDROID_SYSTEM_PARTITION_MMC, + user_partition, + "system", dev_desc, ptable); + + for (i = 0; i <= PTN_RECOVERY_INDEX; i++) + fastboot_flash_add_ptn(&ptable[i]); + + return 0; +} +#endif /*CONFIG_FASTBOOT_STORAGE_SATA || CONFIG_FASTBOOT_STORAGE_MMC*/ + +#if defined(CONFIG_FASTBOOT_STORAGE_NAND) +static unsigned long long _memparse(char *ptr, char **retptr) +{ + char *endptr; /* local pointer to end of parsed string */ + + unsigned long ret = simple_strtoul(ptr, &endptr, 0); + + switch (*endptr) { + case 'M': + case 'm': + ret <<= 10; + case 'K': + case 'k': + ret <<= 10; + endptr++; + default: + break; + } + + if (retptr) + *retptr = endptr; + + return ret; +} + +static int _fastboot_parts_add_env_entry(char *s, char **retptr) +{ + unsigned long size; + unsigned long offset = 0; + char *name; + int name_len; + int delim; + unsigned int flags; + struct fastboot_ptentry part; + + size = _memparse(s, &s); + if (0 == size) { + printf("Error:FASTBOOT size of parition is 0\n"); + return 1; + } + + /* fetch partition name and flags */ + flags = 0; /* this is going to be a regular partition */ + delim = 0; + /* check for offset */ + if (*s == '@') { + s++; + offset = _memparse(s, &s); + } else { + printf("Error:FASTBOOT offset of parition is not given\n"); + return 1; + } + + /* now look for name */ + if (*s == '(') + delim = ')'; + + if (delim) { + char *p; + + name = ++s; + p = strchr((const char *)name, delim); + if (!p) { + printf("Error:FASTBOOT no closing %c found in partition name\n", + delim); + return 1; + } + name_len = p - name; + s = p + 1; + } else { + printf("Error:FASTBOOT no partition name for \'%s\'\n", s); + return 1; + } + + /* check for options */ + while (1) { + if (strncmp(s, "i", 1) == 0) { + flags |= FASTBOOT_PTENTRY_FLAGS_WRITE_I; + s += 1; + } else if (strncmp(s, "ubifs", 5) == 0) { + /* ubifs */ + flags |= FASTBOOT_PTENTRY_FLAGS_WRITE_TRIMFFS; + s += 5; + } else { + break; + } + if (strncmp(s, "|", 1) == 0) + s += 1; + } + + /* enter this partition (offset will be calculated later if it is zero at this point) */ + part.length = size; + part.start = offset; + part.flags = flags; + + if (name) { + if (name_len >= sizeof(part.name)) { + printf("Error:FASTBOOT partition name is too long\n"); + return 1; + } + strncpy(&part.name[0], name, name_len); + /* name is not null terminated */ + part.name[name_len] = '\0'; + } else { + printf("Error:FASTBOOT no name\n"); + return 1; + } + + fastboot_flash_add_ptn(&part); + + /*if the nand partitions envs are not initialized, try to init them*/ + if (check_parts_values(&part)) + save_parts_values(&part, part.start, part.length); + + /* return (updated) pointer command line string */ + *retptr = s; + + /* return partition table */ + return 0; +} + +static int _fastboot_parts_load_from_env(void) +{ + char fbparts[FASTBOOT_FBPARTS_ENV_MAX_LEN], *env; + + env = getenv("fbparts"); + if (env) { + unsigned int len; + len = strlen(env); + if (len && len < FASTBOOT_FBPARTS_ENV_MAX_LEN) { + char *s, *e; + + memcpy(&fbparts[0], env, len + 1); + printf("Fastboot: Adding partitions from environment\n"); + s = &fbparts[0]; + e = s + len; + while (s < e) { + if (_fastboot_parts_add_env_entry(s, &s)) { + printf("Error:Fastboot: Abort adding partitions\n"); + pcount = 0; + return 1; + } + /* Skip a bunch of delimiters */ + while (s < e) { + if ((' ' == *s) || + ('\t' == *s) || + ('\n' == *s) || + ('\r' == *s) || + (',' == *s)) { + s++; + } else { + break; + } + } + } + } + } + + return 0; +} +#endif /*CONFIG_FASTBOOT_STORAGE_NAND*/ + +static void _fastboot_load_partitions(void) +{ + pcount = 0; +#if defined(CONFIG_FASTBOOT_STORAGE_NAND) + _fastboot_parts_load_from_env(); +#elif defined(CONFIG_FASTBOOT_STORAGE_SATA) \ + || defined(CONFIG_FASTBOOT_STORAGE_MMC) + _fastboot_parts_load_from_ptable(); +#endif +} + +/* + * Android style flash utilties */ +void fastboot_flash_add_ptn(struct fastboot_ptentry *ptn) +{ + if (pcount < MAX_PTN) { + memcpy(ptable + pcount, ptn, sizeof(struct fastboot_ptentry)); + pcount++; + } +} + +void fastboot_flash_dump_ptn(void) +{ + unsigned int n; + for (n = 0; n < pcount; n++) { + struct fastboot_ptentry *ptn = ptable + n; + printf("ptn %d name='%s' start=%d len=%d\n", + n, ptn->name, ptn->start, ptn->length); + } +} + + +struct fastboot_ptentry *fastboot_flash_find_ptn(const char *name) +{ + unsigned int n; + + for (n = 0; n < pcount; n++) { + /* Make sure a substring is not accepted */ + if (strlen(name) == strlen(ptable[n].name)) { + if (0 == strcmp(ptable[n].name, name)) + return ptable + n; + } + } + + printf("can't find partition: %s, dump the partition table\n", name); + fastboot_flash_dump_ptn(); + return 0; +} + +struct fastboot_ptentry *fastboot_flash_get_ptn(unsigned int n) +{ + if (n < pcount) + return ptable + n; + else + return 0; +} + +unsigned int fastboot_flash_get_ptn_count(void) +{ + return pcount; +} |