aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Chris Xiong <chirs241097@gmail.com> 2018-04-09 22:42:45 +0800
committerGravatar Chris Xiong <chirs241097@gmail.com> 2018-04-09 22:42:45 +0800
commit6bdb9028af9a5256fbb47b942843e49aef7e3aa1 (patch)
treea2346e35e36a3fcb016cb7ab65bcbcb84dede1bf
downloadlightsd-6bdb9028af9a5256fbb47b942843e49aef7e3aa1.tar.xz
Initial commit.
-rw-r--r--CMakeLists.txt9
-rw-r--r--COPYING28
-rw-r--r--ChangeLog3
-rw-r--r--README.md37
-rw-r--r--brightness_ctrl.cpp106
-rw-r--r--brightness_ctrl.hpp35
-rw-r--r--lightsd.conf34
-rw-r--r--main.cpp163
-rw-r--r--sensor_als.cpp11
-rw-r--r--sensor_als.hpp14
-rw-r--r--sensors.cpp181
-rw-r--r--sensors.hpp58
-rw-r--r--utils.cpp65
-rw-r--r--utils.hpp12
14 files changed, 756 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..e8733c0
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,9 @@
+cmake_minimum_required(VERSION 2.6)
+project(lightsd)
+set(SOURCES brightness_ctrl.cpp sensor_als.cpp sensors.cpp utils.cpp main.cpp)
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDART_REQUIRED ON)
+find_package(Threads REQUIRED)
+add_executable(${PROJECT_NAME} ${SOURCES})
+target_link_libraries(${PROJECT_NAME} stdc++fs)
+target_link_libraries(${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT})
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..eee20b4
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,28 @@
+Copyright 2018 Chris Xiong
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..3d69246
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,3 @@
+2018-04-09 0.0.1
+First public version.
+Almost feature complete. Only works for me though.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..facdd2d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,37 @@
+# lightsd
+
+`lightsd` is a small daemon to make your ~~(actually, my)~~ ambient
+light sensor on your laptop useful in a Linux <sup>interjection</sup>
+desktop without using a full desktop environment (or `systemd`).
+
+This service watches the readings from the ambient light sensor and
+control the backlight of the screen and keyboard. It also creates a
+fifo so that you can adjust relative brightness of the lcd.
+
+The project also demostrates how damn stupid a C++ program could look like.
+
+# Warning
+WIP. Does not yet do any kind of input sanitation. May segmentation fault
+at any time. The author uses Gentoo. _Very_ shitty code.
+
+AS A DAEMON, IT ONLY RUNS AS ROOT!
+
+# Building
+Building _requires_ C++17. Just `mkdir build && cd build && cmake .. && make`.
+
+# Documentation
+None. The code documentes itself.
+
+## fifo usage
+- `u <x,0<x<=100>`
+Makes lcd x% brighter.
+- `d <x,0<x<=100>`
+Makes lcd x% darker.
+- `s <x,-100<=x<=100>`
+Set relative brightness of lcd.
+- `r`
+Reset relative brightness of lcd, equivalent to `s 0`.
+
+The fifo is owned by `root:video` and has permission `0620` so that
+everyone in the video group could potentially mess with your brightness.
+Surprise!
diff --git a/brightness_ctrl.cpp b/brightness_ctrl.cpp
new file mode 100644
index 0000000..3a57c0d
--- /dev/null
+++ b/brightness_ctrl.cpp
@@ -0,0 +1,106 @@
+#include "brightness_ctrl.hpp"
+#include "utils.hpp"
+#include <cmath>
+#include <algorithm>
+#define log10_n(x) ((x)<1?0:log10(x))
+void BrightnessControl::_brightness_slide(int p)
+{
+ p+=offset;
+ if(p>100)p=100;
+ if(p<0)p=0;
+ int pbr=maxbr*p/100;
+ printf("brightness adjust: %d->%d/%d\n",br,pbr,maxbr);
+ int d=1;if(pbr<br)d=-1;double dd=1;
+ while(d>0&&br+round(d*dd)<=pbr||d<0&&br+round(d*dd)>=pbr)
+ {
+ br+=(int)round(d*dd);writeint(brpath.c_str(),br);
+ dd=dd*1.2;std::this_thread::sleep_for(std::chrono::milliseconds(10));
+ }
+ br=pbr;writeint(brpath.c_str(),br);
+}
+
+void BrightnessControl::init(float initv,SensorALS *s)
+{
+ cur=std::upper_bound(thresh.begin(),thresh.end(),(int)roundf(initv))-thresh.begin();
+ als=s;set_offset(0,0);
+}
+void BrightnessControl::set_path(filesystem::path p)
+{
+ cpath=p;
+ brpath=cpath/"brightness";
+ maxbrpath=cpath/"max_brightness";
+ maxbr=readint(maxbrpath.c_str());
+ br=readint(brpath.c_str());
+}
+void BrightnessControl::set_thresh(std::vector<int> _th){thresh=_th;}
+void BrightnessControl::set_value(std::vector<int> _v){value=_v;}
+void BrightnessControl::set_delay(int _d){delay=_d;}
+void BrightnessControl::set_trigrange(int _tr){tr=_tr;}
+
+void BrightnessControl::set_offset(int rel,int off)
+{
+ if(rel)offset+=rel*off;else offset=off;
+ if(offset>100)offset=100;
+ if(offset<-100)offset=-100;
+ brightness_slide(value[cur]);
+}
+
+void BrightnessControl::on_sensor_report(float v)
+{
+ int lb=cur>0?thresh[cur-1]:0;
+ int ub=cur<thresh.size()?thresh[cur]:~0U>>1;
+ if(v<lb-log10_n(lb)*tr||v>ub+log10_n(ub)*tr)
+ {
+ if(direction!=(v>ub))
+ {
+ {
+ std::lock_guard<std::mutex> lck(interrupt_m);
+ direction=(v>ub);
+ }
+ interrupt.notify_one();
+ std::this_thread::yield();
+ }
+ {
+ std::lock_guard<std::mutex> lcd(threshnotify_m);
+ //nothing to do within this lock
+ }
+ threshnotify.notify_one();
+ std::this_thread::yield();
+ }
+}
+void BrightnessControl::brightness_slide(int p)
+{
+ std::thread brth(&BrightnessControl::_brightness_slide,std::ref(*this),p);
+ brth.detach();
+}
+void BrightnessControl::worker()
+{
+ while(1)
+ {
+ std::unique_lock<std::mutex>lock_thresh(threshnotify_m);
+ threshnotify.wait(lock_thresh);
+ lock_thresh.unlock();
+ std::cv_status intr;
+ do{
+ std::unique_lock<std::mutex>lock_interrupt(interrupt_m);
+ intr=interrupt.wait_for(lock_interrupt,std::chrono::seconds(delay));
+ lock_interrupt.unlock();
+ }while(intr==std::cv_status::no_timeout);
+ //carry out brightness adjustment here
+ float val=als->get_value();
+ int lb=cur>0?thresh[cur-1]:0;
+ int ub=cur<thresh.size()?thresh[cur]:~0U>>1;
+ while(val>ub)
+ {
+ ++cur;lb=thresh[cur-1];
+ ub=cur<thresh.size()?thresh[cur]:~0U>>1;
+ }
+ while(val<lb)
+ {
+ --cur;lb=cur>0?thresh[cur-1]:0;
+ ub=thresh[cur];
+ }
+ printf("%f lx\n",val);
+ brightness_slide(value[cur]);
+ }
+}
diff --git a/brightness_ctrl.hpp b/brightness_ctrl.hpp
new file mode 100644
index 0000000..4a94e0b
--- /dev/null
+++ b/brightness_ctrl.hpp
@@ -0,0 +1,35 @@
+#ifndef BRIGHTNESS_CTRL_HPP
+#define BRIGHTNESS_CTRL_HPP
+#include <chrono>
+#include <condition_variable>
+#include <experimental/filesystem>
+#include <thread>
+#include <vector>
+#include "sensor_als.hpp"
+namespace filesystem=std::experimental::filesystem::v1;
+class BrightnessControl
+{
+private:
+ filesystem::path cpath,brpath,maxbrpath;
+ std::vector<int> thresh,value;
+ int delay,direction,br,maxbr,tr,offset;
+ size_t cur;
+ SensorALS *als;
+ std::mutex interrupt_m,threshnotify_m;
+ std::condition_variable interrupt,threshnotify;
+ void _brightness_slide(int p);
+public:
+ void init(float initv,SensorALS *s);
+ void set_path(filesystem::path p);
+ void set_thresh(std::vector<int> _th);
+ void set_value(std::vector<int> _v);
+ void set_delay(int _d);
+ void set_trigrange(int _tr);
+
+ void set_offset(int rel,int off);
+
+ void on_sensor_report(float v);
+ void brightness_slide(int p);
+ void worker();
+};
+#endif
diff --git a/lightsd.conf b/lightsd.conf
new file mode 100644
index 0000000..ca47344
--- /dev/null
+++ b/lightsd.conf
@@ -0,0 +1,34 @@
+#example configuration for lightsd
+
+#path to backlight control in sysfs
+#expected files are max_brightness and brightness
+lcd_backlight_control=/sys/class/backlight/intel_backlight
+
+#path to keyboard backlight control in sysfs
+#expected files are max_brightness and brightness
+kbd_backlight_control=/sys/bus/platform/devices/thinkpad_acpi/leds/tpacpi::kbd_backlight
+
+#list of (*increasing* _integer_) threshold values from the ambient light sensor, separated by commas
+lcd_backlight_thresholds=10,20,50,100,500,2000,10000
+
+#list of (_integer_) brightnesses (percentage) corresponding to the thresholds
+lcd_backlight_values=3,5,10,15,30,60,80,100
+
+#seconds before a brightness change really takes place
+#(any als reading change crossing a threshold resets the timer)
+lcd_backlight_control_delay=3
+
+#don't trigger a brightness change until sensor reading away from
+#the threshold exceeds this value
+#this is a relative value, multiplied by logarithm of the compared
+#threshold before using
+lcd_backlight_trigger_range=2
+
+#same as their lcd conterpart
+kbd_backlight_thresholds=100
+kbd_backlight_values=50,0
+kbd_backlight_control_delay=5
+kbd_backlight_trigger_range=2
+
+#where to create the fifo for remote control
+command_fifo_path=/tmp/lightsd.cmd.fifo
diff --git a/main.cpp b/main.cpp
new file mode 100644
index 0000000..df3af34
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,163 @@
+#include <cstdio>
+#include <cstdlib>
+#include <cmath>
+#include <algorithm>
+#include <condition_variable>
+#include <chrono>
+#include <functional>
+#include <mutex>
+#include <thread>
+#include <vector>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "utils.hpp"
+#include "sensor_als.hpp"
+#include "brightness_ctrl.hpp"
+SensorALS als;
+int als_id;
+
+filesystem::path fifo_path;
+BrightnessControl lcd,kbd;
+FILE *fifo_f;
+
+void als_callback(SensorBase* _s)
+{
+ SensorALS* s=(SensorALS*)_s;
+ float val=s->get_value();
+ lcd.on_sensor_report(val);
+ kbd.on_sensor_report(val);
+}
+void load_config()
+{
+ fifo_path="/tmp/lightsd.cmd.fifo";
+ FILE* cfgf;
+ cfgf=fopen("/etc/lightsd.conf","r");
+ if(!cfgf)
+ cfgf=fopen("lightsd.conf","r");
+ if(!cfgf){LOG('W',"Configuration file not found.",0);return;}
+ char* buf=new char[1024];
+ while(!feof(cfgf))
+ {
+ fgets(buf,1024,cfgf);
+ if(buf[0]=='#')continue;
+ std::string sb=trim(buf);
+ std::vector<std::string> sv;
+ split(sb,'=',sv);
+ if(sv.size()!=2)continue;
+ if(sv[0]=="lcd_backlight_control")lcd.set_path(sv[1]);
+ if(sv[0]=="kbd_backlight_control")kbd.set_path(sv[1]);
+ if(sv[0]=="lcd_backlight_thresholds")
+ {
+ std::vector<std::string> vals;
+ split(sv[1],',',vals);
+ std::vector<int> t;
+ for(auto&i:vals)t.push_back(atoi(i.c_str()));
+ lcd.set_thresh(t);
+ }
+ if(sv[0]=="lcd_backlight_values")
+ {
+ std::vector<std::string> vals;
+ split(sv[1],',',vals);
+ std::vector<int> t;
+ for(auto&i:vals)t.push_back(atoi(i.c_str()));
+ lcd.set_value(t);
+ }
+ if(sv[0]=="lcd_backlight_control_delay")lcd.set_delay(atoi(sv[1].c_str()));
+ if(sv[0]=="lcd_backlight_trigger_range")lcd.set_trigrange(atoi(sv[1].c_str()));
+ if(sv[0]=="kbd_backlight_thresholds")
+ {
+ std::vector<std::string> vals;
+ split(sv[1],',',vals);
+ std::vector<int> t;
+ for(auto&i:vals)t.push_back(atoi(i.c_str()));
+ kbd.set_thresh(t);
+ }
+ if(sv[0]=="kbd_backlight_values")
+ {
+ std::vector<std::string> vals;
+ split(sv[1],',',vals);
+ std::vector<int> t;
+ for(auto&i:vals)t.push_back(atoi(i.c_str()));
+ kbd.set_value(t);
+ }
+ if(sv[0]=="kbd_backlight_control_delay")kbd.set_delay(atoi(sv[1].c_str()));
+ if(sv[0]=="kbd_backlight_trigger_range")kbd.set_trigrange(atoi(sv[1].c_str()));
+ if(sv[0]=="command_fifo_path")fifo_path=sv[1];
+ }
+ delete[] buf;
+}
+int get_gid(std::string group)
+{
+ FILE* grpf=fopen("/etc/group","r");
+ char* buf=new char[1024];
+ while(!feof(grpf))
+ {
+ fgets(buf,1024,grpf);
+ std::vector<std::string> sv;
+ split(buf,':',sv);
+ if(sv[0]==group)
+ {
+ fclose(grpf);
+ delete[] buf;
+ return atoi(sv[2].c_str());
+ }
+ }
+ fclose(grpf);
+ delete[] buf;
+ return -1;
+}
+void setup_fifo()
+{
+ if(!fifo_path.string().length())return;
+ unlink(fifo_path.c_str());
+ mkfifo(fifo_path.c_str(),0620);
+ chown(fifo_path.c_str(),0,get_gid("video"));
+ chmod(fifo_path.c_str(),0620);
+}
+void command_thread()
+{
+ fifo_f=fopen(fifo_path.c_str(),"r");
+ char cmdbuf[256];
+ while(1)
+ {
+ fgets(cmdbuf,256,fifo_f);
+ printf("got command: ");
+ puts(trim(cmdbuf).c_str());
+ std::vector<std::string> cav;
+ split(trim(cmdbuf),' ',cav);
+ if(cav.size()>=1)
+ {
+ if(cav[0]=="u")if(cav.size()>1)lcd.set_offset(1,atoi(cav[1].c_str()));
+ if(cav[0]=="d")if(cav.size()>1)lcd.set_offset(-1,atoi(cav[1].c_str()));
+ if(cav[0]=="s")if(cav.size()>1)lcd.set_offset(0,atoi(cav[1].c_str()));
+ if(cav[0]=="r")lcd.set_offset(0,0);
+ }
+ fclose(fifo_f);
+ fifo_f=fopen(fifo_path.c_str(),"r");
+ }
+}
+void sigterm_handler(int)
+{
+ exit(0);
+}
+int main()
+{
+ signal(SIGTERM,sigterm_handler);
+ als_id=SensorBase::detect_sensor("als");
+ if(!~als_id)return puts("No ALS found!"),1;
+ als.init(als_id,"in_intensity");
+ als.set_reader_callback(als_callback);
+ float init_val=als.get_value();
+ printf("initial value: %f lx\n",init_val);
+ load_config();
+ setup_fifo();
+ lcd.init(init_val,&als);
+ kbd.init(init_val,&als);
+ std::thread lcd_thread(&BrightnessControl::worker,std::ref(lcd));
+ std::thread kbd_thread(&BrightnessControl::worker,std::ref(kbd));
+ std::thread cmd_thread(command_thread);
+ als.worker();
+ return 0;
+}
diff --git a/sensor_als.cpp b/sensor_als.cpp
new file mode 100644
index 0000000..399f2a1
--- /dev/null
+++ b/sensor_als.cpp
@@ -0,0 +1,11 @@
+#include "sensor_als.hpp"
+void SensorALS::enable_scan_elements()
+{
+ enable_scan_element("both");
+}
+void SensorALS::update_values()
+{
+ value=std::any_cast<int>(dict["in_intensity_both_value"])*
+ std::any_cast<float>(dict["in_intensity_scale"]);
+}
+float SensorALS::get_value(){return value;}
diff --git a/sensor_als.hpp b/sensor_als.hpp
new file mode 100644
index 0000000..0548545
--- /dev/null
+++ b/sensor_als.hpp
@@ -0,0 +1,14 @@
+#ifndef SENSOR_ALS_HPP
+#define SENSOR_ALS_HPP
+#include "sensors.hpp"
+class SensorALS:public SensorBase
+{
+ private:
+ float value;
+ protected:
+ void enable_scan_elements();
+ public:
+ void update_values();
+ float get_value();
+};
+#endif
diff --git a/sensors.cpp b/sensors.cpp
new file mode 100644
index 0000000..8817528
--- /dev/null
+++ b/sensors.cpp
@@ -0,0 +1,181 @@
+#include <cstdio>
+#include <cstring>
+#include <variant>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "utils.hpp"
+#include "sensors.hpp"
+void SensorBase::parse_type_string(std::string type,scan_t* ti)
+{
+ char endian[4],sign;
+ if(type.find('X')!=type.npos)
+ sscanf(type.c_str(),"%2s:%c%hhu/%hhuX%hhu>>%hhu",
+ endian,&sign,&ti->bits,&ti->storagebits,&ti->repeat,&ti->shift
+ );
+ else
+ {
+ ti->repeat=0;
+ sscanf(type.c_str(),"%2s:%c%hhu/%hhu>>%hhu",
+ endian,&sign,&ti->bits,&ti->storagebits,&ti->shift
+ );
+ }
+ ti->is_le=std::string(endian)=="le";
+ ti->is_signed=sign=='s';
+}
+void SensorBase::readbuffer()
+{
+ char* buf=new char[readsize];
+ ssize_t sz=read(devfd,buf,readsize);
+ if(sz==readsize)
+ {
+ char *p=buf;
+ for(int i=0;p-buf<readsize;++i)
+ {
+ scan_t ti=std::get<scan_t>(enabled_scan_elem[i]);
+ std::string es=std::get<std::string>(enabled_scan_elem[i])+"_value";
+ if(ti.is_le)
+ switch(ti.storagebits)
+ {
+ case 8:
+ if(ti.is_signed)
+ {int8_t t;memcpy(&t,p,1);t>>=ti.shift;dict[es]=t;}
+ else
+ {uint8_t t;memcpy(&t,p,1);t>>=ti.shift;dict[es]=t;}
+ ++p;
+ break;
+ case 16:
+ if(ti.is_signed)
+ {int16_t t;memcpy(&t,p,2);t>>=ti.shift;dict[es]=t;}
+ else
+ {uint16_t t;memcpy(&t,p,2);t>>=ti.shift;dict[es]=t;}
+ p+=2;
+ break;
+ case 32:
+ if(ti.is_signed)
+ {int32_t t;memcpy(&t,p,4);t>>=ti.shift;dict[es]=t;}
+ else
+ {uint32_t t;memcpy(&t,p,4);t>>=ti.shift;dict[es]=t;}
+ p+=4;
+ break;
+ case 64:
+ if(ti.is_signed)
+ {int64_t t;memcpy(&t,p,8);t>>=ti.shift;dict[es]=t;}
+ else
+ {uint64_t t;memcpy(&t,p,8);t>>=ti.shift;dict[es]=t;}
+ p+=8;
+ break;
+ }
+ }
+ }
+ delete[] buf;
+}
+void SensorBase::enable_buffer()
+{
+ using filesystem::path;
+
+ path buffer_enable_path=sysfspath/"buffer"/"enable";
+ writeint(buffer_enable_path.c_str(),1);
+}
+void SensorBase::enable_scan_element(std::string elem)
+{
+ using filesystem::path;
+ std::string elem_base=sensor_basename+(elem.length()?"_"+elem:"");
+
+ path elem_type_path=sysfspath/"scan_elements"/(elem_base+"_type");
+ std::string ts;dict[elem_base+"_type"]=ts=readstr(elem_type_path.c_str());
+ scan_t st;parse_type_string(ts,&st);
+ readsize+=st.storagebits/8;//assume this shit is aligned to byte
+
+ path elem_en_path=sysfspath/"scan_elements"/(elem_base+"_en");
+ writeint(elem_en_path.c_str(),1);
+
+ path elem_idx_path=sysfspath/"scan_elements"/(elem_base+"_index");
+ int idx;dict[elem_base+"_index"]=idx=readint(elem_idx_path.c_str());
+
+ path raw_val_path=sysfspath/(elem_base+"_raw");//initial value
+ dict[elem_base+"_value"]=readint(raw_val_path.c_str());
+
+ enabled_scan_elem.insert(
+ std::upper_bound(enabled_scan_elem.begin(),enabled_scan_elem.end(),
+ std::make_tuple(idx,elem_base,st),
+ [](const auto&a,const auto&b)->bool{return std::get<0>(a)<std::get<0>(b);}
+ ),std::make_tuple(idx,elem_base,st)
+ );
+}
+void SensorBase::init(int id,std::string _sensor_basename)
+{
+ sysfspath=IIODEV_SYSFS_PATH_BASE+std::to_string(id);
+ devbufpath=DEV_PATH+std::to_string(id);
+ sensor_basename=_sensor_basename;
+
+ using filesystem::path;
+ path name_path=sysfspath/"name";
+ type=readstr(name_path.c_str());
+ path scale_path=sysfspath/(sensor_basename+"_scale");
+ dict[sensor_basename+"_scale"]=readfloat(scale_path.c_str());
+ path offset_path=sysfspath/(sensor_basename+"_offset");
+ dict[sensor_basename+"_offset"]=readfloat(offset_path.c_str());
+
+ readsize=0;
+ enabled_scan_elem.clear();
+ enable_scan_elements();
+ update_values();
+ enable_buffer();
+ devfd=open(devbufpath.c_str(),O_RDONLY);
+ if(!~devfd)LOG('E',"failed to open the iio buffer device: %s",devbufpath.c_str());
+}
+void SensorBase::deinit()
+{
+ if(~devfd)close(devfd);
+ devfd=-1;
+}
+void SensorBase::reset()
+{
+ deinit();
+ using filesystem::path;
+
+ path buffer_enable_path=sysfspath/"buffer"/"enable";
+ writeint(buffer_enable_path.c_str(),0);
+ for(auto& ent:filesystem::directory_iterator(sysfspath/"scan_elements"))
+ if(ent.path().string().substr(ent.path().string().length()-3)=="_en")
+ writeint(ent.path().c_str(),0);
+}
+void SensorBase::worker()
+{
+ for(workerquit=0;!workerquit;)
+ {
+ readbuffer();update_values();
+ if(readercb!=nullptr)readercb(this);
+ }
+}
+void SensorBase::quit_worker()
+{workerquit=1;}
+void SensorBase::set_reader_callback(std::function<void(SensorBase*)> cb)
+{readercb=cb;}
+std::string SensorBase::get_type(int id)
+{
+ using filesystem::path;
+ path sysfspath=path(IIODEV_SYSFS_PATH_BASE+std::to_string(id));
+
+ path name_path=sysfspath/"name";
+ return readstr(name_path.c_str());
+}
+int SensorBase::detect_sensor(std::string type)
+{
+ using filesystem::path;
+ path sysfsbasepath=path(IIODEV_SYSFS_PATH_BASE).remove_filename();
+ for(auto& ent:filesystem::directory_iterator(sysfsbasepath))
+ {
+ path name_path=ent.path()/"name";
+ if(trim(readstr(name_path.c_str()))==type)
+ {
+ std::string es=ent.path().filename().string();
+ size_t i;
+ for(i=0;i<es.length()&&(es[i]<'0'||es[i]>'9');++i);
+ if(i<es.length())return atoi(es.c_str()+i);
+ return -1;
+ }
+ }
+ return -1;
+}
diff --git a/sensors.hpp b/sensors.hpp
new file mode 100644
index 0000000..7d7da31
--- /dev/null
+++ b/sensors.hpp
@@ -0,0 +1,58 @@
+#ifndef SENSORS_HPP
+#define SENSORS_HPP
+#include <cstdint>
+#include <string>
+#include <any>
+#include <experimental/filesystem>
+#include <functional>
+#include <unordered_map>
+#include <tuple>
+#include <vector>
+
+#define IIODEV_SYSFS_PATH_BASE "/sys/bus/iio/devices/iio:device"
+#define DEV_PATH "/dev/iio:device"
+
+namespace filesystem=std::experimental::filesystem::v1;
+
+struct scan_t
+{
+ bool is_le;
+ bool is_signed;
+ uint8_t bits,storagebits;
+ uint8_t shift,repeat;
+};
+
+class SensorBase
+{
+ private:
+ int devfd;
+ int workerquit;
+ int readsize;
+ std::function<void(SensorBase*)> readercb;
+ std::vector<std::tuple<int,std::string,scan_t>> enabled_scan_elem;
+
+ void enable_buffer();
+ void parse_type_string(std::string type,scan_t* ti);
+ void readbuffer();
+ protected:
+ std::string type,sensor_basename;
+ filesystem::path devbufpath;
+ filesystem::path sysfspath;
+ std::unordered_map<std::string,std::any> dict;
+
+ void enable_scan_element(std::string elem);
+
+ virtual void enable_scan_elements()=0;
+ virtual void update_values()=0;
+ public:
+ virtual ~SensorBase(){}
+ void init(int id,std::string _sensor_basename);
+ void deinit();
+ void reset();
+ void worker();
+ void quit_worker();
+ void set_reader_callback(std::function<void(SensorBase*)> cb);
+ static std::string get_type(int id);
+ static int detect_sensor(std::string type);
+};
+#endif
diff --git a/utils.cpp b/utils.cpp
new file mode 100644
index 0000000..8dc5f5a
--- /dev/null
+++ b/utils.cpp
@@ -0,0 +1,65 @@
+#include <cstdio>
+#include <cstring>
+#include <cerrno>
+#include <cctype>
+#include "utils.hpp"
+int readint(const char* path)
+{
+ FILE* f=fopen(path,"r");
+ if(!f)return LOG('W',"failed to open %s for reading: %d",path,errno),0;
+ char buf[16];
+ fgets(buf,16,f);
+ buf[15]=0;
+ fclose(f);
+ return atoi(buf);
+}
+float readfloat(const char* path)
+{
+ FILE* f=fopen(path,"r");
+ if(!f)return LOG('W',"failed to open %s for reading: %d",path,errno),0;
+ char buf[16];
+ fgets(buf,16,f);
+ buf[15]=0;
+ fclose(f);
+ return atof(buf);
+}
+std::string readstr(const char* path)
+{
+ FILE* f=fopen(path,"r");
+ if(!f)return LOG('W',"failed to open %s for reading: %d",path,errno),"";
+ char buf[256];
+ fgets(buf,256,f);
+ buf[255]=0;
+ fclose(f);
+ return std::string(buf);
+}
+void writeint(const char* path,int v)
+{
+ FILE* f=fopen(path,"w");
+ if(!f){LOG('W',"failed to open %s for writing",path);return;}
+ fprintf(f,"%d",v);
+ fclose(f);
+}
+std::string trim(std::string s)
+{
+ size_t st=0;
+ for(;st<s.length()&&isspace(s[st]);++st);
+ if(st==s.length())return "";
+ s=s.substr(st);
+ while(s.length()&&isspace(s.back()))s.pop_back();
+ return s;
+}
+void split(std::string s,char c,std::vector<std::string>& v)
+{
+ v.clear();
+ for(size_t anch=0;;)
+ {
+ std::string sec;
+ if(s.find(c,anch)==std::string::npos)
+ sec=s.substr(anch);
+ else sec=s.substr(anch,s.find(c,anch)-anch);
+ v.push_back(sec);
+ if(s.find(c,anch)==std::string::npos)break;
+ anch=s.find(c,anch)+1;
+ }
+}
diff --git a/utils.hpp b/utils.hpp
new file mode 100644
index 0000000..055201d
--- /dev/null
+++ b/utils.hpp
@@ -0,0 +1,12 @@
+#ifndef UTILS_HPP
+#define UTILS_HPP
+#include <string>
+#include <vector>
+#define LOG(type,format,...) fprintf(stderr,"%c: " format "\n",type,__VA_ARGS__)
+extern int readint(const char* path);
+extern float readfloat(const char* path);
+extern std::string readstr(const char* path);
+extern void writeint(const char* path,int v);
+extern std::string trim(std::string s);
+extern void split(std::string s,char c,std::vector<std::string>& v);
+#endif