blob: fd5069e06f9adf2677336054b08675a0df4be102 [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <fcntl.h>
#include <healthd/healthd.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <cutils/android_reboot.h>
#include <cutils/klog.h>
#define BACKLIGHT_ON_LEVEL 100
#define BACKLIGHT_OFF_LEVEL 0
#define RED_LED_PATH "/sys/class/leds/red/brightness"
#define GREEN_LED_PATH "/sys/class/leds/green/brightness"
#define BLUE_LED_PATH "/sys/class/leds/blue/brightness"
#define BACKLIGHT_PATH "/sys/class/leds/lcd-backlight/brightness"
#define CHARGING_ENABLED_PATH "/sys/class/power_supply/battery/charging_enabled"
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
#define LOGE(x...) do { KLOG_ERROR("charger", x); } while (0)
#define LOGW(x...) do { KLOG_WARNING("charger", x); } while (0)
#define LOGV(x...) do { KLOG_DEBUG("charger", x); } while (0)
#define INIT_TRUE 1
#define INIT_FALSE 0
enum {
RED_LED = 0x01 << 0,
GREEN_LED = 0x01 << 1,
BLUE_LED = 0x01 << 2,
};
struct led_ctl {
int color;
const char *path;
};
struct led_ctl leds[3] =
{{RED_LED, RED_LED_PATH},
{GREEN_LED, GREEN_LED_PATH},
{BLUE_LED, BLUE_LED_PATH}};
struct soc_led_color_mapping {
int soc;
int color;
};
/* Increasing battery charge percentage vs LED color mapping */
struct soc_led_color_mapping soc_leds[3] = {
{15, RED_LED},
{95, RED_LED | GREEN_LED},
{100, GREEN_LED},
};
static int set_tricolor_led(int on, int color)
{
int fd, i;
char buffer[10];
for (i = 0; i < (int)ARRAY_SIZE(leds); i++) {
if ((color & leds[i].color) && (access(leds[i].path, R_OK | W_OK) == 0)) {
fd = open(leds[i].path, O_RDWR);
if (fd < 0) {
LOGE("Could not open red led node\n");
goto cleanup;
}
if (on)
snprintf(buffer, sizeof(int), "%d\n", 255);
else
snprintf(buffer, sizeof(int), "%d\n", 0);
if (write(fd, buffer, strlen(buffer)) < 0)
LOGE("Could not write to led node\n");
cleanup:
if (fd >= 0)
close(fd);
}
}
return 0;
}
static int set_battery_soc_leds(int soc)
{
int i, color;
static int old_color = -1;
int range_max = ARRAY_SIZE(soc_leds) - 1;
if (range_max < 0)
return 0;
color = soc_leds[range_max].color;
for (i = 0; i <= range_max ; i++) {
if (soc < soc_leds[i].soc) {
color = soc_leds[i].color;
break;
}
}
if (old_color != color) {
if (old_color >= 0)
set_tricolor_led(0, old_color);
set_tricolor_led(1, color);
old_color = color;
}
return 0;
}
static int is_usb_charger_valid()
{
const char *usb_charger_path = "/sys/class/power_supply/usb/type";
char buf[32] = {'\0'};
int fd = 0;
int open_count = 3;
int sleep_count = 0;
while ((fd <= 0) && (open_count-- > 0)) {
fd = open(usb_charger_path, O_RDONLY);
if (fd > 0) {
break;
}
usleep(100000);
}
if (fd > 0) {
while (!strstr(buf, "USB") && (sleep_count <= 20)) {
read(fd, buf, 32);
if (strstr(buf, "USB")) {
close(fd);
return 1;
} else {
usleep(100000);
}
sleep_count++;
}
} else {
sleep(2);
}
return 0;
}
static void wait_for_usb_ps_ok()
{
const char *usb_ps_online = "/sys/class/power_supply/usb/online";
char buf[8] = {'\0'};
int fd = 0;
int open_count = 3;
int sleep_count = 0;
while ((fd <= 0) && (open_count-- > 0)) {
fd = open(usb_ps_online, O_RDONLY);
if (fd > 0) {
break;
}
usleep(100000);
}
if (fd > 0) {
while ((buf[0] != '1') && (sleep_count <= 20)) {
read(fd, buf, 2);
if (buf[0] == '1') {
close(fd);
return;
} else {
usleep(100000);
}
sleep_count++;
}
} else {
sleep(2);
}
}
void healthd_board_init(struct healthd_config*)
{
// use defaults
}
int healthd_board_battery_update(struct android::BatteryProperties*)
{
// return 0 to log periodic polled battery status to kernel log
return 0;
}
void healthd_board_mode_charger_draw_battery(struct android::BatteryProperties*)
{
}
void healthd_board_mode_charger_battery_update(struct android::BatteryProperties* props)
{
static int old_soc = -1;
int soc = props->batteryLevel;
if (old_soc != soc) {
old_soc = soc;
set_battery_soc_leds(soc);
}
}
void healthd_board_mode_charger_set_backlight(bool value)
{
int fd;
char buffer[10];
if (access(BACKLIGHT_PATH, R_OK | W_OK) != 0)
{
LOGW("Backlight control not support\n");
}
memset(buffer, '\0', sizeof(buffer));
fd = open(BACKLIGHT_PATH, O_RDWR);
if (fd < 0) {
LOGE("Could not open backlight node : %s\n", strerror(errno));
goto cleanup;
}
LOGV("Enabling backlight\n");
snprintf(buffer, sizeof(buffer), "%d\n",
value?BACKLIGHT_ON_LEVEL:BACKLIGHT_OFF_LEVEL);
if (write(fd, buffer,strlen(buffer)) < 0) {
LOGE("Could not write to backlight node : %s\n", strerror(errno));
goto cleanup;
}
cleanup:
if (fd >= 0)
close(fd);
}
int healthd_board_mode_charger_init()
{
int ret;
char buff[8] = "\0";
int charging_enabled = 0;
int fd;
if (is_usb_charger_valid()) {
wait_for_usb_ps_ok();
}
/* check the charging is enabled or not */
fd = open(CHARGING_ENABLED_PATH, O_RDONLY);
if (fd < 0)
//let return true only because if we send fail it wll reboot.
return INIT_TRUE;
ret = read(fd, buff, sizeof(buff));
close(fd);
if (ret > 0 && sscanf(buff, "%d\n", &charging_enabled)) {
/* if charging is disabled, reboot and exit power off charging */
if (charging_enabled)
return INIT_TRUE;
LOGW("android charging is disabled, exit!\n");
return INIT_FALSE;
}
return INIT_TRUE;
}