| // Copyright 2019 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 <memory> |
| |
| #include <ddk/driver.h> |
| #include <ddk/binding.h> |
| #include <ddktl/device.h> |
| #include <fuchsia/hardware/ramdisk/c/fidl.h> |
| #include <lib/fidl-utils/bind.h> |
| #include <lib/zx/vmo.h> |
| #include <zircon/types.h> |
| |
| #include "ramdisk.h" |
| |
| namespace ramdisk { |
| namespace { |
| |
| class RamdiskController; |
| using RamdiskControllerDeviceType = ddk::Device<RamdiskController, ddk::Messageable>; |
| |
| class RamdiskController : public RamdiskControllerDeviceType { |
| public: |
| RamdiskController(zx_device_t* parent) : RamdiskControllerDeviceType(parent) {} |
| |
| // Device Protocol |
| zx_status_t DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn); |
| void DdkRelease() { |
| delete this; |
| } |
| |
| private: |
| // FIDL Interface RamdiskController. |
| zx_status_t Create(uint64_t block_size, uint64_t block_count, |
| const fuchsia_hardware_ramdisk_GUID* type_guid, fidl_txn_t* txn); |
| zx_status_t CreateFromVmo(zx_handle_t vmo, fidl_txn_t* txn); |
| |
| // Other methods |
| zx_status_t ConfigureDevice(zx::vmo vmo, uint64_t block_size, uint64_t block_count, |
| const uint8_t* type_guid, char* name, size_t buffer_size, |
| size_t* name_length); |
| |
| static const fuchsia_hardware_ramdisk_RamdiskController_ops* Ops() { |
| using Binder = fidl::Binder<RamdiskController>; |
| |
| static const fuchsia_hardware_ramdisk_RamdiskController_ops kOps = { |
| .Create = Binder::BindMember<&RamdiskController::Create>, |
| .CreateFromVmo = Binder::BindMember<&RamdiskController::CreateFromVmo>, |
| }; |
| return &kOps; |
| } |
| }; |
| |
| zx_status_t RamdiskController::DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn) { |
| return fuchsia_hardware_ramdisk_RamdiskController_dispatch(this, txn, msg, Ops()); |
| } |
| |
| zx_status_t RamdiskController::Create(uint64_t block_size, uint64_t block_count, |
| const fuchsia_hardware_ramdisk_GUID* type_guid, fidl_txn_t* txn) { |
| auto failure_response = [&txn](zx_status_t status) -> zx_status_t { |
| return fuchsia_hardware_ramdisk_RamdiskControllerCreate_reply(txn, status, nullptr, 0); |
| }; |
| |
| zx::vmo vmo; |
| zx_status_t status = zx::vmo::create(block_size * block_count, 0, &vmo); |
| if (status != ZX_OK) { |
| return failure_response(status); |
| } |
| char name[fuchsia_hardware_ramdisk_MAX_NAME_LENGTH + 1]; |
| size_t name_length = 0; |
| status = ConfigureDevice(std::move(vmo), block_size, block_count, |
| type_guid ? type_guid->value : nullptr, |
| name, sizeof(name), &name_length); |
| if (status != ZX_OK) { |
| return failure_response(status); |
| } |
| return fuchsia_hardware_ramdisk_RamdiskControllerCreate_reply(txn, ZX_OK, name, name_length); |
| } |
| |
| zx_status_t RamdiskController::CreateFromVmo(zx_handle_t raw_vmo, fidl_txn_t* txn) { |
| zx::vmo vmo(raw_vmo); |
| auto failure_response = [&txn](zx_status_t status) -> zx_status_t { |
| return fuchsia_hardware_ramdisk_RamdiskControllerCreateFromVmo_reply(txn, status, |
| nullptr, 0); |
| }; |
| |
| // Ensure this is the last handle to this VMO; otherwise, the size |
| // may change from underneath us. |
| zx_info_handle_count_t info; |
| zx_status_t status = vmo.get_info(ZX_INFO_HANDLE_COUNT, &info, sizeof(info), nullptr, |
| nullptr); |
| if (status != ZX_OK || info.handle_count != 1) { |
| return failure_response(ZX_ERR_INVALID_ARGS); |
| } |
| |
| uint64_t vmo_size; |
| status = vmo.get_size(&vmo_size); |
| if (status != ZX_OK) { |
| return failure_response(status); |
| } |
| |
| char name[fuchsia_hardware_ramdisk_MAX_NAME_LENGTH + 1]; |
| size_t name_length = 0; |
| status = ConfigureDevice(std::move(vmo), PAGE_SIZE, (vmo_size + PAGE_SIZE - 1) / PAGE_SIZE, |
| nullptr, name, sizeof(name), &name_length); |
| if (status != ZX_OK) { |
| return failure_response(status); |
| } |
| return fuchsia_hardware_ramdisk_RamdiskControllerCreateFromVmo_reply(txn, ZX_OK, name, |
| name_length); |
| } |
| |
| constexpr size_t kMaxRamdiskNameLength = 32; |
| |
| zx_status_t RamdiskController::ConfigureDevice(zx::vmo vmo, uint64_t block_size, |
| uint64_t block_count, const uint8_t* type_guid, |
| char* name, size_t buffer_size, |
| size_t* name_length) { |
| if (buffer_size < kMaxRamdiskNameLength) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| std::unique_ptr<Ramdisk> ramdev; |
| zx_status_t status = Ramdisk::Create(zxdev(), std::move(vmo), block_size, block_count, |
| type_guid, &ramdev); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| size_t namelen = strlcpy(name, ramdev->Name(), buffer_size); |
| |
| if ((status = ramdev->DdkAdd(ramdev->Name()) != ZX_OK)) { |
| ramdev.release()->DdkRelease(); |
| return status; |
| } |
| __UNUSED auto ptr = ramdev.release(); |
| *name_length = namelen; |
| return ZX_OK; |
| } |
| |
| zx_status_t RamdiskDriverBind(void* ctx, zx_device_t* parent) { |
| auto ramctl = std::make_unique<RamdiskController>(parent); |
| |
| zx_status_t status = ramctl->DdkAdd("ramctl"); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| // RamdiskController owned by the DDK after being added successfully. |
| __UNUSED auto ptr = ramctl.release(); |
| return ZX_OK; |
| } |
| |
| zx_driver_ops_t ramdisk_driver_ops = []() { |
| zx_driver_ops_t ops = {}; |
| ops.version = DRIVER_OPS_VERSION; |
| ops.bind = RamdiskDriverBind; |
| return ops; |
| }(); |
| |
| } // namespace |
| } // namespace ramdisk |
| |
| ZIRCON_DRIVER_BEGIN(ramdisk, ramdisk::ramdisk_driver_ops, "zircon", "0.1", 1) |
| BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_MISC_PARENT), |
| ZIRCON_DRIVER_END(ramdisk) |