blob: d9fd2d4978844fd92025ee53939afafd35de52f7 [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>
11
Enrico Granata51cdb942019-06-18 16:40:17 -070012#include "libmems/iio_channel_impl.h"
13#include "libmems/iio_context_impl.h"
14#include "libmems/iio_device_impl.h"
Enrico Granata60a818d2019-05-09 09:56:09 -070015
Harvey Yangc0d19d82019-07-01 12:17:34 +080016#define ERROR_BUFFER_SIZE 256
17
Enrico Granata51cdb942019-06-18 16:40:17 -070018namespace libmems {
Enrico Granata60a818d2019-05-09 09:56:09 -070019
20IioDeviceImpl::IioDeviceImpl(IioContextImpl* ctx, iio_device* dev)
Harvey Yangc0d19d82019-07-01 12:17:34 +080021 : IioDevice(),
22 context_(ctx),
23 device_(dev),
24 buffer_(nullptr, IioBufferDeleter),
25 buffer_size_(0) {
Enrico Granata60a818d2019-05-09 09:56:09 -070026 CHECK(context_);
27 CHECK(device_);
28}
29
30IioContext* IioDeviceImpl::GetContext() const {
31 return context_;
32}
33
34const char* IioDeviceImpl::GetName() const {
35 return iio_device_get_name(device_);
36}
37
38const char* IioDeviceImpl::GetId() const {
39 return iio_device_get_id(device_);
40}
41
42base::FilePath IioDeviceImpl::GetPath() const {
43 auto path = base::FilePath("/sys/bus/iio/devices").Append(GetId());
44 CHECK(base::DirectoryExists(path));
45 return path;
46}
47
48base::Optional<std::string> IioDeviceImpl::ReadStringAttribute(
49 const std::string& name) const {
50 char data[1024] = {0};
51 ssize_t len = iio_device_attr_read(device_, name.c_str(), data, sizeof(data));
52 if (len < 0) {
53 LOG(WARNING) << "Attempting to read attribute " << name
54 << " failed: " << len;
55 return base::nullopt;
56 }
57 return std::string(data, len);
58}
59
60base::Optional<int64_t> IioDeviceImpl::ReadNumberAttribute(
61 const std::string& name) const {
62 long long val = 0; // NOLINT(runtime/int)
63 int error = iio_device_attr_read_longlong(device_, name.c_str(), &val);
64 if (error) {
65 LOG(WARNING) << "Attempting to read attribute " << name
66 << " failed: " << error;
67 return base::nullopt;
68 }
69 return val;
70}
71
Enrico Granatad2e57f42019-07-31 10:46:03 -070072base::Optional<double> IioDeviceImpl::ReadDoubleAttribute(
73 const std::string& name) const {
74 double val = 0;
75 int error = iio_device_attr_read_double(device_, name.c_str(), &val);
76 if (error) {
77 LOG(WARNING) << "Attempting to read attribute " << name
78 << " failed: " << error;
79 return base::nullopt;
80 }
81 return val;
82}
83
Enrico Granata60a818d2019-05-09 09:56:09 -070084bool IioDeviceImpl::WriteStringAttribute(const std::string& name,
85 const std::string& val) {
86 int error =
87 iio_device_attr_write_raw(device_, name.c_str(), val.data(), val.size());
88 if (error) {
89 LOG(WARNING) << "Attempting to write attribute " << name
90 << " failed: " << error;
91 return false;
92 }
93 return true;
94}
Enrico Granatad2e57f42019-07-31 10:46:03 -070095
Enrico Granata60a818d2019-05-09 09:56:09 -070096bool IioDeviceImpl::WriteNumberAttribute(const std::string& name,
97 int64_t val) {
98 int error = iio_device_attr_write_longlong(device_, name.c_str(), val);
99 if (error) {
100 LOG(WARNING) << "Attempting to write attribute " << name
101 << " failed: " << error;
102 return false;
103 }
104 return true;
105}
106
Enrico Granatad2e57f42019-07-31 10:46:03 -0700107bool IioDeviceImpl::WriteDoubleAttribute(const std::string& name, double val) {
108 int error = iio_device_attr_write_double(device_, name.c_str(), val);
109 if (error) {
110 LOG(WARNING) << "Attempting to write attribute " << name
111 << " failed: " << error;
112 return false;
113 }
114 return true;
115}
116
Enrico Granata60a818d2019-05-09 09:56:09 -0700117iio_device* IioDeviceImpl::GetUnderlyingIioDevice() const {
118 return device_;
119}
120
121bool IioDeviceImpl::SetTrigger(IioDevice* trigger_device) {
122 auto impl_device = trigger_device->GetUnderlyingIioDevice();
123 if (!impl_device) {
124 LOG(WARNING) << "cannot find device " << trigger_device->GetId()
125 << " in the current context";
126 return false;
127 }
128 int ok = iio_device_set_trigger(device_, impl_device);
129 if (ok) {
130 LOG(WARNING) << "Unable to set trigger for device " << GetId()
131 << " to be device " << trigger_device->GetId()
132 << ", error: " << ok;
133 return false;
134 }
135 return true;
136}
137
138IioDevice* IioDeviceImpl::GetTrigger() {
139 const iio_device* trigger;
140 int error = iio_device_get_trigger(device_, &trigger);
141 if (error) {
142 LOG(WARNING) << "Unable to get trigger for device " << GetId();
143 return nullptr;
144 }
145 const char* trigger_id = iio_device_get_id(trigger);
146 auto trigger_device = GetContext()->GetDevice(trigger_id);
147 if (trigger_device == nullptr) {
148 LOG(WARNING) << GetId() << " has trigger device " << trigger_id
149 << "which cannot be found in this context";
150 return nullptr;
151 }
152 return trigger_device;
153}
154
155IioChannel* IioDeviceImpl::GetChannel(const std::string& name) {
156 auto k = channels_.find(name);
157 if (k != channels_.end())
158 return k->second.get();
159 iio_channel* channel = iio_device_find_channel(device_, name.c_str(), true);
160 if (channel == nullptr)
161 channel = iio_device_find_channel(device_, name.c_str(), false);
162 if (channel == nullptr)
163 return nullptr;
164 channels_.emplace(name, std::make_unique<IioChannelImpl>(channel));
165 return channels_[name].get();
166}
167
Harvey Yangc0d19d82019-07-01 12:17:34 +0800168base::Optional<size_t> IioDeviceImpl::GetSampleSize() const {
169 ssize_t sample_size = iio_device_get_sample_size(device_);
170 if (sample_size < 0) {
171 char errMsg[ERROR_BUFFER_SIZE];
172 iio_strerror(errno, errMsg, sizeof(errMsg));
173 LOG(WARNING) << "Unable to get sample size: " << errMsg;
174 return base::nullopt;
175 }
176
177 return static_cast<size_t>(sample_size);
178}
179
Enrico Granata60a818d2019-05-09 09:56:09 -0700180bool IioDeviceImpl::EnableBuffer(size_t count) {
181 if (!WriteNumberAttribute("buffer/length", count))
182 return false;
183 if (!WriteNumberAttribute("buffer/enable", 1))
184 return false;
185
186 return true;
187}
188
189bool IioDeviceImpl::DisableBuffer() {
190 return WriteNumberAttribute("buffer/enable", 0);
191}
192
193bool IioDeviceImpl::IsBufferEnabled(size_t* count) const {
194 bool enabled = (ReadNumberAttribute("buffer/enable").value_or(0) == 1);
195 if (enabled && count)
196 *count = ReadNumberAttribute("buffer/length").value_or(0);
197
198 return enabled;
199}
200
Harvey Yangc0d19d82019-07-01 12:17:34 +0800201bool IioDeviceImpl::ReadEvents(uint32_t num_samples,
202 std::vector<uint8_t>* events) {
203 if (!CreateBuffer(num_samples))
204 return false;
205
206 events->clear();
207
208 ssize_t ret = iio_buffer_refill(buffer_.get());
209 if (ret < 0) {
210 char errMsg[ERROR_BUFFER_SIZE];
211 iio_strerror(-ret, errMsg, sizeof(errMsg));
212 LOG(ERROR) << "Unable to refill buffer: " << errMsg;
Harvey Yangae53dc82019-07-25 18:11:20 +0800213 buffer_.reset();
214 buffer_size_ = 0;
215
Harvey Yangc0d19d82019-07-01 12:17:34 +0800216 return false;
217 }
218
219 const auto buf_step = iio_buffer_step(buffer_.get());
220 size_t sample_size = GetSampleSize().value_or(0);
221
222 // There is something wrong when refilling the buffer.
223 if (buf_step != sample_size) {
224 LOG(ERROR) << "sample_size doesn't match in refill: " << buf_step
225 << ", sample_size: " << sample_size;
Harvey Yangae53dc82019-07-25 18:11:20 +0800226 buffer_.reset();
227 buffer_size_ = 0;
Harvey Yangc0d19d82019-07-01 12:17:34 +0800228
229 return false;
230 }
231
232 uint8_t* start = reinterpret_cast<uint8_t*>(iio_buffer_start(buffer_.get()));
233 size_t len = reinterpret_cast<intptr_t>(iio_buffer_end(buffer_.get())) -
234 reinterpret_cast<intptr_t>(start);
235
236 events->reserve(len);
237 events->insert(events->begin(), start, start + len);
238
239 return true;
240}
241
242// static
243void IioDeviceImpl::IioBufferDeleter(iio_buffer* buffer) {
244 iio_buffer_cancel(buffer);
245 iio_buffer_destroy(buffer);
246}
247
248bool IioDeviceImpl::CreateBuffer(uint32_t num_samples) {
249 if (num_samples == 0) {
250 LOG(WARNING) << "Buffer size should not be zero.";
251 return false;
252 }
253
254 if (buffer_ && num_samples == buffer_size_ &&
255 iio_device_get_sample_size(device_) == iio_buffer_step(buffer_.get()))
256 return true;
257
Harvey Yangc0d19d82019-07-01 12:17:34 +0800258 buffer_.reset();
259 buffer_.reset(iio_device_create_buffer(device_, num_samples, false));
260
261 if (!buffer_) {
262 char errMsg[ERROR_BUFFER_SIZE];
263 iio_strerror(errno, errMsg, sizeof(errMsg));
264 LOG(ERROR) << "Unable to allocate buffer: " << errMsg;
265 return false;
266 }
267
Harvey Yangae53dc82019-07-25 18:11:20 +0800268 buffer_size_ = num_samples;
269
Harvey Yangc0d19d82019-07-01 12:17:34 +0800270 return true;
271}
272
Enrico Granata51cdb942019-06-18 16:40:17 -0700273} // namespace libmems