blob: 4c911f0bdbedbec4d7fb87800bf2c4a1592fffc8 [file] [log] [blame]
/*
* 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;
}