blob: 7e7a70ca90d64ee273d6a138dc38aeea990f7b6c [file] [log] [blame]
// Copyright 2018 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 <fcntl.h>
#include <fidl/fuchsia.device/cpp/wire.h>
#include <fuchsia/device/c/fidl.h>
#include <fuchsia/hardware/nand/c/fidl.h>
#include <lib/fdio/cpp/caller.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/fd.h>
#include <lib/fdio/fdio.h>
#include <lib/fdio/unsafe.h>
#include <lib/fdio/watcher.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <zircon/types.h>
#include <utility>
#include <fbl/string_buffer.h>
#include <fbl/unique_fd.h>
#include <ramdevice-client/ramnand.h>
namespace {
// Waits for |file| to appear in |dir|, and opens it when it does. Times out if
// the deadline passes.
zx_status_t WaitForFile(const fbl::unique_fd& dir, const char* file, fbl::unique_fd* out) {
auto watch_func = [](int dirfd, int event, const char* fn, void* cookie) -> zx_status_t {
auto file = reinterpret_cast<const char*>(cookie);
if (event != WATCH_EVENT_ADD_FILE) {
return ZX_OK;
}
if (!strcmp(fn, file)) {
return ZX_ERR_STOP;
}
return ZX_OK;
};
zx_status_t status =
fdio_watch_directory(dir.get(), watch_func, ZX_TIME_INFINITE, const_cast<char*>(file));
if (status != ZX_ERR_STOP) {
return status;
}
out->reset(openat(dir.get(), file, O_RDWR));
if (!out->is_valid()) {
return ZX_ERR_IO;
}
return ZX_OK;
}
} // namespace
namespace ramdevice_client {
__EXPORT
zx_status_t RamNand::Create(const fuchsia_hardware_nand_RamNandInfo* config,
std::optional<RamNand>* out) {
fbl::unique_fd control(open(kBasePath, O_RDWR));
ZX_ASSERT_MSG(control.is_valid(), "Could not open device %s (errno=%s).\n", kBasePath,
strerror(errno));
zx::channel ctl_svc;
zx_status_t st = fdio_get_service_handle(control.release(), ctl_svc.reset_and_get_address());
if (st != ZX_OK) {
fprintf(stderr, "Could not fdio_get_service_handle, %d\n", st);
return st;
}
char name[fuchsia_hardware_nand_NAME_LEN + 1];
size_t out_name_size;
zx_status_t status;
st = fuchsia_hardware_nand_RamNandCtlCreateDevice(ctl_svc.get(), config, &status, name,
fuchsia_hardware_nand_NAME_LEN, &out_name_size);
if (st != ZX_OK || status != ZX_OK) {
st = st != ZX_OK ? st : status;
fprintf(stderr, "Could not create ram_nand device, %d\n", st);
return st;
}
name[out_name_size] = '\0';
fbl::StringBuffer<PATH_MAX> path;
path.Append(kBasePath);
path.Append("/");
path.Append(name);
fbl::unique_fd ram_nand_ctl(open(kBasePath, O_RDONLY | O_DIRECTORY));
if (!ram_nand_ctl) {
fprintf(stderr, "Could not open ram_nand_ctl");
return ZX_ERR_INTERNAL;
}
fbl::unique_fd ram_nand;
st = WaitForFile(ram_nand_ctl, name, &ram_nand);
if (st != ZX_OK) {
fprintf(stderr, "Could not open ram_nand\n");
return st;
}
*out = RamNand(std::move(ram_nand), path.ToString(), fbl::String(name));
return ZX_OK;
}
__EXPORT
RamNand::~RamNand() {
if (unbind && fd_) {
fdio_cpp::FdioCaller caller(std::move(fd_));
auto resp = fidl::WireCall(caller.borrow_as<fuchsia_device::Controller>())->ScheduleUnbind();
zx_status_t status = resp.status();
if (status == ZX_OK && resp->result.is_err()) {
status = resp->result.err();
}
if (status != ZX_OK) {
fprintf(stderr, "Could not unbind ram_nand, %d\n", status);
}
}
}
} // namespace ramdevice_client