blob: c2e3e8ffdd137d3232d3242a190e7cb15a0fd1b2 [file] [log] [blame]
/*
* 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: auto shutdown file
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <poll.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/input.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <errno.h>
#include <pthread.h>
#include <sys/time.h>
#include <signal.h>
#include <dirent.h>
#define INFO(fmt, args...) \
printf("[Auto Shutdown][%s] " fmt, __func__, ##args)
/*timer*/
#define POWER_OFF_TIME_DEFAULT (45) //45min
#define POWER_OFF_TIME_MIN (5) //5min
#define POWER_OFF_TIME_MAX (90) //90min
#define MIN_TO_SEC (60) //60s
#define POWER_OFF_TIME_TO_SEC_DEFAULT (POWER_OFF_TIME_DEFAULT * MIN_TO_SEC)
/*usb*/
#define UEVENT_BUFFER_SIZE 2048
#define MIN_SIZE 30
#define USB_STATE_FILE "/sys/class/android_usb/android0/state"
/*bt*/
#define CTL_SOCKET_PATH "/etc/bluetooth/hfp_ctl_sk"
#define HFP_EVENT_CONNECTION 1
#define HFP_EVENT_CALL 2
#define HFP_EVENT_CALLSETUP 3
#define HFP_EVENT_VGS 4
#define HFP_EVENT_VGM 5
#define HFP_IND_DEVICE_DISCONNECTED 0
#define HFP_IND_DEVICE_CONNECTED 1
#define HFP_IND_CALL_NONE 0
/* at least one call is in progress */
#define HFP_IND_CALL_ACTIVE 1
/* currently not in call set up */
#define HFP_IND_CALLSETUP_NONE 0
/* an incoming call process ongoing */
#define HFP_IND_CALLSETUP_IN 1
/* an outgoing call set up is ongoing */
#define HFP_IND_CALLSETUP_OUT 2
/* remote party being alerted in an outgoing call */
#define HFP_IND_CALLSETUP_OUT_ALERT 3
/*adc key*/
#define ADC_INPUT_EVENT_NAME "adc_keypad"
#define USB_DISCONNECT 0
#define USB_CONNECT 1
#define HFP_STATE_DISCONNECT 0 //handfree disconnect
#define HFP_STATE_CONNECT 1 //handfree connect
#define HFP_STATE_CALL_IN 2 //have call in
#define HFP_STATE_CALL_OUT 3 //have call out
#define HFP_STATE_CALL_OUT_RINGING 4 //call out,ringing
#define HFP_STATE_CALL_IN_OUT_OVER 5 //call in and out over
#define HFP_STATE_CALL_START 6 //call start
#define HFP_STATE_CALL_ENDED 7 //call over
static int usb_state = USB_CONNECT;
static int bt_state = HFP_STATE_DISCONNECT;
static int timing_time_sec = POWER_OFF_TIME_TO_SEC_DEFAULT;
static int stop_timer(void);
static int check_to_start_timer(void);
static int usb_monitor_init(void)
{
struct sockaddr_nl client;
int usb_monitor, ret;
int buffersize = 1024;
usb_monitor = socket(AF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
if (0 > usb_monitor) {
return -1;
}
memset(&client, 0, sizeof(client));
client.nl_family = AF_NETLINK;
client.nl_pid = getpid();
client.nl_groups = 1; /* receive broadcast message*/
ret = setsockopt(usb_monitor, SOL_SOCKET, SO_RCVBUF, &buffersize, sizeof(buffersize));
if (0 > ret) {
return -1;
}
ret = bind(usb_monitor, (struct sockaddr*)&client, sizeof(client));
if (0 > ret) {
return -1;
}
return usb_monitor;
}
static int usb_state_change(void)
{
char usb_state_buffer[MIN_SIZE] = {0};
FILE *fp = fopen(USB_STATE_FILE, "r");
if (fp != NULL) {
if (fgets(usb_state_buffer, MIN_SIZE, fp) == NULL) {
INFO("get android0 state content failure!\n");
fclose(fp);
} else {
if (!strncmp("DISCONNECTED", (const char*)usb_state_buffer, strlen("DISCONNECTED"))) {
INFO("usb is disconnected!\n");
usb_state = USB_DISCONNECT;
check_to_start_timer();
} else if (!strncmp("CONFIGURED", (const char*)usb_state_buffer, strlen("CONFIGURED"))) {
INFO("usb is connected!\n");
usb_state = USB_CONNECT;
stop_timer();
}
fclose(fp);
}
} else {
INFO("can't open usb state file!\n");
}
fp = NULL;
return 0;
}
static int usb_state_read(int fd)
{
int rcvlen;
int buffersize = 1024;
char buf[UEVENT_BUFFER_SIZE] = {0};
/* receive data */
rcvlen = recv(fd, &buf, sizeof(buf), 0);
if (rcvlen > 0 && strstr(buf,"usb") ) {
//printf("%s\n", buf);
usb_state_change();
}
return 0;
}
static int bt_monitor_init(void)
{
struct sockaddr_un address;
int sockfd;
if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
INFO("can't creat socket, errno:%s", strerror(errno));
return -1;
}
address.sun_family = AF_UNIX;
strncpy (address.sun_path, CTL_SOCKET_PATH, sizeof(address.sun_path) - 1);
if (connect(sockfd, (struct sockaddr *)&address, sizeof (address)) == -1) {
/*if socket is not avialable or not ready, don't report error*/
if (errno != 111 && errno != 2)
INFO("connect server: %s", strerror(errno));
close(sockfd);
return -1;
}
return sockfd;
}
static void msg_handler(int event, int value)
{
switch (event) {
case HFP_EVENT_CONNECTION:
switch (value) {
case HFP_IND_DEVICE_DISCONNECTED:
INFO("HFP disconnected!!!\n");
bt_state = HFP_STATE_DISCONNECT;
check_to_start_timer();
break;
case HFP_IND_DEVICE_CONNECTED:
INFO("HFP connected!!!\n");
bt_state = HFP_STATE_CONNECT;
stop_timer();
break;
default:
break;
}
break;
case HFP_EVENT_CALL:
switch (value) {
case HFP_IND_CALL_NONE:
INFO("Call stopped!!!\n");
bt_state = HFP_STATE_CALL_START;
break;
case HFP_IND_CALL_ACTIVE :
INFO("Call active!!!\n");
bt_state = HFP_STATE_CALL_ENDED;
break;
default:
break;
}
break;
case HFP_EVENT_CALLSETUP:
switch (value) {
case HFP_IND_CALLSETUP_NONE:
INFO("Callsetup stopped!!!\n");
if (bt_state != HFP_STATE_CALL_START)
bt_state = HFP_STATE_CALL_IN_OUT_OVER;
break;
case HFP_IND_CALLSETUP_IN :
INFO("An incomming Callsetup!!!\n");
bt_state = HFP_STATE_CALL_IN;
break;
case HFP_IND_CALLSETUP_OUT :
INFO("An outgoing Callsetup!!!\n");
bt_state = HFP_STATE_CALL_OUT;
break;
case HFP_IND_CALLSETUP_OUT_ALERT :
INFO("Remote device being altered!!!\n");
bt_state = HFP_STATE_CALL_OUT_RINGING;
break;
default:
break;
}
break;
default:
break;
}
}
static int bt_state_read(int fd)
{
char msg[64];
int byte, i, value = -1, event =-1;
if (fd < 0) {
INFO("BT fd is error!\n");
return -1;
}
memset(msg, 0, sizeof(msg));
byte = recv(fd, msg, sizeof(msg), 0);
if (byte < 0) {
INFO("rcv error!\n");
} else if (byte == 0) {
INFO("the server is offline, need to reconnect!\n");
bt_state = HFP_STATE_DISCONNECT;
check_to_start_timer();
return -1;
} else if (byte >= 2) {
INFO("incoming msg of %d bytes\n", byte);
for (i = 0; i + 1 < byte; i += 2) {
event = msg[i];
value = msg[i + 1];
INFO("event = %d, value = %d\n", event, value);
msg_handler(event, value);
}
}
return 0;
}
static void *bt_monitor_pthread(void * arc)
{
int bt_fd, res;
struct pollfd pollfds;
init:
if ((bt_fd = bt_monitor_init()) < 0) {
sleep(5);
goto init;
}
pollfds.fd = bt_fd;
pollfds.events = POLLIN;
while (1) {
poll(&pollfds, 1, -1);
if (pollfds.revents & POLLIN) {
pollfds.revents = 0;
res=bt_state_read(bt_fd);
if (res < 0) {
close(bt_fd);
goto init;
}
}
}
close(bt_fd);
return NULL;
}
static int key_monitor_init(void)
{
int fd = -1;
char name[256];
DIR* dir = opendir("/dev/input");
if (dir != NULL) {
struct dirent* de;
while ((de = readdir(dir))) {
if (strncmp(de->d_name, "event", 5)) continue;
fd = openat(dirfd(dir), de->d_name, O_RDONLY);
if (fd < 0) continue;
ioctl(fd, EVIOCGNAME(sizeof(name)), name);
if (!strcmp(ADC_INPUT_EVENT_NAME, name))
break;
close(fd);
fd = -1;
}
closedir(dir);
}
return fd;
}
static int key_state_read(int fd)
{
int rd, i;
struct input_event ev[5];
if (fd < 0) {
INFO("ADC-KEY fd is error!\n");
return -1;
}
memset(ev, 0, sizeof(ev));
rd = read(fd, ev, sizeof(ev));
if (rd < (int) sizeof(struct input_event)) {
INFO("expected %d bytes, got %d\n", (int) sizeof(struct input_event), rd);
perror("\nevtest: error reading");
return 1;
}
for (i = 0; i < rd / sizeof(struct input_event); i++) {
if (ev[i].type !=0 ) {
if (ev[i].value == 1) {
INFO("Have a key press!\n");
stop_timer();
} else if (ev[i].value == 0) {
INFO("Have a key release!\n");
check_to_start_timer();
}
}
}
return 0;
}
static void timer_handle(int sig)
{
system("/etc/adckey/adckey_function.sh longpresspower");
}
static int set_timer_handle(void (*fun)(int))
{
if (fun == NULL)
return -1;
signal(SIGALRM, fun);
return 0;
}
static int start_timer(unsigned int nsec)
{
struct itimerval tick;
memset(&tick, 0, sizeof(tick));
tick.it_value.tv_sec = nsec;
tick.it_value.tv_usec = 0;
tick.it_interval.tv_sec = 0;
tick.it_interval.tv_usec = 0;
if (setitimer(ITIMER_REAL, &tick, NULL) < 0) {
INFO("Set timer failed!\n");
return -1;
}
INFO("Start timer!\n");
return 0;
}
static int stop_timer(void)
{
struct itimerval tick;
memset(&tick, 0, sizeof(tick));
tick.it_value.tv_sec = 0;
tick.it_value.tv_usec = 0;
tick.it_interval.tv_sec = 0;
tick.it_interval.tv_usec = 0;
if (setitimer(ITIMER_REAL, &tick, NULL) < 0) {
INFO("Set timer failed!\n");
return -1;
}
INFO("Stop timer!\n");
return 0;
}
static int check_to_start_timer(void)
{
if (usb_state == USB_DISCONNECT
&& bt_state == HFP_STATE_DISCONNECT)
start_timer(timing_time_sec);
return 0;
}
int main(int argc, char **argv)
{
int usb_fd, key_fd;
int timing_time_min;
pthread_t bt_thread_id;
struct pollfd pollfds[2];
const char *cmd_usage = {
"Usage:\n"
"\t Automatic shutdown when idle \n"
"\t auto_shutdown <Timing time> \n"
"\t <Timing time>: 5 ~ 90\n"
"\t MIN:5min MAX:90min Default:45min\n"
};
if (argc != 2) {
printf("%s", cmd_usage);
return -1;
}
if ((timing_time_min = atoi(argv[1])) != 0) {
if (timing_time_min < POWER_OFF_TIME_MIN || timing_time_min > POWER_OFF_TIME_MAX)
timing_time_min = POWER_OFF_TIME_DEFAULT;
} else {
timing_time_min = POWER_OFF_TIME_DEFAULT;
}
INFO("Timing time: %d min\n", timing_time_min);
timing_time_sec = timing_time_min * MIN_TO_SEC;
memset(pollfds, 0, sizeof(pollfds));
/*usb*/
usb_fd = usb_monitor_init();
if (usb_fd > 0) {
pollfds[0].fd = usb_fd;
pollfds[0].events = POLLIN;
usb_state_change();
check_to_start_timer();
}
/*adc-key*/
key_fd = key_monitor_init();
if (key_fd > 0) {
pollfds[1].fd = key_fd;
pollfds[1].events = POLLIN;
}
/*bt*/
int res = pthread_create(&bt_thread_id, NULL, bt_monitor_pthread, NULL);
if (res < 0)
INFO("bt monitor pthread creat fail!\n");
set_timer_handle(timer_handle);
while (1)
{
poll(pollfds, 2, -1);
for (int i = 0; i < 2; i++) {
if (pollfds[i].revents & POLLIN) {
pollfds[i].revents = 0;
if (pollfds[i].fd == usb_fd)
usb_state_read(usb_fd);
else if (pollfds[i].fd == key_fd)
key_state_read(key_fd);
}
}
}
if (key_fd > 0)
close(key_fd);
if (usb_fd > 0)
close(usb_fd);
}