Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 11f12099 authored by Tom Cherry's avatar Tom Cherry
Browse files

fastboot: remove command queue

There is little advantage and increasingly much disadvantage to
queueing up fastboot commands before executing them.  This change
removes the queue in the most simple way possible to enable further
clean up.

Test: fastboot works
Change-Id: I9abab05df07ed167dbe6a42e1eb9eab8f8d4f157
parent 484f72ab
Loading
Loading
Loading
Loading
+98 −225
Original line number Diff line number Diff line
@@ -44,43 +44,10 @@
#include "constants.h"
#include "transport.h"

enum Op {
    OP_DOWNLOAD,
    OP_COMMAND,
    OP_QUERY,
    OP_NOTICE,
    OP_DOWNLOAD_SPARSE,
    OP_WAIT_FOR_DISCONNECT,
    OP_DOWNLOAD_FD,
    OP_UPLOAD,
};
using android::base::StringPrintf;

struct Action {
    Action(Op op, const std::string& cmd) : op(op), cmd(cmd) {}

    Op op;
    std::string cmd;
    std::string msg;

    std::string product;

    void* data = nullptr;
    // The protocol only supports 32-bit sizes, so you'll have to break
    // anything larger into multiple chunks.
    uint32_t size = 0;

    int fd = -1;

    int (*func)(Action& a, int status, const char* resp) = nullptr;

    double start = -1;
};

static std::vector<std::unique_ptr<Action>> action_list;
static fastboot::FastBootDriver* fb = nullptr;

static constexpr char kStatusFormat[] = "%-50s ";

void fb_init(fastboot::FastBootDriver& fbi) {
    fb = &fbi;
    auto cb = [](std::string& info) { fprintf(stderr, "(bootloader) %s\n", info.c_str()); };
@@ -101,81 +68,72 @@ bool fb_getvar(const std::string& key, std::string* value) {
    return !fb->GetVar(key, value);
}

static int cb_default(Action& a, int status, const char* resp) {
static void HandleResult(double start, int status) {
    if (status) {
        fprintf(stderr,"FAILED (%s)\n", resp);
        fprintf(stderr, "FAILED (%s)\n", fb->Error().c_str());
        die("Command failed");
    } else {
        double split = now();
        fprintf(stderr, "OKAY [%7.3fs]\n", (split - a.start));
        a.start = split;
        fprintf(stderr, "OKAY [%7.3fs]\n", (split - start));
    }
    return status;
}

static Action& queue_action(Op op, const std::string& cmd) {
    std::unique_ptr<Action> a{new Action(op, cmd)};
    a->func = cb_default;

    action_list.push_back(std::move(a));
    return *action_list.back();
#define RUN_COMMAND(command)         \
    {                                \
        double start = now();        \
        auto status = (command);     \
        HandleResult(start, status); \
    }

void fb_set_active(const std::string& slot) {
    Action& a = queue_action(OP_COMMAND, FB_CMD_SET_ACTIVE ":" + slot);
    a.msg = "Setting current slot to '" + slot + "'";
    Status("Setting current slot to '" + slot + "'");
    RUN_COMMAND(fb->SetActive(slot));
}

void fb_queue_erase(const std::string& partition) {
    Action& a = queue_action(OP_COMMAND, FB_CMD_ERASE ":" + partition);
    a.msg = "Erasing '" + partition + "'";
void fb_erase(const std::string& partition) {
    Status("Erasing '" + partition + "'");
    RUN_COMMAND(fb->Erase(partition));
}

void fb_queue_flash_fd(const std::string& partition, int fd, uint32_t sz) {
    Action& a = queue_action(OP_DOWNLOAD_FD, "");
    a.fd = fd;
    a.size = sz;
    a.msg = android::base::StringPrintf("Sending '%s' (%u KB)", partition.c_str(), sz / 1024);
void fb_flash_fd(const std::string& partition, int fd, uint32_t sz) {
    Status(StringPrintf("Sending '%s' (%u KB)", partition.c_str(), sz / 1024));
    RUN_COMMAND(fb->Download(fd, sz));

    Action& b = queue_action(OP_COMMAND, FB_CMD_FLASH ":" + partition);
    b.msg = "Writing '" + partition + "'";
    Status("Writing '" + partition + "'");
    RUN_COMMAND(fb->Flash(partition));
}

void fb_queue_flash(const std::string& partition, void* data, uint32_t sz) {
    Action& a = queue_action(OP_DOWNLOAD, "");
    a.data = data;
    a.size = sz;
    a.msg = android::base::StringPrintf("Sending '%s' (%u KB)", partition.c_str(), sz / 1024);
void fb_flash(const std::string& partition, void* data, uint32_t sz) {
    Status(StringPrintf("Sending '%s' (%u KB)", partition.c_str(), sz / 1024));
    RUN_COMMAND(fb->Download(static_cast<char*>(data), sz));

    Action& b = queue_action(OP_COMMAND, FB_CMD_FLASH ":" + partition);
    b.msg = "Writing '" + partition + "'";
    Status("Writing '" + partition + "'");
    RUN_COMMAND(fb->Flash(partition));
}

void fb_queue_flash_sparse(const std::string& partition, struct sparse_file* s, uint32_t sz,
void fb_flash_sparse(const std::string& partition, struct sparse_file* s, uint32_t sz,
                     size_t current, size_t total) {
    Action& a = queue_action(OP_DOWNLOAD_SPARSE, "");
    a.data = s;
    a.size = 0;
    a.msg = android::base::StringPrintf("Sending sparse '%s' %zu/%zu (%u KB)", partition.c_str(),
                                        current, total, sz / 1024);
    Status(StringPrintf("Sending sparse '%s' %zu/%zu (%u KB)", partition.c_str(), current, total,
                        sz / 1024));
    RUN_COMMAND(fb->Download(s));

    Action& b = queue_action(OP_COMMAND, FB_CMD_FLASH ":" + partition);
    b.msg = android::base::StringPrintf("Writing sparse '%s' %zu/%zu", partition.c_str(), current,
                                        total);
    Status(StringPrintf("Writing sparse '%s' %zu/%zu", partition.c_str(), current, total));
    RUN_COMMAND(fb->Flash(partition));
}

void fb_queue_create_partition(const std::string& partition, const std::string& size) {
    Action& a = queue_action(OP_COMMAND, FB_CMD_CREATE_PARTITION ":" + partition + ":" + size);
    a.msg = "Creating '" + partition + "'";
void fb_create_partition(const std::string& partition, const std::string& size) {
    Status("Creating '" + partition + "'");
    RUN_COMMAND(fb->RawCommand(FB_CMD_CREATE_PARTITION ":" + partition + ":" + size));
}

void fb_queue_delete_partition(const std::string& partition) {
    Action& a = queue_action(OP_COMMAND, FB_CMD_DELETE_PARTITION ":" + partition);
    a.msg = "Deleting '" + partition + "'";
void fb_delete_partition(const std::string& partition) {
    Status("Deleting '" + partition + "'");
    RUN_COMMAND(fb->RawCommand(FB_CMD_DELETE_PARTITION ":" + partition));
}

void fb_queue_resize_partition(const std::string& partition, const std::string& size) {
    Action& a = queue_action(OP_COMMAND, FB_CMD_RESIZE_PARTITION ":" + partition + ":" + size);
    a.msg = "Resizing '" + partition + "'";
void fb_resize_partition(const std::string& partition, const std::string& size) {
    Status("Resizing '" + partition + "'");
    RUN_COMMAND(fb->RawCommand(FB_CMD_RESIZE_PARTITION ":" + partition + ":" + size));
}

static int match(const char* str, const char** value, unsigned count) {
@@ -199,193 +157,108 @@ static int match(const char* str, const char** value, unsigned count) {
    return 0;
}

static int cb_check(Action& a, int status, const char* resp, int invert) {
    const char** value = reinterpret_cast<const char**>(a.data);
    unsigned count = a.size;
    unsigned n;
void fb_require(const std::string& product, const std::string& var, bool invert, size_t count,
                const char** values) {
    Status("Checking '" + var + "'");

    double start = now();

    std::string var_value;
    auto status = fb->GetVar(var, &var_value);

    if (status) {
        fprintf(stderr,"FAILED (%s)\n", resp);
        return status;
        fprintf(stderr, "getvar:%s FAILED (%s)\n", var.c_str(), fb->Error().c_str());
        die("requirements not met!");
    }

    if (!a.product.empty()) {
        if (a.product != cur_product) {
    if (!product.empty()) {
        if (product != cur_product) {
            double split = now();
            fprintf(stderr, "IGNORE, product is %s required only for %s [%7.3fs]\n", cur_product,
                    a.product.c_str(), (split - a.start));
            a.start = split;
            return 0;
                    product.c_str(), (split - start));
            return;
        }
    }

    int yes = match(resp, value, count);
    int yes = match(var_value.c_str(), values, count);
    if (invert) yes = !yes;

    if (yes) {
        double split = now();
        fprintf(stderr, "OKAY [%7.3fs]\n", (split - a.start));
        a.start = split;
        return 0;
        fprintf(stderr, "OKAY [%7.3fs]\n", (split - start));
        return;
    }

    fprintf(stderr, "FAILED\n\n");
    fprintf(stderr, "Device %s is '%s'.\n", a.cmd.c_str() + 7, resp);
    fprintf(stderr, "Update %s '%s'", invert ? "rejects" : "requires", value[0]);
    for (n = 1; n < count; n++) {
        fprintf(stderr, " or '%s'", value[n]);
    fprintf(stderr, "Device %s is '%s'.\n", var.c_str(), var_value.c_str());
    fprintf(stderr, "Update %s '%s'", invert ? "rejects" : "requires", values[0]);
    for (size_t n = 1; n < count; n++) {
        fprintf(stderr, " or '%s'", values[n]);
    }
    fprintf(stderr, ".\n\n");
    return -1;
    die("requirements not met!");
}

static int cb_require(Action& a, int status, const char* resp) {
    return cb_check(a, status, resp, 0);
}

static int cb_reject(Action& a, int status, const char* resp) {
    return cb_check(a, status, resp, 1);
}
void fb_display(const std::string& label, const std::string& var) {
    std::string value;
    auto status = fb->GetVar(var, &value);

void fb_queue_require(const std::string& product, const std::string& var, bool invert,
                      size_t nvalues, const char** values) {
    Action& a = queue_action(OP_QUERY, FB_CMD_GETVAR ":" + var);
    a.product = product;
    a.data = values;
    a.size = nvalues;
    a.msg = "Checking " + var;
    a.func = invert ? cb_reject : cb_require;
    if (a.data == nullptr) die("out of memory");
}

static int cb_display(Action& a, int status, const char* resp) {
    if (status) {
        fprintf(stderr, "%s FAILED (%s)\n", a.cmd.c_str(), resp);
        return status;
        fprintf(stderr, "getvar:%s FAILED (%s)\n", var.c_str(), fb->Error().c_str());
        return;
    }
    fprintf(stderr, "%s: %s\n", static_cast<const char*>(a.data), resp);
    free(static_cast<char*>(a.data));
    return 0;
    fprintf(stderr, "%s: %s\n", label.c_str(), value.c_str());
}

void fb_queue_display(const std::string& label, const std::string& var) {
    Action& a = queue_action(OP_QUERY, FB_CMD_GETVAR ":" + var);
    a.data = xstrdup(label.c_str());
    a.func = cb_display;
}
void fb_query_save(const std::string& var, char* dest, uint32_t dest_size) {
    std::string value;
    auto status = fb->GetVar(var, &value);

static int cb_save(Action& a, int status, const char* resp) {
    if (status) {
        fprintf(stderr, "%s FAILED (%s)\n", a.cmd.c_str(), resp);
        return status;
    }
    strncpy(reinterpret_cast<char*>(a.data), resp, a.size);
    return 0;
        fprintf(stderr, "getvar:%s FAILED (%s)\n", var.c_str(), fb->Error().c_str());
        return;
    }

void fb_queue_query_save(const std::string& var, char* dest, uint32_t dest_size) {
    Action& a = queue_action(OP_QUERY, FB_CMD_GETVAR ":" + var);
    a.data = dest;
    a.size = dest_size;
    a.func = cb_save;
    strncpy(dest, value.c_str(), dest_size);
}

static int cb_do_nothing(Action&, int, const char*) {
void fb_reboot() {
    fprintf(stderr, "Rebooting");
    fb->Reboot();
    fprintf(stderr, "\n");
    return 0;
}

void fb_queue_reboot() {
    Action& a = queue_action(OP_COMMAND, FB_CMD_REBOOT);
    a.func = cb_do_nothing;
    a.msg = "Rebooting";
}

void fb_queue_command(const std::string& cmd, const std::string& msg) {
    Action& a = queue_action(OP_COMMAND, cmd);
    a.msg = msg;
}

void fb_queue_download(const std::string& name, void* data, uint32_t size) {
    Action& a = queue_action(OP_DOWNLOAD, "");
    a.data = data;
    a.size = size;
    a.msg = "Downloading '" + name + "'";
void fb_command(const std::string& cmd, const std::string& msg) {
    Status(msg);
    RUN_COMMAND(fb->RawCommand(cmd));
}

void fb_queue_download_fd(const std::string& name, int fd, uint32_t sz) {
    Action& a = queue_action(OP_DOWNLOAD_FD, "");
    a.fd = fd;
    a.size = sz;
    a.msg = android::base::StringPrintf("Sending '%s' (%u KB)", name.c_str(), sz / 1024);
void fb_download(const std::string& name, void* data, uint32_t size) {
    Status("Downloading '" + name + "'");
    RUN_COMMAND(fb->Download(static_cast<char*>(data), size));
}

void fb_queue_upload(const std::string& outfile) {
    Action& a = queue_action(OP_UPLOAD, "");
    a.data = xstrdup(outfile.c_str());
    a.msg = "Uploading '" + outfile + "'";
void fb_download_fd(const std::string& name, int fd, uint32_t sz) {
    Status(StringPrintf("Sending '%s' (%u KB)", name.c_str(), sz / 1024));
    RUN_COMMAND(fb->Download(fd, sz));
}

void fb_queue_notice(const std::string& notice) {
    Action& a = queue_action(OP_NOTICE, "");
    a.msg = notice;
void fb_upload(const std::string& outfile) {
    Status("Uploading '" + outfile + "'");
    RUN_COMMAND(fb->Upload(outfile));
}

void fb_queue_wait_for_disconnect() {
    queue_action(OP_WAIT_FOR_DISCONNECT, "");
void fb_notice(const std::string& notice) {
    Status(notice);
    fprintf(stderr, "\n");
}

int64_t fb_execute_queue() {
    int64_t status = 0;
    for (auto& a : action_list) {
        a->start = now();
        if (!a->msg.empty()) {
            fprintf(stderr, kStatusFormat, a->msg.c_str());
            verbose("\n");
        }
        if (a->op == OP_DOWNLOAD) {
            char* cbuf = static_cast<char*>(a->data);
            status = fb->Download(cbuf, a->size);
            status = a->func(*a, status, status ? fb_get_error().c_str() : "");
            if (status) break;
        } else if (a->op == OP_DOWNLOAD_FD) {
            status = fb->Download(a->fd, a->size);
            status = a->func(*a, status, status ? fb_get_error().c_str() : "");
            if (status) break;
        } else if (a->op == OP_COMMAND) {
            status = fb->RawCommand(a->cmd);
            status = a->func(*a, status, status ? fb_get_error().c_str() : "");
            if (status) break;
        } else if (a->op == OP_QUERY) {
            std::string resp;
            status = fb->RawCommand(a->cmd, &resp);
            status = a->func(*a, status, status ? fb_get_error().c_str() : resp.c_str());
            if (status) break;
        } else if (a->op == OP_NOTICE) {
            // We already showed the notice because it's in `Action::msg`.
            fprintf(stderr, "\n");
        } else if (a->op == OP_DOWNLOAD_SPARSE) {
            status = fb->Download(reinterpret_cast<sparse_file*>(a->data));
            status = a->func(*a, status, status ? fb_get_error().c_str() : "");
            if (status) break;
        } else if (a->op == OP_WAIT_FOR_DISCONNECT) {
void fb_wait_for_disconnect() {
    fb->WaitForDisconnect();
        } else if (a->op == OP_UPLOAD) {
            status = fb->Upload(reinterpret_cast<const char*>(a->data));
            status = a->func(*a, status, status ? fb_get_error().c_str() : "");
        } else {
            die("unknown action: %d", a->op);
        }
    }
    action_list.clear();
    return status;
}

bool fb_reboot_to_userspace() {
    // First ensure that the queue is flushed.
    fb_execute_queue();

    fprintf(stderr, kStatusFormat, "Rebooting to userspace fastboot");
    Status("Rebooting to userspace fastboot");
    verbose("\n");

    if (fb->RebootTo("fastboot") != fastboot::RetCode::SUCCESS) {
+19 −26
Original line number Diff line number Diff line
@@ -44,36 +44,29 @@ struct sparse_file;

const std::string fb_get_error();

//#define FB_COMMAND_SZ (fastboot::FB_COMMAND_SZ)
//#define FB_RESPONSE_SZ (fastboot::FB_RESPONSE_SZ)

/* engine.c - high level command queue engine */

void fb_init(fastboot::FastBootDriver& fbi);
void fb_reinit(Transport* transport);

bool fb_getvar(const std::string& key, std::string* value);
void fb_queue_flash(const std::string& partition, void* data, uint32_t sz);
void fb_queue_flash_fd(const std::string& partition, int fd, uint32_t sz);
void fb_queue_flash_sparse(const std::string& partition, struct sparse_file* s, uint32_t sz,
void fb_flash(const std::string& partition, void* data, uint32_t sz);
void fb_flash_fd(const std::string& partition, int fd, uint32_t sz);
void fb_flash_sparse(const std::string& partition, struct sparse_file* s, uint32_t sz,
                     size_t current, size_t total);
void fb_queue_erase(const std::string& partition);
void fb_queue_format(const std::string& partition, int skip_if_not_supported, int32_t max_chunk_sz);
void fb_queue_require(const std::string& prod, const std::string& var, bool invert, size_t nvalues,
void fb_erase(const std::string& partition);
void fb_require(const std::string& prod, const std::string& var, bool invert, size_t nvalues,
                const char** values);
void fb_queue_display(const std::string& label, const std::string& var);
void fb_queue_query_save(const std::string& var, char* dest, uint32_t dest_size);
void fb_queue_reboot(void);
void fb_queue_command(const std::string& cmd, const std::string& msg);
void fb_queue_download(const std::string& name, void* data, uint32_t size);
void fb_queue_download_fd(const std::string& name, int fd, uint32_t sz);
void fb_queue_upload(const std::string& outfile);
void fb_queue_notice(const std::string& notice);
void fb_queue_wait_for_disconnect(void);
void fb_queue_create_partition(const std::string& partition, const std::string& size);
void fb_queue_delete_partition(const std::string& partition);
void fb_queue_resize_partition(const std::string& partition, const std::string& size);
int64_t fb_execute_queue();
void fb_display(const std::string& label, const std::string& var);
void fb_query_save(const std::string& var, char* dest, uint32_t dest_size);
void fb_reboot();
void fb_command(const std::string& cmd, const std::string& msg);
void fb_download(const std::string& name, void* data, uint32_t size);
void fb_download_fd(const std::string& name, int fd, uint32_t sz);
void fb_upload(const std::string& outfile);
void fb_notice(const std::string& notice);
void fb_wait_for_disconnect(void);
void fb_create_partition(const std::string& partition, const std::string& size);
void fb_delete_partition(const std::string& partition);
void fb_resize_partition(const std::string& partition, const std::string& size);
void fb_set_active(const std::string& slot);
bool fb_reboot_to_userspace();

+43 −49

File changed.

Preview size limit exceeded, changes collapsed.

+2 −2
Original line number Diff line number Diff line
@@ -97,9 +97,9 @@ RetCode FastBootDriver::RebootTo(std::string target, std::string* response,
    return RawCommand("reboot-" + target, response, info);
}

RetCode FastBootDriver::SetActive(const std::string& part, std::string* response,
RetCode FastBootDriver::SetActive(const std::string& slot, std::string* response,
                                  std::vector<std::string>* info) {
    return RawCommand(Commands::SET_ACTIVE + part, response, info);
    return RawCommand(Commands::SET_ACTIVE + slot, response, info);
}

RetCode FastBootDriver::FlashPartition(const std::string& part, const std::vector<char>& data) {
+1 −1
Original line number Diff line number Diff line
@@ -89,7 +89,7 @@ class FastBootDriver {
    RetCode Reboot(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
    RetCode RebootTo(std::string target, std::string* response = nullptr,
                     std::vector<std::string>* info = nullptr);
    RetCode SetActive(const std::string& part, std::string* response = nullptr,
    RetCode SetActive(const std::string& slot, std::string* response = nullptr,
                      std::vector<std::string>* info = nullptr);
    RetCode Upload(const std::string& outfile, std::string* response = nullptr,
                   std::vector<std::string>* info = nullptr);
Loading