| // 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 "sysdev.h" | |
| #include <fuchsia/boot/c/fidl.h> | |
| #include <lib/zx/channel.h> | |
| #include <lib/zx/vmar.h> | |
| #include <lib/zx/vmo.h> | |
| #include <zircon/assert.h> | |
| #include <zircon/boot/image.h> | |
| #include <zircon/types.h> | |
| #include <memory> | |
| #include <ddk/binding.h> | |
| #include <ddk/device.h> | |
| #include <ddk/driver.h> | |
| #include <ddk/platform-defs.h> | |
| #include <ddktl/device.h> | |
| namespace { | |
| class Sysdev; | |
| using SysdevType = ddk::Device<Sysdev>; | |
| class Sysdev : public SysdevType { | |
| public: | |
| explicit Sysdev(zx_device_t* device) : SysdevType(device) {} | |
| static zx_status_t Create(void* ctx, zx_device_t* parent, const char* name, const char* args, | |
| zx_handle_t items_svc_handle); | |
| // Device protocol implementation. | |
| void DdkRelease() { | |
| // sysdev should never have its release called. | |
| ZX_ASSERT_MSG(false, "Sysdev::DdkRelease() invoked!\n"); | |
| } | |
| zx_status_t MakeComposite(); | |
| }; | |
| zx_status_t Sysdev::Create(void* ctx, zx_device_t* parent, const char* name, const char* args, | |
| zx_handle_t items_svc_handle) { | |
| zx::channel items_svc(items_svc_handle); | |
| auto sysdev = std::make_unique<Sysdev>(parent); | |
| // Check if we were sent configuration data | |
| zx::vmo payload; | |
| uint32_t payload_len = 0; | |
| if (items_svc.is_valid()) { | |
| zx_status_t status = fuchsia_boot_ItemsGet(items_svc.get(), ZBI_TYPE_DRV_BOARD_PRIVATE, 0, | |
| payload.reset_and_get_address(), &payload_len); | |
| if (status != ZX_OK) { | |
| return status; | |
| } | |
| } | |
| uintptr_t payload_addr = 0; | |
| if (payload_len > 0) { | |
| zx_status_t status = | |
| zx::vmar::root_self()->map(0, payload, 0, payload_len, ZX_VM_PERM_READ, &payload_addr); | |
| if (status != ZX_OK) { | |
| return status; | |
| } | |
| } | |
| payload.reset(); | |
| zx_status_t status = sysdev->DdkAdd(ddk::DeviceAddArgs("sys").set_flags(DEVICE_ADD_NON_BINDABLE)); | |
| if (status != ZX_OK) { | |
| return status; | |
| } | |
| // If we were sent configuration data, check to see if we were told to | |
| // create a composite. If so, we will create a composite out of | |
| // "well-known" devices that the test may create. These are children with | |
| // the PLATFORM_DEV properties | |
| // (PDEV_VID_TEST, PDEV_PID_LIBDRIVER_TEST, PDEV_DID_TEST_CHILD_1) and | |
| // (PDEV_VID_TEST, PDEV_PID_LIBDRIVER_TEST, PDEV_DID_TEST_CHILD_2). | |
| // The resulting composite will have PLATFORM_DEV properties | |
| // (PDEV_VID_TEST, PDEV_PID_LIBDRIVER_TEST, PDEV_DID_TEST_COMPOSITE). | |
| if (payload_len >= sizeof(bool)) { | |
| auto should_create_composite = reinterpret_cast<const bool*>(payload_addr); | |
| if (*should_create_composite) { | |
| status = sysdev->MakeComposite(); | |
| ZX_ASSERT(status == ZX_OK); | |
| } | |
| } | |
| // Now owned by devmgr. | |
| __UNUSED auto ptr = sysdev.release(); | |
| return ZX_OK; | |
| } | |
| zx_status_t Sysdev::MakeComposite() { | |
| // Composite binding rules for the well-known composite that | |
| // libdriver-integration-test uses. | |
| const zx_bind_inst_t root_match[] = { | |
| BI_MATCH(), | |
| }; | |
| const zx_bind_inst_t fragment1_match[] = { | |
| BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_TEST), | |
| BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_LIBDRIVER_TEST), | |
| BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_TEST_CHILD_1), | |
| }; | |
| const zx_bind_inst_t fragment2_match[] = { | |
| BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_TEST), | |
| BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_LIBDRIVER_TEST), | |
| BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_TEST_CHILD_2), | |
| }; | |
| const device_fragment_part_t fragment1[] = { | |
| {countof(root_match), root_match}, | |
| {countof(fragment1_match), fragment1_match}, | |
| }; | |
| const device_fragment_part_t fragment2[] = { | |
| {countof(root_match), root_match}, | |
| {countof(fragment2_match), fragment2_match}, | |
| }; | |
| const device_fragment_t fragments[] = { | |
| {"fragment-1", countof(fragment1), fragment1}, | |
| {"fragment-2", countof(fragment2), fragment2}, | |
| }; | |
| const zx_device_prop_t props[] = { | |
| {BIND_PLATFORM_DEV_VID, 0, PDEV_VID_TEST}, | |
| {BIND_PLATFORM_DEV_PID, 0, PDEV_PID_LIBDRIVER_TEST}, | |
| {BIND_PLATFORM_DEV_DID, 0, PDEV_DID_TEST_COMPOSITE}, | |
| }; | |
| const composite_device_desc_t comp_desc = { | |
| .props = props, | |
| .props_count = countof(props), | |
| .fragments = fragments, | |
| .fragments_count = countof(fragments), | |
| .coresident_device_index = UINT32_MAX, | |
| .metadata_list = nullptr, | |
| .metadata_count = 0, | |
| }; | |
| return device_add_composite(zxdev(), "composite", &comp_desc); | |
| } | |
| static constexpr zx_driver_ops_t driver_ops = []() { | |
| zx_driver_ops_t ops = {}; | |
| ops.version = DRIVER_OPS_VERSION; | |
| ops.create = Sysdev::Create; | |
| return ops; | |
| }(); | |
| } // namespace | |
| // clang-format off | |
| ZIRCON_DRIVER_BEGIN(test_sysdev, driver_ops, "zircon", "0.1", 1) | |
| BI_ABORT_IF_AUTOBIND, | |
| ZIRCON_DRIVER_END(test_sysdev) | |
| // clang-format on |