Skip to content

Commit 12bd3d9

Browse files
committed
vigor: add chargeled program
1 parent 526bb5e commit 12bd3d9

4 files changed

Lines changed: 301 additions & 6 deletions

File tree

chargeled/Android.mk

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#
2+
# Copyright (C) 2014 The CyanogenMod Project
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
17+
LOCAL_PATH := $(call my-dir)
18+
include $(CLEAR_VARS)
19+
20+
LOCAL_SRC_FILES := chargeled.c
21+
22+
LOCAL_CFLAGS += -Wall
23+
24+
LOCAL_STATIC_LIBRARIES := libc liblog libcutils
25+
LOCAL_FORCE_STATIC_EXECUTABLE := true
26+
27+
LOCAL_MODULE := chargeled
28+
LOCAL_MODULE_TAGS := optional eng
29+
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
30+
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
31+
include $(BUILD_EXECUTABLE)

chargeled/chargeled.c

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
/*
2+
* Copyright (C) 2014 The CyanogenMod Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#define LOG_TAG "[CHRG_LED]"
18+
#define KLOG_LEVEL 6
19+
20+
#include <errno.h>
21+
#include <stdio.h>
22+
#include <stdlib.h>
23+
#include <string.h>
24+
#include <unistd.h>
25+
#include <sys/time.h>
26+
#include <cutils/klog.h>
27+
#include <cutils/uevent.h>
28+
#include <sys/epoll.h>
29+
#include <sys/timerfd.h>
30+
31+
#define POWER_SUPPLY_SUBSYSTEM "SUBSYSTEM=power_supply"
32+
33+
#define BATTERY_STATUS_FILE "/sys/class/power_supply/battery/status"
34+
#define AMBER_LED "/sys/class/leds/amber/brightness"
35+
#define GREEN_LED "/sys/class/leds/green/brightness"
36+
37+
#define LED_OFF "0"
38+
#define LED_ON "1"
39+
40+
#define STR_BUF_SIZE 128
41+
#define UEVENT_BUF_SIZE 64*1024
42+
#define UEVENT_MSG_LEN 1024
43+
static int uevent_fd;
44+
45+
/* Matches frameworks/native/include/batteryservice/BatteryService.h */
46+
enum {
47+
BATTERY_STATUS_UNKNOWN = 1,
48+
BATTERY_STATUS_CHARGING = 2,
49+
BATTERY_STATUS_DISCHARGING = 3,
50+
BATTERY_STATUS_NOT_CHARGING = 4,
51+
BATTERY_STATUS_FULL = 5,
52+
};
53+
54+
struct sysfs_string_enum_map {
55+
char* str;
56+
int val;
57+
} battery_status_map[] = {
58+
{ "Unknown", BATTERY_STATUS_UNKNOWN },
59+
{ "Charging", BATTERY_STATUS_CHARGING },
60+
{ "Discharging", BATTERY_STATUS_DISCHARGING },
61+
{ "Not charging", BATTERY_STATUS_NOT_CHARGING },
62+
{ "Full", BATTERY_STATUS_FULL },
63+
{ NULL, 0 },
64+
};
65+
66+
static int map_sysfs_string(const char* str) {
67+
int i;
68+
int ret = -1;
69+
70+
for (i = 0; battery_status_map[i].str; i++) {
71+
if (!strcmp(str, battery_status_map[i].str))
72+
ret = battery_status_map[i].val;
73+
}
74+
75+
if (ret == -1)
76+
KLOG_ERROR(LOG_TAG, "%s: unknown charging status '%s'\n",
77+
__func__, str);
78+
79+
return ret;
80+
}
81+
82+
static int get_charging_status() {
83+
char batt_stat_str[STR_BUF_SIZE];
84+
int ret = -1;
85+
size_t ln;
86+
87+
FILE *bstat;
88+
bstat = fopen(BATTERY_STATUS_FILE, "r");
89+
if (bstat) {
90+
if (fgets(batt_stat_str, STR_BUF_SIZE, bstat) == NULL) {
91+
KLOG_ERROR(LOG_TAG, "%s: failed to read %s; errno=%s\n",
92+
__func__, BATTERY_STATUS_FILE, strerror(errno));
93+
return -(errno);
94+
}
95+
96+
if (!strcmp(batt_stat_str, "")) {
97+
KLOG_ERROR(LOG_TAG, "%s: empty battery status file %s\n",
98+
__func__, BATTERY_STATUS_FILE);
99+
return 0;
100+
}
101+
102+
ln = strlen(batt_stat_str) - 1;
103+
if (batt_stat_str[ln] == '\n')
104+
batt_stat_str[ln] = '\0';
105+
106+
fclose(bstat);
107+
ret = map_sysfs_string(batt_stat_str);
108+
} else {
109+
KLOG_ERROR(LOG_TAG, "%s: could not open %s; errno=%s\n",
110+
__func__, BATTERY_STATUS_FILE, strerror(errno));
111+
return -(errno);
112+
}
113+
114+
return ret;
115+
}
116+
117+
static void update_led(int charge_status) {
118+
FILE *aled, *gled;
119+
aled = fopen(AMBER_LED, "w");
120+
if (!aled) {
121+
KLOG_ERROR(LOG_TAG, "%s: could not open amber LED: %s\n",
122+
__func__, AMBER_LED);
123+
return;
124+
} else {
125+
gled = fopen(GREEN_LED, "w");
126+
if (!gled) {
127+
fclose(aled);
128+
KLOG_ERROR(LOG_TAG, "%s: could not open green LED: %s\n",
129+
__func__, GREEN_LED);
130+
return;
131+
}
132+
}
133+
134+
KLOG_INFO(LOG_TAG, "%s: setting charging status '%d'\n",
135+
__func__, charge_status);
136+
137+
switch (charge_status) {
138+
case BATTERY_STATUS_CHARGING:
139+
fputs(LED_ON, aled);
140+
fputs(LED_OFF, gled);
141+
break;
142+
case BATTERY_STATUS_FULL:
143+
fputs(LED_OFF, aled);
144+
fputs(LED_ON, gled);
145+
break;
146+
default:
147+
fputs(LED_OFF, aled);
148+
fputs(LED_OFF, gled);
149+
break;
150+
}
151+
152+
fclose(aled);
153+
fclose(gled);
154+
}
155+
156+
static void chargeled_update() {
157+
static int last_charge_status = BATTERY_STATUS_UNKNOWN;
158+
int charge_status;
159+
160+
charge_status = get_charging_status();
161+
if (charge_status <= 0 || charge_status == last_charge_status)
162+
return;
163+
164+
last_charge_status = charge_status;
165+
update_led(charge_status);
166+
}
167+
168+
static int uevent_init() {
169+
uevent_fd = uevent_open_socket(UEVENT_BUF_SIZE, true);
170+
171+
if (uevent_fd >= 0) {
172+
fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
173+
} else {
174+
KLOG_ERROR(LOG_TAG, "%s: uevent_open_socket failed; errno=%s\n",
175+
__func__, strerror(errno));
176+
return errno;
177+
}
178+
179+
return 0;
180+
}
181+
182+
static void uevent_event() {
183+
char msg[UEVENT_MSG_LEN+2];
184+
char *cp;
185+
int n;
186+
187+
n = uevent_kernel_multicast_recv(uevent_fd, msg, UEVENT_MSG_LEN);
188+
if (n <= 0)
189+
return;
190+
if (n >= UEVENT_MSG_LEN) /* overflow -- discard */
191+
return;
192+
193+
msg[n] = '\0';
194+
msg[n+1] = '\0';
195+
cp = msg;
196+
197+
while (*cp) {
198+
if (!strcmp(cp, POWER_SUPPLY_SUBSYSTEM)) {
199+
chargeled_update();
200+
break;
201+
}
202+
203+
/* advance to after the next \0 */
204+
while (*cp++)
205+
;
206+
}
207+
}
208+
209+
static int chargeled_mainloop() {
210+
struct epoll_event ev;
211+
int epollfd;
212+
int maxevents = 1;
213+
struct epoll_event events[maxevents];
214+
int nevents;
215+
int n;
216+
217+
epollfd = epoll_create(1);
218+
if (epollfd == -1) {
219+
KLOG_ERROR(LOG_TAG, "%s: epoll_create failed; errno=%s\n",
220+
__func__, strerror(errno));
221+
return errno;
222+
}
223+
224+
ev.events = EPOLLIN | EPOLLWAKEUP;
225+
ev.data.ptr = (void *)uevent_event;
226+
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1) {
227+
KLOG_ERROR(LOG_TAG,
228+
"%s: epoll_ctl for uevent_fd failed; errno=%s\n",
229+
__func__, strerror(errno));
230+
return errno;
231+
}
232+
233+
/* If we've gotten this far, perform an LED update before the loop */
234+
chargeled_update();
235+
236+
while (1) {
237+
nevents = epoll_wait(epollfd, events, maxevents, -1);
238+
239+
if (nevents == -1) {
240+
if (errno == EINTR)
241+
continue;
242+
KLOG_ERROR(LOG_TAG, "%s: epoll_wait failed\n", __func__);
243+
break;
244+
}
245+
246+
for (n = 0; n < nevents; ++n) {
247+
if (events[n].data.ptr)
248+
(*(void (*)())events[n].data.ptr)();
249+
}
250+
}
251+
252+
return errno;
253+
}
254+
255+
int main() {
256+
int ret;
257+
258+
klog_set_level(KLOG_LEVEL);
259+
260+
ret = uevent_init();
261+
if (ret == 0)
262+
ret = chargeled_mainloop();
263+
264+
return ret;
265+
}
Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
on boot
2-
3-
service choice_fn /sbin/choice_fn
4-
oneshot
2+
start chargeled
53

64
service detect_key /sbin/detect_key
75
disabled
8-
oneshot
96

107
service offmode_charging /sbin/offmode_charging
118
disabled
12-
oneshot
139

1410
service power_test /sbin/power_test
1511
disabled
16-
oneshot
1712

1813
service htcbatt /sbin/htcbatt
1914
oneshot
15+
16+
service chargeled /sbin/chargeled
17+
disabled

vigor.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ PRODUCT_PACKAGES += \
3232
detect_key \
3333
htcbatt \
3434
offmode_charging \
35+
chargeled \
3536
power_test
3637

3738
# Ramdisk

0 commit comments

Comments
 (0)