blob: 846545994720d7f4e0ad4010a55e917f3c7c9bb9 [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
72bool IioDeviceImpl::WriteStringAttribute(const std::string& name,
73 const std::string& val) {
74 int error =
75 iio_device_attr_write_raw(device_, name.c_str(), val.data(), val.size());
76 if (error) {
77 LOG(WARNING) << "Attempting to write attribute " << name
78 << " failed: " << error;
79 return false;
80 }
81 return true;
82}
83bool IioDeviceImpl::WriteNumberAttribute(const std::string& name,
84 int64_t val) {
85 int error = iio_device_attr_write_longlong(device_, name.c_str(), val);
86 if (error) {
87 LOG(WARNING) << "Attempting to write attribute " << name
88 << " failed: " << error;
89 return false;
90 }
91 return true;
92}
93
94iio_device* IioDeviceImpl::GetUnderlyingIioDevice() const {
95 return device_;
96}
97
98bool IioDeviceImpl::SetTrigger(IioDevice* trigger_device) {
99 auto impl_device = trigger_device->GetUnderlyingIioDevice();
100 if (!impl_device) {
101 LOG(WARNING) << "cannot find device " << trigger_device->GetId()
102 << " in the current context";
103 return false;
104 }
105 int ok = iio_device_set_trigger(device_, impl_device);
106 if (ok) {
107 LOG(WARNING) << "Unable to set trigger for device " << GetId()
108 << " to be device " << trigger_device->GetId()
109 << ", error: " << ok;
110 return false;
111 }
112 return true;
113}
114
115IioDevice* IioDeviceImpl::GetTrigger() {
116 const iio_device* trigger;
117 int error = iio_device_get_trigger(device_, &trigger);
118 if (error) {
119 LOG(WARNING) << "Unable to get trigger for device " << GetId();
120 return nullptr;
121 }
122 const char* trigger_id = iio_device_get_id(trigger);
123 auto trigger_device = GetContext()->GetDevice(trigger_id);
124 if (trigger_device == nullptr) {
125 LOG(WARNING) << GetId() << " has trigger device " << trigger_id
126 << "which cannot be found in this context";
127 return nullptr;
128 }
129 return trigger_device;
130}
131
132IioChannel* IioDeviceImpl::GetChannel(const std::string& name) {
133 auto k = channels_.find(name);
134 if (k != channels_.end())
135 return k->second.get();
136 iio_channel* channel = iio_device_find_channel(device_, name.c_str(), true);
137 if (channel == nullptr)
138 channel = iio_device_find_channel(device_, name.c_str(), false);
139 if (channel == nullptr)
140 return nullptr;
141 channels_.emplace(name, std::make_unique<IioChannelImpl>(channel));
142 return channels_[name].get();
143}
144
Harvey Yangc0d19d82019-07-01 12:17:34 +0800145base::Optional<size_t> IioDeviceImpl::GetSampleSize() const {
146 ssize_t sample_size = iio_device_get_sample_size(device_);
147 if (sample_size < 0) {
148 char errMsg[ERROR_BUFFER_SIZE];
149 iio_strerror(errno, errMsg, sizeof(errMsg));
150 LOG(WARNING) << "Unable to get sample size: " << errMsg;
151 return base::nullopt;
152 }
153
154 return static_cast<size_t>(sample_size);
155}
156
Enrico Granata60a818d2019-05-09 09:56:09 -0700157bool IioDeviceImpl::EnableBuffer(size_t count) {
158 if (!WriteNumberAttribute("buffer/length", count))
159 return false;
160 if (!WriteNumberAttribute("buffer/enable", 1))
161 return false;
162
163 return true;
164}
165
166bool IioDeviceImpl::DisableBuffer() {
167 return WriteNumberAttribute("buffer/enable", 0);
168}
169
170bool IioDeviceImpl::IsBufferEnabled(size_t* count) const {
171 bool enabled = (ReadNumberAttribute("buffer/enable").value_or(0) == 1);
172 if (enabled && count)
173 *count = ReadNumberAttribute("buffer/length").value_or(0);
174
175 return enabled;
176}
177
Harvey Yangc0d19d82019-07-01 12:17:34 +0800178bool IioDeviceImpl::ReadEvents(uint32_t num_samples,
179 std::vector<uint8_t>* events) {
180 if (!CreateBuffer(num_samples))
181 return false;
182
183 events->clear();
184
185 ssize_t ret = iio_buffer_refill(buffer_.get());
186 if (ret < 0) {
187 char errMsg[ERROR_BUFFER_SIZE];
188 iio_strerror(-ret, errMsg, sizeof(errMsg));
189 LOG(ERROR) << "Unable to refill buffer: " << errMsg;
190 return false;
191 }
192
193 const auto buf_step = iio_buffer_step(buffer_.get());
194 size_t sample_size = GetSampleSize().value_or(0);
195
196 // There is something wrong when refilling the buffer.
197 if (buf_step != sample_size) {
198 LOG(ERROR) << "sample_size doesn't match in refill: " << buf_step
199 << ", sample_size: " << sample_size;
200
201 return false;
202 }
203
204 uint8_t* start = reinterpret_cast<uint8_t*>(iio_buffer_start(buffer_.get()));
205 size_t len = reinterpret_cast<intptr_t>(iio_buffer_end(buffer_.get())) -
206 reinterpret_cast<intptr_t>(start);
207
208 events->reserve(len);
209 events->insert(events->begin(), start, start + len);
210
211 return true;
212}
213
214// static
215void IioDeviceImpl::IioBufferDeleter(iio_buffer* buffer) {
216 iio_buffer_cancel(buffer);
217 iio_buffer_destroy(buffer);
218}
219
220bool IioDeviceImpl::CreateBuffer(uint32_t num_samples) {
221 if (num_samples == 0) {
222 LOG(WARNING) << "Buffer size should not be zero.";
223 return false;
224 }
225
226 if (buffer_ && num_samples == buffer_size_ &&
227 iio_device_get_sample_size(device_) == iio_buffer_step(buffer_.get()))
228 return true;
229
230 buffer_size_ = num_samples;
231
232 buffer_.reset();
233 buffer_.reset(iio_device_create_buffer(device_, num_samples, false));
234
235 if (!buffer_) {
236 char errMsg[ERROR_BUFFER_SIZE];
237 iio_strerror(errno, errMsg, sizeof(errMsg));
238 LOG(ERROR) << "Unable to allocate buffer: " << errMsg;
239 return false;
240 }
241
242 return true;
243}
244
Enrico Granata51cdb942019-06-18 16:40:17 -0700245} // namespace libmems