1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <dirent.h>
6 #include <fbl/algorithm.h>
7 #include <fbl/string_buffer.h>
8 #include <fbl/vector.h>
9 #include <fcntl.h>
10 #include <fuchsia/hardware/power/c/fidl.h>
11 #include <lib/fdio/unsafe.h>
12 #include <lib/fdio/util.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <unistd.h>
17 #include <zircon/syscalls.h>
18 #include <zircon/types.h>
19 
20 typedef struct {
21     int type;
22     char name[255];
23     uint8_t state;
24     zx_handle_t events;
25     zx_handle_t fidl_channel;
26 } pwrdev_t;
27 
28 struct arg_data {
29     bool debug;
30     bool poll_events;
31 };
32 
33 static const char* type_to_string[] = {"AC", "battery"};
34 
get_source_info(zx_handle_t channel,struct fuchsia_hardware_power_SourceInfo * info)35 zx_status_t get_source_info(zx_handle_t channel, struct fuchsia_hardware_power_SourceInfo* info) {
36     zx_status_t status, op_status;
37 
38     // If either fails return the error we see (0 is success, errors are negative)
39     status = fuchsia_hardware_power_SourceGetPowerInfo(channel, &op_status, info);
40     zx_status_t result = fbl::min(status, op_status);
41     if (result != ZX_OK) {
42         fprintf(stderr, "SourceGetPowerInfo failed (transport: %d, operation: %d)\n",
43                 status, op_status);
44     }
45     return result;
46 }
47 
48 static const char* state_to_string[] = {"online", "discharging", "charging", "critical"};
49 static const char* state_offline = "offline/not present";
get_state_string(uint32_t state,fbl::StringBuffer<256> * buf)50 const char* get_state_string(uint32_t state, fbl::StringBuffer<256>* buf) {
51     buf->Clear();
52     for (size_t i = 0; i < countof(state_to_string); i++) {
53         if (state & (1 << i)) {
54             if (buf->length()) {
55                 buf->Append(", ");
56             }
57             buf->Append(state_to_string[i]);
58         }
59     }
60 
61     return (buf->length() > 0) ? buf->c_str() : state_offline;
62 }
63 
get_battery_info(zx_handle_t ch)64 static zx_status_t get_battery_info(zx_handle_t ch) {
65     struct fuchsia_hardware_power_BatteryInfo binfo = {};
66     zx_status_t op_status;
67     zx_status_t status = fuchsia_hardware_power_SourceGetBatteryInfo(ch, &op_status, &binfo);
68     if (status != ZX_OK) {
69         printf("GetBatteryInfo returned %d\n", status);
70         return status;
71     }
72 
73     const char* unit = (binfo.unit == fuchsia_hardware_power_BatteryUnit_MW) ? "mW" : "mA";
74     printf("             design capacity: %d %s\n", binfo.design_capacity, unit);
75     printf("          last full capacity: %d %s\n", binfo.last_full_capacity, unit);
76     printf("              design voltage: %d mV\n", binfo.design_voltage);
77     printf("            warning capacity: %d %s\n", binfo.capacity_warning, unit);
78     printf("                low capacity: %d %s\n", binfo.capacity_low, unit);
79     printf("     low/warning granularity: %d %s\n",
80            binfo.capacity_granularity_low_warning, unit);
81     printf("    warning/full granularity: %d %s\n",
82            binfo.capacity_granularity_warning_full, unit);
83     printf("                present rate: %d %s\n", binfo.present_rate, unit);
84     printf("          remaining capacity: %d %s\n", binfo.remaining_capacity, unit);
85     printf("             present voltage: %d mV\n", binfo.present_voltage);
86     printf("==========================================\n");
87     printf("remaining battery percentage: %d %%\n",
88            binfo.remaining_capacity * 100 / binfo.last_full_capacity);
89     if (binfo.present_rate < 0) {
90         printf("      remaining battery life: %.2f h\n",
91                (float)binfo.remaining_capacity / (float)binfo.present_rate * -1);
92     }
93     putchar('\n');
94     return ZX_OK;
95 }
96 
parse_arguments(int argc,char ** argv,struct arg_data * args)97 void parse_arguments(int argc, char** argv, struct arg_data* args) {
98     int opt;
99     while ((opt = getopt(argc, argv, "p")) != -1) {
100         switch (opt) {
101         case 'p':
102             args->poll_events = true;
103             break;
104         default:
105             fprintf(stderr, "Invalid arg: %c\nUsage: %s [-p]\n", opt, argv[0]);
106             exit(EXIT_FAILURE);
107         }
108     }
109 }
110 
handle_event(pwrdev_t & interface)111 void handle_event(pwrdev_t& interface) {
112     zx_status_t status;
113     struct fuchsia_hardware_power_SourceInfo info;
114     if ((status = get_source_info(interface.fidl_channel, &info)) != ZX_OK) {
115         exit(EXIT_FAILURE);
116     }
117 
118     fbl::StringBuffer<256> old_buf;
119     fbl::StringBuffer<256> new_buf;
120     printf("%s (%s): state change %s (%#x) -> %s (%#x)\n", interface.name,
121             type_to_string[interface.type],
122             get_state_string(interface.state, &old_buf), interface.state,
123             get_state_string(info.state, &new_buf), info.state);
124 
125     if (interface.type == fuchsia_hardware_power_PowerType_BATTERY &&
126         (info.state & fuchsia_hardware_power_POWER_STATE_ONLINE)) {
127         if (get_battery_info(interface.fidl_channel) != ZX_OK) {
128             exit(EXIT_FAILURE);
129         }
130     }
131 
132     interface.state = info.state;
133 }
134 
poll_events(const fbl::Vector<pwrdev_t> & interfaces)135 void poll_events(const fbl::Vector<pwrdev_t>& interfaces) {
136 
137     zx_wait_item_t* items = new zx_wait_item_t[interfaces.size()];
138     for (size_t i = 0; i < interfaces.size(); i++) {
139         items[i].handle = interfaces[i].events;
140         items[i].waitfor = ZX_USER_SIGNAL_0;
141         items[i].pending = 0;
142     }
143 
144     zx_status_t status;
145     printf("waiting for events...\n\n");
146     for (;;) {
147         status = zx_object_wait_many(items, interfaces.size(), ZX_TIME_INFINITE);
148         if (status != ZX_OK) {
149             printf("zx_object_wait_many() returned %d\n", status);
150             exit(EXIT_FAILURE);
151         }
152 
153         for (size_t i = 0; i < interfaces.size(); i++) {
154             if (items[i].pending & ZX_USER_SIGNAL_0) {
155                 handle_event(interfaces[i]);
156             }
157         }
158     }
159 }
160 
main(int argc,char ** argv)161 int main(int argc, char** argv) {
162     struct arg_data args = {};
163     parse_arguments(argc, argv, &args);
164 
165     struct dirent* de;
166     DIR* dir = opendir("/dev/class/power");
167     if (!dir) {
168         printf("Failed to read /dev/class/power\n");
169         exit(EXIT_FAILURE);
170     }
171 
172     fbl::StringBuffer<256> state_str;
173     fbl::Vector<pwrdev_t> interfaces;
174     while ((de = readdir(dir)) != NULL) {
175         int fd = openat(dirfd(dir), de->d_name, O_RDONLY);
176         if (fd < 0) {
177             printf("Failed to read %s, skipping: %d\n", de->d_name, fd);
178             continue;
179         }
180 
181         struct fuchsia_hardware_power_SourceInfo pinfo;
182         zx_handle_t ch;
183         zx_status_t status;
184         zx_status_t op_status;
185 
186         status = fdio_get_service_handle(fd, &ch);
187         if (status != ZX_OK) {
188             printf("Failed to get service handle for %s, skipping: %d!\n", de->d_name, status);
189             continue;
190         }
191 
192         status = get_source_info(ch, &pinfo);
193         if (status != ZX_OK) {
194             printf("Failed to read from source %s, skipping\n", de->d_name);
195             continue;
196         }
197 
198 
199         printf("[%s] type: %s, state: %s (%#x)\n", de->d_name, type_to_string[pinfo.type],
200                 get_state_string(pinfo.state, &state_str), pinfo.state);
201 
202         if (pinfo.type == fuchsia_hardware_power_PowerType_BATTERY &&
203             (pinfo.state & fuchsia_hardware_power_POWER_STATE_ONLINE)) {
204             if (get_battery_info(ch) != ZX_OK) {
205                 fprintf(stderr, "Couldn't read battery information for %s, skipping\n", de->d_name);
206                 continue;
207             }
208         }
209 
210         if (args.poll_events) {
211             zx_handle_t h = ZX_HANDLE_INVALID;
212             status = fuchsia_hardware_power_SourceGetStateChangeEvent(ch, &op_status, &h);
213             if (status != ZX_OK || op_status != ZX_OK) {
214                 printf("failed to get event: %d / %d\n", status, op_status);
215                 return status;
216             }
217 
218             pwrdev_t dev = {};
219             dev.type = pinfo.type;
220             dev.state = pinfo.state;
221             dev.fidl_channel = ch;
222             dev.events = h;
223             memcpy(dev.name, de->d_name, sizeof(dev.name));
224             interfaces.push_back(dev);
225         }
226     }
227 
228     if (args.poll_events) {
229         poll_events(interfaces);
230     }
231 
232     return 0;
233 }
234