/* * Copyright (C) 2010-2014 Freescale Semiconductor, Inc. All Rights Reserved. * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include #include #include #include #include #ifdef CONFIG_ANDROID_RECOVERY #include #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; }