1 | /* |
2 | KSysGuard, the KDE System Guard |
3 | |
4 | Copyright (c) 2003 Stephan Uhlmann <su@su2.info> |
5 | Copyright (c) 2005 Sirtaj Singh Kang <taj@kde.org> -- Battery fixes and Thermal |
6 | |
7 | This program is free software; you can redistribute it and/or |
8 | modify it under the terms of version 2 of the GNU General Public |
9 | License as published by the Free Software Foundation. |
10 | |
11 | This program is distributed in the hope that it will be useful, |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | GNU General Public License for more details. |
15 | |
16 | You should have received a copy of the GNU General Public License |
17 | along with this program; if not, write to the Free Software |
18 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
19 | */ |
20 | |
21 | #include <dirent.h> |
22 | #include <fcntl.h> |
23 | #include <stdio.h> |
24 | #include <string.h> |
25 | #include <sys/stat.h> |
26 | #include <sys/types.h> |
27 | #include <unistd.h> |
28 | |
29 | #include "Command.h" |
30 | #include "ksysguardd.h" |
31 | |
32 | #include "acpi.h" |
33 | |
34 | #define ACPIFILENAMELENGTHMAX 64 |
35 | #define ACPIBATTERYNUMMAX 6 |
36 | #define ACPIBATTERYINFOBUFSIZE 1024 |
37 | #define ACPIBATTERYSTATEBUFSIZE 512 |
38 | |
39 | static int AcpiBatteryNum = 0; |
40 | static char AcpiBatteryNames[ ACPIBATTERYNUMMAX ][ 8 ]; |
41 | static int AcpiBatteryCharge[ ACPIBATTERYNUMMAX ]; |
42 | static int AcpiBatteryUsage[ ACPIBATTERYNUMMAX ]; |
43 | |
44 | static int AcpiBatteryOk = 1; |
45 | /* |
46 | ================================ public part ================================= |
47 | */ |
48 | |
49 | void initAcpi(struct SensorModul* sm) |
50 | { |
51 | initAcpiBattery(sm); |
52 | initAcpiThermal(sm); |
53 | } |
54 | |
55 | int updateAcpi( void ) |
56 | { |
57 | if (AcpiBatteryOk && AcpiBatteryNum > 0) updateAcpiBattery(); |
58 | return 0; |
59 | } |
60 | |
61 | void exitAcpi( void ) |
62 | { |
63 | AcpiBatteryNum = -1; |
64 | AcpiBatteryOk = 0; |
65 | } |
66 | |
67 | |
68 | /************ ACPI Battery **********/ |
69 | |
70 | void initAcpiBattery( struct SensorModul* sm ) |
71 | { |
72 | DIR *d; |
73 | struct dirent *de; |
74 | char s[ ACPIFILENAMELENGTHMAX ]; |
75 | |
76 | if ( ( d = opendir( "/proc/acpi/battery" ) ) == NULL ) { |
77 | AcpiBatteryNum = -1; |
78 | AcpiBatteryOk = 0; |
79 | return; |
80 | } else { |
81 | AcpiBatteryNum = 0; |
82 | AcpiBatteryOk = 1; |
83 | while ( ( de = readdir( d ) ) ) |
84 | if ( ( strcmp( de->d_name, "." ) != 0 ) && ( strcmp( de->d_name, ".." ) != 0 ) ) { |
85 | strncpy( AcpiBatteryNames[ AcpiBatteryNum ], de->d_name, 8 ); |
86 | snprintf( s, sizeof( s ), "acpi/battery/%d/batterycharge" , AcpiBatteryNum ); |
87 | registerMonitor( s, "integer" , printAcpiBatFill, printAcpiBatFillInfo, sm ); |
88 | snprintf( s, sizeof( s ), "acpi/battery/%d/batteryusage" , AcpiBatteryNum ); |
89 | registerMonitor( s, "integer" , printAcpiBatUsage, printAcpiBatUsageInfo, sm); |
90 | AcpiBatteryCharge[ AcpiBatteryNum ] = 0; |
91 | AcpiBatteryNum++; |
92 | } |
93 | closedir( d ); |
94 | } |
95 | } |
96 | |
97 | |
98 | int updateAcpiBattery( void ) |
99 | { |
100 | int i, fd; |
101 | char s[ ACPIFILENAMELENGTHMAX ]; |
102 | size_t n; |
103 | char AcpiBatInfoBuf[ ACPIBATTERYINFOBUFSIZE ]; |
104 | char AcpiBatStateBuf[ ACPIBATTERYSTATEBUFSIZE ]; |
105 | char *p; |
106 | int AcpiBatCapacity = 1; |
107 | int AcpiBatRemainingCapacity = 0; |
108 | |
109 | if ( AcpiBatteryNum <= 0 ) |
110 | return -1; |
111 | |
112 | for ( i = 0; i < AcpiBatteryNum; i++ ) { |
113 | /* get total capacity */ |
114 | snprintf( s, sizeof( s ), "/proc/acpi/battery/%s/info" , AcpiBatteryNames[ i ] ); |
115 | if ( ( fd = open( s, O_RDONLY ) ) < 0 ) { |
116 | print_error( "Cannot open file \'%s\'!\n" |
117 | "Load the battery ACPI kernel module or\n" |
118 | "compile it into your kernel.\n" , s ); |
119 | AcpiBatteryOk = 0; |
120 | return -1; |
121 | } |
122 | if ( ( n = read( fd, AcpiBatInfoBuf, ACPIBATTERYINFOBUFSIZE - 1 ) ) == |
123 | ACPIBATTERYINFOBUFSIZE - 1 ) { |
124 | log_error( "Internal buffer too small to read \'%s\'" , s ); |
125 | close( fd ); |
126 | AcpiBatteryOk = 0; |
127 | return -1; |
128 | } |
129 | close( fd ); |
130 | p = AcpiBatInfoBuf; |
131 | if ( p && strstr(p, "ERROR: Unable to read battery" ) ) |
132 | return 0; /* If we can't read the battery, reuse the last value */ |
133 | while ( ( p!= NULL ) && ( sscanf( p, "last full capacity: %d " , |
134 | &AcpiBatCapacity ) != 1 ) ) { |
135 | p = strchr( p, '\n' ); |
136 | if ( p ) |
137 | p++; |
138 | } |
139 | /* get remaining capacity */ |
140 | snprintf( s, sizeof( s ), "/proc/acpi/battery/%s/state" , AcpiBatteryNames[ i ] ); |
141 | if ( ( fd = open( s, O_RDONLY ) ) < 0 ) { |
142 | print_error( "Cannot open file \'%s\'!\n" |
143 | "Load the battery ACPI kernel module or\n" |
144 | "compile it into your kernel.\n" , s ); |
145 | AcpiBatteryOk = 0; |
146 | return -1; |
147 | } |
148 | if ( ( n = read( fd, AcpiBatStateBuf, ACPIBATTERYSTATEBUFSIZE - 1 ) ) == |
149 | ACPIBATTERYSTATEBUFSIZE - 1 ) { |
150 | log_error( "Internal buffer too small to read \'%s\'" , s); |
151 | close( fd ); |
152 | AcpiBatteryOk = 0; |
153 | return -1; |
154 | } |
155 | close( fd ); |
156 | p = AcpiBatStateBuf; |
157 | while ( ( p!= NULL ) && ( sscanf( p, "remaining capacity: %d " , |
158 | &AcpiBatRemainingCapacity ) != 1 ) ) { |
159 | p = strchr( p, '\n' ); |
160 | if ( p ) |
161 | p++; |
162 | } |
163 | |
164 | /* get current battery usage, (current Current) */ |
165 | p = AcpiBatStateBuf; |
166 | while ( ( p!= NULL ) && ( sscanf( p, "present rate: %d " , |
167 | &AcpiBatteryUsage[i] ) != 1 ) ) { |
168 | p = strchr( p, '\n' ); |
169 | if ( p ) |
170 | p++; |
171 | } |
172 | |
173 | |
174 | /* calculate charge rate */ |
175 | if ( AcpiBatCapacity > 0 ) |
176 | AcpiBatteryCharge[ i ] = AcpiBatRemainingCapacity * 100 / AcpiBatCapacity; |
177 | else |
178 | AcpiBatteryCharge[ i ] = 0; |
179 | } |
180 | AcpiBatteryOk = 1; |
181 | return 0; |
182 | } |
183 | |
184 | void printAcpiBatFill( const char* cmd ) |
185 | { |
186 | int i; |
187 | |
188 | sscanf( cmd + 13, "%d" , &i ); |
189 | output( "%d\n" , AcpiBatteryCharge[ i ] ); |
190 | } |
191 | |
192 | void printAcpiBatFillInfo( const char* cmd ) |
193 | { |
194 | int i; |
195 | |
196 | sscanf( cmd + 13, "%d" , &i ); |
197 | output( "Battery %d charge\t0\t100\t%%\n" , i ); |
198 | } |
199 | |
200 | void printAcpiBatUsage( const char* cmd) |
201 | { |
202 | int i; |
203 | |
204 | sscanf( cmd + 13, "%d" , &i ); |
205 | output( "%d\n" , AcpiBatteryUsage[ i ] ); |
206 | } |
207 | |
208 | void printAcpiBatUsageInfo( const char* cmd) |
209 | { |
210 | |
211 | int i; |
212 | |
213 | sscanf(cmd+13, "%d" , &i); |
214 | |
215 | output( "Battery %d usage\t0\t2500\tmA\n" , i ); |
216 | } |
217 | |
218 | /************** ACPI Thermal *****************/ |
219 | |
220 | #define OLD_THERMAL_ZONE_DIR "/proc/acpi/thermal_zone" |
221 | #define OLD_TEMPERATURE_FILE "temperature" |
222 | #define OLD_TEMPERATURE_FILE_MAXLEN 255 |
223 | |
224 | #define OLD_FAN_DIR "/proc/acpi/fan" |
225 | #define OLD_FAN_STATE_FILE "state" |
226 | #define OLD_FAN_STATE_FILE_MAXLEN 255 |
227 | |
228 | |
229 | /*static char **zone_names = NULL;*/ |
230 | |
231 | /** Find the thermal zone name from the command. |
232 | * Assumes the command is of the form acpi/thermal_zone/<zone name>/... |
233 | * @p startidx is set to the start of the zone name. May be set to an |
234 | * undefined value if zone name is not found. |
235 | * @return length of found name, or 0 if nothing found. |
236 | */ |
237 | static int (char **startidx, const char *cmd) |
238 | { |
239 | char *idx = NULL; |
240 | idx = strchr(cmd, '/'); |
241 | if (idx == NULL) return 0; |
242 | idx = strchr(idx+1, '/'); |
243 | if (idx == NULL) return 0; |
244 | *startidx = idx+1; |
245 | idx = strchr(*startidx, '/'); |
246 | if (idx == NULL) return 0; |
247 | return idx - *startidx; |
248 | } |
249 | |
250 | void initAcpiThermal(struct SensorModul *sm) |
251 | { |
252 | char th_ref[ ACPIFILENAMELENGTHMAX ]; |
253 | DIR *d = NULL; |
254 | struct dirent *de; |
255 | |
256 | d = opendir("/sys/class/thermal/" ); |
257 | if (d != NULL) { |
258 | while ( (de = readdir(d)) != NULL ) { |
259 | if (!de->d_name || de->d_name[0] == '.') |
260 | continue; |
261 | if (strncmp( de->d_name, "thermal_zone" , sizeof("thermal_zone" )-1) == 0) { |
262 | snprintf(th_ref, sizeof(th_ref), |
263 | "acpi/Thermal_Zone/%s/Temperature" , de->d_name + (sizeof("thermal_zone" )-1)); |
264 | registerMonitor(th_ref, "integer" , printSysThermalZoneTemperature, |
265 | printThermalZoneTemperatureInfo, sm); |
266 | |
267 | /*For compatibility, register a legacy sensor*/ |
268 | int zone_number; |
269 | if (sscanf(de->d_name, "thermal_zone%d" , &zone_number) > 0) { |
270 | snprintf(th_ref, sizeof(th_ref), |
271 | "acpi/thermal_zone/TZ%02d/temperature" , zone_number); |
272 | registerLegacyMonitor(th_ref, "integer" , printSysCompatibilityThermalZoneTemperature, |
273 | printThermalZoneTemperatureInfo, sm); |
274 | } |
275 | } else if (strncmp( de->d_name, "cooling_device" , sizeof("cooling_device" )-1) == 0) { |
276 | snprintf(th_ref, sizeof(th_ref), |
277 | "acpi/Cooling_Device/%s/Current_State" , de->d_name+( sizeof("cooling_device" )-1)); |
278 | registerMonitor(th_ref, "integer" , printSysFanState, |
279 | printFanStateInfo, sm); |
280 | } |
281 | } |
282 | closedir( d ); |
283 | } else { |
284 | d = opendir(OLD_THERMAL_ZONE_DIR); |
285 | if (d != NULL) { |
286 | while ( (de = readdir(d)) != NULL ) { |
287 | if (!de->d_name || de->d_name[0] == '.') |
288 | continue; |
289 | |
290 | snprintf(th_ref, sizeof(th_ref), |
291 | "acpi/thermal_zone/%s/temperature" , de->d_name); |
292 | registerMonitor(th_ref, "integer" , printThermalZoneTemperature, |
293 | printThermalZoneTemperatureInfo, sm); |
294 | } |
295 | closedir( d ); |
296 | } |
297 | |
298 | d = opendir(OLD_FAN_DIR); |
299 | if (d != NULL) { |
300 | while ( (de = readdir(d)) != NULL ) { |
301 | if (!de->d_name || de->d_name[0] == '.') |
302 | continue; |
303 | |
304 | snprintf(th_ref, sizeof(th_ref), |
305 | "acpi/fan/%s/state" , de->d_name); |
306 | registerMonitor(th_ref, "integer" , printFanState, |
307 | printFanStateInfo, sm); |
308 | } |
309 | closedir( d ); |
310 | } |
311 | } |
312 | |
313 | return; |
314 | } |
315 | |
316 | static int getSysFileValue(const char *group, int value, const char *file) { |
317 | static int shownError = 0; |
318 | char th_file[ ACPIFILENAMELENGTHMAX ]; |
319 | char input_buf[ 100 ]; |
320 | snprintf(th_file, sizeof(th_file), "/sys/class/thermal/%s%d/%s" ,group, value, file); |
321 | int fd = open(th_file, O_RDONLY); |
322 | if (fd < 0) { |
323 | if (!shownError) |
324 | print_error( "Cannot open file \'%s\'!\n" |
325 | "Load the thermal ACPI kernel module or\n" |
326 | "compile it into your kernel.\n" , th_file ); |
327 | shownError = 1; |
328 | return -1; |
329 | } |
330 | int read_bytes = read( fd, input_buf, sizeof(input_buf) - 1 ); |
331 | if ( read_bytes == sizeof(input_buf) - 1 ) { |
332 | if (!shownError) |
333 | log_error( "Internal buffer too small to read \'%s\'" , th_file ); |
334 | shownError = 1; |
335 | close( fd ); |
336 | return -1; |
337 | } |
338 | close(fd); |
339 | |
340 | int result=0; |
341 | sscanf(input_buf, "%d" , &result); |
342 | return result; |
343 | } |
344 | |
345 | void printSysThermalZoneTemperature(const char *cmd) { |
346 | int zone = 0; |
347 | if (sscanf(cmd, "acpi/Thermal_Zone/%d" , &zone) <= 0) { |
348 | output("-1\n" ); |
349 | return; |
350 | } |
351 | |
352 | output( "%d\n" , getSysFileValue("thermal_zone" , zone, "temp" ) / 1000); |
353 | } |
354 | void printSysCompatibilityThermalZoneTemperature(const char *cmd) { |
355 | int zone = 0; |
356 | if (sscanf(cmd, "acpi/thermal_zone/TZ%d" , &zone) <= 0) { |
357 | output( "-1\n" ); |
358 | return; |
359 | } |
360 | output( "%d\n" , getSysFileValue("thermal_zone" , zone, "temp" )/1000); |
361 | } |
362 | void printSysFanState(const char *cmd) { |
363 | int fan = 0; |
364 | if (sscanf(cmd, "acpi/Cooling_Device/%d" , &fan) <= 0) { |
365 | output( "-1\n" ); |
366 | return; |
367 | } |
368 | output( "%d\n" , getSysFileValue("cooling_device" , fan, "cur_state" )); |
369 | } |
370 | |
371 | static int getCurrentTemperature(const char *cmd) |
372 | { |
373 | char th_file[ ACPIFILENAMELENGTHMAX ]; |
374 | char input_buf[ OLD_TEMPERATURE_FILE_MAXLEN ]; |
375 | char *zone_name = NULL; |
376 | int read_bytes = 0, fd = 0, len_zone_name = 0; |
377 | int temperature=0; |
378 | |
379 | len_zone_name = extract_zone_name(&zone_name, cmd); |
380 | if (len_zone_name <= 0) return -1; |
381 | |
382 | snprintf(th_file, sizeof(th_file), |
383 | OLD_THERMAL_ZONE_DIR "/%.*s/" OLD_TEMPERATURE_FILE, |
384 | len_zone_name, zone_name); |
385 | |
386 | fd = open(th_file, O_RDONLY); |
387 | if (fd < 0) { |
388 | print_error( "Cannot open file \'%s\'!\n" |
389 | "Load the thermal ACPI kernel module or\n" |
390 | "compile it into your kernel.\n" , th_file ); |
391 | return -1; |
392 | } |
393 | |
394 | read_bytes = read( fd, input_buf, sizeof(input_buf) - 1 ); |
395 | if ( read_bytes == sizeof(input_buf) - 1 ) { |
396 | log_error( "Internal buffer too small to read \'%s\'" , th_file ); |
397 | close( fd ); |
398 | return -1; |
399 | } |
400 | close(fd); |
401 | |
402 | sscanf(input_buf, "temperature: %d C" , &temperature); |
403 | return temperature; |
404 | } |
405 | |
406 | void printThermalZoneTemperature(const char *cmd) { |
407 | int temperature = getCurrentTemperature(cmd); |
408 | output( "%d\n" , temperature); |
409 | } |
410 | |
411 | void printThermalZoneTemperatureInfo(const char *cmd) |
412 | { |
413 | (void)cmd; |
414 | |
415 | output( "Current temperature\t0\t0\tC\n" ); |
416 | } |
417 | |
418 | /********** ACPI Fan State***************/ |
419 | |
420 | static int getFanState(const char *cmd) |
421 | { |
422 | char fan_state_file[ ACPIFILENAMELENGTHMAX ]; |
423 | char input_buf[ OLD_FAN_STATE_FILE_MAXLEN ]; |
424 | char *fan_name = NULL; |
425 | int read_bytes = 0, fd = 0, len_fan_name = 0; |
426 | char fan_state[4]; |
427 | |
428 | len_fan_name = extract_zone_name(&fan_name, cmd); |
429 | if (len_fan_name <= 0) { |
430 | return -1; |
431 | } |
432 | |
433 | snprintf(fan_state_file, sizeof(fan_state_file), |
434 | OLD_FAN_DIR "/%.*s/" OLD_FAN_STATE_FILE, |
435 | len_fan_name, fan_name); |
436 | |
437 | fd = open(fan_state_file, O_RDONLY); |
438 | if (fd < 0) { |
439 | print_error( "Cannot open file \'%s\'!\n" |
440 | "Load the fan ACPI kernel module or\n" |
441 | "compile it into your kernel.\n" , fan_state_file ); |
442 | |
443 | return -1; |
444 | } |
445 | |
446 | read_bytes = read( fd, input_buf, sizeof(input_buf) - 1 ); |
447 | if ( read_bytes == sizeof(input_buf) - 1 ) { |
448 | log_error( "Internal buffer too small to read \'%s\'" , fan_state_file ); |
449 | close( fd ); |
450 | return -1; |
451 | } |
452 | close(fd); |
453 | |
454 | sscanf(input_buf, "status: %2s" , fan_state); |
455 | return (fan_state[1] == 'n') ? 1 : 0; |
456 | } |
457 | |
458 | void printFanState(const char *cmd) { |
459 | int fan_state = getFanState(cmd); |
460 | output( "%d\n" , fan_state); |
461 | } |
462 | |
463 | void printFanStateInfo(const char *cmd) |
464 | { |
465 | (void)cmd; |
466 | |
467 | output( "Fan status\t0\t1\tboolean\n" ); |
468 | } |
469 | |