| /* |
| * 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); |
| } |