android_device_qcom_common/healthd/healthd_board_msm.cpp

325 lines
9.6 KiB
C++

/*
*Copyright (c) 2015, The Linux Foundation. All rights reserved.
*
*Redistribution and use in source and binary forms, with or without
*modification, are permitted provided that the following conditions are
*met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
*THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
*WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
*MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
*ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
*BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
*CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
*SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
*BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
*WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
*OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
*IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <dirent.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <cutils/klog.h>
#include <batteryservice/BatteryService.h>
#include <cutils/android_reboot.h>
#include <healthd.h>
#include "minui/minui.h"
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
#define HVDCP_CHARGER "USB_HVDCP"
#define HVDCP_BLINK_TYPE 2
#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 RED_LED_BLINK_PATH "/sys/class/leds/red/blink"
#define GREEN_LED_BLINK_PATH "/sys/class/leds/green/blink"
#define BACKLIGHT_PATH "/sys/class/leds/lcd-backlight/brightness"
#define CHARGING_ENABLED_PATH "/sys/class/power_supply/battery/charging_enabled"
#define CHARGER_TYPE_PATH "/sys/class/power_supply/usb/type"
#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)
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}};
#define HVDCP_COLOR_MAP (RED_LED | GREEN_LED)
struct soc_led_color_mapping {
int soc;
int color;
};
struct soc_led_color_mapping soc_leds[3] = {
{15, RED_LED},
{90, RED_LED | GREEN_LED},
{100, GREEN_LED},
};
static int write_file_int(char const* path, int value)
{
int fd;
char buffer[20];
int rc = -1, bytes;
fd = open(path, O_WRONLY);
if (fd >= 0) {
bytes = snprintf(buffer, sizeof(buffer), "%d\n", value);
rc = write(fd, buffer, bytes);
close(fd);
}
return rc > 0 ? 0 : -1;
}
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 bool is_hvdcp_inserted()
{
bool hvdcp = false;
char buff[12] = "\0";
int fd, cnt;
fd = open(CHARGER_TYPE_PATH, O_RDONLY);
if (fd >= 0) {
cnt = read(fd, buff, sizeof(buff));
if (cnt > 0 && !strncmp(buff, HVDCP_CHARGER, 9))
hvdcp = true;
close(fd);
}
return hvdcp;
}
static int leds_blink_for_hvdcp_allow(void)
{
int rc = 0, bytes;
int red_blink_fd = -1, green_blink_fd = -1, type_fd = -1;
char buf[20];
green_blink_fd = open(GREEN_LED_BLINK_PATH, O_RDWR);
red_blink_fd = open(RED_LED_BLINK_PATH, O_RDWR);
if (red_blink_fd < 0 && green_blink_fd < 0) {
LOGE("Could not open red && green led blink node\n");
} else {
type_fd = open(CHARGER_TYPE_PATH, O_RDONLY);
if (type_fd < 0) {
LOGE("Could not open USB type node\n");
close(red_blink_fd);
close(green_blink_fd);
return rc;
} else {
close(type_fd);
if (red_blink_fd > 0) {
rc |= RED_LED;
bytes = snprintf(buf, sizeof(buf), "%d\n", 0);
if (write(red_blink_fd, buf, bytes) < 0) {
LOGE("Fail to write: %s\n", RED_LED_BLINK_PATH);
rc = 0;
}
close(red_blink_fd);
}
if (green_blink_fd > 0) {
rc |= GREEN_LED;
bytes = snprintf(buf, sizeof(buf), "%d\n", 0);
if (write(green_blink_fd, buf, bytes) < 0) {
LOGE("Fail to write: %s\n", GREEN_LED_BLINK_PATH);
rc = 0;
}
close(green_blink_fd);
}
}
}
return rc;
}
#define STR_LEN 8
void healthd_board_mode_charger_draw_battery(
struct android::BatteryProperties *batt_prop)
{
char cap_str[STR_LEN];
int x, y;
int str_len_px;
static int char_height = -1, char_width = -1;
if (char_height == -1 && char_width == -1)
gr_font_size(&char_width, &char_height);
snprintf(cap_str, (STR_LEN - 1), "%d%%", batt_prop->batteryLevel);
str_len_px = gr_measure(cap_str);
x = (gr_fb_width() - str_len_px) / 2;
y = (gr_fb_height() + char_height) / 2;
gr_color(0xa4, 0xc6, 0x39, 255);
gr_text(x, y, cap_str, 0);
}
void healthd_board_mode_charger_battery_update(
struct android::BatteryProperties *batt_prop)
{
static int blink_for_hvdcp = -1;
static int old_color = 0;
int i, color, soc, rc;
bool blink = false;
if (blink_for_hvdcp == -1)
blink_for_hvdcp = leds_blink_for_hvdcp_allow();
if ((blink_for_hvdcp > 0) && is_hvdcp_inserted())
blink = true;
soc = batt_prop->batteryLevel;
for (i = 0; i < ((int)ARRAY_SIZE(soc_leds) - 1); i++) {
if (soc < soc_leds[i].soc)
break;
}
color = soc_leds[i].color;
if (old_color != color) {
if ((color == HVDCP_COLOR_MAP) && blink) {
if (blink_for_hvdcp & RED_LED) {
rc = write_file_int(RED_LED_BLINK_PATH, HVDCP_BLINK_TYPE);
if (rc < 0) {
LOGE("Fail to write: %s\n", RED_LED_BLINK_PATH);
return;
}
}
if (blink_for_hvdcp & GREEN_LED) {
rc = write_file_int(GREEN_LED_BLINK_PATH, HVDCP_BLINK_TYPE);
if (rc < 0) {
LOGE("Fail to write: %s\n", GREEN_LED_BLINK_PATH);
return;
}
}
} else {
set_tricolor_led(0, old_color);
set_tricolor_led(1, color);
old_color = color;
LOGV("soc = %d, set led color 0x%x\n", soc, soc_leds[i].color);
}
}
}
#define BACKLIGHT_ON_LEVEL 100
#define BACKLIGHT_OFF_LEVEL 0
void healthd_board_mode_charger_set_backlight(bool en)
{
int fd;
char buffer[10];
if (access(BACKLIGHT_PATH, R_OK | W_OK) != 0)
{
LOGW("Backlight control not support\n");
return;
}
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("set backlight status to %d\n", en);
if (en)
snprintf(buffer, sizeof(buffer), "%d\n", BACKLIGHT_ON_LEVEL);
else
snprintf(buffer, sizeof(buffer), "%d\n", 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);
}
void healthd_board_mode_charger_init()
{
int ret;
char buff[8] = "\0";
int charging_enabled = 0;
int fd;
/* check the charging is enabled or not */
fd = open(CHARGING_ENABLED_PATH, O_RDONLY);
if (fd < 0)
return;
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;
LOGW("android charging is disabled, exit!\n");
android_reboot(ANDROID_RB_RESTART, 0, 0);
}
}
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 1;
}