iioservice: Support hrtimer for acpi-als

This commit adds support on acpi-als as a light sensor with hrtimer. The
changes can also be used to support further sensors that need hrtimer
for samples.

BUG=b:174544536
TEST=unit tests and run on nightfury

Change-Id: I0f0bbc2adccd4244aa93194d92da139722933755
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2574151
Commit-Queue: Cheng-Hao Yang <chenghaoyang@chromium.org>
Tested-by: Cheng-Hao Yang <chenghaoyang@chromium.org>
Reviewed-by: Gwendal Grignou <gwendal@chromium.org>
diff --git a/iioservice/daemon/samples_handler.cc b/iioservice/daemon/samples_handler.cc
index 7130c34..93c5f9d 100644
--- a/iioservice/daemon/samples_handler.cc
+++ b/iioservice/daemon/samples_handler.cc
@@ -27,6 +27,9 @@
 constexpr char kNoBatchChannels[][10] = {"timestamp", "count"};
 constexpr char kHWFifoFlushPath[] = "buffer/hwfifo_flush";
 
+constexpr double kAcpiAlsMinFrequency = 0.1;
+constexpr double kAcpiAlsMaxFrequency = 2.0;
+
 }  // namespace
 
 // static
@@ -56,7 +59,7 @@
 }
 
 // static
-SamplesHandler::ScopedSamplesHandler SamplesHandler::CreateWithFifo(
+SamplesHandler::ScopedSamplesHandler SamplesHandler::Create(
     scoped_refptr<base::SequencedTaskRunner> ipc_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> sample_task_runner,
     libmems::IioDevice* iio_device,
@@ -68,46 +71,16 @@
     return handler;
 
   double min_freq, max_freq;
-  if (!iio_device->GetMinMaxFrequency(&min_freq, &max_freq))
+  if (strcmp(iio_device->GetName(), "acpi-als") == 0) {
+    min_freq = kAcpiAlsMinFrequency;
+    max_freq = kAcpiAlsMaxFrequency;
+  } else if (!iio_device->GetMinMaxFrequency(&min_freq, &max_freq)) {
     return handler;
-
-  handler.reset(new SamplesHandler(
-      std::move(ipc_task_runner), std::move(sample_task_runner), iio_device,
-      min_freq, max_freq, std::move(on_sample_updated_callback),
-      std::move(on_error_occurred_callback)));
-  return handler;
-}
-
-// static
-SamplesHandler::ScopedSamplesHandler SamplesHandler::CreateWithoutFifo(
-    scoped_refptr<base::SequencedTaskRunner> ipc_task_runner,
-    scoped_refptr<base::SingleThreadTaskRunner> sample_task_runner,
-    libmems::IioContext* iio_context,
-    libmems::IioDevice* iio_device,
-    OnSampleUpdatedCallback on_sample_updated_callback,
-    OnErrorOccurredCallback on_error_occurred_callback) {
-  ScopedSamplesHandler handler(nullptr, SamplesHandlerDeleter);
-
-  if (!DisableBufferAndEnableChannels(iio_device))
-    return handler;
-
-  double min_freq, max_freq;
-  if (!iio_device->GetMinMaxFrequency(&min_freq, &max_freq))
-    return handler;
-
-  auto trigger_device = iio_device->GetTrigger();
-  if (!trigger_device) {
-    trigger_device = iio_context->GetTriggerById(iio_device->GetId() + 1);
-    if (!trigger_device) {
-      LOGF(ERROR) << "Failed to find trigger with id: " << iio_device->GetId();
-
-      return handler;
-    }
   }
 
   handler.reset(new SamplesHandler(
       std::move(ipc_task_runner), std::move(sample_task_runner), iio_device,
-      trigger_device, min_freq, max_freq, std::move(on_sample_updated_callback),
+      min_freq, max_freq, std::move(on_sample_updated_callback),
       std::move(on_error_occurred_callback)));
   return handler;
 }
@@ -181,40 +154,7 @@
     OnErrorOccurredCallback on_error_occurred_callback)
     : ipc_task_runner_(std::move(ipc_task_runner)),
       sample_task_runner_(std::move(sample_task_runner)),
-      use_fifo_(true),
       iio_device_(iio_device),
-      trigger_device_(nullptr),
-      dev_min_frequency_(min_freq),
-      dev_max_frequency_(max_freq),
-      on_sample_updated_callback_(std::move(on_sample_updated_callback)),
-      on_error_occurred_callback_(std::move(on_error_occurred_callback)) {
-  DCHECK_GE(dev_max_frequency_, dev_min_frequency_);
-
-  auto channels = iio_device_->GetAllChannels();
-  for (size_t i = 0; i < channels.size(); ++i) {
-    for (size_t j = 0; j < base::size(kNoBatchChannels); ++j) {
-      if (strcmp(channels[i]->GetId(), kNoBatchChannels[j]) == 0) {
-        no_batch_chn_indices.emplace(i);
-        break;
-      }
-    }
-  }
-}
-
-SamplesHandler::SamplesHandler(
-    scoped_refptr<base::SequencedTaskRunner> ipc_task_runner,
-    scoped_refptr<base::SingleThreadTaskRunner> sample_task_runner,
-    libmems::IioDevice* iio_device,
-    libmems::IioDevice* trigger_device,
-    double min_freq,
-    double max_freq,
-    OnSampleUpdatedCallback on_sample_updated_callback,
-    OnErrorOccurredCallback on_error_occurred_callback)
-    : ipc_task_runner_(std::move(ipc_task_runner)),
-      sample_task_runner_(std::move(sample_task_runner)),
-      use_fifo_(false),
-      iio_device_(iio_device),
-      trigger_device_(trigger_device),
       dev_min_frequency_(min_freq),
       dev_max_frequency_(max_freq),
       on_sample_updated_callback_(std::move(on_sample_updated_callback)),
@@ -236,8 +176,10 @@
   DCHECK(sample_task_runner_->BelongsToCurrentThread());
 
   // Flush the old samples in EC FIFO.
-  if (!iio_device_->WriteStringAttribute(kHWFifoFlushPath, "1\n"))
+  if (!iio_device_->GetTrigger() &&
+      !iio_device_->WriteStringAttribute(kHWFifoFlushPath, "1\n")) {
     LOGF(ERROR) << "Failed to flush the old samples in EC FIFO";
+  }
 
   auto fd = iio_device_->GetBufferFd();
   if (!fd.has_value()) {
@@ -472,22 +414,20 @@
     return true;
 
   requested_frequency_ = frequency;
-  if (!iio_device_->WriteDoubleAttribute(libmems::kSamplingFrequencyAttr,
-                                         frequency)) {
-    LOGF(ERROR) << "Failed to set frequency";
-    if (use_fifo_)  // ignore this error if no fifo
+  if (!iio_device_->GetTrigger()) {
+    if (!iio_device_->WriteDoubleAttribute(libmems::kSamplingFrequencyAttr,
+                                           frequency)) {
+      LOGF(ERROR) << "Failed to set frequency";
+    }
+
+    auto freq_opt =
+        iio_device_->ReadDoubleAttribute(libmems::kSamplingFrequencyAttr);
+    if (!freq_opt.has_value()) {
+      LOGF(ERROR) << "Failed to get frequency";
       return false;
-  }
+    }
+    dev_frequency_ = freq_opt.value();
 
-  auto freq_opt =
-      iio_device_->ReadDoubleAttribute(libmems::kSamplingFrequencyAttr);
-  if (!freq_opt.has_value()) {
-    LOGF(ERROR) << "Failed to get frequency";
-    return false;
-  }
-  dev_frequency_ = freq_opt.value();
-
-  if (use_fifo_) {
     if (dev_frequency_ < libmems::kFrequencyEpsilon)
       return true;
 
@@ -500,14 +440,19 @@
     return true;
   }
 
-  // no fifo
-  DCHECK(trigger_device_);
-
-  if (!trigger_device_->WriteDoubleAttribute(libmems::kSamplingFrequencyAttr,
-                                             frequency)) {
+  iio_device_->WriteDoubleAttribute(libmems::kSamplingFrequencyAttr, frequency);
+  if (!iio_device_->GetTrigger()->WriteDoubleAttribute(
+          libmems::kSamplingFrequencyAttr, frequency)) {
     LOGF(ERROR) << "Failed to set trigger's frequency";
     return false;
   }
+  auto freq_opt = iio_device_->GetTrigger()->ReadDoubleAttribute(
+      libmems::kSamplingFrequencyAttr);
+  if (!freq_opt.has_value()) {
+    LOGF(ERROR) << "Failed to get frequency";
+    return false;
+  }
+  dev_frequency_ = freq_opt.value();
 
   return true;
 }
diff --git a/iioservice/daemon/samples_handler.h b/iioservice/daemon/samples_handler.h
index c1ff76f..6ff3400 100644
--- a/iioservice/daemon/samples_handler.h
+++ b/iioservice/daemon/samples_handler.h
@@ -41,21 +41,12 @@
       mojo::ReceiverId, cros::mojom::ObserverErrorType)>;
 
   static bool DisableBufferAndEnableChannels(libmems::IioDevice* iio_device);
-  // use fifo
-  static ScopedSamplesHandler CreateWithFifo(
+  static ScopedSamplesHandler Create(
       scoped_refptr<base::SequencedTaskRunner> ipc_task_runner,
       scoped_refptr<base::SingleThreadTaskRunner> sample_task_runner,
       libmems::IioDevice* iio_device,
       OnSampleUpdatedCallback on_sample_updated_callback,
       OnErrorOccurredCallback on_error_occurred_callback);
-  // no fifo
-  static ScopedSamplesHandler CreateWithoutFifo(
-      scoped_refptr<base::SequencedTaskRunner> ipc_task_runner,
-      scoped_refptr<base::SingleThreadTaskRunner> sample_task_runner,
-      libmems::IioContext* iio_context,
-      libmems::IioDevice* iio_device,
-      OnSampleUpdatedCallback on_sample_updated_callback,
-      OnErrorOccurredCallback on_error_occurred_callback);
 
   virtual ~SamplesHandler();
 
@@ -99,15 +90,6 @@
                  double max_freq,
                  OnSampleUpdatedCallback on_sample_updated_callback,
                  OnErrorOccurredCallback on_error_occurred_callback);
-  // no fifo
-  SamplesHandler(scoped_refptr<base::SequencedTaskRunner> ipc_task_runner,
-                 scoped_refptr<base::SingleThreadTaskRunner> sample_task_runner,
-                 libmems::IioDevice* iio_device,
-                 libmems::IioDevice* trigger_device,
-                 double in_freq,
-                 double ax_freq,
-                 OnSampleUpdatedCallback on_sample_updated_callback,
-                 OnErrorOccurredCallback on_error_occurred_callback);
 
   void SetSampleWatcherOnThread();
   void StartAcceptingSamples(
@@ -151,9 +133,7 @@
 
   scoped_refptr<base::SequencedTaskRunner> ipc_task_runner_;
   scoped_refptr<base::SingleThreadTaskRunner> sample_task_runner_;
-  bool use_fifo_ = true;
   libmems::IioDevice* iio_device_;
-  libmems::IioDevice* trigger_device_ = nullptr;
 
   // Clients that either have invalid frequency or no enabled channels.
   std::set<ClientData*> inactive_clients_;
diff --git a/iioservice/daemon/samples_handler_test.cc b/iioservice/daemon/samples_handler_test.cc
index 35eef2e..1cfebe2 100644
--- a/iioservice/daemon/samples_handler_test.cc
+++ b/iioservice/daemon/samples_handler_test.cc
@@ -32,6 +32,9 @@
 constexpr double kFooFrequency = 20.0;
 constexpr int kNumFailures = 10;
 
+constexpr char kFakeTriggerName[] = "FakeTrigger";
+constexpr int kFakeTriggerId = 1;
+
 double FixFrequency(double frequency) {
   if (frequency < libmems::kFrequencyEpsilon)
     return 0.0;
@@ -51,7 +54,9 @@
                                libmems::IioDevice::IioSample sample) {
     CHECK(
         task_environment_.GetMainThreadTaskRunner()->BelongsToCurrentThread());
-    CHECK_LT(id, observers_.size());
+
+    if (id >= observers_.size())
+      return;
 
     observers_[id]->OnSampleUpdated(std::move(sample));
   }
@@ -67,9 +72,15 @@
     observers_[id]->OnErrorOccurred(type);
   }
 
-  void SetUpBase() {
+  void SetUpBase(bool with_trigger) {
     device_ = std::make_unique<libmems::fakes::FakeIioDevice>(
         nullptr, fakes::kAccelDeviceName, fakes::kAccelDeviceId);
+    if (with_trigger) {
+      trigger_ = std::make_unique<libmems::fakes::FakeIioDevice>(
+          nullptr, kFakeTriggerName, kFakeTriggerId);
+      device_->SetTrigger(trigger_.get());
+    }
+
     EXPECT_TRUE(
         device_->WriteStringAttribute(libmems::kSamplingFrequencyAvailable,
                                       fakes::kFakeSamplingFrequencyAvailable));
@@ -82,7 +93,7 @@
     EXPECT_TRUE(
         device_->WriteDoubleAttribute(libmems::kSamplingFrequencyAttr, 0.0));
 
-    handler_ = fakes::FakeSamplesHandler::CreateWithFifo(
+    handler_ = fakes::FakeSamplesHandler::Create(
         task_environment_.GetMainThreadTaskRunner(),
         task_environment_.GetMainThreadTaskRunner(), device_.get(),
         base::BindRepeating(&SamplesHandlerTestBase::OnSampleUpdatedCallback,
@@ -108,6 +119,7 @@
       base::test::TaskEnvironment::MainThreadType::IO};
 
   std::unique_ptr<libmems::fakes::FakeIioDevice> device_;
+  std::unique_ptr<libmems::fakes::FakeIioDevice> trigger_;
 
   fakes::FakeSamplesHandler::ScopedFakeSamplesHandler handler_ = {
       nullptr, SamplesHandler::SamplesHandlerDeleter};
@@ -118,7 +130,7 @@
 class SamplesHandlerTest : public ::testing::Test,
                            public SamplesHandlerTestBase {
  protected:
-  void SetUp() override { SetUpBase(); }
+  void SetUp() override { SetUpBase(/*with_trigger=*/false); }
 
   void TearDown() override { TearDownBase(); }
 };
@@ -233,7 +245,7 @@
     : public ::testing::TestWithParam<std::vector<std::pair<double, double>>>,
       public SamplesHandlerTestBase {
  protected:
-  void SetUp() override { SetUpBase(); }
+  void SetUp() override { SetUpBase(/*with_trigger=*/false); }
 
   void TearDown() override { TearDownBase(); }
 };
@@ -364,6 +376,13 @@
 
   base::RunLoop().RunUntilIdle();
 
+  EXPECT_EQ(device_->ReadDoubleAttribute(libmems::kSamplingFrequencyAttr)
+                .value_or(-1),
+            max_freq);
+  EXPECT_EQ(
+      device_->ReadDoubleAttribute(libmems::kHWFifoTimeoutAttr).value_or(-1),
+      1.0 / max_freq);
+
   device_->SetPauseCallbackAtKthSamples(
       fakes::kPauseIndex,
       base::BindOnce(
@@ -400,6 +419,13 @@
   // Wait until all observers receive all samples
   base::RunLoop().RunUntilIdle();
 
+  EXPECT_EQ(device_->ReadDoubleAttribute(libmems::kSamplingFrequencyAttr)
+                .value_or(-1),
+            max_freq2);
+  EXPECT_EQ(
+      device_->ReadDoubleAttribute(libmems::kHWFifoTimeoutAttr).value_or(-1),
+      1.0 / max_freq2);
+
   for (const auto& observer : observers_)
     EXPECT_TRUE(observer->FinishedObserving());
 
@@ -429,6 +455,36 @@
                       std::vector<std::pair<double, double>>{{20.0, 30.0},
                                                              {10.0, 10.0}}));
 
+class SamplesHandlerWithTriggerTest : public ::testing::Test,
+                                      public SamplesHandlerTestBase {
+ protected:
+  void SetUp() override { SetUpBase(/*with_trigger=*/true); }
+
+  void TearDown() override { TearDownBase(); }
+};
+
+TEST_F(SamplesHandlerWithTriggerTest, CheckFrequenciesSet) {
+  ClientData client_data;
+
+  double frequency = kMaxFrequency;
+
+  client_data.id = 0;
+  client_data.iio_device = device_.get();
+  client_data.enabled_chn_indices.emplace(0);  // accel_x
+  client_data.frequency = frequency;
+
+  handler_->AddClient(&client_data);
+
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(device_->ReadDoubleAttribute(libmems::kSamplingFrequencyAttr)
+                .value_or(-1),
+            frequency);
+  EXPECT_EQ(trigger_->ReadDoubleAttribute(libmems::kSamplingFrequencyAttr)
+                .value_or(-1),
+            frequency);
+}
+
 }  // namespace
 
 }  // namespace iioservice
diff --git a/iioservice/daemon/sensor_device_impl.cc b/iioservice/daemon/sensor_device_impl.cc
index 6fa4a68..6484216 100644
--- a/iioservice/daemon/sensor_device_impl.cc
+++ b/iioservice/daemon/sensor_device_impl.cc
@@ -46,9 +46,8 @@
     return device;
   }
 
-  // TODO(chenghaoyang): Check how to detect it's Samus, which doesn't use fifo.
   device.reset(new SensorDeviceImpl(std::move(ipc_task_runner), context,
-                                    std::move(thread), true));
+                                    std::move(thread)));
 
   return device;
 }
@@ -265,12 +264,10 @@
 SensorDeviceImpl::SensorDeviceImpl(
     scoped_refptr<base::SequencedTaskRunner> ipc_task_runner,
     libmems::IioContext* context,
-    std::unique_ptr<base::Thread> thread,
-    bool use_fifo)
+    std::unique_ptr<base::Thread> thread)
     : ipc_task_runner_(std::move(ipc_task_runner)),
       context_(std::move(context)),
-      sample_thread_(std::move(thread)),
-      use_fifo_(use_fifo) {
+      sample_thread_(std::move(thread)) {
   DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence());
 
   receiver_set_.set_disconnect_handler(base::BindRepeating(
@@ -353,15 +350,9 @@
   auto error_cb = base::BindRepeating(
       &SensorDeviceImpl::OnErrorOccurredCallback, weak_factory_.GetWeakPtr());
 
-  if (use_fifo_) {
-    handler = SamplesHandler::CreateWithFifo(
-        ipc_task_runner_, sample_thread_->task_runner(), iio_device,
-        std::move(sample_cb), std::move(error_cb));
-  } else {
-    handler = SamplesHandler::CreateWithoutFifo(
-        ipc_task_runner_, sample_thread_->task_runner(), context_, iio_device,
-        std::move(sample_cb), std::move(error_cb));
-  }
+  handler = SamplesHandler::Create(ipc_task_runner_,
+                                   sample_thread_->task_runner(), iio_device,
+                                   std::move(sample_cb), std::move(error_cb));
 
   if (!handler) {
     LOGF(ERROR) << "Failed to create the samples handler for device: "
diff --git a/iioservice/daemon/sensor_device_impl.h b/iioservice/daemon/sensor_device_impl.h
index ccdef98..69dec3e 100644
--- a/iioservice/daemon/sensor_device_impl.h
+++ b/iioservice/daemon/sensor_device_impl.h
@@ -63,8 +63,7 @@
  private:
   SensorDeviceImpl(scoped_refptr<base::SequencedTaskRunner> ipc_task_runner,
                    libmems::IioContext* context,
-                   std::unique_ptr<base::Thread> thread,
-                   bool use_fifo);
+                   std::unique_ptr<base::Thread> thread);
 
   void AddReceiverOnThread(
       int32_t iio_device_id,
@@ -87,7 +86,6 @@
   libmems::IioContext* context_;  // non-owned
   mojo::ReceiverSet<cros::mojom::SensorDevice> receiver_set_;
   std::unique_ptr<base::Thread> sample_thread_;
-  bool use_fifo_ = true;
 
   std::map<mojo::ReceiverId, ClientData> clients_;
 
diff --git a/iioservice/daemon/sensor_service_impl.cc b/iioservice/daemon/sensor_service_impl.cc
index cc8a238..446d89c 100644
--- a/iioservice/daemon/sensor_service_impl.cc
+++ b/iioservice/daemon/sensor_service_impl.cc
@@ -201,26 +201,43 @@
   }
 }
 
+void SensorServiceImpl::FailedToLoadDevice(libmems::IioDevice* device) {
+  DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence());
+
+  const int32_t id = device->GetId();
+  if (++iio_device_permission_trials_[id] >=
+      kNumFailedPermTrialsBeforeGivingUp) {
+    LOGF(ERROR) << "Too many failed permission trials. Giving up on device: "
+                << id;
+    return;
+  }
+
+  LOGF(WARNING) << "Permissions and ownerships may not be set yet for device: "
+                << id;
+  ipc_task_runner_->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&SensorServiceImpl::AddDevice, weak_factory_.GetWeakPtr(),
+                     device),
+      base::TimeDelta::FromMilliseconds(kPermTrialDelayInMilliseconds));
+}
+
 void SensorServiceImpl::AddDevice(libmems::IioDevice* device) {
   DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence());
 
   const int32_t id = device->GetId();
   if (!device->DisableBuffer()) {
-    if (++iio_device_permission_trials_[id] >=
-        kNumFailedPermTrialsBeforeGivingUp) {
-      LOGF(ERROR) << "Too many failed permission trials. Giving up on device: "
-                  << id;
+    FailedToLoadDevice(device);
+    return;
+  }
+
+  if (strcmp(device->GetName(), "acpi-als") == 0 && !device->GetTrigger()) {
+    // Reloads context for the hrtimer, as it'd be added after mems_setup.
+    context_->Reload();
+
+    if (!device->GetTrigger()) {
+      FailedToLoadDevice(device);
       return;
     }
-
-    LOGF(WARNING)
-        << "Permissions and ownerships may not be set yet for device: " << id;
-    ipc_task_runner_->PostDelayedTask(
-        FROM_HERE,
-        base::BindOnce(&SensorServiceImpl::AddDevice,
-                       weak_factory_.GetWeakPtr(), device),
-        base::TimeDelta::FromMilliseconds(kPermTrialDelayInMilliseconds));
-    return;
   }
 
   std::vector<cros::mojom::DeviceType> types;
diff --git a/iioservice/daemon/sensor_service_impl.h b/iioservice/daemon/sensor_service_impl.h
index 9955cae..b9dd7ca 100644
--- a/iioservice/daemon/sensor_service_impl.h
+++ b/iioservice/daemon/sensor_service_impl.h
@@ -62,6 +62,7 @@
  private:
   static const uint32_t kNumFailedPermTrialsBeforeGivingUp = 10;
 
+  void FailedToLoadDevice(libmems::IioDevice* device);
   void AddDevice(libmems::IioDevice* device);
 
   scoped_refptr<base::SequencedTaskRunner> ipc_task_runner_;
diff --git a/iioservice/daemon/test_fakes.cc b/iioservice/daemon/test_fakes.cc
index 1204ad6..6ac6805 100644
--- a/iioservice/daemon/test_fakes.cc
+++ b/iioservice/daemon/test_fakes.cc
@@ -30,7 +30,7 @@
 }  // namespace
 
 // static
-FakeSamplesHandler::ScopedFakeSamplesHandler FakeSamplesHandler::CreateWithFifo(
+FakeSamplesHandler::ScopedFakeSamplesHandler FakeSamplesHandler::Create(
     scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
     libmems::fakes::FakeIioDevice* fake_iio_device,
diff --git a/iioservice/daemon/test_fakes.h b/iioservice/daemon/test_fakes.h
index c77ba8b..480800c 100644
--- a/iioservice/daemon/test_fakes.h
+++ b/iioservice/daemon/test_fakes.h
@@ -37,7 +37,7 @@
   using ScopedFakeSamplesHandler =
       std::unique_ptr<FakeSamplesHandler, decltype(&SamplesHandlerDeleter)>;
 
-  static ScopedFakeSamplesHandler CreateWithFifo(
+  static ScopedFakeSamplesHandler Create(
       scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner,
       scoped_refptr<base::SingleThreadTaskRunner> task_runner,
       libmems::fakes::FakeIioDevice* fake_iio_device,