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