Merge UDP fastboot support from AOSP: https://android.googlesource.com/platform/external/u-boot/+/android-o-mr1-iot-preview-8 Signed-off-by: Alex Kiernan <alex.kiernan@gmail.com> Signed-off-by: Alex Deymo <deymo@google.com> Signed-off-by: Jocelyn Bohr <bohr@google.com> Reviewed-by: Simon Glass <sjg@chromium.org>lime2-spi
parent
c232d14d11
commit
f73a7df984
@ -1,6 +1,7 @@ |
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
obj-y += fb_common.o
|
||||
|
||||
obj-$(CONFIG_UDP_FUNCTION_FASTBOOT) += fb_getvar.o
|
||||
obj-$(CONFIG_UDP_FUNCTION_FASTBOOT) += fb_command.o
|
||||
obj-$(CONFIG_FASTBOOT_FLASH_MMC) += fb_mmc.o
|
||||
obj-$(CONFIG_FASTBOOT_FLASH_NAND) += fb_nand.o
|
||||
|
@ -0,0 +1,302 @@ |
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <fastboot.h> |
||||
#include <fastboot-internal.h> |
||||
#include <fb_mmc.h> |
||||
#include <fb_nand.h> |
||||
#include <part.h> |
||||
#include <stdlib.h> |
||||
|
||||
/**
|
||||
* image_size - final fastboot image size |
||||
*/ |
||||
static u32 image_size; |
||||
|
||||
/**
|
||||
* fastboot_bytes_received - number of bytes received in the current download |
||||
*/ |
||||
static u32 fastboot_bytes_received; |
||||
|
||||
/**
|
||||
* fastboot_bytes_expected - number of bytes expected in the current download |
||||
*/ |
||||
static u32 fastboot_bytes_expected; |
||||
|
||||
static void okay(char *, char *); |
||||
static void getvar(char *, char *); |
||||
static void download(char *, char *); |
||||
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH) |
||||
static void flash(char *, char *); |
||||
static void erase(char *, char *); |
||||
#endif |
||||
static void reboot_bootloader(char *, char *); |
||||
|
||||
static const struct { |
||||
const char *command; |
||||
void (*dispatch)(char *cmd_parameter, char *response); |
||||
} commands[FASTBOOT_COMMAND_COUNT] = { |
||||
[FASTBOOT_COMMAND_GETVAR] = { |
||||
.command = "getvar", |
||||
.dispatch = getvar |
||||
}, |
||||
[FASTBOOT_COMMAND_DOWNLOAD] = { |
||||
.command = "download", |
||||
.dispatch = download |
||||
}, |
||||
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH) |
||||
[FASTBOOT_COMMAND_FLASH] = { |
||||
.command = "flash", |
||||
.dispatch = flash |
||||
}, |
||||
[FASTBOOT_COMMAND_ERASE] = { |
||||
.command = "erase", |
||||
.dispatch = erase |
||||
}, |
||||
#endif |
||||
[FASTBOOT_COMMAND_BOOT] = { |
||||
.command = "boot", |
||||
.dispatch = okay |
||||
}, |
||||
[FASTBOOT_COMMAND_CONTINUE] = { |
||||
.command = "continue", |
||||
.dispatch = okay |
||||
}, |
||||
[FASTBOOT_COMMAND_REBOOT] = { |
||||
.command = "reboot", |
||||
.dispatch = okay |
||||
}, |
||||
[FASTBOOT_COMMAND_REBOOT_BOOTLOADER] = { |
||||
.command = "reboot-bootloader", |
||||
.dispatch = reboot_bootloader |
||||
}, |
||||
[FASTBOOT_COMMAND_SET_ACTIVE] = { |
||||
.command = "set_active", |
||||
.dispatch = okay |
||||
}, |
||||
}; |
||||
|
||||
/**
|
||||
* fastboot_handle_command - Handle fastboot command |
||||
* |
||||
* @cmd_string: Pointer to command string |
||||
* @response: Pointer to fastboot response buffer |
||||
* |
||||
* Return: Executed command, or -1 if not recognized |
||||
*/ |
||||
int fastboot_handle_command(char *cmd_string, char *response) |
||||
{ |
||||
int i; |
||||
char *cmd_parameter; |
||||
|
||||
cmd_parameter = cmd_string; |
||||
strsep(&cmd_parameter, ":"); |
||||
|
||||
for (i = 0; i < FASTBOOT_COMMAND_COUNT; i++) { |
||||
if (!strcmp(commands[i].command, cmd_string)) { |
||||
if (commands[i].dispatch) { |
||||
commands[i].dispatch(cmd_parameter, |
||||
response); |
||||
return i; |
||||
} else { |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
pr_err("command %s not recognized.\n", cmd_string); |
||||
fastboot_fail("unrecognized command", response); |
||||
return -1; |
||||
} |
||||
|
||||
/**
|
||||
* okay() - Send bare OKAY response |
||||
* |
||||
* @cmd_parameter: Pointer to command parameter |
||||
* @response: Pointer to fastboot response buffer |
||||
* |
||||
* Send a bare OKAY fastboot response. This is used where the command is |
||||
* valid, but all the work is done after the response has been sent (e.g. |
||||
* boot, reboot etc.) |
||||
*/ |
||||
static void okay(char *cmd_parameter, char *response) |
||||
{ |
||||
fastboot_okay(NULL, response); |
||||
} |
||||
|
||||
/**
|
||||
* getvar() - Read a config/version variable |
||||
* |
||||
* @cmd_parameter: Pointer to command parameter |
||||
* @response: Pointer to fastboot response buffer |
||||
*/ |
||||
static void getvar(char *cmd_parameter, char *response) |
||||
{ |
||||
fastboot_getvar(cmd_parameter, response); |
||||
} |
||||
|
||||
/**
|
||||
* fastboot_download() - Start a download transfer from the client |
||||
* |
||||
* @cmd_parameter: Pointer to command parameter |
||||
* @response: Pointer to fastboot response buffer |
||||
*/ |
||||
static void download(char *cmd_parameter, char *response) |
||||
{ |
||||
char *tmp; |
||||
|
||||
if (!cmd_parameter) { |
||||
fastboot_fail("Expected command parameter", response); |
||||
return; |
||||
} |
||||
fastboot_bytes_received = 0; |
||||
fastboot_bytes_expected = simple_strtoul(cmd_parameter, &tmp, 16); |
||||
if (fastboot_bytes_expected == 0) { |
||||
fastboot_fail("Expected nonzero image size", response); |
||||
return; |
||||
} |
||||
/*
|
||||
* Nothing to download yet. Response is of the form: |
||||
* [DATA|FAIL]$cmd_parameter |
||||
* |
||||
* where cmd_parameter is an 8 digit hexadecimal number |
||||
*/ |
||||
if (fastboot_bytes_expected > fastboot_buf_size) { |
||||
fastboot_fail(cmd_parameter, response); |
||||
} else { |
||||
printf("Starting download of %d bytes\n", |
||||
fastboot_bytes_expected); |
||||
fastboot_response("DATA", response, "%s", cmd_parameter); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* fastboot_data_remaining() - return bytes remaining in current transfer |
||||
* |
||||
* Return: Number of bytes left in the current download |
||||
*/ |
||||
u32 fastboot_data_remaining(void) |
||||
{ |
||||
return fastboot_bytes_expected - fastboot_bytes_received; |
||||
} |
||||
|
||||
/**
|
||||
* fastboot_data_download() - Copy image data to fastboot_buf_addr. |
||||
* |
||||
* @fastboot_data: Pointer to received fastboot data |
||||
* @fastboot_data_len: Length of received fastboot data |
||||
* @response: Pointer to fastboot response buffer |
||||
* |
||||
* Copies image data from fastboot_data to fastboot_buf_addr. Writes to |
||||
* response. fastboot_bytes_received is updated to indicate the number |
||||
* of bytes that have been transferred. |
||||
* |
||||
* On completion sets image_size and ${filesize} to the total size of the |
||||
* downloaded image. |
||||
*/ |
||||
void fastboot_data_download(const void *fastboot_data, |
||||
unsigned int fastboot_data_len, |
||||
char *response) |
||||
{ |
||||
#define BYTES_PER_DOT 0x20000 |
||||
u32 pre_dot_num, now_dot_num; |
||||
|
||||
if (fastboot_data_len == 0 || |
||||
(fastboot_bytes_received + fastboot_data_len) > |
||||
fastboot_bytes_expected) { |
||||
fastboot_fail("Received invalid data length", |
||||
response); |
||||
return; |
||||
} |
||||
/* Download data to fastboot_buf_addr */ |
||||
memcpy(fastboot_buf_addr + fastboot_bytes_received, |
||||
fastboot_data, fastboot_data_len); |
||||
|
||||
pre_dot_num = fastboot_bytes_received / BYTES_PER_DOT; |
||||
fastboot_bytes_received += fastboot_data_len; |
||||
now_dot_num = fastboot_bytes_received / BYTES_PER_DOT; |
||||
|
||||
if (pre_dot_num != now_dot_num) { |
||||
putc('.'); |
||||
if (!(now_dot_num % 74)) |
||||
putc('\n'); |
||||
} |
||||
*response = '\0'; |
||||
} |
||||
|
||||
/**
|
||||
* fastboot_data_complete() - Mark current transfer complete |
||||
* |
||||
* @response: Pointer to fastboot response buffer |
||||
* |
||||
* Set image_size and ${filesize} to the total size of the downloaded image. |
||||
*/ |
||||
void fastboot_data_complete(char *response) |
||||
{ |
||||
/* Download complete. Respond with "OKAY" */ |
||||
fastboot_okay(NULL, response); |
||||
printf("\ndownloading of %d bytes finished\n", fastboot_bytes_received); |
||||
image_size = fastboot_bytes_received; |
||||
env_set_hex("filesize", image_size); |
||||
fastboot_bytes_expected = 0; |
||||
fastboot_bytes_received = 0; |
||||
} |
||||
|
||||
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH) |
||||
/**
|
||||
* flash() - write the downloaded image to the indicated partition. |
||||
* |
||||
* @cmd_parameter: Pointer to partition name |
||||
* @response: Pointer to fastboot response buffer |
||||
* |
||||
* Writes the previously downloaded image to the partition indicated by |
||||
* cmd_parameter. Writes to response. |
||||
*/ |
||||
static void flash(char *cmd_parameter, char *response) |
||||
{ |
||||
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC) |
||||
fastboot_mmc_flash_write(cmd_parameter, fastboot_buf_addr, image_size, |
||||
response); |
||||
#endif |
||||
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_NAND) |
||||
fastboot_nand_flash_write(cmd_parameter, fastboot_buf_addr, image_size, |
||||
response); |
||||
#endif |
||||
} |
||||
|
||||
/**
|
||||
* erase() - erase the indicated partition. |
||||
* |
||||
* @cmd_parameter: Pointer to partition name |
||||
* @response: Pointer to fastboot response buffer |
||||
* |
||||
* Erases the partition indicated by cmd_parameter (clear to 0x00s). Writes |
||||
* to response. |
||||
*/ |
||||
static void erase(char *cmd_parameter, char *response) |
||||
{ |
||||
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC) |
||||
fastboot_mmc_erase(cmd_parameter, response); |
||||
#endif |
||||
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_NAND) |
||||
fastboot_nand_erase(cmd_parameter, response); |
||||
#endif |
||||
} |
||||
#endif |
||||
|
||||
/**
|
||||
* reboot_bootloader() - Sets reboot bootloader flag. |
||||
* |
||||
* @cmd_parameter: Pointer to command parameter |
||||
* @response: Pointer to fastboot response buffer |
||||
*/ |
||||
static void reboot_bootloader(char *cmd_parameter, char *response) |
||||
{ |
||||
if (fastboot_set_reboot_flag()) |
||||
fastboot_fail("Cannot set reboot flag", response); |
||||
else |
||||
fastboot_okay(NULL, response); |
||||
} |
@ -0,0 +1,230 @@ |
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <fastboot.h> |
||||
#include <fastboot-internal.h> |
||||
#include <fb_mmc.h> |
||||
#include <fb_nand.h> |
||||
#include <fs.h> |
||||
#include <version.h> |
||||
|
||||
static void getvar_version(char *var_parameter, char *response); |
||||
static void getvar_bootloader_version(char *var_parameter, char *response); |
||||
static void getvar_downloadsize(char *var_parameter, char *response); |
||||
static void getvar_serialno(char *var_parameter, char *response); |
||||
static void getvar_version_baseband(char *var_parameter, char *response); |
||||
static void getvar_product(char *var_parameter, char *response); |
||||
static void getvar_current_slot(char *var_parameter, char *response); |
||||
static void getvar_slot_suffixes(char *var_parameter, char *response); |
||||
static void getvar_has_slot(char *var_parameter, char *response); |
||||
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC) |
||||
static void getvar_partition_type(char *part_name, char *response); |
||||
#endif |
||||
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH) |
||||
static void getvar_partition_size(char *part_name, char *response); |
||||
#endif |
||||
|
||||
static const struct { |
||||
const char *variable; |
||||
void (*dispatch)(char *var_parameter, char *response); |
||||
} getvar_dispatch[] = { |
||||
{ |
||||
.variable = "version", |
||||
.dispatch = getvar_version |
||||
}, { |
||||
.variable = "bootloader-version", |
||||
.dispatch = getvar_bootloader_version |
||||
}, { |
||||
.variable = "version-bootloader", |
||||
.dispatch = getvar_bootloader_version |
||||
}, { |
||||
.variable = "downloadsize", |
||||
.dispatch = getvar_downloadsize |
||||
}, { |
||||
.variable = "max-download-size", |
||||
.dispatch = getvar_downloadsize |
||||
}, { |
||||
.variable = "serialno", |
||||
.dispatch = getvar_serialno |
||||
}, { |
||||
.variable = "version-baseband", |
||||
.dispatch = getvar_version_baseband |
||||
}, { |
||||
.variable = "product", |
||||
.dispatch = getvar_product |
||||
}, { |
||||
.variable = "current-slot", |
||||
.dispatch = getvar_current_slot |
||||
}, { |
||||
.variable = "slot-suffixes", |
||||
.dispatch = getvar_slot_suffixes |
||||
}, { |
||||
.variable = "has_slot", |
||||
.dispatch = getvar_has_slot |
||||
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC) |
||||
}, { |
||||
.variable = "partition-type", |
||||
.dispatch = getvar_partition_type |
||||
#endif |
||||
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH) |
||||
}, { |
||||
.variable = "partition-size", |
||||
.dispatch = getvar_partition_size |
||||
#endif |
||||
} |
||||
}; |
||||
|
||||
static void getvar_version(char *var_parameter, char *response) |
||||
{ |
||||
fastboot_okay(FASTBOOT_VERSION, response); |
||||
} |
||||
|
||||
static void getvar_bootloader_version(char *var_parameter, char *response) |
||||
{ |
||||
fastboot_okay(U_BOOT_VERSION, response); |
||||
} |
||||
|
||||
static void getvar_downloadsize(char *var_parameter, char *response) |
||||
{ |
||||
fastboot_response("OKAY", response, "0x%08x", fastboot_buf_size); |
||||
} |
||||
|
||||
static void getvar_serialno(char *var_parameter, char *response) |
||||
{ |
||||
const char *tmp = env_get("serial#"); |
||||
|
||||
if (tmp) |
||||
fastboot_okay(tmp, response); |
||||
else |
||||
fastboot_fail("Value not set", response); |
||||
} |
||||
|
||||
static void getvar_version_baseband(char *var_parameter, char *response) |
||||
{ |
||||
fastboot_okay("N/A", response); |
||||
} |
||||
|
||||
static void getvar_product(char *var_parameter, char *response) |
||||
{ |
||||
const char *board = env_get("board"); |
||||
|
||||
if (board) |
||||
fastboot_okay(board, response); |
||||
else |
||||
fastboot_fail("Board not set", response); |
||||
} |
||||
|
||||
static void getvar_current_slot(char *var_parameter, char *response) |
||||
{ |
||||
/* A/B not implemented, for now always return _a */ |
||||
fastboot_okay("_a", response); |
||||
} |
||||
|
||||
static void getvar_slot_suffixes(char *var_parameter, char *response) |
||||
{ |
||||
fastboot_okay("_a,_b", response); |
||||
} |
||||
|
||||
static void getvar_has_slot(char *part_name, char *response) |
||||
{ |
||||
if (part_name && (!strcmp(part_name, "boot") || |
||||
!strcmp(part_name, "system"))) |
||||
fastboot_okay("yes", response); |
||||
else |
||||
fastboot_okay("no", response); |
||||
} |
||||
|
||||
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC) |
||||
static void getvar_partition_type(char *part_name, char *response) |
||||
{ |
||||
int r; |
||||
struct blk_desc *dev_desc; |
||||
disk_partition_t part_info; |
||||
|
||||
r = fastboot_mmc_get_part_info(part_name, &dev_desc, &part_info, |
||||
response); |
||||
if (r >= 0) { |
||||
r = fs_set_blk_dev_with_part(dev_desc, r); |
||||
if (r < 0) |
||||
fastboot_fail("failed to set partition", response); |
||||
else |
||||
fastboot_okay(fs_get_type_name(), response); |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH) |
||||
static void getvar_partition_size(char *part_name, char *response) |
||||
{ |
||||
int r; |
||||
size_t size; |
||||
|
||||
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC) |
||||
struct blk_desc *dev_desc; |
||||
disk_partition_t part_info; |
||||
|
||||
r = fastboot_mmc_get_part_info(part_name, &dev_desc, &part_info, |
||||
response); |
||||
if (r >= 0) |
||||
size = part_info.size; |
||||
#endif |
||||
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_NAND) |
||||
struct part_info *part_info; |
||||
|
||||
r = fastboot_nand_get_part_info(part_name, &part_info, response); |
||||
if (r >= 0) |
||||
size = part_info->size; |
||||
#endif |
||||
if (r >= 0) |
||||
fastboot_response("OKAY", response, "0x%016zx", size); |
||||
} |
||||
#endif |
||||
|
||||
/**
|
||||
* fastboot_getvar() - Writes variable indicated by cmd_parameter to response. |
||||
* |
||||
* @cmd_parameter: Pointer to command parameter |
||||
* @response: Pointer to fastboot response buffer |
||||
* |
||||
* Look up cmd_parameter first as an environment variable of the form |
||||
* fastboot.<cmd_parameter>, if that exists return use its value to set |
||||
* response. |
||||
* |
||||
* Otherwise lookup the name of variable and execute the appropriate |
||||
* function to return the requested value. |
||||
*/ |
||||
void fastboot_getvar(char *cmd_parameter, char *response) |
||||
{ |
||||
if (!cmd_parameter) { |
||||
fastboot_fail("missing var", response); |
||||
} else { |
||||
#define FASTBOOT_ENV_PREFIX "fastboot." |
||||
int i; |
||||
char *var_parameter = cmd_parameter; |
||||
char envstr[FASTBOOT_RESPONSE_LEN]; |
||||
const char *s; |
||||
|
||||
snprintf(envstr, sizeof(envstr) - 1, |
||||
FASTBOOT_ENV_PREFIX "%s", cmd_parameter); |
||||
s = env_get(envstr); |
||||
if (s) { |
||||
fastboot_response("OKAY", response, "%s", s); |
||||
return; |
||||
} |
||||
|
||||
strsep(&var_parameter, ":"); |
||||
for (i = 0; i < ARRAY_SIZE(getvar_dispatch); ++i) { |
||||
if (!strcmp(getvar_dispatch[i].variable, |
||||
cmd_parameter)) { |
||||
getvar_dispatch[i].dispatch(var_parameter, |
||||
response); |
||||
return; |
||||
} |
||||
} |
||||
pr_warn("WARNING: unknown variable: %s\n", cmd_parameter); |
||||
fastboot_fail("Variable not implemented", response); |
||||
} |
||||
} |
@ -0,0 +1,36 @@ |
||||
/* SPDX-License-Identifier: GPL-2.0+ */ |
||||
|
||||
#ifndef _FASTBOOT_INTERNAL_H_ |
||||
#define _FASTBOOT_INTERNAL_H_ |
||||
|
||||
/**
|
||||
* fastboot_buf_addr - base address of the fastboot download buffer |
||||
*/ |
||||
extern void *fastboot_buf_addr; |
||||
|
||||
/**
|
||||
* fastboot_buf_size - size of the fastboot download buffer |
||||
*/ |
||||
extern u32 fastboot_buf_size; |
||||
|
||||
/**
|
||||
* fastboot_progress_callback - callback executed during long operations |
||||
*/ |
||||
extern void (*fastboot_progress_callback)(const char *msg); |
||||
|
||||
/**
|
||||
* fastboot_getvar() - Writes variable indicated by cmd_parameter to response. |
||||
* |
||||
* @cmd_parameter: Pointer to command parameter |
||||
* @response: Pointer to fastboot response buffer |
||||
* |
||||
* Look up cmd_parameter first as an environment variable of the form |
||||
* fastboot.<cmd_parameter>, if that exists return use its value to set |
||||
* response. |
||||
* |
||||
* Otherwise lookup the name of variable and execute the appropriate |
||||
* function to return the requested value. |
||||
*/ |
||||
void fastboot_getvar(char *cmd_parameter, char *response); |
||||
|
||||
#endif |
@ -0,0 +1,21 @@ |
||||
/* SPDX-License-Identifier: BSD-2-Clause
|
||||
* |
||||
* Copyright (C) 2016 The Android Open Source Project |
||||
*/ |
||||
|
||||
#ifndef __NET_FASTBOOT_H__ |
||||
#define __NET_FASTBOOT_H__ |
||||
|
||||
/**********************************************************************/ |
||||
/*
|
||||
* Global functions and variables. |
||||
*/ |
||||
|
||||
/**
|
||||
* Wait for incoming fastboot comands. |
||||
*/ |
||||
void fastboot_start_server(void); |
||||
|
||||
/**********************************************************************/ |
||||
|
||||
#endif /* __NET_FASTBOOT_H__ */ |
@ -0,0 +1,317 @@ |
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <fastboot.h> |
||||
#include <net.h> |
||||
#include <net/fastboot.h> |
||||
|
||||
/* Fastboot port # defined in spec */ |
||||
#define WELL_KNOWN_PORT 5554 |
||||
|
||||
enum { |
||||
FASTBOOT_ERROR = 0, |
||||
FASTBOOT_QUERY = 1, |
||||
FASTBOOT_INIT = 2, |
||||
FASTBOOT_FASTBOOT = 3, |
||||
}; |
||||
|
||||
struct __packed fastboot_header { |
||||
uchar id; |
||||
uchar flags; |
||||
unsigned short seq; |
||||
}; |
||||
|
||||
#define PACKET_SIZE 1024 |
||||
#define DATA_SIZE (PACKET_SIZE - sizeof(struct fastboot_header)) |
||||
|
||||
/* Sequence number sent for every packet */ |
||||
static unsigned short sequence_number = 1; |
||||
static const unsigned short packet_size = PACKET_SIZE; |
||||
static const unsigned short udp_version = 1; |
||||
|
||||
/* Keep track of last packet for resubmission */ |
||||
static uchar last_packet[PACKET_SIZE]; |
||||
static unsigned int last_packet_len; |
||||
|
||||
static struct in_addr fastboot_remote_ip; |
||||
/* The UDP port at their end */ |
||||
static int fastboot_remote_port; |
||||
/* The UDP port at our end */ |
||||
static int fastboot_our_port; |
||||
|
||||
static void boot_downloaded_image(void); |
||||
|
||||
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH) |
||||
/**
|
||||
* fastboot_udp_send_info() - Send an INFO packet during long commands. |
||||
* |
||||
* @msg: String describing the reason for waiting |
||||
*/ |
||||
static void fastboot_udp_send_info(const char *msg) |
||||
{ |
||||
uchar *packet; |
||||
uchar *packet_base; |
||||
int len = 0; |
||||
char response[FASTBOOT_RESPONSE_LEN] = {0}; |
||||
|
||||
struct fastboot_header response_header = { |
||||
.id = FASTBOOT_FASTBOOT, |
||||
.flags = 0, |
||||
.seq = htons(sequence_number) |
||||
}; |
||||
++sequence_number; |
||||
packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE; |
||||
packet_base = packet; |
||||
|
||||
/* Write headers */ |
||||
memcpy(packet, &response_header, sizeof(response_header)); |
||||
packet += sizeof(response_header); |
||||
/* Write response */ |
||||
fastboot_response("INFO", response, "%s", msg); |
||||
memcpy(packet, response, strlen(response)); |
||||
packet += strlen(response); |
||||
|
||||
len = packet - packet_base; |
||||
|
||||
/* Save packet for retransmitting */ |
||||
last_packet_len = len; |
||||
memcpy(last_packet, packet_base, last_packet_len); |
||||
|
||||
net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip, |
||||
fastboot_remote_port, fastboot_our_port, len); |
||||
} |
||||
|
||||
/**
|
||||
* fastboot_timed_send_info() - Send INFO packet every 30 seconds |
||||
* |
||||
* @msg: String describing the reason for waiting |
||||
* |
||||
* Send an INFO packet during long commands based on timer. An INFO packet |
||||
* is sent if the time is 30 seconds after start. Else, noop. |
||||
*/ |
||||
static void fastboot_timed_send_info(const char *msg) |
||||
{ |
||||
static ulong start; |
||||
|
||||
/* Initialize timer */ |
||||
if (start == 0) |
||||
start = get_timer(0); |
||||
ulong time = get_timer(start); |
||||
/* Send INFO packet to host every 30 seconds */ |
||||
if (time >= 30000) { |
||||
start = get_timer(0); |
||||
fastboot_udp_send_info(msg); |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
/**
|
||||
* fastboot_send() - Sends a packet in response to received fastboot packet |
||||
* |
||||
* @header: Header for response packet |
||||
* @fastboot_data: Pointer to received fastboot data |
||||
* @fastboot_data_len: Length of received fastboot data |
||||
* @retransmit: Nonzero if sending last sent packet |
||||
*/ |
||||
static void fastboot_send(struct fastboot_header header, char *fastboot_data, |
||||
unsigned int fastboot_data_len, uchar retransmit) |
||||
{ |
||||
uchar *packet; |
||||
uchar *packet_base; |
||||
int len = 0; |
||||
const char *error_msg = "An error occurred."; |
||||
short tmp; |
||||
struct fastboot_header response_header = header; |
||||
static char command[FASTBOOT_COMMAND_LEN]; |
||||
static int cmd = -1; |
||||
static bool pending_command; |
||||
char response[FASTBOOT_RESPONSE_LEN] = {0}; |
||||
|
||||
/*
|
||||
* We will always be sending some sort of packet, so |
||||
* cobble together the packet headers now. |
||||
*/ |
||||
packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE; |
||||
packet_base = packet; |
||||
|
||||
/* Resend last packet */ |
||||
if (retransmit) { |
||||
memcpy(packet, last_packet, last_packet_len); |
||||
net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip, |
||||
fastboot_remote_port, fastboot_our_port, |
||||
last_packet_len); |
||||
return; |
||||
} |
||||
|
||||
response_header.seq = htons(response_header.seq); |
||||
memcpy(packet, &response_header, sizeof(response_header)); |
||||
packet += sizeof(response_header); |
||||
|
||||
switch (header.id) { |
||||
case FASTBOOT_QUERY: |
||||
tmp = htons(sequence_number); |
||||
memcpy(packet, &tmp, sizeof(tmp)); |
||||
packet += sizeof(tmp); |
||||
break; |
||||
case FASTBOOT_INIT: |
||||
tmp = htons(udp_version); |
||||
memcpy(packet, &tmp, sizeof(tmp)); |
||||
packet += sizeof(tmp); |
||||
tmp = htons(packet_size); |
||||
memcpy(packet, &tmp, sizeof(tmp)); |
||||
packet += sizeof(tmp); |
||||
break; |
||||
case FASTBOOT_ERROR: |
||||
memcpy(packet, error_msg, strlen(error_msg)); |
||||
packet += strlen(error_msg); |
||||
break; |
||||
case FASTBOOT_FASTBOOT: |
||||
if (cmd == FASTBOOT_COMMAND_DOWNLOAD) { |
||||
if (!fastboot_data_len && !fastboot_data_remaining()) { |
||||
fastboot_data_complete(response); |
||||
} else { |
||||
fastboot_data_download(fastboot_data, |
||||
fastboot_data_len, |
||||
response); |
||||
} |
||||
} else if (!pending_command) { |
||||
strlcpy(command, fastboot_data, |
||||
min((size_t)fastboot_data_len + 1, |
||||
sizeof(command))); |
||||
pending_command = true; |
||||
} else { |
||||
cmd = fastboot_handle_command(command, response); |
||||
pending_command = false; |
||||
} |
||||
/*
|
||||
* Sent some INFO packets, need to update sequence number in |
||||
* header |
||||
*/ |
||||
if (header.seq != sequence_number) { |
||||
response_header.seq = htons(sequence_number); |
||||
memcpy(packet_base, &response_header, |
||||
sizeof(response_header)); |
||||
} |
||||
/* Write response to packet */ |
||||
memcpy(packet, response, strlen(response)); |
||||
packet += strlen(response); |
||||
break; |
||||
default: |
||||
pr_err("ID %d not implemented.\n", header.id); |
||||
return; |
||||
} |
||||
|
||||
len = packet - packet_base; |
||||
|
||||
/* Save packet for retransmitting */ |
||||
last_packet_len = len; |
||||
memcpy(last_packet, packet_base, last_packet_len); |
||||
|
||||
net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip, |
||||
fastboot_remote_port, fastboot_our_port, len); |
||||
|
||||
/* Continue boot process after sending response */ |
||||
if (!strncmp("OKAY", response, 4)) { |
||||
switch (cmd) { |
||||
case FASTBOOT_COMMAND_BOOT: |
||||
boot_downloaded_image(); |
||||
break; |
||||
|
||||
case FASTBOOT_COMMAND_CONTINUE: |
||||
net_set_state(NETLOOP_SUCCESS); |
||||
break; |
||||
|
||||
case FASTBOOT_COMMAND_REBOOT: |
||||
case FASTBOOT_COMMAND_REBOOT_BOOTLOADER: |
||||
do_reset(NULL, 0, 0, NULL); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (!strncmp("OKAY", response, 4) || !strncmp("FAIL", response, 4)) |
||||
cmd = -1; |
||||
} |
||||
|
||||
/**
|
||||
* boot_downloaded_image() - Boots into downloaded image. |
||||
*/ |
||||
static void boot_downloaded_image(void) |
||||
{ |
||||
fastboot_boot(); |
||||
net_set_state(NETLOOP_SUCCESS); |
||||
} |
||||
|
||||
/**
|
||||
* fastboot_handler() - Incoming UDP packet handler. |
||||
* |
||||
* @packet: Pointer to incoming UDP packet |
||||
* @dport: Destination UDP port |
||||
* @sip: Source IP address |
||||
* @sport: Source UDP port |
||||
* @len: Packet length |
||||
*/ |
||||
static void fastboot_handler(uchar *packet, unsigned int dport, |
||||
struct in_addr sip, unsigned int sport, |
||||
unsigned int len) |
||||
{ |
||||
struct fastboot_header header; |
||||
char fastboot_data[DATA_SIZE] = {0}; |
||||
unsigned int fastboot_data_len = 0; |
||||
|
||||
if (dport != fastboot_our_port) |
||||
return; |
||||
|
||||
fastboot_remote_ip = sip; |
||||
fastboot_remote_port = sport; |
||||
|
||||
if (len < sizeof(struct fastboot_header) || len > PACKET_SIZE) |
||||
return; |
||||
memcpy(&header, packet, sizeof(header)); |
||||
header.flags = 0; |
||||
header.seq = ntohs(header.seq); |
||||
packet += sizeof(header); |
||||
len -= sizeof(header); |
||||
|
||||
switch (header.id) { |
||||
case FASTBOOT_QUERY: |
||||
fastboot_send(header, fastboot_data, 0, 0); |
||||
break; |
||||
case FASTBOOT_INIT: |
||||
case FASTBOOT_FASTBOOT: |
||||
fastboot_data_len = len; |
||||
if (len > 0) |
||||
memcpy(fastboot_data, packet, len); |
||||
if (header.seq == sequence_number) { |
||||
fastboot_send(header, fastboot_data, |
||||
fastboot_data_len, 0); |
||||
sequence_number++; |
||||
} else if (header.seq == sequence_number - 1) { |
||||
/* Retransmit last sent packet */ |
||||
fastboot_send(header, fastboot_data, |
||||
fastboot_data_len, 1); |
||||
} |
||||
break; |
||||
default: |
||||
pr_err("ID %d not implemented.\n", header.id); |
||||
header.id = FASTBOOT_ERROR; |
||||
fastboot_send(header, fastboot_data, 0, 0); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
void fastboot_start_server(void) |
||||
{ |
||||
printf("Using %s device\n", eth_get_name()); |
||||
printf("Listening for fastboot command on %pI4\n", &net_ip); |
||||
|
||||
fastboot_our_port = WELL_KNOWN_PORT; |
||||
|
||||
fastboot_set_progress_callback(fastboot_timed_send_info); |
||||
net_set_udp_handler(fastboot_handler); |
||||
|
||||
/* zero out server ether in case the server ip has changed */ |
||||
memset(net_server_ethaddr, 0, 6); |
||||
} |
Loading…
Reference in new issue