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
39static int AcpiBatteryNum = 0;
40static char AcpiBatteryNames[ ACPIBATTERYNUMMAX ][ 8 ];
41static int AcpiBatteryCharge[ ACPIBATTERYNUMMAX ];
42static int AcpiBatteryUsage[ ACPIBATTERYNUMMAX ];
43
44static int AcpiBatteryOk = 1;
45/*
46================================ public part =================================
47*/
48
49void initAcpi(struct SensorModul* sm)
50{
51 initAcpiBattery(sm);
52 initAcpiThermal(sm);
53}
54
55int updateAcpi( void )
56{
57 if (AcpiBatteryOk && AcpiBatteryNum > 0) updateAcpiBattery();
58 return 0;
59}
60
61void exitAcpi( void )
62{
63 AcpiBatteryNum = -1;
64 AcpiBatteryOk = 0;
65}
66
67
68/************ ACPI Battery **********/
69
70void 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
98int 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
184void printAcpiBatFill( const char* cmd )
185{
186 int i;
187
188 sscanf( cmd + 13, "%d", &i );
189 output( "%d\n", AcpiBatteryCharge[ i ] );
190}
191
192void 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
200void printAcpiBatUsage( const char* cmd)
201{
202 int i;
203
204 sscanf( cmd + 13, "%d", &i );
205 output( "%d\n", AcpiBatteryUsage[ i ] );
206}
207
208void 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 */
237static int extract_zone_name(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
250void 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
316static 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
345void 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}
354void 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}
362void 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
371static 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
406void printThermalZoneTemperature(const char *cmd) {
407 int temperature = getCurrentTemperature(cmd);
408 output( "%d\n", temperature);
409}
410
411void printThermalZoneTemperatureInfo(const char *cmd)
412{
413 (void)cmd;
414
415 output( "Current temperature\t0\t0\tC\n");
416}
417
418/********** ACPI Fan State***************/
419
420static 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
458void printFanState(const char *cmd) {
459 int fan_state = getFanState(cmd);
460 output( "%d\n", fan_state);
461}
462
463void printFanStateInfo(const char *cmd)
464{
465 (void)cmd;
466
467 output( "Fan status\t0\t1\tboolean\n");
468}
469