1 /*
2 * xenpmd.c
3 *
4 * xen power management daemon - Facilitates power management
5 * functionality within xen guests.
6 *
7 * Copyright (c) 2008 Kamala Narasimhan
8 * Copyright (c) 2008 Citrix Systems, Inc.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 /* Xen extended power management support provides HVM guest power management
25 * features beyond S3, S4, S5. For example, it helps expose system level
26 * battery status and battery meter information and in future will be extended
27 * to include more power management support. This extended power management
28 * support is enabled by setting xen_extended_power_mgmt to 1 or 2 in the HVM
29 * config file. When set to 2, non-pass through mode is enabled which heavily
30 * relies on this power management daemon to glean battery information from
31 * dom0 and store it xenstore which would then be queries and used by qemu and
32 * passed to the guest when appropriate battery ports are read/written to.
33 */
34
35 #define _GNU_SOURCE /* for asprintf() */
36 #include <stdio.h>
37 #include <stdarg.h>
38 #include <string.h>
39 #include <stdlib.h>
40 #include <dirent.h>
41 #include <unistd.h>
42 #include <sys/stat.h>
43 #include <xenstore.h>
44 #include <assert.h>
45
46 /* #define RUN_STANDALONE */
47 #define RUN_IN_SIMULATE_MODE
48
49 enum BATTERY_INFO_TYPE {
50 BIF,
51 BST
52 };
53
54 enum BATTERY_PRESENT {
55 NO,
56 YES
57 };
58
59 enum BATTERY_TECHNOLOGY {
60 NON_RECHARGEABLE,
61 RECHARGEABLE
62 };
63
64 struct battery_info {
65 enum BATTERY_PRESENT present;
66 unsigned long design_capacity;
67 unsigned long last_full_capacity;
68 enum BATTERY_TECHNOLOGY battery_technology;
69 unsigned long design_voltage;
70 unsigned long design_capacity_warning;
71 unsigned long design_capacity_low;
72 unsigned long capacity_granularity_1;
73 unsigned long capacity_granularity_2;
74 char model_number[32];
75 char serial_number[32];
76 char battery_type[32];
77 char oem_info[32];
78 };
79
80 struct battery_status {
81 enum BATTERY_PRESENT present;
82 unsigned long state;
83 unsigned long present_rate;
84 unsigned long remaining_capacity;
85 unsigned long present_voltage;
86 };
87
88 static struct xs_handle *xs;
89
90 #ifdef RUN_IN_SIMULATE_MODE
91 #define BATTERY_DIR_PATH "/tmp/battery"
92 #define BATTERY_INFO_FILE_PATH "/tmp/battery/%s/info"
93 #define BATTERY_STATE_FILE_PATH "/tmp/battery/%s/state"
94 #else
95 #define BATTERY_DIR_PATH "/proc/acpi/battery"
96 #define BATTERY_INFO_FILE_PATH "/proc/acpi/battery/%s/info"
97 #define BATTERY_STATE_FILE_PATH "/proc/acpi/battery/%s/state"
98 #endif
99
get_next_battery_file(DIR * battery_dir,enum BATTERY_INFO_TYPE battery_info_type)100 FILE *get_next_battery_file(DIR *battery_dir,
101 enum BATTERY_INFO_TYPE battery_info_type)
102 {
103 FILE *file = 0;
104 struct dirent *dir_entries;
105 char *file_name;
106 int ret;
107
108 do
109 {
110 dir_entries = readdir(battery_dir);
111 if ( !dir_entries )
112 return 0;
113 if ( strlen(dir_entries->d_name) < 4 )
114 continue;
115 if ( battery_info_type == BIF )
116 ret = asprintf(&file_name, BATTERY_INFO_FILE_PATH,
117 dir_entries->d_name);
118 else
119 ret = asprintf(&file_name, BATTERY_STATE_FILE_PATH,
120 dir_entries->d_name);
121 /* This should not happen but is needed to pass gcc checks */
122 if (ret < 0)
123 continue;
124 file = fopen(file_name, "r");
125 free(file_name);
126 } while ( !file );
127
128 return file;
129 }
130
set_attribute_battery_info(char * attrib_name,char * attrib_value,struct battery_info * info)131 void set_attribute_battery_info(char *attrib_name,
132 char *attrib_value,
133 struct battery_info *info)
134 {
135 if ( strstr(attrib_name, "present") )
136 {
137 if ( strstr(attrib_value, "yes") )
138 info->present = YES;
139 return;
140 }
141
142 if ( strstr(attrib_name, "design capacity warning") )
143 {
144 info->design_capacity_warning = strtoull(attrib_value, NULL, 10);
145 return;
146 }
147
148 if ( strstr(attrib_name, "design capacity low") )
149 {
150 info->design_capacity_low = strtoull(attrib_value, NULL, 10);
151 return;
152 }
153
154 if ( strstr(attrib_name, "design capacity") )
155 {
156 info->design_capacity = strtoull(attrib_value, NULL, 10);
157 return;
158 }
159
160 if ( strstr(attrib_name, "last full capacity") )
161 {
162 info->last_full_capacity = strtoull(attrib_value, NULL, 10);
163 return;
164 }
165
166 if ( strstr(attrib_name, "design voltage") )
167 {
168 info->design_voltage = strtoull(attrib_value, NULL, 10);
169 return;
170 }
171
172 if ( strstr(attrib_name, "capacity granularity 1") )
173 {
174 info->capacity_granularity_1 = strtoull(attrib_value, NULL, 10);
175 return;
176 }
177
178 if ( strstr(attrib_name, "capacity granularity 2") )
179 {
180 info->capacity_granularity_2 = strtoull(attrib_value, NULL, 10);
181 return;
182 }
183
184 if ( strstr(attrib_name, "battery technology") )
185 {
186 if ( strncmp(attrib_value, "rechargeable",
187 strlen("rechargeable")) == 0 )
188 info->battery_technology = RECHARGEABLE;
189 else
190 info->battery_technology = NON_RECHARGEABLE;
191 return;
192 }
193
194 if ( strstr(attrib_name, "model number") )
195 {
196 strncpy(info->model_number, attrib_value, 31);
197 info->model_number[31] = '\0';
198 return;
199 }
200
201 if ( strstr(attrib_name, "serial number") )
202 {
203 strncpy(info->serial_number, attrib_value, 31);
204 info->serial_number[31] = '\0';
205 return;
206 }
207
208 if ( strstr(attrib_name, "battery type") )
209 {
210 strncpy(info->battery_type, attrib_value, 31);
211 info->battery_type[31] = '\0';
212 return;
213 }
214
215 if ( strstr(attrib_name, "OEM info") )
216 {
217 strncpy(info->oem_info, attrib_value, 31);
218 info->oem_info[31] = '\0';
219 return;
220 }
221
222 return;
223 }
224
set_attribute_battery_status(char * attrib_name,char * attrib_value,struct battery_status * status)225 void set_attribute_battery_status(char *attrib_name,
226 char *attrib_value,
227 struct battery_status *status)
228 {
229 if ( strstr(attrib_name, "charging state") )
230 {
231 /* Check this, below is half baked */
232 if ( strstr(attrib_value, "charged") )
233 status->state = 0;
234 else
235 status->state = 1;
236 return;
237 }
238
239 if ( strstr(attrib_name, "present rate") )
240 {
241 status->present_rate = strtoull(attrib_value, NULL, 10);
242 return;
243 }
244
245 if ( strstr(attrib_name, "remaining capacity") )
246 {
247 status->remaining_capacity = strtoull(attrib_value, NULL, 10);
248 return;
249 }
250
251 if ( strstr(attrib_name, "present voltage") )
252 {
253 status->present_voltage = strtoull(attrib_value, NULL, 10);
254 return;
255 }
256
257 if ( strstr(attrib_name, "present") )
258 {
259 if ( strstr(attrib_value, "yes") )
260 status->present = YES;
261 return;
262 }
263 }
264
parse_battery_info_or_status(char * line_info,enum BATTERY_INFO_TYPE type,void * info_or_status)265 void parse_battery_info_or_status(char *line_info,
266 enum BATTERY_INFO_TYPE type,
267 void *info_or_status)
268 {
269 char attrib_name[128];
270 char attrib_value[64];
271 char *delimiter;
272 unsigned long length;
273
274 length = strlen(line_info);
275 delimiter = (char *) strchr( line_info, ':');
276 if ( (!delimiter) || (delimiter == line_info) ||
277 (delimiter == line_info + length) )
278 return;
279
280 strncpy(attrib_name, line_info, delimiter-line_info);
281 while ( *(delimiter+1) == ' ' )
282 {
283 delimiter++;
284 if ( delimiter+1 == line_info + length)
285 return;
286 }
287 strncpy(attrib_value, delimiter+1,
288 (unsigned long)line_info + length -(unsigned long)delimiter);
289
290 if ( type == BIF )
291 set_attribute_battery_info(attrib_name, attrib_value,
292 (struct battery_info *)info_or_status);
293 else
294 set_attribute_battery_status(attrib_name, attrib_value,
295 (struct battery_status *)info_or_status);
296
297 return;
298 }
299
get_next_battery_info_or_status(DIR * battery_dir,enum BATTERY_INFO_TYPE type,void * info_or_status)300 int get_next_battery_info_or_status(DIR *battery_dir,
301 enum BATTERY_INFO_TYPE type,
302 void *info_or_status)
303 {
304 FILE *file;
305 char line_info[256];
306
307 if ( !info_or_status )
308 return 0;
309
310 if (type == BIF)
311 memset(info_or_status, 0, sizeof(struct battery_info));
312 else
313 memset(info_or_status, 0, sizeof(struct battery_status));
314
315 file = get_next_battery_file(battery_dir, type);
316 if ( !file )
317 return 0;
318
319 while ( fgets(line_info, sizeof(line_info), file) != NULL )
320 parse_battery_info_or_status(line_info, type, info_or_status);
321
322 fclose(file);
323 return 1;
324 }
325
326 #ifdef RUN_STANDALONE
print_battery_info(struct battery_info * info)327 void print_battery_info(struct battery_info *info)
328 {
329 printf("present: %d\n", info->present);
330 printf("design capacity: %d\n", info->design_capacity);
331 printf("last full capacity: %d\n", info->last_full_capacity);
332 printf("battery technology: %d\n", info->battery_technology);
333 printf("design voltage: %d\n", info->design_voltage);
334 printf("design capacity warning:%d\n", info->design_capacity_warning);
335 printf("design capacity low: %d\n", info->design_capacity_low);
336 printf("capacity granularity 1: %d\n", info->capacity_granularity_1);
337 printf("capacity granularity 2: %d\n", info->capacity_granularity_2);
338 printf("model number: %s\n", info->model_number);
339 printf("serial number: %s\n", info->serial_number);
340 printf("battery type: %s\n", info->battery_type);
341 printf("OEM info: %s\n", info->oem_info);
342 }
343 #endif /*RUN_STANDALONE*/
344
write_ulong_lsb_first(char * temp_val,unsigned long val)345 void write_ulong_lsb_first(char *temp_val, unsigned long val)
346 {
347 snprintf(temp_val, 9, "%02x%02x%02x%02x", (unsigned int)val & 0xff,
348 (unsigned int)(val & 0xff00) >> 8, (unsigned int)(val & 0xff0000) >> 16,
349 (unsigned int)(val & 0xff000000) >> 24);
350 }
351
write_battery_info_to_xenstore(struct battery_info * info)352 void write_battery_info_to_xenstore(struct battery_info *info)
353 {
354 char val[1024], string_info[256];
355 unsigned int len;
356
357 xs_mkdir(xs, XBT_NULL, "/pm");
358
359 memset(val, 0, 1024);
360 memset(string_info, 0, 256);
361 /* write 9 dwords (so 9*4) + length of 4 strings + 4 null terminators */
362 len = 9 * 4 + strlen(info->model_number) + strlen(info->serial_number) +
363 strlen(info->battery_type) + strlen(info->oem_info) + 4;
364 assert(len < 255);
365 snprintf(val, 3, "%02x", len);
366 write_ulong_lsb_first(val+2, info->present);
367 write_ulong_lsb_first(val+10, info->design_capacity);
368 write_ulong_lsb_first(val+18, info->last_full_capacity);
369 write_ulong_lsb_first(val+26, info->battery_technology);
370 write_ulong_lsb_first(val+34, info->design_voltage);
371 write_ulong_lsb_first(val+42, info->design_capacity_warning);
372 write_ulong_lsb_first(val+50, info->design_capacity_low);
373 write_ulong_lsb_first(val+58, info->capacity_granularity_1);
374 write_ulong_lsb_first(val+66, info->capacity_granularity_2);
375
376 snprintf(string_info, 256, "%02x%s%02x%s%02x%s%02x%s",
377 (unsigned int)strlen(info->model_number), info->model_number,
378 (unsigned int)strlen(info->serial_number), info->serial_number,
379 (unsigned int)strlen(info->battery_type), info->battery_type,
380 (unsigned int)strlen(info->oem_info), info->oem_info);
381 strncat(val+73, string_info, 1024-73-1);
382 xs_write(xs, XBT_NULL, "/pm/bif",
383 val, 73+8+strlen(info->model_number)+strlen(info->serial_number)+
384 strlen(info->battery_type)+strlen(info->oem_info)+1);
385 }
386
write_one_time_battery_info(void)387 int write_one_time_battery_info(void)
388 {
389 DIR *dir;
390 int ret = 0;
391 struct battery_info info;
392
393 dir = opendir(BATTERY_DIR_PATH);
394 if ( !dir )
395 return 0;
396
397 while ( get_next_battery_info_or_status(dir, BIF, (void *)&info) )
398 {
399 #ifdef RUN_STANDALONE
400 print_battery_info(&info);
401 #endif
402 if ( info.present == YES )
403 {
404 write_battery_info_to_xenstore(&info);
405 ret = 1;
406 break; /* rethink this... */
407 }
408 }
409
410 closedir(dir);
411 return ret;
412 }
413
414 #ifdef RUN_STANDALONE
print_battery_status(struct battery_status * status)415 void print_battery_status(struct battery_status *status)
416 {
417 printf("present: %d\n", status->present);
418 printf("Battery state %d\n", status->state);
419 printf("Battery present rate %d\n", status->present_rate);
420 printf("Battery remining capacity %d\n", status->remaining_capacity);
421 printf("Battery present voltage %d\n", status->present_voltage);
422 }
423 #endif /*RUN_STANDALONE*/
424
write_battery_status_to_xenstore(struct battery_status * status)425 void write_battery_status_to_xenstore(struct battery_status *status)
426 {
427 char val[35];
428
429 xs_mkdir(xs, XBT_NULL, "/pm");
430
431 memset(val, 0, 35);
432 snprintf(val, 3, "%02x", 16);
433 write_ulong_lsb_first(val+2, status->state);
434 write_ulong_lsb_first(val+10, status->present_rate);
435 write_ulong_lsb_first(val+18, status->remaining_capacity);
436 write_ulong_lsb_first(val+26, status->present_voltage);
437
438 xs_write(xs, XBT_NULL, "/pm/bst", val, 35);
439 }
440
wait_for_and_update_battery_status_request(void)441 int wait_for_and_update_battery_status_request(void)
442 {
443 DIR *dir;
444 int ret = 0;
445 unsigned int count;
446 struct battery_status status;
447
448 while ( true )
449 {
450 /* KN:@TODO - It is rather inefficient to not cache the file handle.
451 * Switch to caching file handle.
452 */
453 dir = opendir(BATTERY_DIR_PATH);
454 if ( !dir )
455 return 0;
456
457 while ( get_next_battery_info_or_status(dir, BST, (void *)&status) )
458 {
459 #ifdef RUN_STANDALONE
460 print_battery_status(&status);
461 #endif
462 if ( status.present == YES )
463 {
464 write_battery_status_to_xenstore(&status);
465 ret = 1;
466 /* rethink this; though I have never seen, there might be
467 * systems out there with more than one battery device
468 * present
469 */
470 break;
471 }
472 }
473 closedir(dir);
474 xs_watch(xs, "/pm/events", "refreshbatterystatus");
475 xs_read_watch(xs, &count);
476 }
477
478 return ret;
479 }
480
481 /* Borrowed daemonize from xenstored - Initially written by Stevens. */
daemonize(void)482 static void daemonize(void)
483 {
484 pid_t pid;
485
486 if ( (pid = fork()) < 0 )
487 exit(1);
488
489 if ( pid != 0 )
490 exit(0);
491
492 setsid();
493
494 if ( (pid = fork()) < 0 )
495 exit(1);
496
497 if ( pid != 0 )
498 exit(0);
499
500 if ( chdir("/") == -1 )
501 exit(1);
502
503 umask(0);
504 }
505
main(int argc,char * argv[])506 int main(int argc, char *argv[])
507 {
508 #ifndef RUN_STANDALONE
509 daemonize();
510 #endif
511 xs = xs_open(0);
512 if ( xs == NULL )
513 return -1;
514
515 if ( write_one_time_battery_info() == 0 )
516 {
517 xs_close(xs);
518 return -1;
519 }
520
521 wait_for_and_update_battery_status_request();
522 xs_close(xs);
523 return 0;
524 }
525
526