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