Scans /sys/class/hidraw for HID devices that advertise a Battery System feature report in their report descriptor but have no kernel power_supply entry (e.g. some 2.4 GHz wireless keyboards whose battery the kernel doesn't surface via the standard power_supply class).
| 2134 | // entry (e.g. some 2.4 GHz wireless keyboards whose battery the kernel doesn't |
| 2135 | // surface via the standard power_supply class). |
| 2136 | QVector<Device> scanHidrawBatteries() { |
| 2137 | QVector<Device> out; |
| 2138 | |
| 2139 | // Collect canonical sysfs paths of HID devices already covered by a |
| 2140 | // kernel power_supply entry so we don't create duplicate entries. |
| 2141 | QSet<QString> coveredHidPaths; |
| 2142 | { |
| 2143 | QDir ps("/sys/class/power_supply"); |
| 2144 | for (const QString &e : ps.entryList(QDir::AllEntries | QDir::NoDotAndDotDot)) { |
| 2145 | QString canon = QFileInfo(ps.absoluteFilePath(e)).canonicalFilePath(); |
| 2146 | if (readSysFile(canon + "/type") == QLatin1String("Battery")) |
| 2147 | coveredHidPaths.insert(canon); |
| 2148 | } |
| 2149 | } |
| 2150 | |
| 2151 | QDir hidrawDir("/sys/class/hidraw"); |
| 2152 | if (!hidrawDir.exists()) |
| 2153 | return out; |
| 2154 | |
| 2155 | QSet<QString> seenHidDevs; |
| 2156 | |
| 2157 | for (const QString &entry : hidrawDir.entryList( |
| 2158 | QDir::AllEntries | QDir::NoDotAndDotDot)) { |
| 2159 | // Resolve the HID device that backs this hidraw node. |
| 2160 | QString hidDevPath = QFileInfo( |
| 2161 | hidrawDir.absoluteFilePath(entry) + "/device" |
| 2162 | ).canonicalFilePath(); |
| 2163 | if (hidDevPath.isEmpty() || seenHidDevs.contains(hidDevPath)) |
| 2164 | continue; |
| 2165 | |
| 2166 | // Skip if a power_supply entry already covers this HID device. |
| 2167 | bool covered = false; |
| 2168 | for (const QString &cp : coveredHidPaths) { |
| 2169 | if (cp.startsWith(hidDevPath + '/')) { covered = true; break; } |
| 2170 | } |
| 2171 | if (covered) |
| 2172 | continue; |
| 2173 | |
| 2174 | QString devNode = "/dev/" + entry; |
| 2175 | |
| 2176 | // O_RDWR is required for HIDIOCGFEATURE; fall back to O_RDONLY for |
| 2177 | // descriptor-only detection when the user only has read permission. |
| 2178 | int fd = ::open(devNode.toLocal8Bit().constData(), O_RDWR | O_NONBLOCK); |
| 2179 | bool canGetFeature = (fd >= 0); |
| 2180 | if (fd < 0) |
| 2181 | fd = ::open(devNode.toLocal8Bit().constData(), O_RDONLY | O_NONBLOCK); |
| 2182 | if (fd < 0) |
| 2183 | continue; |
| 2184 | |
| 2185 | // Read the HID report descriptor. |
| 2186 | int descSize = 0; |
| 2187 | struct hidraw_report_descriptor rptDesc = {}; |
| 2188 | bool gotDesc = (::ioctl(fd, HIDIOCGRDESCSIZE, &descSize) >= 0 |
| 2189 | && descSize > 0); |
| 2190 | if (gotDesc) { |
| 2191 | rptDesc.size = (unsigned int)descSize; |
| 2192 | gotDesc = (::ioctl(fd, HIDIOCGRDESC, &rptDesc) >= 0); |
| 2193 | } |
no test coverage detected