/****************************************************************************** * * Copyright (C) 2020-2021 SeekWave Technology * * Licensed under the Apache License, Version 2.0 (the "License"); * ******************************************************************************/ #include #include #include #include #include #include #include #include #include "skw_common.h" #include "skw_ext.h" #define SKW_EXT_BUF_SIZE 2048 typedef struct { pthread_t thread_id; char thread_running; int epoll_fd; int signal_fd[2]; } skw_inotify_thread_info_st; skw_inotify_thread_info_st skw_inotify_thread_info; extern void scomm_vendor_write_wakeup_adv_enable(); /* struct inotify_event { int wd; // Watch descriptor uint32_t mask; // Mask of events uint32_t cookie; // Unique cookie associating related events (for rename(2)) uint32_t len; // Size of name field char name[]; // Optional null-terminated name }; */ /******************************************************************************* ** ** Function skw_ext_inotify_thread ** ** Description inotify thread function ** ** Returns void * ** *******************************************************************************/ static void *skw_ext_inotify_thread(void *arg) { int fd = -1; int wd = -1; int read_len = 0; int event_pos = 0; int event_size = 0; char buffer[SKW_EXT_BUF_SIZE + 1] = {0}; struct inotify_event *event = NULL; skw_inotify_thread_info_st *thread_info = &skw_inotify_thread_info; int epfd = epoll_create(4); struct epoll_event ev; struct epoll_event events[20]; SKWBT_LOG("%s enter", __func__); SKW_UNUSED(arg); fd = inotify_init1(IN_NONBLOCK); if(fd < 0) { ALOGE("inotify_init1 failed: %s", strerror(errno)); return NULL; } wd = inotify_add_watch(fd, "/dev", IN_CREATE); if (wd < 0) { ALOGE("inotify_add_watch fail: %s\n", strerror(errno)); return NULL; } if(socketpair(AF_UNIX, SOCK_STREAM, 0, thread_info->signal_fd) == -1) { ALOGE("signal socket creare fail: %s\n", strerror(errno)); return NULL; } thread_info->epoll_fd = epfd; memset(&ev, 0, sizeof(ev)); ev.data.fd = fd; ev.events = EPOLLIN | EPOLLET; epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev); ev.events = EPOLLIN | EPOLLHUP | EPOLLRDHUP | EPOLLERR; ev.data.fd = thread_info->signal_fd[0]; if (epoll_ctl(epfd, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1) { ALOGE("%s unable to register signal fd %d to epoll set: %s", __func__, ev.data.fd, strerror(errno)); return NULL; } while(thread_info->thread_running) { int nfds = epoll_wait(epfd, events, 20, 500); for (int i = 0; i < nfds; ++i) { if(thread_info->thread_running == 0) { goto thread_exit; } if ((events[i].data.fd != fd) || (!(events[i].events & EPOLLIN))) { continue; } read_len = read(fd, buffer, SKW_EXT_BUF_SIZE); //SKWBT_LOG("select read_len:%d, nfds:%d", read_len, nfds); if(read_len <= 0) { continue; } event_pos = 0; while((read_len >= (int)sizeof(struct inotify_event)) && (event_pos < SKW_EXT_BUF_SIZE))//may receive multiple events { event = (struct inotify_event *)(buffer + event_pos); if(event->len) { if(event->mask & IN_CREATE) { SKWBT_LOG("recv command msg mask:0x%X name:%s", event->mask, event->name); if (((event->mask & IN_ISDIR) == 0) && (memcmp(event->name, "shutdown", 8) == 0)) { scomm_vendor_write_wakeup_adv_enable(); SKWBT_LOG("received shutdown command"); goto thread_exit; } } else { SKWBT_LOG("othre event, mask:0x%X, name: %s", event->mask, event->name); } } event_size = sizeof(struct inotify_event) + event->len; read_len -= event_size; event_pos += event_size; } } } thread_exit: SKWBT_LOG("%s exit", __func__); inotify_rm_watch(fd, wd); close(fd); thread_info->thread_running = FALSE; return NULL; } /******************************************************************************* ** ** Function skw_ext_inotify_thread_init ** ** Description inotify thread init ** ** Returns None ** *******************************************************************************/ void skw_ext_inotify_thread_init(void) { #if BLE_ADV_WAKEUP_ENABLE skw_inotify_thread_info_st *thread_info = &skw_inotify_thread_info; pthread_attr_t thread_attr; pthread_attr_init(&thread_attr); pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_JOINABLE); memset(thread_info, 0, sizeof(skw_inotify_thread_info_st)); SKWBT_LOG("%s enter", __func__); thread_info->signal_fd[0] = -1; thread_info->signal_fd[1] = -1; if(pthread_create(&thread_info->thread_id, &thread_attr, skw_ext_inotify_thread, NULL) != 0) { ALOGE("%s pthread_create : %s", __func__, strerror(errno)); thread_info->thread_id = -1; return ; } thread_info->thread_running = TRUE; #else SKW_UNUSED(skw_ext_inotify_thread); #endif } /******************************************************************************* ** ** Function skw_ext_inotify_thread_exit ** ** Description inotify thread exit ** ** Returns None ** *******************************************************************************/ void skw_ext_inotify_thread_exit(void) { #if BLE_ADV_WAKEUP_ENABLE char is_closed = 0; skw_inotify_thread_info_st *thread_info = &skw_inotify_thread_info; if(thread_info->thread_running && (thread_info->thread_id != -1)) { unsigned char close_signal = 1; thread_info->thread_running = FALSE; if(thread_info->epoll_fd >= 0) { ssize_t ret; RW_NO_INTR(ret = write(thread_info->signal_fd[1], &close_signal, 1)); SKWBT_LOG("%s signal_fd:%d, ret:%d", __func__, thread_info->epoll_fd, (int)ret); epoll_ctl(thread_info->epoll_fd, EPOLL_CTL_DEL, thread_info->signal_fd[0], NULL); close(thread_info->signal_fd[0]); close(thread_info->signal_fd[1]); is_closed = 1; } pthread_join(thread_info->thread_id, NULL); } if(is_closed == 0)//The thread may have exited { if(thread_info->signal_fd[0] != -1) { close(thread_info->signal_fd[0]); } if(thread_info->signal_fd[1] != -1) { close(thread_info->signal_fd[1]); } } if(thread_info->epoll_fd >= 0) { if(is_closed == 0) { epoll_ctl(thread_info->epoll_fd, EPOLL_CTL_DEL, thread_info->signal_fd[0], NULL); } close(thread_info->epoll_fd); } thread_info->signal_fd[0] = -1; thread_info->signal_fd[1] = -1; thread_info->epoll_fd = -1; SKWBT_LOG("%s exit", __func__); #endif }