blob: f8b59fe9b70af52425c056150e977ae00f166186 [file] [log] [blame]
Enrico Granata60a818d2019-05-09 09:56:09 -07001// Copyright 2019 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <memory>
6#include <string>
Harvey Yangc0d19d82019-07-01 12:17:34 +08007#include <vector>
Enrico Granata60a818d2019-05-09 09:56:09 -07008
9#include <base/files/file_util.h>
10#include <base/logging.h>
Harvey Yang9260b662019-08-12 15:48:03 +080011#include <base/strings/stringprintf.h>
Enrico Granata60a818d2019-05-09 09:56:09 -070012
Harvey Yang6698c862019-09-16 17:24:38 +080013#include "libmems/common_types.h"
Enrico Granata51cdb942019-06-18 16:40:17 -070014#include "libmems/iio_channel_impl.h"
15#include "libmems/iio_context_impl.h"
16#include "libmems/iio_device_impl.h"
Harvey Yang9260b662019-08-12 15:48:03 +080017#include "libmems/iio_device_trigger_impl.h"
Enrico Granata60a818d2019-05-09 09:56:09 -070018
Harvey Yangc0d19d82019-07-01 12:17:34 +080019#define ERROR_BUFFER_SIZE 256
20
Enrico Granata51cdb942019-06-18 16:40:17 -070021namespace libmems {
Enrico Granata60a818d2019-05-09 09:56:09 -070022
Harvey Yang75452e82019-10-02 16:57:01 +080023namespace {
24
Harvey Yang75452e82019-10-02 16:57:01 +080025constexpr int kNumSamples = 1;
26
27}; // namespace
28
Harvey Yang9260b662019-08-12 15:48:03 +080029base::Optional<int> IioDeviceImpl::GetIdFromString(const char* id_str) {
30 return IioDevice::GetIdAfterPrefix(id_str, kDeviceIdPrefix);
31}
32
33std::string IioDeviceImpl::GetStringFromId(int id) {
34 return base::StringPrintf("%s%d", kDeviceIdPrefix, id);
35}
36
Enrico Granata60a818d2019-05-09 09:56:09 -070037IioDeviceImpl::IioDeviceImpl(IioContextImpl* ctx, iio_device* dev)
Harvey Yangc0d19d82019-07-01 12:17:34 +080038 : IioDevice(),
39 context_(ctx),
40 device_(dev),
Harvey Yang75452e82019-10-02 16:57:01 +080041 buffer_(nullptr, IioBufferDeleter) {
Enrico Granata60a818d2019-05-09 09:56:09 -070042 CHECK(context_);
43 CHECK(device_);
Harvey Yang5e6cb702020-01-15 10:57:44 +080044
Harvey Yang9f48d812020-06-30 16:24:30 +080045 uint32_t chn_count = iio_device_get_channels_count(device_);
46 channels_.resize(chn_count);
47
48 for (uint32_t i = 0; i < chn_count; ++i) {
49 iio_channel* channel = iio_device_get_channel(device_, i);
50 if (channel == nullptr) {
51 LOG(WARNING) << "Unable to get " << i
52 << "th channel from device: " << GetId();
53 continue;
54 }
55
56 channels_[i].chn = std::make_unique<IioChannelImpl>(channel);
57 channels_[i].chn_id = channels_[i].chn->GetId();
58 }
59
Harvey Yang5e6cb702020-01-15 10:57:44 +080060 EnableAllChannels();
Enrico Granata60a818d2019-05-09 09:56:09 -070061}
62
63IioContext* IioDeviceImpl::GetContext() const {
64 return context_;
65}
66
67const char* IioDeviceImpl::GetName() const {
68 return iio_device_get_name(device_);
69}
70
Harvey Yang9260b662019-08-12 15:48:03 +080071int IioDeviceImpl::GetId() const {
72 const char* id_str = iio_device_get_id(device_);
73
74 auto id = GetIdFromString(id_str);
75 DCHECK(id.has_value());
76 return id.value();
Enrico Granata60a818d2019-05-09 09:56:09 -070077}
78
79base::FilePath IioDeviceImpl::GetPath() const {
Harvey Yang9260b662019-08-12 15:48:03 +080080 std::string id_str = GetStringFromId(GetId());
81 auto path = base::FilePath("/sys/bus/iio/devices").Append(id_str);
Enrico Granata60a818d2019-05-09 09:56:09 -070082 CHECK(base::DirectoryExists(path));
83 return path;
84}
85
86base::Optional<std::string> IioDeviceImpl::ReadStringAttribute(
87 const std::string& name) const {
Harvey Yang9260b662019-08-12 15:48:03 +080088 char data[kReadAttrBufferSize] = {0};
Enrico Granata60a818d2019-05-09 09:56:09 -070089 ssize_t len = iio_device_attr_read(device_, name.c_str(), data, sizeof(data));
90 if (len < 0) {
91 LOG(WARNING) << "Attempting to read attribute " << name
92 << " failed: " << len;
93 return base::nullopt;
94 }
95 return std::string(data, len);
96}
97
98base::Optional<int64_t> IioDeviceImpl::ReadNumberAttribute(
99 const std::string& name) const {
100 long long val = 0; // NOLINT(runtime/int)
101 int error = iio_device_attr_read_longlong(device_, name.c_str(), &val);
102 if (error) {
103 LOG(WARNING) << "Attempting to read attribute " << name
104 << " failed: " << error;
105 return base::nullopt;
106 }
107 return val;
108}
109
Enrico Granatad2e57f42019-07-31 10:46:03 -0700110base::Optional<double> IioDeviceImpl::ReadDoubleAttribute(
111 const std::string& name) const {
112 double val = 0;
113 int error = iio_device_attr_read_double(device_, name.c_str(), &val);
114 if (error) {
115 LOG(WARNING) << "Attempting to read attribute " << name
116 << " failed: " << error;
117 return base::nullopt;
118 }
119 return val;
120}
121
Enrico Granata60a818d2019-05-09 09:56:09 -0700122bool IioDeviceImpl::WriteStringAttribute(const std::string& name,
Harvey Yang9260b662019-08-12 15:48:03 +0800123 const std::string& value) {
124 int error = iio_device_attr_write_raw(device_, name.c_str(), value.data(),
125 value.size());
126 if (error < 0) {
Enrico Granata60a818d2019-05-09 09:56:09 -0700127 LOG(WARNING) << "Attempting to write attribute " << name
128 << " failed: " << error;
129 return false;
130 }
131 return true;
132}
Enrico Granatad2e57f42019-07-31 10:46:03 -0700133
Enrico Granata60a818d2019-05-09 09:56:09 -0700134bool IioDeviceImpl::WriteNumberAttribute(const std::string& name,
Harvey Yang9260b662019-08-12 15:48:03 +0800135 int64_t value) {
136 int error = iio_device_attr_write_longlong(device_, name.c_str(), value);
Enrico Granata60a818d2019-05-09 09:56:09 -0700137 if (error) {
138 LOG(WARNING) << "Attempting to write attribute " << name
139 << " failed: " << error;
140 return false;
141 }
142 return true;
143}
144
Harvey Yang9260b662019-08-12 15:48:03 +0800145bool IioDeviceImpl::WriteDoubleAttribute(const std::string& name,
146 double value) {
147 int error = iio_device_attr_write_double(device_, name.c_str(), value);
Enrico Granatad2e57f42019-07-31 10:46:03 -0700148 if (error) {
149 LOG(WARNING) << "Attempting to write attribute " << name
150 << " failed: " << error;
151 return false;
152 }
153 return true;
154}
155
Enrico Granata60a818d2019-05-09 09:56:09 -0700156iio_device* IioDeviceImpl::GetUnderlyingIioDevice() const {
157 return device_;
158}
159
160bool IioDeviceImpl::SetTrigger(IioDevice* trigger_device) {
Gwendal Grignou66393e22019-11-20 16:46:22 -0800161 // Reset the old - if any - and then add the new trigger.
162 int error = iio_device_set_trigger(device_, NULL);
163 if (error) {
Harvey Yang9260b662019-08-12 15:48:03 +0800164 LOG(WARNING) << "Unable to clean trigger of device " << GetId()
Gwendal Grignou66393e22019-11-20 16:46:22 -0800165 << ", error: " << error;
166 return false;
167 }
168 if (trigger_device == nullptr)
169 return true;
170
Harvey Yang9260b662019-08-12 15:48:03 +0800171 const iio_device* impl_device = nullptr;
172 int id = trigger_device->GetId();
173 if (id == -2) {
174 impl_device = iio_context_find_device(GetContext()->GetCurrentContext(),
175 kIioSysfsTrigger);
176 } else {
177 std::string id_str = IioDeviceTriggerImpl::GetStringFromId(id);
178 impl_device = iio_context_find_device(GetContext()->GetCurrentContext(),
179 id_str.c_str());
180 }
Enrico Granata60a818d2019-05-09 09:56:09 -0700181 if (!impl_device) {
Harvey Yang9260b662019-08-12 15:48:03 +0800182 LOG(WARNING) << "cannot find device " << id << " in the current context";
Enrico Granata60a818d2019-05-09 09:56:09 -0700183 return false;
184 }
Gwendal Grignou66393e22019-11-20 16:46:22 -0800185
186 error = iio_device_set_trigger(device_, impl_device);
187 if (error) {
Enrico Granata60a818d2019-05-09 09:56:09 -0700188 LOG(WARNING) << "Unable to set trigger for device " << GetId()
189 << " to be device " << trigger_device->GetId()
Gwendal Grignou66393e22019-11-20 16:46:22 -0800190 << ", error: " << error;
Enrico Granata60a818d2019-05-09 09:56:09 -0700191 return false;
192 }
193 return true;
194}
195
196IioDevice* IioDeviceImpl::GetTrigger() {
197 const iio_device* trigger;
198 int error = iio_device_get_trigger(device_, &trigger);
199 if (error) {
200 LOG(WARNING) << "Unable to get trigger for device " << GetId();
201 return nullptr;
202 }
Gwendal Grignou66393e22019-11-20 16:46:22 -0800203
204 if (trigger == nullptr)
205 return nullptr;
206
Harvey Yang9260b662019-08-12 15:48:03 +0800207 const char* id_str = iio_device_get_id(trigger);
208 auto id = IioDeviceTriggerImpl::GetIdFromString(id_str);
209
210 IioDevice* trigger_device = nullptr;
211 if (id.has_value())
212 trigger_device = GetContext()->GetTriggerById(id.value());
213
Enrico Granata60a818d2019-05-09 09:56:09 -0700214 if (trigger_device == nullptr) {
Harvey Yang9260b662019-08-12 15:48:03 +0800215 LOG(WARNING) << GetId() << " has trigger device " << id_str
Enrico Granata60a818d2019-05-09 09:56:09 -0700216 << "which cannot be found in this context";
Enrico Granata60a818d2019-05-09 09:56:09 -0700217 }
Harvey Yang9260b662019-08-12 15:48:03 +0800218
Enrico Granata60a818d2019-05-09 09:56:09 -0700219 return trigger_device;
220}
221
Harvey Yangb0c30962019-09-17 15:00:25 +0800222std::vector<IioChannel*> IioDeviceImpl::GetAllChannels() {
223 std::vector<IioChannel*> channels;
Harvey Yang9f48d812020-06-30 16:24:30 +0800224 for (const ChannelData& channel_data : channels_)
225 channels.push_back(channel_data.chn.get());
Harvey Yangb0c30962019-09-17 15:00:25 +0800226
227 return channels;
228}
229
Harvey Yang9f48d812020-06-30 16:24:30 +0800230IioChannel* IioDeviceImpl::GetChannel(int32_t index) {
231 if (index < 0 || index >= channels_.size())
Enrico Granata60a818d2019-05-09 09:56:09 -0700232 return nullptr;
Harvey Yang9f48d812020-06-30 16:24:30 +0800233
234 return channels_[index].chn.get();
235}
236
237IioChannel* IioDeviceImpl::GetChannel(const std::string& name) {
238 for (size_t i = 0; i < channels_.size(); ++i) {
239 if (channels_[i].chn_id == name)
240 return channels_[i].chn.get();
241 }
242
243 return nullptr;
Enrico Granata60a818d2019-05-09 09:56:09 -0700244}
245
Harvey Yangc0d19d82019-07-01 12:17:34 +0800246base::Optional<size_t> IioDeviceImpl::GetSampleSize() const {
247 ssize_t sample_size = iio_device_get_sample_size(device_);
248 if (sample_size < 0) {
Harvey Yang75452e82019-10-02 16:57:01 +0800249 char errMsg[kErrorBufferSize];
Harvey Yangc0d19d82019-07-01 12:17:34 +0800250 iio_strerror(errno, errMsg, sizeof(errMsg));
251 LOG(WARNING) << "Unable to get sample size: " << errMsg;
252 return base::nullopt;
253 }
254
255 return static_cast<size_t>(sample_size);
256}
257
Enrico Granata60a818d2019-05-09 09:56:09 -0700258bool IioDeviceImpl::EnableBuffer(size_t count) {
259 if (!WriteNumberAttribute("buffer/length", count))
260 return false;
261 if (!WriteNumberAttribute("buffer/enable", 1))
262 return false;
263
264 return true;
265}
266
267bool IioDeviceImpl::DisableBuffer() {
268 return WriteNumberAttribute("buffer/enable", 0);
269}
270
271bool IioDeviceImpl::IsBufferEnabled(size_t* count) const {
272 bool enabled = (ReadNumberAttribute("buffer/enable").value_or(0) == 1);
273 if (enabled && count)
274 *count = ReadNumberAttribute("buffer/length").value_or(0);
275
276 return enabled;
277}
278
Harvey Yange8b301b2020-05-19 14:43:03 +0800279base::Optional<int32_t> IioDeviceImpl::GetBufferFd() {
280 if (!CreateBuffer())
281 return base::nullopt;
282
283 int32_t fd = iio_buffer_get_poll_fd(buffer_.get());
284 if (fd < 0) {
285 LOG(ERROR) << "Failed to get poll fd: " << fd;
286 return base::nullopt;
287 }
288
289 return fd;
290}
291
Harvey Yang34c54d52020-05-20 13:34:20 +0800292base::Optional<IioDevice::IioSample> IioDeviceImpl::ReadSample() {
Harvey Yang75452e82019-10-02 16:57:01 +0800293 if (!CreateBuffer())
Harvey Yang34c54d52020-05-20 13:34:20 +0800294 return base::nullopt;
Harvey Yangc0d19d82019-07-01 12:17:34 +0800295
296 ssize_t ret = iio_buffer_refill(buffer_.get());
297 if (ret < 0) {
Harvey Yang75452e82019-10-02 16:57:01 +0800298 char errMsg[kErrorBufferSize];
Harvey Yangc0d19d82019-07-01 12:17:34 +0800299 iio_strerror(-ret, errMsg, sizeof(errMsg));
300 LOG(ERROR) << "Unable to refill buffer: " << errMsg;
Harvey Yangae53dc82019-07-25 18:11:20 +0800301 buffer_.reset();
Harvey Yangae53dc82019-07-25 18:11:20 +0800302
Harvey Yang34c54d52020-05-20 13:34:20 +0800303 return base::nullopt;
Harvey Yangc0d19d82019-07-01 12:17:34 +0800304 }
305
306 const auto buf_step = iio_buffer_step(buffer_.get());
307 size_t sample_size = GetSampleSize().value_or(0);
308
309 // There is something wrong when refilling the buffer.
310 if (buf_step != sample_size) {
311 LOG(ERROR) << "sample_size doesn't match in refill: " << buf_step
312 << ", sample_size: " << sample_size;
Harvey Yangae53dc82019-07-25 18:11:20 +0800313 buffer_.reset();
Harvey Yangc0d19d82019-07-01 12:17:34 +0800314
Harvey Yang34c54d52020-05-20 13:34:20 +0800315 return base::nullopt;
Harvey Yangc0d19d82019-07-01 12:17:34 +0800316 }
317
318 uint8_t* start = reinterpret_cast<uint8_t*>(iio_buffer_start(buffer_.get()));
Harvey Yangc0d19d82019-07-01 12:17:34 +0800319
Harvey Yang34c54d52020-05-20 13:34:20 +0800320 return DeserializeSample(start);
Harvey Yangc0d19d82019-07-01 12:17:34 +0800321}
322
323// static
324void IioDeviceImpl::IioBufferDeleter(iio_buffer* buffer) {
325 iio_buffer_cancel(buffer);
326 iio_buffer_destroy(buffer);
327}
328
Harvey Yang5e6cb702020-01-15 10:57:44 +0800329void IioDeviceImpl::EnableAllChannels() {
330 for (IioChannel* chn : GetAllChannels()) {
331 if (!chn->SetEnabledAndCheck(true))
332 LOG(ERROR) << "Failed to enable channel: " << chn->GetId();
333 }
334}
335
Harvey Yang75452e82019-10-02 16:57:01 +0800336bool IioDeviceImpl::CreateBuffer() {
337 if (buffer_ &&
Harvey Yangc0d19d82019-07-01 12:17:34 +0800338 iio_device_get_sample_size(device_) == iio_buffer_step(buffer_.get()))
339 return true;
340
Harvey Yangc0d19d82019-07-01 12:17:34 +0800341 buffer_.reset();
Harvey Yang75452e82019-10-02 16:57:01 +0800342 buffer_.reset(iio_device_create_buffer(device_, kNumSamples, false));
Harvey Yangc0d19d82019-07-01 12:17:34 +0800343
344 if (!buffer_) {
Harvey Yang75452e82019-10-02 16:57:01 +0800345 char errMsg[kErrorBufferSize];
Harvey Yangc0d19d82019-07-01 12:17:34 +0800346 iio_strerror(errno, errMsg, sizeof(errMsg));
347 LOG(ERROR) << "Unable to allocate buffer: " << errMsg;
348 return false;
349 }
350
351 return true;
352}
353
Harvey Yang34c54d52020-05-20 13:34:20 +0800354IioDevice::IioSample IioDeviceImpl::DeserializeSample(const uint8_t* src) {
Harvey Yang9f48d812020-06-30 16:24:30 +0800355 IioSample sample;
Harvey Yang34c54d52020-05-20 13:34:20 +0800356 int64_t pos = 0;
357
Harvey Yang9f48d812020-06-30 16:24:30 +0800358 auto channels = GetAllChannels();
359 for (int32_t i = 0; i < channels.size(); ++i) {
360 IioChannelImpl* chn = dynamic_cast<IioChannelImpl*>(channels[i]);
Harvey Yang34c54d52020-05-20 13:34:20 +0800361 if (!chn->IsEnabled())
362 continue;
363
364 size_t len = chn->Length().value_or(0);
365 if (len == 0)
366 continue;
367 len /= CHAR_BIT;
368
369 size_t space_in_block = sizeof(int64_t) - (pos % sizeof(int64_t));
370 if (len > space_in_block) {
371 pos += space_in_block;
372 }
373
374 base::Optional<int64_t> value = chn->Convert(src + pos);
375 pos += len;
376
377 if (value.has_value())
Harvey Yang9f48d812020-06-30 16:24:30 +0800378 sample[i] = value.value();
Harvey Yang34c54d52020-05-20 13:34:20 +0800379 }
380
Harvey Yang9f48d812020-06-30 16:24:30 +0800381 return sample;
Harvey Yang34c54d52020-05-20 13:34:20 +0800382}
383
Enrico Granata51cdb942019-06-18 16:40:17 -0700384} // namespace libmems