blob: 0f3ce7de8f8f763d5ccce7d5d2fd5421f44d937f [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fidl/fuchsia.hardware.fastboot/cpp/wire.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/component/incoming/cpp/protocol.h>
#include <lib/fastboot/fastboot.h>
#include <lib/sys/cpp/component_context.h>
#include <lib/syslog/cpp/macros.h>
#include <zircon/syscalls.h>
#include "src/lib/fsl/io/device_watcher.h"
constexpr char kUsbFastbootDirectory[] = "/dev/class/fastboot";
zx::result<fidl::WireSyncClient<fuchsia_hardware_fastboot::FastbootImpl>>
ConnectToUsbFastbootDevice() {
async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
fidl::ClientEnd<fuchsia_hardware_fastboot::FastbootImpl> client;
auto watcher = fsl::DeviceWatcher::Create(
kUsbFastbootDirectory,
[&](const fidl::ClientEnd<fuchsia_io::Directory> &dir, const std::string &filename) {
if (client.is_valid()) {
FX_LOGS(INFO) << "There's already a connected client. Ignoring new devices";
return;
}
FX_LOGS(INFO) << "Connecting to " << kUsbFastbootDirectory << "/" << filename.data();
auto client_end =
component::ConnectAt<fuchsia_hardware_fastboot::FastbootImpl>(dir, filename);
if (!client_end.is_ok()) {
FX_LOGS(INFO) << "Fail to call connect to driver";
return;
}
client = std::move(client_end.value());
loop.Quit();
},
loop.dispatcher());
if (!watcher) {
FX_LOGS(ERROR) << "Could not create device watcher";
return zx::error(ZX_ERR_NOT_FOUND);
}
loop.Run();
return zx::ok(fidl::WireSyncClient(std::move(client)));
}
class UsbPacketTransport : public fastboot::Transport {
public:
UsbPacketTransport(fidl::WireSyncClient<fuchsia_hardware_fastboot::FastbootImpl> &device,
std::string_view packet)
: device_(&device), packet_(packet) {}
zx::result<size_t> ReceivePacket(void *dst, size_t capacity) override {
if (capacity < PeekPacketSize()) {
return zx::error(ZX_ERR_BUFFER_TOO_SMALL);
}
memcpy(dst, packet_.data(), packet_.size());
return zx::ok(packet_.size());
}
size_t PeekPacketSize() override { return packet_.size(); }
zx::result<> Send(std::string_view packet) override {
fzl::OwnedVmoMapper mapper;
zx_status_t status = mapper.CreateAndMap(packet.size(), "fastboot usb send");
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to create vmo mapper for sending " << zx_status_get_string(status);
return zx::error(status);
}
memcpy(mapper.start(), packet.data(), packet.size());
if (zx_status_t status = mapper.vmo().set_prop_content_size(packet.size()); status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to set content size " << zx_status_get_string(status);
return zx::error(status);
}
auto res = (*device_)->Send(mapper.Release());
if (res->is_error()) {
return res->take_error();
}
return zx::ok();
}
private:
fidl::WireSyncClient<fuchsia_hardware_fastboot::FastbootImpl> *device_ = nullptr;
std::string_view packet_;
};
int main(int argc, const char **argv) {
FX_LOGS(INFO) << "Starting fastboot usb";
auto connect_device = ConnectToUsbFastbootDevice();
if (connect_device.is_error()) {
FX_LOGS(ERROR) << "Failed to connect to usb fastboot device" << connect_device.status_string();
return 1;
}
fidl::WireSyncClient<fuchsia_hardware_fastboot::FastbootImpl> device =
std::move(connect_device.value());
fastboot::Fastboot fastboot(zx_system_get_physmem());
while (true) {
// Note: fastboot.remaining_download_size() returns 0 in command stage.
size_t request_size =
std::min(fastboot.remaining_download_size(), static_cast<size_t>(512 * 1024));
auto packet_res = device->Receive(request_size);
if (packet_res->is_error()) {
FX_LOGS(ERROR) << "Failed while receiving packet " << packet_res.status_string();
continue;
}
fzl::VmoMapper mapper;
zx_status_t status = mapper.Map(packet_res.value()->data);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to map packet vmo" << zx_status_get_string(status);
continue;
}
size_t packet_size = 0;
if (zx_status_t status = packet_res.value()->data.get_prop_content_size(&packet_size);
status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to get content size " << zx_status_get_string(status);
continue;
}
UsbPacketTransport transport(
device, std::string_view{reinterpret_cast<const char *>(mapper.start()), packet_size});
auto fastboot_res = fastboot.ProcessPacket(&transport);
if (fastboot_res.is_error()) {
FX_LOGS(ERROR) << "Failed to process fastboot packet " << fastboot_res.status_string();
}
}
return 0;
}