1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #define LOG_TAG "GoogleIIOSensorSubHal"
17
18 #include "iio_utils.h"
19 #include <errno.h>
20 #include <limits.h>
21 #include <log/log.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/stat.h>
26 #include <algorithm>
27 #include <fstream>
28 #include <iostream>
29 #include <memory>
30
31 static const char* IIO_DEVICE_BASE = "iio:device";
32 static const char* IIO_SCAN_ELEMENTS_EN = "_en";
33 static const char* IIO_SFA_FILENAME = "sampling_frequency_available";
34 static const char* IIO_SCALE_FILENAME = "_scale";
35 static const char* IIO_SAMPLING_FREQUENCY = "_sampling_frequency";
36 static const char* IIO_BUFFER_ENABLE = "buffer/enable";
37 static const char* IIO_NAME_FILENAME = "name";
38 static const char* IIO_RANGE_AVAIL_FILENAME = "raw_available";
39
40 namespace android {
41 namespace hardware {
42 namespace sensors {
43 namespace V2_1 {
44 namespace subhal {
45 namespace implementation {
46
47 const char* DEFAULT_IIO_DIR = "/sys/bus/iio/devices/";
48
49 using DirPtr = std::unique_ptr<DIR, decltype(&closedir)>;
50 using FilePtr = std::unique_ptr<FILE, decltype(&fclose)>;
51
str_has_prefix(const char * s,const char * prefix)52 static bool str_has_prefix(const char* s, const char* prefix) {
53 if (!s || !prefix) return false;
54
55 const auto len_s = strlen(s);
56 const auto len_prefix = strlen(prefix);
57 if (len_s < len_prefix) return false;
58 return std::equal(s, s + len_prefix, prefix);
59 }
60
str_has_suffix(const char * s,const char * suffix)61 static bool str_has_suffix(const char* s, const char* suffix) {
62 if (!s || !suffix) return false;
63
64 const auto len_s = strlen(s);
65 const auto len_suffix = strlen(suffix);
66 if (len_s < len_suffix) return false;
67 return std::equal(s + len_s - len_suffix, s + len_s, suffix);
68 }
69
sysfs_opendir(const std::string & name,DirPtr * dp)70 static int sysfs_opendir(const std::string& name, DirPtr* dp) {
71 if (dp == nullptr) {
72 return -EINVAL;
73 }
74
75 /*
76 * Check if path exists, if a component of path does not exist,
77 * or path is an empty string return ENOENT
78 * If path is not accessible return EACCES
79 */
80 struct stat sb;
81 if (stat(name.c_str(), &sb) == -1) {
82 return -errno;
83 }
84
85 /* Open sysfs directory */
86 DIR* tmp = opendir(name.c_str());
87 if (tmp == nullptr) return -errno;
88
89 dp->reset(tmp);
90
91 return 0;
92 }
93
94 // TODO(egranata): could this (and _read_ below), infer the fmt string directly
95 // from the type of value being passed in? that would be a safer alternative
96 template <typename T>
sysfs_write_val(const std::string & f,const std::string & fmt,const T value)97 static int sysfs_write_val(const std::string& f, const std::string& fmt, const T value) {
98 FilePtr fp = {fopen(f.c_str(), "r+"), fclose};
99 if (nullptr == fp) return -errno;
100
101 fprintf(fp.get(), fmt.c_str(), value);
102
103 return 0;
104 }
105
sysfs_write_uint(const std::string & file,const unsigned int val)106 static int sysfs_write_uint(const std::string& file, const unsigned int val) {
107 return sysfs_write_val(file, "%u", val);
108 }
109
sysfs_write_double(const std::string & file,const double val)110 static int sysfs_write_double(const std::string& file, const double val) {
111 return sysfs_write_val(file, "%f", val);
112 }
113
114 template <typename T>
sysfs_read_val(const std::string & f,const std::string & fmt,const T * value)115 static int sysfs_read_val(const std::string& f, const std::string& fmt, const T* value) {
116 if (!value) return -EINVAL;
117
118 FilePtr fp = {fopen(f.c_str(), "r"), fclose};
119 if (nullptr == fp) return -errno;
120
121 const int ret = fscanf(fp.get(), fmt.c_str(), value);
122 return (ret == 1) ? 0 : -EINVAL;
123 }
124
sysfs_read_uint8(const std::string & file,uint8_t * val)125 static int sysfs_read_uint8(const std::string& file, uint8_t* val) {
126 return sysfs_read_val(file, "%hhu\n", val);
127 }
128
sysfs_read_float(const std::string & file,float * val)129 static int sysfs_read_float(const std::string& file, float* val) {
130 return sysfs_read_val(file, "%f\n", val);
131 }
132
sysfs_read_str(const std::string & file,std::string * str)133 static int sysfs_read_str(const std::string& file, std::string* str) {
134 std::ifstream infile(file);
135 if (!infile.is_open()) return -EINVAL;
136
137 if (!std::getline(infile, *str))
138 return -EINVAL;
139 else
140 return 0;
141 }
142
check_file(const std::string & filename)143 static int check_file(const std::string& filename) {
144 struct stat info;
145 return stat(filename.c_str(), &info);
146 }
147
enable_sensor(const std::string & device_dir,const bool enable)148 int enable_sensor(const std::string& device_dir, const bool enable) {
149 int err = check_file(device_dir);
150 if (!err) {
151 std::string enable_file = device_dir;
152 enable_file += "/";
153 enable_file += IIO_BUFFER_ENABLE;
154 err = sysfs_write_uint(enable_file, enable);
155 }
156
157 return err;
158 }
159
get_sampling_frequency_available(const std::string & device_dir,std::vector<double> * sfa)160 static int get_sampling_frequency_available(const std::string& device_dir,
161 std::vector<double>* sfa) {
162 int ret = 0;
163 char* rest;
164 std::string line;
165 DirPtr dp(nullptr, closedir);
166 const struct dirent* ent;
167
168 ret = sysfs_opendir(device_dir, &dp);
169 if (ret) return ret;
170 while (ent = readdir(dp.get()), ent != nullptr) {
171 if (str_has_suffix(ent->d_name, IIO_SFA_FILENAME)) {
172 std::string filename = device_dir;
173 filename += "/";
174 filename += ent->d_name;
175 ret = sysfs_read_str(filename, &line);
176 if (ret < 0) return ret;
177 char* pch = strtok_r(const_cast<char*>(line.c_str()), " ,", &rest);
178 while (pch != nullptr) {
179 sfa->push_back(atof(pch));
180 pch = strtok_r(nullptr, " ,", &rest);
181 }
182 }
183 }
184
185 return ret < 0 ? ret : 0;
186 }
187
get_sensor_range(const std::string & device_dir,float * resolution,int64_t * max_range)188 static int get_sensor_range(const std::string& device_dir, float* resolution, int64_t* max_range) {
189 int ret = 0;
190 char* rest;
191 std::string line;
192 DirPtr dp(nullptr, closedir);
193 const struct dirent* ent;
194
195 ret = sysfs_opendir(device_dir, &dp);
196 if (ret) return ret;
197 while (ent = readdir(dp.get()), ent != nullptr) {
198 if (str_has_suffix(ent->d_name, IIO_RANGE_AVAIL_FILENAME)) {
199 std::string filename = device_dir;
200 filename += "/";
201 filename += ent->d_name;
202
203 ret = sysfs_read_str(filename, &line);
204 if (ret < 0) return ret;
205 char* pch = strtok_r(const_cast<char*>(line.c_str()), " ", &rest);
206 std::vector<std::string> range_avail;
207 while (pch != nullptr) {
208 range_avail.push_back(pch);
209 pch = strtok_r(nullptr, " ", &rest);
210 }
211 *resolution = atof(range_avail[1].c_str());
212 *max_range = atoll(range_avail[2].c_str());
213 }
214 }
215
216 return ret < 0 ? ret : 0;
217 }
218
get_sensor_name(const std::string & device_dir,std::string * name)219 static int get_sensor_name(const std::string& device_dir, std::string* name) {
220 const std::string filename = device_dir + "/" + IIO_NAME_FILENAME;
221
222 return sysfs_read_str(filename, name);
223 }
224
set_sampling_frequency(const std::string & device_dir,const double frequency)225 int set_sampling_frequency(const std::string& device_dir, const double frequency) {
226 DirPtr dp(nullptr, closedir);
227 const struct dirent* ent;
228
229 int ret = sysfs_opendir(device_dir, &dp);
230 if (ret) return ret;
231 while (ent = readdir(dp.get()), ent != nullptr) {
232 if (str_has_suffix(ent->d_name, IIO_SAMPLING_FREQUENCY)) {
233 std::string filename = device_dir;
234 filename += "/";
235 filename += ent->d_name;
236 ret = sysfs_write_double(filename, frequency);
237 }
238 }
239 return ret;
240 }
241
get_sensor_scale(const std::string & device_dir,float * scale)242 static int get_sensor_scale(const std::string& device_dir, float* scale) {
243 DirPtr dp(nullptr, closedir);
244 const struct dirent* ent;
245 int err;
246 std::string filename;
247 if (scale == nullptr) {
248 return -EINVAL;
249 }
250 err = sysfs_opendir(device_dir, &dp);
251 if (err) return err;
252 while (ent = readdir(dp.get()), ent != nullptr) {
253 if (str_has_suffix(ent->d_name, IIO_SCALE_FILENAME)) {
254 filename = device_dir;
255 filename += "/";
256 filename += ent->d_name;
257 err = sysfs_read_float(filename, scale);
258 }
259 }
260 return err;
261 }
262
load_iio_devices(std::string iio_dir,std::vector<iio_device_data> * iio_data,DeviceFilterFunction filter)263 int load_iio_devices(std::string iio_dir, std::vector<iio_device_data>* iio_data,
264 DeviceFilterFunction filter) {
265 DirPtr dp(nullptr, closedir);
266 const struct dirent* ent;
267 int err;
268
269 if (!iio_dir.empty() && iio_dir.back() != '/') iio_dir += '/';
270
271 std::ifstream iio_file;
272 const auto iio_base_len = strlen(IIO_DEVICE_BASE);
273 err = sysfs_opendir(iio_dir, &dp);
274 if (err) return err;
275 while (ent = readdir(dp.get()), ent != nullptr) {
276 if (!str_has_prefix(ent->d_name, IIO_DEVICE_BASE)) continue;
277
278 std::string path_device = iio_dir;
279 path_device += ent->d_name;
280
281 iio_device_data iio_dev_data;
282 iio_dev_data.sysfspath.append(path_device, 0, iio_dir.size() + strlen(ent->d_name));
283 err = get_sensor_name(iio_dev_data.sysfspath, &iio_dev_data.name);
284 if (err) {
285 ALOGE("get_sensor_name for %s returned error %d", path_device.c_str(), err);
286 continue;
287 }
288
289 if (!filter(&iio_dev_data)) continue;
290
291 ALOGI("found sensor %s at path %s", iio_dev_data.name.c_str(), path_device.c_str());
292 err = get_sampling_frequency_available(iio_dev_data.sysfspath,
293 &iio_dev_data.sampling_freq_avl);
294 if (err) {
295 ALOGE("get_sampling_frequency_available for %s returned error %d", path_device.c_str(),
296 err);
297 continue;
298 }
299
300 std::sort(iio_dev_data.sampling_freq_avl.begin(), iio_dev_data.sampling_freq_avl.end());
301 err = get_sensor_scale(iio_dev_data.sysfspath, &iio_dev_data.scale);
302 if (err) {
303 ALOGE("get_sensor_scale for %s returned error %d", path_device.c_str(), err);
304 continue;
305 }
306 err = get_sensor_range(iio_dev_data.sysfspath, &iio_dev_data.resolution,
307 &iio_dev_data.max_range);
308 if (err) {
309 ALOGE("get_sensor_range for %s returned error %d", path_device.c_str(), err);
310 continue;
311 }
312
313 sscanf(ent->d_name + iio_base_len, "%hhu", &iio_dev_data.iio_dev_num);
314
315 iio_data->push_back(iio_dev_data);
316 }
317 return err;
318 }
319
get_scan_type(const std::string & device_dir,struct iio_info_channel * chanInfo)320 static int get_scan_type(const std::string& device_dir, struct iio_info_channel* chanInfo) {
321 DirPtr dp(nullptr, closedir);
322 const struct dirent* ent;
323 std::string scan_dir;
324 std::string filename;
325 std::string type_name;
326 char signchar, endianchar;
327 unsigned int storage_bits;
328
329 if (chanInfo == nullptr) {
330 return -EINVAL;
331 }
332 scan_dir = device_dir;
333 scan_dir += "/scan_elements";
334 const int err = sysfs_opendir(scan_dir, &dp);
335 if (err) return err;
336 type_name = chanInfo->name;
337 type_name += "_type";
338 while (ent = readdir(dp.get()), ent != nullptr) {
339 if (strcmp(ent->d_name, type_name.c_str()) == 0) {
340 filename = scan_dir;
341 filename += "/";
342 filename += ent->d_name;
343 FilePtr fp = {fopen(filename.c_str(), "r"), fclose};
344 if (fp == nullptr) continue;
345 const int ret = fscanf(fp.get(), "%ce:%c%hhu/%u>>%hhu", &endianchar, &signchar,
346 &chanInfo->bits_used, &storage_bits, &chanInfo->shift);
347 if (ret < 0) continue;
348 chanInfo->big_endian = (endianchar == 'b');
349 chanInfo->sign = (signchar == 's');
350 chanInfo->storage_bytes = (storage_bits >> 3);
351 }
352 }
353 return 0;
354 }
355
scan_elements(const std::string & device_dir,struct iio_device_data * iio_data)356 int scan_elements(const std::string& device_dir, struct iio_device_data* iio_data) {
357 DirPtr dp(nullptr, closedir);
358 const struct dirent* ent;
359 std::string scan_dir;
360 std::string filename;
361 uint8_t temp;
362 int ret;
363
364 if (iio_data == nullptr) {
365 return -EINVAL;
366 }
367 scan_dir = device_dir;
368 scan_dir += "/scan_elements";
369 ret = sysfs_opendir(scan_dir, &dp);
370 if (ret) return ret;
371 while (ent = readdir(dp.get()), ent != nullptr) {
372 if (str_has_suffix(ent->d_name, IIO_SCAN_ELEMENTS_EN)) {
373 filename = scan_dir;
374 filename += "/";
375 filename += ent->d_name;
376 ret = sysfs_write_uint(filename, ENABLE_CHANNEL);
377 if (ret == 0) {
378 ret = sysfs_read_uint8(filename, &temp);
379 if ((ret == 0) && (temp == 1)) {
380 iio_info_channel chan_info;
381 chan_info.name = strndup(ent->d_name,
382 strlen(ent->d_name) - strlen(IIO_SCAN_ELEMENTS_EN));
383 filename = scan_dir;
384 filename += "/";
385 filename += chan_info.name;
386 filename += "_index";
387 ret = sysfs_read_uint8(filename, &chan_info.index);
388 if (ret) {
389 ALOGE("Getting index for channel %s for sensor %s returned error %d",
390 chan_info.name.c_str(), device_dir.c_str(), ret);
391 return ret;
392 }
393 ret = get_scan_type(device_dir, &chan_info);
394 if (ret) {
395 ALOGE("Getting scan type for channel %s sensor %s returned error %d",
396 chan_info.name.c_str(), device_dir.c_str(), ret);
397 return ret;
398 }
399 iio_data->channelInfo.push_back(chan_info);
400 } else {
401 ALOGE("Not able to successfully enable channel %s for sensor %s error %d",
402 ent->d_name, device_dir.c_str(), ret);
403 return ret;
404 }
405 } else {
406 ALOGE("Enabling scan channel %s for sensor %s returned error %d", ent->d_name,
407 device_dir.c_str(), ret);
408 return ret;
409 }
410 }
411 }
412 return ret;
413 }
414 } // namespace implementation
415 } // namespace subhal
416 } // namespace V2_1
417 } // namespace sensors
418 } // namespace hardware
419 } // namespace android
420