blob: 6a765e4b5b15b1d79cacad4f7d592e5856fe7a7b [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
45 EnableAllChannels();
Enrico Granata60a818d2019-05-09 09:56:09 -070046}
47
48IioContext* IioDeviceImpl::GetContext() const {
49 return context_;
50}
51
52const char* IioDeviceImpl::GetName() const {
53 return iio_device_get_name(device_);
54}
55
Harvey Yang9260b662019-08-12 15:48:03 +080056int IioDeviceImpl::GetId() const {
57 const char* id_str = iio_device_get_id(device_);
58
59 auto id = GetIdFromString(id_str);
60 DCHECK(id.has_value());
61 return id.value();
Enrico Granata60a818d2019-05-09 09:56:09 -070062}
63
64base::FilePath IioDeviceImpl::GetPath() const {
Harvey Yang9260b662019-08-12 15:48:03 +080065 std::string id_str = GetStringFromId(GetId());
66 auto path = base::FilePath("/sys/bus/iio/devices").Append(id_str);
Enrico Granata60a818d2019-05-09 09:56:09 -070067 CHECK(base::DirectoryExists(path));
68 return path;
69}
70
71base::Optional<std::string> IioDeviceImpl::ReadStringAttribute(
72 const std::string& name) const {
Harvey Yang9260b662019-08-12 15:48:03 +080073 char data[kReadAttrBufferSize] = {0};
Enrico Granata60a818d2019-05-09 09:56:09 -070074 ssize_t len = iio_device_attr_read(device_, name.c_str(), data, sizeof(data));
75 if (len < 0) {
76 LOG(WARNING) << "Attempting to read attribute " << name
77 << " failed: " << len;
78 return base::nullopt;
79 }
80 return std::string(data, len);
81}
82
83base::Optional<int64_t> IioDeviceImpl::ReadNumberAttribute(
84 const std::string& name) const {
85 long long val = 0; // NOLINT(runtime/int)
86 int error = iio_device_attr_read_longlong(device_, name.c_str(), &val);
87 if (error) {
88 LOG(WARNING) << "Attempting to read attribute " << name
89 << " failed: " << error;
90 return base::nullopt;
91 }
92 return val;
93}
94
Enrico Granatad2e57f42019-07-31 10:46:03 -070095base::Optional<double> IioDeviceImpl::ReadDoubleAttribute(
96 const std::string& name) const {
97 double val = 0;
98 int error = iio_device_attr_read_double(device_, name.c_str(), &val);
99 if (error) {
100 LOG(WARNING) << "Attempting to read attribute " << name
101 << " failed: " << error;
102 return base::nullopt;
103 }
104 return val;
105}
106
Enrico Granata60a818d2019-05-09 09:56:09 -0700107bool IioDeviceImpl::WriteStringAttribute(const std::string& name,
Harvey Yang9260b662019-08-12 15:48:03 +0800108 const std::string& value) {
109 int error = iio_device_attr_write_raw(device_, name.c_str(), value.data(),
110 value.size());
111 if (error < 0) {
Enrico Granata60a818d2019-05-09 09:56:09 -0700112 LOG(WARNING) << "Attempting to write attribute " << name
113 << " failed: " << error;
114 return false;
115 }
116 return true;
117}
Enrico Granatad2e57f42019-07-31 10:46:03 -0700118
Enrico Granata60a818d2019-05-09 09:56:09 -0700119bool IioDeviceImpl::WriteNumberAttribute(const std::string& name,
Harvey Yang9260b662019-08-12 15:48:03 +0800120 int64_t value) {
121 int error = iio_device_attr_write_longlong(device_, name.c_str(), value);
Enrico Granata60a818d2019-05-09 09:56:09 -0700122 if (error) {
123 LOG(WARNING) << "Attempting to write attribute " << name
124 << " failed: " << error;
125 return false;
126 }
127 return true;
128}
129
Harvey Yang9260b662019-08-12 15:48:03 +0800130bool IioDeviceImpl::WriteDoubleAttribute(const std::string& name,
131 double value) {
132 int error = iio_device_attr_write_double(device_, name.c_str(), value);
Enrico Granatad2e57f42019-07-31 10:46:03 -0700133 if (error) {
134 LOG(WARNING) << "Attempting to write attribute " << name
135 << " failed: " << error;
136 return false;
137 }
138 return true;
139}
140
Enrico Granata60a818d2019-05-09 09:56:09 -0700141iio_device* IioDeviceImpl::GetUnderlyingIioDevice() const {
142 return device_;
143}
144
145bool IioDeviceImpl::SetTrigger(IioDevice* trigger_device) {
Gwendal Grignou66393e22019-11-20 16:46:22 -0800146 // Reset the old - if any - and then add the new trigger.
147 int error = iio_device_set_trigger(device_, NULL);
148 if (error) {
Harvey Yang9260b662019-08-12 15:48:03 +0800149 LOG(WARNING) << "Unable to clean trigger of device " << GetId()
Gwendal Grignou66393e22019-11-20 16:46:22 -0800150 << ", error: " << error;
151 return false;
152 }
153 if (trigger_device == nullptr)
154 return true;
155
Harvey Yang9260b662019-08-12 15:48:03 +0800156 const iio_device* impl_device = nullptr;
157 int id = trigger_device->GetId();
158 if (id == -2) {
159 impl_device = iio_context_find_device(GetContext()->GetCurrentContext(),
160 kIioSysfsTrigger);
161 } else {
162 std::string id_str = IioDeviceTriggerImpl::GetStringFromId(id);
163 impl_device = iio_context_find_device(GetContext()->GetCurrentContext(),
164 id_str.c_str());
165 }
Enrico Granata60a818d2019-05-09 09:56:09 -0700166 if (!impl_device) {
Harvey Yang9260b662019-08-12 15:48:03 +0800167 LOG(WARNING) << "cannot find device " << id << " in the current context";
Enrico Granata60a818d2019-05-09 09:56:09 -0700168 return false;
169 }
Gwendal Grignou66393e22019-11-20 16:46:22 -0800170
171 error = iio_device_set_trigger(device_, impl_device);
172 if (error) {
Enrico Granata60a818d2019-05-09 09:56:09 -0700173 LOG(WARNING) << "Unable to set trigger for device " << GetId()
174 << " to be device " << trigger_device->GetId()
Gwendal Grignou66393e22019-11-20 16:46:22 -0800175 << ", error: " << error;
Enrico Granata60a818d2019-05-09 09:56:09 -0700176 return false;
177 }
178 return true;
179}
180
181IioDevice* IioDeviceImpl::GetTrigger() {
182 const iio_device* trigger;
183 int error = iio_device_get_trigger(device_, &trigger);
184 if (error) {
185 LOG(WARNING) << "Unable to get trigger for device " << GetId();
186 return nullptr;
187 }
Gwendal Grignou66393e22019-11-20 16:46:22 -0800188
189 if (trigger == nullptr)
190 return nullptr;
191
Harvey Yang9260b662019-08-12 15:48:03 +0800192 const char* id_str = iio_device_get_id(trigger);
193 auto id = IioDeviceTriggerImpl::GetIdFromString(id_str);
194
195 IioDevice* trigger_device = nullptr;
196 if (id.has_value())
197 trigger_device = GetContext()->GetTriggerById(id.value());
198
Enrico Granata60a818d2019-05-09 09:56:09 -0700199 if (trigger_device == nullptr) {
Harvey Yang9260b662019-08-12 15:48:03 +0800200 LOG(WARNING) << GetId() << " has trigger device " << id_str
Enrico Granata60a818d2019-05-09 09:56:09 -0700201 << "which cannot be found in this context";
Enrico Granata60a818d2019-05-09 09:56:09 -0700202 }
Harvey Yang9260b662019-08-12 15:48:03 +0800203
Enrico Granata60a818d2019-05-09 09:56:09 -0700204 return trigger_device;
205}
206
Harvey Yangb0c30962019-09-17 15:00:25 +0800207std::vector<IioChannel*> IioDeviceImpl::GetAllChannels() {
208 std::vector<IioChannel*> channels;
209 uint32_t chn_count = iio_device_get_channels_count(device_);
210
211 for (int i = 0; i < chn_count; ++i) {
212 iio_channel* channel = iio_device_get_channel(device_, i);
213 if (channel == nullptr) {
214 LOG(WARNING) << "Unable to get " << i
215 << "th channel from device: " << GetId();
216 continue;
217 }
218
219 channels.push_back(GetChannel(iio_channel_get_id(channel)));
220 }
221
222 return channels;
223}
224
Enrico Granata60a818d2019-05-09 09:56:09 -0700225IioChannel* IioDeviceImpl::GetChannel(const std::string& name) {
226 auto k = channels_.find(name);
227 if (k != channels_.end())
228 return k->second.get();
229 iio_channel* channel = iio_device_find_channel(device_, name.c_str(), true);
230 if (channel == nullptr)
231 channel = iio_device_find_channel(device_, name.c_str(), false);
232 if (channel == nullptr)
233 return nullptr;
234 channels_.emplace(name, std::make_unique<IioChannelImpl>(channel));
235 return channels_[name].get();
236}
237
Harvey Yangc0d19d82019-07-01 12:17:34 +0800238base::Optional<size_t> IioDeviceImpl::GetSampleSize() const {
239 ssize_t sample_size = iio_device_get_sample_size(device_);
240 if (sample_size < 0) {
Harvey Yang75452e82019-10-02 16:57:01 +0800241 char errMsg[kErrorBufferSize];
Harvey Yangc0d19d82019-07-01 12:17:34 +0800242 iio_strerror(errno, errMsg, sizeof(errMsg));
243 LOG(WARNING) << "Unable to get sample size: " << errMsg;
244 return base::nullopt;
245 }
246
247 return static_cast<size_t>(sample_size);
248}
249
Enrico Granata60a818d2019-05-09 09:56:09 -0700250bool IioDeviceImpl::EnableBuffer(size_t count) {
251 if (!WriteNumberAttribute("buffer/length", count))
252 return false;
253 if (!WriteNumberAttribute("buffer/enable", 1))
254 return false;
255
256 return true;
257}
258
259bool IioDeviceImpl::DisableBuffer() {
260 return WriteNumberAttribute("buffer/enable", 0);
261}
262
263bool IioDeviceImpl::IsBufferEnabled(size_t* count) const {
264 bool enabled = (ReadNumberAttribute("buffer/enable").value_or(0) == 1);
265 if (enabled && count)
266 *count = ReadNumberAttribute("buffer/length").value_or(0);
267
268 return enabled;
269}
270
Harvey Yange8b301b2020-05-19 14:43:03 +0800271base::Optional<int32_t> IioDeviceImpl::GetBufferFd() {
272 if (!CreateBuffer())
273 return base::nullopt;
274
275 int32_t fd = iio_buffer_get_poll_fd(buffer_.get());
276 if (fd < 0) {
277 LOG(ERROR) << "Failed to get poll fd: " << fd;
278 return base::nullopt;
279 }
280
281 return fd;
282}
283
Harvey Yang34c54d52020-05-20 13:34:20 +0800284base::Optional<IioDevice::IioSample> IioDeviceImpl::ReadSample() {
Harvey Yang75452e82019-10-02 16:57:01 +0800285 if (!CreateBuffer())
Harvey Yang34c54d52020-05-20 13:34:20 +0800286 return base::nullopt;
Harvey Yangc0d19d82019-07-01 12:17:34 +0800287
288 ssize_t ret = iio_buffer_refill(buffer_.get());
289 if (ret < 0) {
Harvey Yang75452e82019-10-02 16:57:01 +0800290 char errMsg[kErrorBufferSize];
Harvey Yangc0d19d82019-07-01 12:17:34 +0800291 iio_strerror(-ret, errMsg, sizeof(errMsg));
292 LOG(ERROR) << "Unable to refill buffer: " << errMsg;
Harvey Yangae53dc82019-07-25 18:11:20 +0800293 buffer_.reset();
Harvey Yangae53dc82019-07-25 18:11:20 +0800294
Harvey Yang34c54d52020-05-20 13:34:20 +0800295 return base::nullopt;
Harvey Yangc0d19d82019-07-01 12:17:34 +0800296 }
297
298 const auto buf_step = iio_buffer_step(buffer_.get());
299 size_t sample_size = GetSampleSize().value_or(0);
300
301 // There is something wrong when refilling the buffer.
302 if (buf_step != sample_size) {
303 LOG(ERROR) << "sample_size doesn't match in refill: " << buf_step
304 << ", sample_size: " << sample_size;
Harvey Yangae53dc82019-07-25 18:11:20 +0800305 buffer_.reset();
Harvey Yangc0d19d82019-07-01 12:17:34 +0800306
Harvey Yang34c54d52020-05-20 13:34:20 +0800307 return base::nullopt;
Harvey Yangc0d19d82019-07-01 12:17:34 +0800308 }
309
310 uint8_t* start = reinterpret_cast<uint8_t*>(iio_buffer_start(buffer_.get()));
Harvey Yangc0d19d82019-07-01 12:17:34 +0800311
Harvey Yang34c54d52020-05-20 13:34:20 +0800312 return DeserializeSample(start);
Harvey Yangc0d19d82019-07-01 12:17:34 +0800313}
314
315// static
316void IioDeviceImpl::IioBufferDeleter(iio_buffer* buffer) {
317 iio_buffer_cancel(buffer);
318 iio_buffer_destroy(buffer);
319}
320
Harvey Yang5e6cb702020-01-15 10:57:44 +0800321void IioDeviceImpl::EnableAllChannels() {
322 for (IioChannel* chn : GetAllChannels()) {
323 if (!chn->SetEnabledAndCheck(true))
324 LOG(ERROR) << "Failed to enable channel: " << chn->GetId();
325 }
326}
327
Harvey Yang75452e82019-10-02 16:57:01 +0800328bool IioDeviceImpl::CreateBuffer() {
329 if (buffer_ &&
Harvey Yangc0d19d82019-07-01 12:17:34 +0800330 iio_device_get_sample_size(device_) == iio_buffer_step(buffer_.get()))
331 return true;
332
Harvey Yangc0d19d82019-07-01 12:17:34 +0800333 buffer_.reset();
Harvey Yang75452e82019-10-02 16:57:01 +0800334 buffer_.reset(iio_device_create_buffer(device_, kNumSamples, false));
Harvey Yangc0d19d82019-07-01 12:17:34 +0800335
336 if (!buffer_) {
Harvey Yang75452e82019-10-02 16:57:01 +0800337 char errMsg[kErrorBufferSize];
Harvey Yangc0d19d82019-07-01 12:17:34 +0800338 iio_strerror(errno, errMsg, sizeof(errMsg));
339 LOG(ERROR) << "Unable to allocate buffer: " << errMsg;
340 return false;
341 }
342
343 return true;
344}
345
Harvey Yang34c54d52020-05-20 13:34:20 +0800346IioDevice::IioSample IioDeviceImpl::DeserializeSample(const uint8_t* src) {
347 IioSample event;
348 int64_t pos = 0;
349
350 for (IioChannel* chn_base : GetAllChannels()) {
351 IioChannelImpl* chn = dynamic_cast<IioChannelImpl*>(chn_base);
352 if (!chn->IsEnabled())
353 continue;
354
355 size_t len = chn->Length().value_or(0);
356 if (len == 0)
357 continue;
358 len /= CHAR_BIT;
359
360 size_t space_in_block = sizeof(int64_t) - (pos % sizeof(int64_t));
361 if (len > space_in_block) {
362 pos += space_in_block;
363 }
364
365 base::Optional<int64_t> value = chn->Convert(src + pos);
366 pos += len;
367
368 if (value.has_value())
369 event[chn->GetId()] = value.value();
370 }
371
372 return event;
373}
374
Enrico Granata51cdb942019-06-18 16:40:17 -0700375} // namespace libmems