| // Copyright 2021 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 "src/ui/input/drivers/goldfish_sensor/root_device.h" |
| |
| #include <fidl/fuchsia.hardware.goldfish/cpp/wire.h> |
| #include <fidl/fuchsia.input.report/cpp/wire.h> |
| #include <lib/ddk/binding_driver.h> |
| #include <lib/ddk/debug.h> |
| #include <lib/ddk/trace/event.h> |
| #include <lib/fit/function.h> |
| #include <lib/zx/result.h> |
| #include <zircon/assert.h> |
| #include <zircon/errors.h> |
| |
| #include <memory> |
| |
| #include <fbl/auto_lock.h> |
| |
| #include "src/devices/lib/goldfish/pipe_io/pipe_io.h" |
| #include "src/ui/input/drivers/goldfish_sensor/input_device.h" |
| #include "src/ui/input/drivers/goldfish_sensor/parser.h" |
| #include "zircon/status.h" |
| |
| namespace goldfish::sensor { |
| namespace { |
| |
| const char* kPipeName = "pipe:qemud:sensors"; |
| const char* kTag = "goldfish-sensor"; |
| |
| const std::map<uint64_t, InputDeviceInfo> kInputDevices = { |
| {0x0001, {"acceleration", AccelerationInputDevice::Create}}, |
| {0x0002, {"gyroscope", GyroscopeInputDevice::Create}}, |
| {0x8000, {"rgbc-light", RgbcLightInputDevice::Create}}, |
| }; |
| |
| } // namespace |
| |
| // static |
| zx_status_t RootDevice::Create(void* ctx, zx_device_t* device) { |
| auto sensor_root = std::make_unique<RootDevice>(device); |
| zx_status_t status = sensor_root->Bind(); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| // Create and bind all sensor input devices. |
| status = sensor_root->Setup(kInputDevices); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "cannot setup input devices: %s", zx_status_get_string(status)); |
| return status; |
| } |
| |
| // the root device will be managed by devmgr. |
| [[maybe_unused]] auto* dev = sensor_root.release(); |
| return ZX_OK; |
| } |
| |
| RootDevice::RootDevice(zx_device_t* parent) |
| : RootDeviceType(parent), pipe_io_loop_(&kAsyncLoopConfigNeverAttachToThread) { |
| pipe_io_loop_.StartThread("pipe-event-thread"); |
| } |
| |
| RootDevice::~RootDevice() { pipe_io_loop_.Shutdown(); } |
| |
| zx_status_t RootDevice::Setup(const std::map<uint64_t, InputDeviceInfo>& input_devices) { |
| zx::result client_end = DdkConnectFidlProtocol<fuchsia_hardware_goldfish_pipe::Service::Device>(); |
| if (client_end.is_error()) { |
| zxlogf(ERROR, "%s: could not connect to goldfish-pipe protocol: %s", kTag, |
| client_end.status_string()); |
| return client_end.status_value(); |
| } |
| fidl::WireSyncClient client{std::move(client_end.value())}; |
| |
| auto_reader_ = |
| std::make_unique<PipeAutoReader>(std::move(client), kPipeName, pipe_io_loop_.dispatcher(), |
| fit::bind_member<&RootDevice::OnReadSensor>(this)); |
| if (!auto_reader_->valid()) { |
| zxlogf(ERROR, "%s: PipeAutoReader() initialization failed", kTag); |
| return ZX_ERR_INTERNAL; |
| } |
| |
| // "list-sensors" returns a binary mask of all available sensors. |
| auto_reader_->WriteWithHeader("list-sensors", /* blocking= */ true); |
| auto result = auto_reader_->ReadWithHeader(); |
| uint32_t sensor_mask = 0u; |
| if (result.is_ok()) { |
| auto* sensor_mask_str = reinterpret_cast<const char*>(result.value().data()); |
| int size_read = sscanf(sensor_mask_str, "%d", &sensor_mask); |
| if (size_read != 1) { |
| zxlogf(ERROR, "%s: invalid list-sensors mask: %s", kTag, sensor_mask_str); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| } else { |
| zxlogf(ERROR, "%s: cannot list sensors!", kTag); |
| return ZX_ERR_INTERNAL; |
| } |
| |
| for (const auto& kv : input_devices) { |
| if (sensor_mask & kv.first) { |
| auto create_result = kv.second.create_fn( |
| this, fdf_dispatcher_get_async_dispatcher(fdf_dispatcher_get_current_dispatcher())); |
| if (create_result.is_ok()) { |
| input_devices_.AddDevice(create_result.value(), kv.second.name); |
| |
| char msg[256]; |
| snprintf(msg, sizeof(msg), "set:%s:1", kv.second.name.c_str()); |
| auto_reader_->WriteWithHeader(msg, /* blocking= */ true); |
| zxlogf(INFO, "%s: created device: %s", kTag, kv.second.name.c_str()); |
| } else { |
| zxlogf(ERROR, "%s: cannot create device: %s", kTag, kv.second.name.c_str()); |
| return create_result.error(); |
| } |
| } |
| } |
| |
| zx_status_t status = auto_reader_->BeginRead(); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "%s: BeginRead() failed: %d", kTag, status); |
| return status; |
| } |
| return ZX_OK; |
| } |
| |
| void RootDevice::OnReadSensor(PipeIo::ReadResult<char> result) { |
| if (result.is_error()) { |
| zxlogf(INFO, "Pipe error: %s", zx_status_get_string(result.error_value())); |
| return; |
| } |
| |
| ZX_DEBUG_ASSERT(result.is_ok()); |
| const char* data = reinterpret_cast<const char*>(result.value().data()); |
| |
| auto report = ParseSensorReport(data, result.value().size()); |
| |
| // TODO(https://fxbug.dev/42158306): Handle non-device report headers, e.g. "sync" |
| // and "device-sync". |
| input_devices_.DispatchReportToDevice(report.name, report); |
| } |
| |
| zx_status_t RootDevice::Bind() { |
| return DdkAdd(ddk::DeviceAddArgs("goldfish-sensor").set_flags(DEVICE_ADD_NON_BINDABLE)); |
| } |
| |
| void RootDevice::DdkUnbind(ddk::UnbindTxn txn) { txn.Reply(); } |
| |
| void RootDevice::DdkRelease() { delete this; } |
| |
| } // namespace goldfish::sensor |
| |
| static constexpr zx_driver_ops_t goldfish_sensor_driver_ops = []() -> zx_driver_ops_t { |
| zx_driver_ops_t ops = {}; |
| ops.version = DRIVER_OPS_VERSION; |
| ops.bind = goldfish::sensor::RootDevice::Create; |
| return ops; |
| }(); |
| |
| ZIRCON_DRIVER(goldfish_sensor, goldfish_sensor_driver_ops, "zircon", "0.1"); |
| |
| // clang-format on |