• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wirenboard / wb-mqtt-gpio / 93

31 Jul 2026 10:43AM UTC coverage: 44.281% (+4.5%) from 39.772%
93

push

github

web-flow
Use exported vars instead of MAKEFLAGS for coverage options (#82)

493 of 1086 branches covered (45.4%)

875 of 1976 relevant lines covered (44.28%)

3.96 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/utils.cpp
1
#include "utils.h"
2
#include "exceptions.h"
3
#include "gpio_chip.h"
4
#include "gpio_line.h"
5
#include "log.h"
6

7
#include <wblib/utils.h>
8

9
#include <cassert>
10
#include <cstring>
11
#include <dirent.h>
12
#include <fstream>
13
#include <set>
14
#include <unordered_map>
15

16
#define LOG(logger) ::logger.Log() << "[utils] "
17

18
using namespace std;
19

20
namespace Utils
21
{
22
    namespace
23
    {
24
        int chip_filter(const dirent* dir)
×
25
        {
26
            return string(dir->d_name).find("gpiochip") == 0;
×
27
        }
28

29
        vector<pair<uint32_t, uint32_t>> EnumerateGpioChipsSorted()
×
30
        {
31
            static const auto sysfsBase = string("/sys/class/gpio/");
×
32

33
            dirent** dirs;
34
            auto chipCount = scandir(sysfsBase.c_str(), &dirs, chip_filter, alphasort);
×
35

36
            if (chipCount < 0) {
×
37
                wb_throw(TGpioDriverException, "scandir of '" + sysfsBase + "' failed: " + strerror(errno));
×
38
            }
39

40
            vector<pair<uint32_t, uint32_t>> chipPaths;
×
41

42
            chipPaths.reserve(chipCount);
×
43

44
            for (size_t i = 0; i < static_cast<size_t>(chipCount); ++i) {
×
45
                const auto dirName = dirs[i]->d_name;
×
46

47
                uint32_t base;
48
                {
49
                    auto baseFileName = sysfsBase + dirName + "/base";
×
50
                    ifstream baseFile(baseFileName);
×
51

52
                    if (!baseFile.is_open()) {
×
53
                        wb_throw(TGpioDriverException, "unable to read base of gpio chip (" + baseFileName + ")");
×
54
                    }
55

56
                    baseFile >> base;
×
57
                }
×
58

59
                uint32_t number;
60
                {
61
                    dirent** devDirs;
62
                    auto deviceDir = sysfsBase + dirName + "/device";
×
63
                    auto retVal = scandir(deviceDir.c_str(), &devDirs, chip_filter, alphasort);
×
64
                    if (retVal < 0) {
×
65
                        wb_throw(TGpioDriverException, "scandir of '" + deviceDir + "' failed: " + strerror(errno));
×
66
                    } else if (retVal != 1) {
×
67
                        wb_throw(TGpioDriverException,
×
68
                                 "too many gpiochips in '" + deviceDir + "': " + to_string(retVal) +
69
                                     "; expected only one");
70
                    }
71
                    number = GpioPathToChipNumber("/dev/" + string(devDirs[0]->d_name));
×
72
                }
×
73

74
                chipPaths.push_back({number, base});
×
75
            }
76

77
            return chipPaths;
×
78
        }
×
79

80
        struct TAbstractRange
81
        {
82
            virtual ~TAbstractRange() = default;
×
83
        };
84

85
        using PAbstractRange = shared_ptr<TAbstractRange>;
86

87
        struct TChipRange: virtual TAbstractRange
88
        {
89
            uint32_t ChipNumber;
90

91
            TChipRange(uint32_t chipNumber): ChipNumber(chipNumber)
×
92
            {}
×
93

94
            TChipRange(const PGpioChip& chip): ChipNumber(chip->GetNumber())
×
95
            {}
×
96
        };
97

98
        struct TGpioRange: virtual TAbstractRange
99
        {
100
            uint32_t Begin, End;
101

102
            TGpioRange(uint32_t begin, uint32_t end): Begin(begin), End(end)
×
103
            {}
×
104

105
            bool InRange(uint32_t gpio) const
106
            {
107
                return Begin <= gpio && gpio < End;
108
            }
109
        };
110

111
        struct TRange: TChipRange, TGpioRange
112
        {
113
            TRange(const PGpioChip& chip, uint32_t positionOffset)
×
114
                : TChipRange(chip->GetNumber()),
×
115
                  TGpioRange(positionOffset, positionOffset + chip->GetLineCount())
×
116
            {}
×
117
        };
118

119
        struct TRangeCompare
120
        {
121
            bool operator()(const PAbstractRange& a, const PAbstractRange& b) const
×
122
            {
123
                if (auto rangeA = dynamic_pointer_cast<TRange>(a)) {
×
124
                    if (auto rangeB = dynamic_pointer_cast<TGpioRange>(b)) {
×
125
                        return rangeA->End <= rangeB->Begin;
×
126
                    }
×
127
                    if (auto rangeB = dynamic_pointer_cast<TChipRange>(b)) {
×
128
                        return rangeA->ChipNumber < rangeB->ChipNumber;
×
129
                    }
×
130
                } else if (auto rangeB = dynamic_pointer_cast<TRange>(b)) {
×
131
                    if (auto rangeA = dynamic_pointer_cast<TGpioRange>(b)) {
×
132
                        return rangeA->End <= rangeB->Begin;
×
133
                    }
×
134
                    if (auto rangeA = dynamic_pointer_cast<TChipRange>(b)) {
×
135
                        return rangeA->ChipNumber < rangeB->ChipNumber;
×
136
                    }
×
137
                }
×
138

139
                assert(false);
×
140

141
                LOG(Error) << "bug: invariant 1 violation";
142

143
                return false;
144
            }
145
        };
146

147
        set<PAbstractRange, TRangeCompare> ChipSet;
148
        unordered_map<string, uint32_t> ChipLabelToNumber;
149
        bool MappingsAreReady = false, MappingsWereCleared = false;
150

151
        void EnsureMappingsAreReady()
×
152
        {
153
            assert(!MappingsWereCleared);
×
154

155
            if (MappingsAreReady) {
×
156
                return;
×
157
            }
158

159
            for (const auto& numberBase: EnumerateGpioChipsSorted()) {
×
160
                auto number = numberBase.first;
×
161
                auto base = numberBase.second;
×
162

163
                const auto& chip = make_shared<TGpioChip>(GpioChipNumberToPath(number));
×
164

165
                ChipSet.insert(make_shared<TRange>(chip, base));
×
166
                ChipLabelToNumber[chip->GetLabel()] = chip->GetNumber();
×
167
            }
×
168

169
            MappingsAreReady = true;
×
170
        }
171
    } // namespace
172

173
    string GpioChipNumberToPath(uint32_t number)
×
174
    {
175
        return "/dev/gpiochip" + to_string(number);
×
176
    }
177

178
    uint32_t GpioPathToChipNumber(const string& path)
×
179
    {
180
        return stoul(path.substr(13));
×
181
    }
182

183
    uint32_t GpioChipLabelToNumber(const string& label)
×
184
    {
185
        EnsureMappingsAreReady();
×
186

187
        auto itLabelNumber = ChipLabelToNumber.find(label);
×
188
        if (itLabelNumber == ChipLabelToNumber.end()) {
×
189
            wb_throw(TGpioDriverException, "unable to find chip with label '" + label + "'");
×
190
        }
191
        return itLabelNumber->second;
×
192
    }
193

194
    uint32_t ToSysfsGpio(const PGpioLine& line)
×
195
    {
196
        EnsureMappingsAreReady();
×
197

198
        auto itRange = ChipSet.find(make_shared<TChipRange>(line->AccessChip()));
×
199

200
        if (itRange == ChipSet.end()) {
×
201
            wb_throw(TGpioDriverException, "unable to convert to sysfs " + line->DescribeShort() + ": out of range");
×
202
        }
203

204
        auto pRange = dynamic_pointer_cast<TRange>(*itRange);
×
205

206
        assert(pRange);
×
207

208
        uint32_t gpio = pRange->Begin + line->GetOffset();
×
209

210
        LOG(Debug) << line->DescribeShort() << " => sysfs GPIO number " << gpio;
×
211

212
        return gpio;
×
213
    }
×
214

215
    pair<uint32_t, uint32_t> FromSysfsGpio(uint32_t gpio)
×
216
    {
217
        EnsureMappingsAreReady();
×
218

219
        auto itRange = ChipSet.find(make_shared<TGpioRange>(gpio, gpio));
×
220

221
        if (itRange == ChipSet.end()) {
×
222
            wb_throw(TGpioDriverException, "unable to convert from sysfs GPIO " + to_string(gpio) + ": out of range");
×
223
        }
224

225
        auto pRange = dynamic_pointer_cast<TRange>(*itRange);
×
226

227
        pair<uint32_t, uint32_t> res{pRange->ChipNumber, gpio - pRange->Begin};
×
228

229
        LOG(Info) << "Sysfs GPIO number " << gpio << " => GPIO line " << res.first << ": " << res.second;
×
230

231
        return res;
×
232
    }
×
233

234
    string SetDecimalPlaces(float value, int set_decimal_places)
×
235
    {
236
        ostringstream out;
×
237
        out << fixed << setprecision(set_decimal_places) << value;
×
238
        return out.str();
×
239
    }
×
240

241
    void ClearMappingCache()
×
242
    {
243
        ChipSet.clear();
×
244
        ChipLabelToNumber.clear();
×
245
        MappingsAreReady = false;
×
246
        MappingsWereCleared = true;
×
247
    }
×
248
} // namespace Utils
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc