battery_monitor: add battery monitor app [1/2]
PD#SWPL-88481
Problem:
Unable to monitor battery status.
Solution:
Add battery monitor app to monitor battery status.
Verify:
A113L-AD403
Change-Id: I8d6c3e84ed337de1df9fea0bb3cb07d0b3292f94
Signed-off-by: shu.wang <shu.wang@amlogic.com>
diff --git a/utils/LICENSE b/utils/LICENSE
new file mode 100644
index 0000000..ec38242
--- /dev/null
+++ b/utils/LICENSE
@@ -0,0 +1,12 @@
+Amlogic Reference Source Code (Software) is licensed, not sold,
+and that title to and ownership of the Software and any portion thereof remain with Amlogic or its licensors.
+
+All express and implied warranties are disclaimed on behalf of Amlogic.
+Liability of Amlogic and its licensors is excluded for any special, indirect, exemplary, incidental or consequential damages.
+
+You are prohibited from
+(a) copying the Software, except as reasonably necessary for internal back-up purposes,
+(b) using and/or transferring the Software to any third party apart from the Android TV build release,
+(c) modifying the Software,
+(d) attempting to reverse engineer, decompile or disassemble any portion of the Software, or
+(e) exporting the Software or any underlying technology in contravention of any applicable U.S. or foreign export laws and regulations.
diff --git a/utils/Makefile b/utils/Makefile
index 0a180b7..485892c 100644
--- a/utils/Makefile
+++ b/utils/Makefile
@@ -5,6 +5,9 @@
ifeq ($(USE_AUTO_SHUTDOWN),y)
UTILS += auto_shutdown
endif
+ifeq ($(USE_BATTERY_MONITOR),y)
+UTILS += battery_monitor
+endif
.PHONY: all install clean
#CFLAGS += $(AML_UTIL_PRIV_FLAGS)
@@ -46,6 +49,9 @@
auto_shutdown: auto_shutdown.c
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS) -lpthread
+battery_monitor: battery_monitor.c
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
all: $(UTILS)
diff --git a/utils/battery_monitor.c b/utils/battery_monitor.c
new file mode 100644
index 0000000..fc51904
--- /dev/null
+++ b/utils/battery_monitor.c
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 2022 Amlogic, Inc. All rights reserved.
+ * This source code is subject to the terms and conditions defined
+ * in the file 'LICENSE' which is part of this source code package.
+ * Description: battery monitor file
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <error.h>
+#include <errno.h>
+#include <poll.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <linux/netlink.h>
+
+#define CONFIG_LOGLEVEL (log_level)
+
+#define PRINT_ERROR_LEVEL 1
+#define PRINT_WARNING_LEVEL 2
+#define PRINT_INFO_LEVEL 3
+#define PRINT_DEBUG_LEVEL 4
+
+#define _printf(level, fmt, ...) ({level <= CONFIG_LOGLEVEL ? printf(fmt, ##__VA_ARGS__) : 0;})
+#define pr_error(fmt, ...) _printf(PRINT_ERROR_LEVEL, "[Battery Monitor][ERROR]" fmt, ##__VA_ARGS__)
+#define pr_warning(fmt, ...) _printf(PRINT_WARNING_LEVEL, "[Battery Monitor][WARNING]" fmt, ##__VA_ARGS__)
+#define pr_info(fmt, ...) _printf(PRINT_INFO_LEVEL, "[Battery Monitor][INFO]" fmt, ##__VA_ARGS__)
+#define pr_debug(fmt, ...) _printf(PRINT_DEBUG_LEVEL, "[Battery Monitor][DEBUG]" fmt, ##__VA_ARGS__)
+
+#define UEVENT_BUFFER_SIZE 2048
+
+#define DEVICE_PATH "/sys/class/power_supply"
+#define REPORT_PERIOD_SETTING_FILE "uevent_period"
+
+/*battery state item*/
+#define STATE_PRESENT "POWER_SUPPLY_PRESENT="
+#define STATE_HEALTH "POWER_SUPPLY_HEALTH="
+#define STATE_CAPACITY "POWER_SUPPLY_CAPACITY="
+#define STATE_VOLTAGE_NOW "POWER_SUPPLY_VOLTAGE_NOW="
+#define STATE_VOLTAGE_OCV "POWER_SUPPLY_VOLTAGE_OCV="
+#define STATE_CURRENT_NOW "POWER_SUPPLY_CURRENT_NOW="
+#define STATE_RESIST "POWER_SUPPLY_RESIST="
+#define STATE_CAPACITY_LEVEL "POWER_SUPPLY_CAPACITY_LEVEL="
+#define STATE_STATUS "POWER_SUPPLY_STATUS="
+#define STATE_TEMPERATURE "POWER_SUPPLY_TEMP="
+#define STATE_UEVENT_PERIOD "POWER_SUPPLY_UEVENT_PERIOD="
+#define STATE__SERIAL_NUMBER "POWER_SUPPLY_SERIAL_NUMBER="
+
+#define REPORT_TIME_MAX 90
+#define REPORT_TIME_MIN 1
+#define REPORT_TIME_DEFAULT 60
+#define REPORT_TIME_LOW_ALARM 1
+
+#define ALARM_CAPACITY_MAX 99
+#define ALARM_CAPACITY_MIN 1
+#define ALARM_CAPACITY_DEFAULT 1
+
+#define DELTA_CAPACITY_ENTER_PRE_ALARM 2
+
+typedef void (* fun)(char *, char *);
+
+struct battery_state_item {
+ char * name;
+ fun fn;
+};
+
+static int set_report_time(int time);
+static void state_capacity_analysis(char *buf, char *name);
+
+static int report_time = REPORT_TIME_DEFAULT;
+static int alarm_capacity = ALARM_CAPACITY_DEFAULT;
+static int report_time_current = 0;
+static char device_name[32] = {0};
+static char log_level = PRINT_INFO_LEVEL;
+
+static const struct battery_state_item battery_state[] = {
+ {
+ .name = STATE_PRESENT,
+ .fn = NULL,
+ },
+ {
+ .name = STATE_HEALTH,
+ .fn = NULL,
+ },
+ {
+ .name = STATE_CAPACITY,
+ .fn = state_capacity_analysis,
+ },
+ {
+ .name = STATE_VOLTAGE_NOW,
+ .fn = NULL,
+ },
+ {
+ .name = STATE_VOLTAGE_OCV,
+ .fn = NULL,
+ },
+ {
+ .name = STATE_CURRENT_NOW,
+ .fn = NULL,
+ },
+ {
+ .name = STATE_RESIST,
+ .fn = NULL,
+ },
+ {
+ .name = STATE_CAPACITY_LEVEL,
+ .fn = NULL,
+ },
+ {
+ .name = STATE_STATUS,
+ .fn = NULL,
+ },
+ {
+ .name = STATE_TEMPERATURE,
+ .fn = NULL,
+ },
+ {
+ .name = STATE_UEVENT_PERIOD,
+ .fn = NULL,
+ },
+ {
+ .name = STATE__SERIAL_NUMBER,
+ .fn = NULL,
+ },
+};
+
+static void state_capacity_analysis(char *buf, char *name)
+{
+ if (!buf || !name) {
+ return;
+ }
+
+ char *val = buf + strlen(name);
+ int capacity = atoi(val);
+
+ if (capacity <= alarm_capacity) {
+ pr_warning("Low power alarm!\n");
+ system("/etc/adckey/adckey_function.sh longpresspower");
+ } else {
+ int delta= capacity - alarm_capacity;
+ if (delta <= DELTA_CAPACITY_ENTER_PRE_ALARM) {
+ if (report_time_current != REPORT_TIME_LOW_ALARM) {
+ set_report_time(REPORT_TIME_LOW_ALARM);
+ }
+ } else {
+ if (report_time_current != report_time) {
+ set_report_time(report_time);
+ }
+ }
+ }
+}
+
+static int battery_monitor_init(void)
+{
+ struct sockaddr_nl client;
+ int fd = -1, ret = -1;
+ int buffersize = 1024;
+ fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
+ if (fd < 0) {
+ pr_error("can't creat socket!\n");
+ return -EPERM;
+ }
+ memset(&client, 0, sizeof(client));
+ client.nl_family = AF_NETLINK;
+ client.nl_pid = getpid();
+ client.nl_groups = 1; /* receive broadcast message*/
+ ret = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &buffersize, sizeof(buffersize));
+ if (ret < 0) {
+ pr_error("can't set socket!\n");
+ close(fd);
+ return -errno;
+ }
+ ret = bind(fd, (struct sockaddr*)&client, sizeof(client));
+ if (ret < 0) {
+ pr_error("can't bind socket!\n");
+ close(fd);
+ return -errno;
+ }
+ return fd;
+}
+
+static int battery_state_analysis(char * buf)
+{
+ if (!buf) {
+ return -EINVAL;
+ }
+
+ for (int i = 0; i < sizeof(battery_state)/sizeof(battery_state[0]); i++) {
+ if (!strncmp(buf, battery_state[i].name, strlen(battery_state[i].name))) {
+ if (battery_state[i].fn)
+ battery_state[i].fn(buf, battery_state[i].name);
+ }
+ }
+}
+
+static int battery_state_change(char *buf, int size)
+{
+ int len = 0;
+ if (!buf) {
+ return -EINVAL;
+ }
+ while (size > 0) {
+ pr_debug("%s\n", buf);
+ len = strlen(buf) + 1;
+ battery_state_analysis(buf);
+ buf += len;
+ size -= len;
+ }
+ pr_debug("\n\n");
+ return 0;
+}
+
+static int battery_state_read(int fd)
+{
+ int len = 0;
+ char buf[UEVENT_BUFFER_SIZE] = {0};
+ /* receive data */
+ len = recv(fd, buf, sizeof(buf), 0);
+ if (len > 0 && strstr(buf, device_name)) {
+ buf[len] = 0;
+ battery_state_change(buf, len);
+ }
+ return 0;
+}
+
+static int set_report_time(int time)
+{
+ char temp[64] = {0};
+ sprintf(temp, "%s/%s/%s", DEVICE_PATH, device_name, REPORT_PERIOD_SETTING_FILE);
+ FILE *fp = fopen(temp, "w");
+ if (fp != NULL) {
+ char buf[16] = {0};
+ sprintf(buf, "%d", time);
+ fwrite(buf, strlen(buf) + 1, 1, fp);
+ fclose(fp);
+ report_time_current = time;
+ } else {
+ pr_error("can't open pmic uevent setting file!\n");
+ }
+}
+
+static void usage() {
+ fprintf(stderr, "%s [-d <device>] [-r <time>] [-a <capacity>] [-p] [-h] \n"
+ " -d <device> Monitored device name \n"
+ " Refer to the device name in the \'/sys/class/power_supply/\' directory \n"
+ " -r <time> Time of reporting interval (1-90)s\n"
+ " -a <capacity> Capacity of low battery alarm (1-99)%%\n"
+ " -p Print uevent to debug\n"
+ " -h Print help\n", "battery_monitor");
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ int battery_fd = -1;
+ int nr = -1;
+ int c = 0;
+ struct pollfd pollfds = {0};
+ char temp[64] = {0};
+
+ while ((c = getopt(argc, argv, "d:r:a:ph")) != -1) {
+ switch (c) {
+ case 'd':
+ strcpy(device_name, optarg);
+ break;
+ case 'r':
+ report_time = atoi(optarg);
+ if (report_time < REPORT_TIME_MIN || REPORT_TIME_MIN > REPORT_TIME_MAX)
+ report_time = REPORT_TIME_DEFAULT;
+ break;
+ case 'a':
+ alarm_capacity = atoi(optarg);
+ if (alarm_capacity < ALARM_CAPACITY_MIN || alarm_capacity > ALARM_CAPACITY_MAX)
+ alarm_capacity = ALARM_CAPACITY_DEFAULT;
+ break;
+ case 'p':
+ log_level = PRINT_DEBUG_LEVEL;
+ break;
+ case 'h':
+ usage();
+ break;
+ default:
+ error(1, 0, "invalid option -%c", optopt);
+ }
+ }
+
+ if (strlen(device_name) == 0) {
+ pr_error("The entered device does not exist\n");
+ exit(1);
+ } else {
+ sprintf(temp, "%s/%s", DEVICE_PATH, device_name);
+ if (access(temp, F_OK) == -1) {
+ pr_error("The entered device does not exist\n");
+ exit(1);
+ } else {
+ sprintf(temp, "%s/%s/%s", DEVICE_PATH, device_name, REPORT_PERIOD_SETTING_FILE);
+ if (access(temp, F_OK) == -1) {
+ pr_error("The entered device does not support monitoring\n");
+ exit(1);
+ }
+ }
+ }
+
+ pr_info("Device:%s, Report interval: %ds, Alarm capacity: %d%%\n", device_name, report_time, alarm_capacity);
+ set_report_time(report_time);
+
+ memset(&pollfds, 0, sizeof(pollfds));
+ battery_fd = battery_monitor_init();
+ if (battery_fd < 0) {
+ pr_error("can't open battery monitor\n");
+ exit(1);
+ }
+ pollfds.fd = battery_fd;
+ pollfds.events = POLLIN;
+
+ while (1) {
+ nr = poll(&pollfds, 1, -1);
+ if (nr > 0 && pollfds.revents & POLLIN) {
+ battery_state_read(pollfds.fd);
+ }
+ }
+
+ if (battery_fd > 0)
+ close(battery_fd);
+}