| // Copyright 2020 The Chromium OS 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 "iioservice/daemon/sensor_device_impl.h" |
| |
| #include <utility> |
| |
| #include <base/bind.h> |
| #include <base/strings/string_util.h> |
| #include <libmems/common_types.h> |
| #include <libmems/iio_channel.h> |
| |
| #include "iioservice/include/common.h" |
| |
| namespace iioservice { |
| |
| // static |
| void SensorDeviceImpl::SensorDeviceImplDeleter(SensorDeviceImpl* device) { |
| if (device == nullptr) |
| return; |
| |
| if (!device->ipc_task_runner_->RunsTasksInCurrentSequence()) { |
| device->ipc_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&SensorDeviceImpl::SensorDeviceImplDeleter, device)); |
| return; |
| } |
| |
| delete device; |
| } |
| |
| // static |
| SensorDeviceImpl::ScopedSensorDeviceImpl SensorDeviceImpl::Create( |
| scoped_refptr<base::SequencedTaskRunner> ipc_task_runner, |
| libmems::IioContext* context) { |
| DCHECK(ipc_task_runner->RunsTasksInCurrentSequence()); |
| |
| ScopedSensorDeviceImpl device(nullptr, SensorDeviceImplDeleter); |
| |
| std::unique_ptr<base::Thread> thread(new base::Thread("SensorDeviceImpl")); |
| if (!thread->StartWithOptions( |
| base::Thread::Options(base::MessagePumpType::IO, 0))) { |
| LOGF(ERROR) << "Failed to start thread with TYPE_IO"; |
| device.reset(); |
| 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)); |
| |
| return device; |
| } |
| |
| SensorDeviceImpl::~SensorDeviceImpl() { |
| DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence()); |
| |
| samples_handlers_.clear(); |
| sample_thread_->Stop(); |
| receiver_set_.Clear(); |
| clients_.clear(); |
| } |
| |
| void SensorDeviceImpl::AddReceiver( |
| int32_t iio_device_id, |
| mojo::PendingReceiver<cros::mojom::SensorDevice> request) { |
| ipc_task_runner_->PostTask( |
| FROM_HERE, base::BindOnce(&SensorDeviceImpl::AddReceiverOnThread, |
| weak_factory_.GetWeakPtr(), iio_device_id, |
| std::move(request))); |
| } |
| |
| void SensorDeviceImpl::SetTimeout(uint32_t timeout) { |
| DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence()); |
| |
| mojo::ReceiverId id = receiver_set_.current_receiver(); |
| clients_[id].timeout = timeout; |
| } |
| |
| void SensorDeviceImpl::GetAttributes(const std::vector<std::string>& attr_names, |
| GetAttributesCallback callback) { |
| DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence()); |
| |
| mojo::ReceiverId id = receiver_set_.current_receiver(); |
| std::vector<base::Optional<std::string>> values; |
| values.reserve(attr_names.size()); |
| for (const auto& attr_name : attr_names) { |
| auto value_opt = clients_[id].iio_device->ReadStringAttribute(attr_name); |
| if (value_opt.has_value()) { |
| value_opt = |
| base::TrimString(value_opt.value(), base::StringPiece("\0\n", 2), |
| base::TRIM_TRAILING) |
| .as_string(); |
| } |
| |
| values.push_back(std::move(value_opt)); |
| } |
| |
| std::move(callback).Run(std::move(values)); |
| } |
| |
| void SensorDeviceImpl::SetFrequency(double frequency, |
| SetFrequencyCallback callback) { |
| DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence()); |
| |
| mojo::ReceiverId id = receiver_set_.current_receiver(); |
| ClientData& client = clients_[id]; |
| |
| if (AddSamplesHandlerIfNotSet(client.iio_device)) { |
| samples_handlers_.at(client.iio_device) |
| ->UpdateFrequency(&client, frequency, std::move(callback)); |
| return; |
| } |
| |
| // Failed to add the SamplesHandler |
| client.frequency = frequency; |
| std::move(callback).Run(frequency); |
| } |
| |
| void SensorDeviceImpl::StartReadingSamples( |
| mojo::PendingRemote<cros::mojom::SensorDeviceSamplesObserver> observer) { |
| DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence()); |
| |
| mojo::ReceiverId id = receiver_set_.current_receiver(); |
| ClientData& client = clients_[id]; |
| |
| if (client.observer.is_bound()) { |
| LOGF(ERROR) << "Reading already started: " << id; |
| mojo::Remote<cros::mojom::SensorDeviceSamplesObserver>(std::move(observer)) |
| ->OnErrorOccurred(cros::mojom::ObserverErrorType::ALREADY_STARTED); |
| return; |
| } |
| |
| if (!AddSamplesHandlerIfNotSet(client.iio_device)) { |
| observer.reset(); |
| return; |
| } |
| client.observer.Bind(std::move(observer)); |
| client.observer.set_disconnect_handler( |
| base::BindOnce(&SensorDeviceImpl::OnSamplesObserverDisconnect, |
| weak_factory_.GetWeakPtr(), id)); |
| |
| samples_handlers_.at(client.iio_device)->AddClient(&client); |
| } |
| |
| void SensorDeviceImpl::StopReadingSamples() { |
| DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence()); |
| |
| mojo::ReceiverId id = receiver_set_.current_receiver(); |
| StopReadingSamplesOnClient(id); |
| } |
| |
| void SensorDeviceImpl::GetAllChannelIds(GetAllChannelIdsCallback callback) { |
| DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence()); |
| |
| mojo::ReceiverId id = receiver_set_.current_receiver(); |
| auto iio_device = clients_[id].iio_device; |
| std::vector<std::string> chn_ids; |
| for (auto iio_channel : iio_device->GetAllChannels()) |
| chn_ids.push_back(iio_channel->GetId()); |
| |
| std::move(callback).Run(std::move(chn_ids)); |
| } |
| |
| void SensorDeviceImpl::SetChannelsEnabled( |
| const std::vector<int32_t>& iio_chn_indices, |
| bool en, |
| SetChannelsEnabledCallback callback) { |
| DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence()); |
| |
| mojo::ReceiverId id = receiver_set_.current_receiver(); |
| ClientData& client = clients_[id]; |
| |
| if (AddSamplesHandlerIfNotSet(client.iio_device)) { |
| samples_handlers_.at(client.iio_device) |
| ->UpdateChannelsEnabled(&client, std::move(iio_chn_indices), en, |
| std::move(callback)); |
| return; |
| } |
| |
| // List of channels failed to enabled. |
| std::vector<int32_t> failed_indices; |
| |
| if (en) { |
| for (int32_t chn_index : iio_chn_indices) { |
| auto chn = client.iio_device->GetChannel(chn_index); |
| if (!chn || !chn->IsEnabled()) { |
| LOG(ERROR) << "Failed to enable chn with index: " << chn_index; |
| failed_indices.push_back(chn_index); |
| continue; |
| } |
| |
| client.enabled_chn_indices.emplace(chn_index); |
| } |
| } else { |
| for (int32_t chn_index : iio_chn_indices) |
| client.enabled_chn_indices.erase(chn_index); |
| } |
| |
| std::move(callback).Run(std::move(failed_indices)); |
| } |
| |
| void SensorDeviceImpl::GetChannelsEnabled( |
| const std::vector<int32_t>& iio_chn_indices, |
| GetChannelsEnabledCallback callback) { |
| DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence()); |
| |
| mojo::ReceiverId id = receiver_set_.current_receiver(); |
| ClientData& client = clients_[id]; |
| |
| if (AddSamplesHandlerIfNotSet(client.iio_device)) { |
| samples_handlers_.at(client.iio_device) |
| ->GetChannelsEnabled(&client, std::move(iio_chn_indices), |
| std::move(callback)); |
| return; |
| } |
| |
| // List of channels failed to enabled. |
| std::vector<bool> enabled; |
| |
| for (int32_t chn_index : iio_chn_indices) { |
| enabled.push_back(client.enabled_chn_indices.find(chn_index) != |
| client.enabled_chn_indices.end()); |
| } |
| |
| std::move(callback).Run(std::move(enabled)); |
| } |
| |
| void SensorDeviceImpl::GetChannelsAttributes( |
| const std::vector<int32_t>& iio_chn_indices, |
| const std::string& attr_name, |
| GetChannelsAttributesCallback callback) { |
| DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence()); |
| |
| mojo::ReceiverId id = receiver_set_.current_receiver(); |
| ClientData& client = clients_[id]; |
| auto iio_device = client.iio_device; |
| |
| std::vector<base::Optional<std::string>> values; |
| |
| for (int32_t chn_index : iio_chn_indices) { |
| auto chn = iio_device->GetChannel(chn_index); |
| |
| if (!chn) { |
| LOG(ERROR) << "Cannot find chn with index: " << chn_index; |
| values.push_back(base::nullopt); |
| continue; |
| } |
| |
| base::Optional<std::string> value_opt = chn->ReadStringAttribute(attr_name); |
| if (value_opt.has_value()) { |
| value_opt = |
| base::TrimString(value_opt.value(), base::StringPiece("\0\n", 2), |
| base::TRIM_TRAILING) |
| .as_string(); |
| } |
| |
| values.push_back(value_opt); |
| } |
| |
| std::move(callback).Run(std::move(values)); |
| } |
| |
| SensorDeviceImpl::SensorDeviceImpl( |
| scoped_refptr<base::SequencedTaskRunner> ipc_task_runner, |
| libmems::IioContext* context, |
| std::unique_ptr<base::Thread> thread, |
| bool use_fifo) |
| : ipc_task_runner_(std::move(ipc_task_runner)), |
| context_(std::move(context)), |
| sample_thread_(std::move(thread)), |
| use_fifo_(use_fifo) { |
| DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence()); |
| |
| receiver_set_.set_disconnect_handler(base::BindRepeating( |
| &SensorDeviceImpl::OnSensorDeviceDisconnect, weak_factory_.GetWeakPtr())); |
| } |
| |
| void SensorDeviceImpl::AddReceiverOnThread( |
| int32_t iio_device_id, |
| mojo::PendingReceiver<cros::mojom::SensorDevice> request) { |
| DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence()); |
| |
| auto iio_device = context_->GetDeviceById(iio_device_id); |
| if (!iio_device) { |
| LOGF(ERROR) << "Invalid iio_device_id: " << iio_device_id; |
| return; |
| } |
| |
| mojo::ReceiverId id = |
| receiver_set_.Add(this, std::move(request), ipc_task_runner_); |
| clients_[id].id = id; |
| clients_[id].iio_device = iio_device; |
| } |
| |
| void SensorDeviceImpl::OnSensorDeviceDisconnect() { |
| DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence()); |
| |
| mojo::ReceiverId id = receiver_set_.current_receiver(); |
| |
| LOGF(INFO) << "SensorDevice disconnected. ReceiverId: " << id; |
| StopReadingSamplesOnClient(id); |
| |
| // Run RemoveClient(id) on |sample_thread_| so that tasks to remove the client |
| // in SamplesHandler have been done. |
| sample_thread_->task_runner()->PostTask( |
| FROM_HERE, base::BindOnce(&SensorDeviceImpl::RemoveClient, |
| base::Unretained(this), id)); |
| } |
| |
| void SensorDeviceImpl::RemoveClient(mojo::ReceiverId id) { |
| DCHECK(sample_thread_->task_runner()->RunsTasksInCurrentSequence()); |
| |
| clients_.erase(id); |
| } |
| |
| void SensorDeviceImpl::OnSamplesObserverDisconnect(mojo::ReceiverId id) { |
| DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence()); |
| |
| LOGF(ERROR) << "SamplesObserver disconnected. ReceiverId: " << id; |
| StopReadingSamplesOnClient(id); |
| } |
| |
| void SensorDeviceImpl::StopReadingSamplesOnClient(mojo::ReceiverId id) { |
| DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence()); |
| |
| ClientData& client = clients_[id]; |
| |
| if (!client.observer.is_bound()) { |
| // The client is not reading samples. |
| return; |
| } |
| |
| if (samples_handlers_.find(client.iio_device) != samples_handlers_.end()) |
| samples_handlers_.at(client.iio_device)->RemoveClient(&client); |
| |
| client.observer.reset(); |
| } |
| |
| bool SensorDeviceImpl::AddSamplesHandlerIfNotSet( |
| libmems::IioDevice* iio_device) { |
| DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence()); |
| |
| if (samples_handlers_.find(iio_device) != samples_handlers_.end()) |
| return true; |
| |
| SamplesHandler::ScopedSamplesHandler handler = { |
| nullptr, SamplesHandler::SamplesHandlerDeleter}; |
| |
| auto sample_cb = base::BindRepeating( |
| &SensorDeviceImpl::OnSampleUpdatedCallback, weak_factory_.GetWeakPtr()); |
| 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)); |
| } |
| |
| if (!handler) { |
| LOGF(ERROR) << "Failed to create the samples handler for device: " |
| << iio_device->GetId(); |
| return false; |
| } |
| |
| samples_handlers_.emplace(iio_device, std::move(handler)); |
| return true; |
| } |
| |
| void SensorDeviceImpl::OnSampleUpdatedCallback( |
| mojo::ReceiverId id, libmems::IioDevice::IioSample sample) { |
| DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence()); |
| auto it = clients_.find(id); |
| if (it == clients_.end()) { |
| LOGF(WARNING) << "Sample not sent, as the client doesn't exist: " << id; |
| return; |
| } |
| |
| if (!it->second.observer.is_bound()) { |
| LOGF(WARNING) << "Sample not sent, as the client has stopped reading: " |
| << id; |
| return; |
| } |
| |
| it->second.observer->OnSampleUpdated(std::move(sample)); |
| } |
| |
| void SensorDeviceImpl::OnErrorOccurredCallback( |
| mojo::ReceiverId id, cros::mojom::ObserverErrorType type) { |
| DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence()); |
| auto it = clients_.find(id); |
| if (it == clients_.end()) { |
| LOGF(WARNING) << "Error not sent, as the client doesn't exist: " << id; |
| return; |
| } |
| |
| if (!it->second.observer.is_bound()) { |
| LOGF(WARNING) << "Sample not sent, as the client has stopped reading: " |
| << id; |
| return; |
| } |
| |
| it->second.observer->OnErrorOccurred(type); |
| } |
| |
| } // namespace iioservice |