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