| // Copyright 2023 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 "adc-buttons-device.h" |
| |
| #include <lib/driver/logging/cpp/structured_logger.h> |
| #include <lib/zx/clock.h> |
| |
| namespace adc_buttons_device { |
| |
| void AdcButtonsDevice::AdcButtonInputReport::ToFidlInputReport( |
| fidl::WireTableBuilder<::fuchsia_input_report::wire::InputReport>& input_report, |
| fidl::AnyArena& allocator) { |
| fidl::VectorView<fuchsia_input_report::wire::ConsumerControlButton> pressed_buttons_rpt( |
| allocator, buttons.size()); |
| std::copy(buttons.begin(), buttons.end(), pressed_buttons_rpt.begin()); |
| |
| auto consumer_control_report = |
| fuchsia_input_report::wire::ConsumerControlInputReport::Builder(allocator).pressed_buttons( |
| pressed_buttons_rpt); |
| input_report.event_time(event_time.get()).consumer_control(consumer_control_report.Build()); |
| } |
| |
| void AdcButtonsDevice::PollingTask(async_dispatcher_t* dispatcher, async::TaskBase* task, |
| zx_status_t status) { |
| if (status != ZX_OK) { |
| return; |
| } |
| |
| polling_task_.PostDelayed(dispatcher_, zx::usec(polling_rate_usec_)); |
| |
| auto report = GetInputReport(); |
| if (report.is_error()) { |
| FDF_LOG(ERROR, "Failed to get report %d", report.error_value()); |
| return; |
| } |
| if (rpt_.has_value() && rpt_->buttons == report->buttons) { |
| return; |
| } |
| |
| rpt_ = std::move(*report); |
| readers_.SendReportToAllReaders(*rpt_); |
| } |
| |
| zx::result<AdcButtonsDevice::AdcButtonInputReport> AdcButtonsDevice::GetInputReport() { |
| std::set<fuchsia_input_report::ConsumerControlButton> buttons; |
| for (const auto& client : clients_) { |
| auto result = client.adc_->GetSample(); |
| if (!result.ok()) { |
| FDF_LOG(ERROR, "%s: GetSample failed %s", __func__, result.FormatDescription().c_str()); |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| if (result->is_error()) { |
| FDF_LOG(ERROR, "%s: GetSample failed %d", __func__, result->error_value()); |
| return zx::error(result->error_value()); |
| } |
| |
| for (const auto& cfg : client.buttons_) { |
| const auto& saradc = cfg.button_config()->adc(); |
| if (result.value()->value >= saradc->press_threshold() && |
| result.value()->value < saradc->release_threshold()) { |
| buttons.insert(cfg.types()->begin(), cfg.types()->end()); |
| } |
| } |
| } |
| ZX_DEBUG_ASSERT_MSG( |
| buttons.size() <= fuchsia_input_report::kConsumerControlMaxNumButtons, |
| "More buttons than expected (max = %d). Please increase kConsumerControlMaxNumButtons ", |
| fuchsia_input_report::kConsumerControlMaxNumButtons); |
| |
| return zx::ok(AdcButtonInputReport{ |
| .event_time = zx::clock::get_monotonic(), |
| .buttons = std::move(buttons), |
| }); |
| } |
| |
| void AdcButtonsDevice::GetInputReportsReader(GetInputReportsReaderRequestView request, |
| GetInputReportsReaderCompleter::Sync& completer) { |
| auto initial_report = GetInputReport(); |
| if (initial_report.is_error()) { |
| FDF_LOG(ERROR, "Failed to get initial report %d", initial_report.error_value()); |
| } |
| auto status = readers_.CreateReader( |
| dispatcher_, std::move(request->reader), |
| initial_report.is_ok() ? std::make_optional(initial_report.value()) : std::nullopt); |
| if (status != ZX_OK) { |
| FDF_LOG(ERROR, "%s: CreateReader failed %d", __func__, status); |
| } |
| } |
| |
| void AdcButtonsDevice::GetDescriptor(GetDescriptorCompleter::Sync& completer) { |
| fidl::Arena<kFeatureAndDescriptorBufferSize> allocator; |
| |
| auto device_info = fuchsia_input_report::wire::DeviceInformation::Builder(allocator); |
| device_info.vendor_id(static_cast<uint32_t>(fuchsia_input_report::wire::VendorId::kGoogle)); |
| device_info.product_id( |
| static_cast<uint32_t>(fuchsia_input_report::wire::VendorGoogleProductId::kAdcButtons)); |
| device_info.polling_rate(polling_rate_usec_); |
| |
| fidl::VectorView<fuchsia_input_report::wire::ConsumerControlButton> buttons(allocator, |
| buttons_.size()); |
| std::copy(buttons_.begin(), buttons_.end(), buttons.begin()); |
| |
| const auto input = fuchsia_input_report::wire::ConsumerControlInputDescriptor::Builder(allocator) |
| .buttons(buttons) |
| .Build(); |
| |
| const auto consumer_control = |
| fuchsia_input_report::wire::ConsumerControlDescriptor::Builder(allocator) |
| .input(input) |
| .Build(); |
| |
| const auto descriptor = fuchsia_input_report::wire::DeviceDescriptor::Builder(allocator) |
| .device_information(device_info.Build()) |
| .consumer_control(consumer_control) |
| .Build(); |
| |
| completer.Reply(descriptor); |
| } |
| |
| void AdcButtonsDevice::Shutdown() { polling_task_.Cancel(); } |
| |
| } // namespace adc_buttons_device |