master
c 207 lines 6.26 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "windows_plugin.h"
4 #include "windows-internals.h"
5
6 #define _COMMON_PLUGIN_NAME "windows.plugin"
7 #define _COMMON_PLUGIN_MODULE_NAME "GetPowerSupply"
8 #include "../common-contexts/common-contexts.h"
9
10 #define INITGUID
11 #include <windows.h>
12
13 #include <initguid.h>
14 #include <devguid.h> // for GUID_DEVCLASS_BATTERY
15 #include <setupapi.h> // for SetupDi*
16 #include <batclass.h> // for BATTERY_*
17
18 struct win_battery {
19 struct power_supply ps;
20 struct simple_property voltage;
21 bool seen; // true if discovered in the current collection pass
22 struct win_battery *next;
23 };
24
25 static struct win_battery *batteries_root = NULL;
26
27 static struct win_battery *netdata_get_or_create_battery(const char *name)
28 {
29 for (struct win_battery *battery = batteries_root; battery; battery = battery->next) {
30 if (battery->ps.name && !strcmp(battery->ps.name, name))
31 return battery;
32 }
33
34 struct win_battery *battery = callocz(1, sizeof(*battery));
35 battery->ps.name = strdupz(name);
36 battery->ps.capacity = callocz(1, sizeof(struct simple_property));
37 battery->next = batteries_root;
38 batteries_root = battery;
39
40 return battery;
41 }
42
43 static inline void netdata_update_power_supply_values(
44 struct win_battery *battery,
45 HANDLE hBattery,
46 BATTERY_INFORMATION *bi,
47 BATTERY_QUERY_INFORMATION *bqi)
48 {
49 BATTERY_WAIT_STATUS bws = {0};
50 bws.BatteryTag = bqi->BatteryTag;
51 DWORD dwOut;
52
53 BATTERY_STATUS bs;
54 if (!DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_STATUS, &bws, sizeof(bws), &bs, sizeof(bs), &dwOut, NULL))
55 return;
56
57 if (bs.Capacity != BATTERY_UNKNOWN_CAPACITY) {
58 NETDATA_DOUBLE num = bs.Capacity;
59 NETDATA_DOUBLE den = bi->FullChargedCapacity;
60 num = (den) ? num / den : 0;
61
62 battery->ps.capacity->value = (unsigned long long)(num * 100.0);
63 }
64
65 if (bs.Voltage != BATTERY_UNKNOWN_VOLTAGE)
66 battery->voltage.value = bs.Voltage;
67 }
68
69 static void netdata_power_supply_plot(struct win_battery *battery, int update_every)
70 {
71 rrdset_create_simple_prop(
72 &battery->ps,
73 battery->ps.capacity,
74 "Battery capacity",
75 "capacity",
76 1,
77 "percentage",
78 NETDATA_CHART_PRIO_POWER_SUPPLY_CAPACITY,
79 update_every);
80
81 rrdset_create_simple_prop(
82 &battery->ps,
83 &battery->voltage,
84 "Power supply voltage",
85 "now",
86 1000,
87 "v",
88 NETDATA_CHART_PRIO_POWER_SUPPLY_VOLTAGE,
89 update_every);
90 }
91
92 void do_GetPowerSupply_cleanup(void)
93 {
94 while (batteries_root) {
95 struct win_battery *battery = batteries_root;
96 batteries_root = battery->next;
97
98 if (battery->ps.capacity && battery->ps.capacity->st)
99 rrdset_is_obsolete___safe_from_collector_thread(battery->ps.capacity->st);
100 if (battery->voltage.st)
101 rrdset_is_obsolete___safe_from_collector_thread(battery->voltage.st);
102
103 freez(battery->ps.name);
104 freez(battery->ps.capacity);
105 freez(battery);
106 }
107 }
108
109 int do_GetPowerSupply(int update_every, usec_t dt __maybe_unused)
110 {
111 for (struct win_battery *b = batteries_root; b; b = b->next)
112 b->seen = false;
113
114 HDEVINFO hdev = SetupDiGetClassDevs(&GUID_DEVCLASS_BATTERY, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
115 if (hdev == INVALID_HANDLE_VALUE)
116 return -1;
117
118 SP_DEVICE_INTERFACE_DATA did = {0};
119 did.cbSize = sizeof(did);
120
121 for (LONG i = 0; i < 32 && SetupDiEnumDeviceInterfaces(hdev, 0, &GUID_DEVCLASS_BATTERY, i, &did); i++) {
122 DWORD cbRequired = 0;
123 PSP_DEVICE_INTERFACE_DETAIL_DATA pdidd = NULL;
124 HANDLE hBattery = NULL;
125
126 SetupDiGetDeviceInterfaceDetail(hdev, &did, 0, 0, &cbRequired, 0);
127 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
128 goto endPowerSupply;
129
130 pdidd = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LPTR, cbRequired);
131 if (!pdidd)
132 goto endPowerSupply;
133
134 pdidd->cbSize = sizeof(*pdidd);
135 if (!SetupDiGetDeviceInterfaceDetail(hdev, &did, pdidd, cbRequired, &cbRequired, 0))
136 goto endPowerSupply;
137
138 hBattery = CreateFile(
139 pdidd->DevicePath,
140 GENERIC_READ | GENERIC_WRITE,
141 FILE_SHARE_READ | FILE_SHARE_WRITE,
142 NULL,
143 OPEN_EXISTING,
144 FILE_ATTRIBUTE_NORMAL,
145 NULL);
146 if (hBattery == INVALID_HANDLE_VALUE)
147 goto endPowerSupply;
148
149 BATTERY_QUERY_INFORMATION bqi = {0};
150 DWORD dwWait = 0;
151 DWORD dwOut;
152
153 if (!DeviceIoControl(
154 hBattery,
155 IOCTL_BATTERY_QUERY_TAG,
156 &dwWait,
157 sizeof(dwWait),
158 &bqi.BatteryTag,
159 sizeof(bqi.BatteryTag),
160 &dwOut,
161 NULL) ||
162 !bqi.BatteryTag)
163 goto endPowerSupply;
164
165 BATTERY_INFORMATION bi = {0};
166 bqi.InformationLevel = BatteryInformation;
167 if (!DeviceIoControl(
168 hBattery, IOCTL_BATTERY_QUERY_INFORMATION, &bqi, sizeof(bqi), &bi, sizeof(bi), &dwOut, NULL))
169 goto endPowerSupply;
170
171 char name[RRD_ID_LENGTH_MAX + 1];
172 snprintfz(name, sizeof(name), "BAT%d", i + 1);
173 struct win_battery *battery = netdata_get_or_create_battery(name);
174 battery->seen = true;
175 netdata_update_power_supply_values(battery, hBattery, &bi, &bqi);
176 netdata_power_supply_plot(battery, update_every);
177
178 endPowerSupply:
179 if (hBattery != NULL && hBattery != INVALID_HANDLE_VALUE)
180 CloseHandle(hBattery);
181
182 if (pdidd)
183 LocalFree(pdidd);
184 }
185
186 SetupDiDestroyDeviceInfoList(hdev);
187
188 // Retire batteries that were not seen in this discovery pass (e.g. physically removed).
189 struct win_battery **pp = &batteries_root;
190 while (*pp) {
191 struct win_battery *b = *pp;
192 if (!b->seen) {
193 if (b->ps.capacity && b->ps.capacity->st)
194 rrdset_is_obsolete___safe_from_collector_thread(b->ps.capacity->st);
195 if (b->voltage.st)
196 rrdset_is_obsolete___safe_from_collector_thread(b->voltage.st);
197 *pp = b->next;
198 freez(b->ps.name);
199 freez(b->ps.capacity);
200 freez(b);
201 } else {
202 pp = &b->next;
203 }
204 }
205
206 return 0;
207 }