blob: 8f0e269b48681e78387e7ee873ba9cb4164a71ab [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 Yang75452e82019-10-02 16:57:01 +0800284bool IioDeviceImpl::ReadEvent(std::vector<uint8_t>* event) {
285 if (!CreateBuffer())
Harvey Yangc0d19d82019-07-01 12:17:34 +0800286 return false;
287
Harvey Yang75452e82019-10-02 16:57:01 +0800288 event->clear();
Harvey Yangc0d19d82019-07-01 12:17:34 +0800289
290 ssize_t ret = iio_buffer_refill(buffer_.get());
291 if (ret < 0) {
Harvey Yang75452e82019-10-02 16:57:01 +0800292 char errMsg[kErrorBufferSize];
Harvey Yangc0d19d82019-07-01 12:17:34 +0800293 iio_strerror(-ret, errMsg, sizeof(errMsg));
294 LOG(ERROR) << "Unable to refill buffer: " << errMsg;
Harvey Yangae53dc82019-07-25 18:11:20 +0800295 buffer_.reset();
Harvey Yangae53dc82019-07-25 18:11:20 +0800296
Harvey Yangc0d19d82019-07-01 12:17:34 +0800297 return false;
298 }
299
300 const auto buf_step = iio_buffer_step(buffer_.get());
301 size_t sample_size = GetSampleSize().value_or(0);
302
303 // There is something wrong when refilling the buffer.
304 if (buf_step != sample_size) {
305 LOG(ERROR) << "sample_size doesn't match in refill: " << buf_step
306 << ", sample_size: " << sample_size;
Harvey Yangae53dc82019-07-25 18:11:20 +0800307 buffer_.reset();
Harvey Yangc0d19d82019-07-01 12:17:34 +0800308
309 return false;
310 }
311
312 uint8_t* start = reinterpret_cast<uint8_t*>(iio_buffer_start(buffer_.get()));
313 size_t len = reinterpret_cast<intptr_t>(iio_buffer_end(buffer_.get())) -
314 reinterpret_cast<intptr_t>(start);
315
Harvey Yang75452e82019-10-02 16:57:01 +0800316 event->reserve(len);
317 event->insert(event->begin(), start, start + len);
Harvey Yangc0d19d82019-07-01 12:17:34 +0800318
319 return true;
320}
321
322// static
323void IioDeviceImpl::IioBufferDeleter(iio_buffer* buffer) {
324 iio_buffer_cancel(buffer);
325 iio_buffer_destroy(buffer);
326}
327
Harvey Yang5e6cb702020-01-15 10:57:44 +0800328void IioDeviceImpl::EnableAllChannels() {
329 for (IioChannel* chn : GetAllChannels()) {
330 if (!chn->SetEnabledAndCheck(true))
331 LOG(ERROR) << "Failed to enable channel: " << chn->GetId();
332 }
333}
334
Harvey Yang75452e82019-10-02 16:57:01 +0800335bool IioDeviceImpl::CreateBuffer() {
336 if (buffer_ &&
Harvey Yangc0d19d82019-07-01 12:17:34 +0800337 iio_device_get_sample_size(device_) == iio_buffer_step(buffer_.get()))
338 return true;
339
Harvey Yangc0d19d82019-07-01 12:17:34 +0800340 buffer_.reset();
Harvey Yang75452e82019-10-02 16:57:01 +0800341 buffer_.reset(iio_device_create_buffer(device_, kNumSamples, false));
Harvey Yangc0d19d82019-07-01 12:17:34 +0800342
343 if (!buffer_) {
Harvey Yang75452e82019-10-02 16:57:01 +0800344 char errMsg[kErrorBufferSize];
Harvey Yangc0d19d82019-07-01 12:17:34 +0800345 iio_strerror(errno, errMsg, sizeof(errMsg));
346 LOG(ERROR) << "Unable to allocate buffer: " << errMsg;
347 return false;
348 }
349
350 return true;
351}
352
Enrico Granata51cdb942019-06-18 16:40:17 -0700353} // namespace libmems