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