From: Cristina Ciocan Date: Tue, 10 Feb 2015 16:50:43 +0000 (+0200) Subject: [Activity HAL] Added activity HAL source files X-Git-Tag: android-x86-7.1-r1~33^2~2 X-Git-Url: http://git.osdn.net/view?p=android-x86%2Fhardware-intel-libsensors.git;a=commitdiff_plain;h=d8886bbf0f40ea9f0019423f4d9f6d4eccb7ce6f [Activity HAL] Added activity HAL source files The activity_event_entry file is the implementation of the activity recognition HAL, as described in hardware/libhardware/include/hardware/activity_recognition.h . In order to test the HAL, run the activity test program. Running activity without parameters will list a summary of the program usage. Change-Id: I1717c3882340ec40016f303880e55b0b9bba45f8 Signed-off-by: Cristina Ciocan Reviewed-on: https://android.intel.com:443/331750 --- diff --git a/Android.mk b/Android.mk index 5021cdb..37dcf60 100644 --- a/Android.mk +++ b/Android.mk @@ -48,4 +48,41 @@ LOCAL_SHARED_LIBRARIES := liblog libcutils libdl LOCAL_SRC_FILES := sens.c LOCAL_MODULE_TAGS := eng include $(BUILD_EXECUTABLE) + +endif + + +# Activity HAL module implementation + +ifeq ($(USE_INTEL_ACTIVITY_RECOGNITION_HAL),true) + +include $(CLEAR_VARS) + +src_path := . +activity_src_files := $(src_path)/activity_event_entry.c \ + $(src_path)/discovery.c \ + $(src_path)/utils.c \ + +LOCAL_C_INCLUDES += $(LOCAL_PATH) vendor/intel/hardware/iio-sensors +LOCAL_MODULE := activity.$(TARGET_BOARD_PLATFORM) +LOCAL_MODULE_OWNER := intel +LOCAL_MODULE_RELATIVE_PATH := hw +LOCAL_MODULE_TAGS := optional +LOCAL_CFLAGS := -DLOG_TAG=\"Activity\" -fvisibility=hidden +LOCAL_LDFLAGS := -Wl,--gc-sections +LOCAL_SHARED_LIBRARIES := liblog libcutils +LOCAL_PRELINK_MODULE := false +LOCAL_SRC_FILES := $(activity_src_files) + +include $(BUILD_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_C_INCLUDES += $(LOCAL_PATH) vendor/intel/hardware/iio-sensors +LOCAL_MODULE := activity +LOCAL_CFLAGS := -DLOG_TAG=\"Activity\" -fvisibility=hidden +LOCAL_SHARED_LIBRARIES := liblog libcutils libdl +LOCAL_SRC_FILES := activity.c +LOCAL_MODULE_TAGS := eng +include $(BUILD_EXECUTABLE) + endif diff --git a/Makefile b/Makefile index 8389e51..1a3d398 100644 --- a/Makefile +++ b/Makefile @@ -1,19 +1,26 @@ USE_INTEL_SENSOR_HAL := true +USE_INTEL_ACTIVITY_RECOGNITION_HAL := true include Android.mk LIBHARDWARE?=../../../../hardware/libhardware/ CFLAGS=-DLOG_TAG=\"sens\" -I$(LIBHARDWARE)include/ -I./linux -fPIC -Wall LDFLAGS=-ldl -lpthread -lm -lrt -all: sensors.gmin.so sens +all: sensors.gmin.so sens activity.gmin.so activity linux_src = linux/log.o sens: sens.o $(linux_src) cc -o $@ $^ $(LDFLAGS) +activity: activity.o $(linux_src) + cc -o $@ $^ $(LDFLAGS) + sensors.gmin.so: $(patsubst %.c,%.o,$(src_files) $(linux_src)) cc -o $@ $^ $(LDFLAGS) -shared +activity.gmin.so: $(patsubst %.c,%.o,$(activity_src_files) $(linux_src)) + cc -o $@ $^ $(LDFLAGS) -shared + clean: - -rm $(patsubst %.c,%.o,$(src_files) $(linux_src) sens.c) sens sensors.gmin.so 2>/dev/null + -rm $(patsubst %.c,%.o,$(src_files) $(activity_src_files) $(linux_src) sens.c activity.c) sens sensors.gmin.so activity activity.gmin.so 2>/dev/null diff --git a/activity.c b/activity.c new file mode 100644 index 0000000..55e0d9d --- /dev/null +++ b/activity.c @@ -0,0 +1,548 @@ +/* + * Copyright (C) 2015 Intel Corporation. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "activity_event_utils.h" + +/* Return error codes */ +#define ARGV_ERR 1 +#define HAL_ACCESS_ERR 1 +#define HAL_OPEN_ERR 2 +#define HAL_SYMBOL_ERR 3 + +#define START_CMD "start" +#define STOP_CMD "stop" +#define REGISTER_CMD "register_callback" +#define ENABLE_CMD "enable" +#define DISABLE_CMD "disable" +#define LIST_CMD "list" +#define FLUSH_CMD "flush" +#define MONITOR_START_CMD "monitor_start" +#define MONITOR_STOP_CMD "monitor_stop" + +#define MAX_CMD_SIZE 512 +#define MAX_ARGS 8 + +#define CMD_SEPARATOR " " + +#define ACK_FLAG 1 +#define MONITOR_START_FLAG 2 +#define MONITOR_STOP_FLAG 3 + +/* We define a default latency since current HAL does not use this latency */ +#define DEFAULT_LATENCY 0 + +static struct activity_recognition_module *hmi; +static struct hw_device_t *device; + +static FILE *client, *monitor_client; +static int monitor_connection = -1; + +#ifdef ANDROID +#define ACTIVITY_SERVER_NAME "/dev/socket/activity-server" +#else +#define ACTIVITY_SERVER_NAME "/tmp/activity-server" +#endif + +#define CLIENT_ERR(f, fmt...) \ + { if (f) { fprintf(f, fmt); fprintf(f, "\n"); } ALOGE(fmt); } + +struct sockaddr_un server_addr = { + .sun_family = AF_UNIX, + .sun_path = ACTIVITY_SERVER_NAME, +}; + +/* Event types are indexed so that a type is positioned at an index represented + * by its value in the ACTIVITY_TYPE_* enum in activity_recognition.h . + */ +static const char* event_types[] = { + "FLUSH_COMPLETE", + "ENTER", + "EXIT", +}; + +static void dummy_activity_callback(const activity_recognition_callback_procs_t *procs __attribute((unused)), + const activity_event_t *events, int count) +{ + int i; + + /* Exit if nobody is monitoring */ + if (monitor_client == NULL) + return; + + fprintf(monitor_client, "No. of activity events: %d\n", count); + + for (i = 0; i < count; i++) + fprintf(monitor_client, "\t occurred at %lld (ns)\n", + events[i].activity, + event_types[events[i].event_type], + events[i].timestamp); +} + +static activity_recognition_callback_procs_t dummy_callback_procs = { + .activity_callback = dummy_activity_callback +}; + +/* We register a dummy callback that just prints the events reported from HAL. */ +static void register_activity_callback(void) +{ + struct activity_recognition_device *activity_dev = + (struct activity_recognition_device *) device; + + activity_dev->register_activity_callback(activity_dev, &dummy_callback_procs); +} + +static int enable_activity_event(uint32_t handle, uint32_t event_type) +{ + struct activity_recognition_device *activity_dev = + (struct activity_recognition_device *) device; + + return activity_dev->enable_activity_event(activity_dev, handle, + event_type, DEFAULT_LATENCY); +} + +static int disable_activity_event(uint32_t handle, uint32_t event_type) +{ + struct activity_recognition_device *activity_dev = + (struct activity_recognition_device*) device; + + return activity_dev->disable_activity_event(activity_dev, handle, event_type); +} + +static int flush(void) +{ + struct activity_recognition_device *activity_dev = + (struct activity_recognition_device *) device; + + return activity_dev->flush(activity_dev); +} + +static int print_usage(void) +{ + fprintf(stderr, "Program usage:\n"); + fprintf(stderr, "\tactivity %s\n", START_CMD); + fprintf(stderr, "\tactivity %s\n", STOP_CMD); + fprintf(stderr, "\tactivity %s\n", REGISTER_CMD); + fprintf(stderr, "\tactivity %s\n", LIST_CMD); + fprintf(stderr, "\tactivity %s \n", ENABLE_CMD); + fprintf(stderr, "\tactivity %s \n", DISABLE_CMD); + fprintf(stderr, "\tactivity %s\n", FLUSH_CMD); + fprintf(stderr, "\tactivity %s\n", MONITOR_START_CMD); + fprintf(stderr, "\tactivity %s\n", MONITOR_STOP_CMD); + fprintf(stderr, "\t* is the index of the activity as shown by running \"activity list\"\n"); + fprintf(stderr, "\t* is one of the following:\n"); + fprintf(stderr, "\t\t%d => activity event ENTER\n", ACTIVITY_EVENT_ENTER); + fprintf(stderr, "\t\t%d => activity event EXIT\n", ACTIVITY_EVENT_EXIT); + fprintf(stderr, "\t\t%d => activity event FLUSH COMPLETE\n", ACTIVITY_EVENT_FLUSH_COMPLETE); + + return ARGV_ERR; +} + +static int parse_cmd(char buffer[]) +{ + char *tmp, *args[MAX_ARGS]; + int count; + + tmp = strtok(buffer, CMD_SEPARATOR); + count = 0; + while (tmp) { + args[count++] = tmp; + tmp = strtok(NULL, CMD_SEPARATOR); + } + + if (!count) { + CLIENT_ERR(client, "Invalid command %s", buffer); + return -1; + } + + if (strncmp(args[0], STOP_CMD, sizeof(STOP_CMD)) == 0) { + if (count != 1) { + CLIENT_ERR(client, "Too many arguments. Trimming command down to \ + 'activity %s'", STOP_CMD); + } else { + fprintf(client, "Stopping server\n"); + fflush(client); + } + + unlink(ACTIVITY_SERVER_NAME); + + exit(EXIT_SUCCESS); + } + + if (strncmp(args[0], LIST_CMD, sizeof(LIST_CMD)) == 0) { + const char * const* activities; + int size, i; + + if (count != 1) + CLIENT_ERR(client, "Too many arguments. Trimming command down to \ + 'activity %s'", LIST_CMD); + size = hmi->get_supported_activities_list(hmi, &activities); + if (client) { + fprintf(client, "Activities list:\n"); + for (i = 0; i < size; i++) + fprintf(client, "\t[%d] %s\n", i + 1, activities[i]); + } + + return 0; + } + + if (strncmp(args[0], REGISTER_CMD, sizeof(REGISTER_CMD)) == 0) { + if (count != 1) + CLIENT_ERR(client, "Too many arguments. Trimming command down to \ + 'activity %s'", REGISTER_CMD); + register_activity_callback(); + + return 0; + } + + if (strncmp(args[0], ENABLE_CMD, sizeof(ENABLE_CMD)) == 0) { + if (count > 3) + CLIENT_ERR(client, "Too many arguments. Trimming command down to \ + 'activity %s %s %s'", ENABLE_CMD, args[1], args[2]) + else if (count != 3) { + CLIENT_ERR(client, "Insufficient arguments. Command should be \ + 'activity %s '", ENABLE_CMD); + return -1; + } + + return enable_activity_event(atoi(args[1]), atoi(args[2])); + } + + if (strncmp(args[0], DISABLE_CMD, sizeof(DISABLE_CMD)) == 0) { + if (count > 3) + CLIENT_ERR(client, "Too many arguments. Trimming command down to \ + 'activity %s %s %s'", DISABLE_CMD, args[1], args[2]) + else if (count != 3) { + CLIENT_ERR(client, "Insufficient arguments. Command should be \ + 'activity %s '", DISABLE_CMD); + return -1; + } + + return disable_activity_event(atoi(args[1]), atoi(args[2])); + } + + if (strncmp(args[0], FLUSH_CMD, sizeof(FLUSH_CMD)) == 0) { + if (count != 1) + CLIENT_ERR(client, "Too many arguments. Trimming command down to \ + 'activity %s'", FLUSH_CMD); + + return flush(); + } + + if (strncmp(args[0], MONITOR_START_CMD, sizeof(MONITOR_START_CMD)) == 0) { + if (count != 1) + CLIENT_ERR(client, "Too many arguments. Trimming command \ + down to 'activity %s'", MONITOR_START_CMD); + return MONITOR_START_FLAG; + } + + if (strncmp(args[0], MONITOR_STOP_CMD, sizeof(MONITOR_STOP_CMD)) == 0) { + if (count != 1) + CLIENT_ERR(client, "Too many arguments. Trimming command \ + down to 'activity %s'", MONITOR_STOP_CMD); + return MONITOR_STOP_FLAG; + } + + CLIENT_ERR(client, "Invalid command %s", buffer); + + return -1; +} + +static void stop_monitoring(int *flag) +{ + int current_flag = ACK_FLAG; + + /* Send ACK to client */ + write(monitor_connection, ¤t_flag, sizeof(current_flag)); + + /* Cleanup */ + fclose(monitor_client); + close(monitor_connection); + monitor_client = NULL; + monitor_connection = -1; + + *flag = ACK_FLAG; +} + +static void start_monitoring(int conn, int *flag) +{ + /* Stop other started monitor if that's the case */ + if (monitor_client != NULL) + stop_monitoring(flag); + + monitor_client = client; + monitor_connection = conn; + *flag = MONITOR_START_FLAG; + +} + +static void start_server(void) +{ + int sock, conn, ret; + int flag; + + /* Just to make sure we do not have more than one server instance */ + unlink(ACTIVITY_SERVER_NAME); + + sock = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (sock == -1) { + ALOGE("Error %s creating socket\n", strerror(errno)); + exit(1); + } + + ret = bind(sock, (struct sockaddr *) &server_addr, sizeof(server_addr)); + if (ret == -1) { + ALOGE("Error %s binding socket\n", strerror(errno)); + exit(1); + } + + ret = listen(sock, 1); + if (ret == -1) + ALOGW("Error %s setting socket to listening state\n", strerror(errno)); + + /* Accept commands and send them to HAL */ + while (1) { + char buffer[526], cmsg_buffer[526]; + struct sockaddr_un from; + struct iovec recv_buff = { + .iov_base = buffer, + .iov_len = sizeof(buffer), + }; + struct msghdr msg = { + .msg_name = &from, + .msg_namelen = sizeof(from), + .msg_iov = &recv_buff, + .msg_iovlen = 1, + .msg_control = cmsg_buffer, + .msg_controllen = sizeof(cmsg_buffer), + }; + struct cmsghdr *cmsg; + bool close_now = true; + + conn = accept(sock, NULL, NULL); + if (conn == -1) { + ALOGE("Error %s accepting connection\n", strerror(errno)); + continue; + } + + ret = recvmsg(conn, &msg, 0); + if (ret == -1) { + ALOGE("Error %s in receiving message, conn = %d\n", strerror(errno), conn); + close(conn); + + continue; + } + + if (!ret) + continue; + + /* Check for shutdown from the peer */ + if (ret == 0) + break; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { + int *received_fd = (int *)CMSG_DATA(cmsg); + client = fdopen(*received_fd, "w"); + break; + } + } + + ret = parse_cmd(buffer); + if (ret < 0) { + close(conn); + continue; + } + if (ret == MONITOR_START_FLAG) { + start_monitoring(conn, &flag); + close_now = false; + } else if (ret == MONITOR_STOP_FLAG) + stop_monitoring(&flag); + else + flag = ACK_FLAG; + + /* Confirm succesfull dispatch */ + write(conn, &flag, sizeof(flag)); + + if (close_now) { + fclose(client); + close(conn); + } + } +} + +static const char *hal_paths[] ={ + "./activity.gmin.so", + "/lib/activity.gmin.so", + "/system/lib/hw/activity.gmin.so", +}; + +static int start_hal(void) +{ + void *hal; + int i, ret, no_paths; + const char *path = NULL; + pid_t child; + + no_paths = sizeof(hal_paths)/sizeof(const char*); + for (i = 0; i < no_paths; i++) { + if (access(hal_paths[i], R_OK) == 0) { + path = hal_paths[i]; + break; + } + } + + if (!path) { + fprintf(stderr, "Unable to find HAL\n"); + exit(1); + } + + hal = dlopen(path, RTLD_NOW); + if (!hal) { + fprintf(stderr, "Error \"%s\" opening activity HAL\n", dlerror()); + return HAL_OPEN_ERR; + } + + hmi = dlsym(hal, HAL_MODULE_INFO_SYM_AS_STR); + if (!hmi) { + fprintf(stderr, "Error \"%s\" finding entry symbol\n", dlerror()); + return HAL_SYMBOL_ERR; + } + + printf("Activity HAL loaded: name %s vendor %s version %d.%d id %s\n", + hmi->common.name, hmi->common.author, + hmi->common.version_major, hmi->common.version_minor, + hmi->common.id); + + /* Daemonize it */ + child = fork(); + if (child) { + usleep(100); + return 0; + } + + if (setsid() == (pid_t)-1) { + fprintf(stderr, "failed to send process to background\n"); + exit(1); + } + + /* Close stdio */ + close(0); close(1); close(2); + + ALOGI("Proceeding to HAL initialization\n"); + + ret = hmi->common.methods->open((struct hw_module_t *) hmi, + ACTIVITY_RECOGNITION_HARDWARE_INTERFACE, + &device); + if (ret) { + ALOGE("Error %d occurred at HAL module opening\n", ret); + exit(1); + } + + start_server(); + + return 0; +} + +static int send_cmd(int argc, char **argv) +{ + char cmd[MAX_CMD_SIZE]; + int i, sock, ret, flag; + struct iovec rcv_buffer = { + .iov_base = cmd, + .iov_len = sizeof(cmd) + 1, + }; + struct cmsg_fd { + struct cmsghdr hdr; + int fd; + } cmsg_buff = { + .hdr = { + .cmsg_level = SOL_SOCKET, + .cmsg_type = SCM_RIGHTS, + .cmsg_len = CMSG_LEN(sizeof(int)), + }, + .fd = 1, + }; + struct msghdr msg = { + .msg_name = NULL, + .msg_namelen = 0, + .msg_iov = &rcv_buffer, + .msg_iovlen = 1, + .msg_control = &cmsg_buff, + .msg_controllen = sizeof(cmsg_buff), + }; + + strcpy(cmd, argv[1]); + for (i = 2; i < argc; i++) { + strncat(cmd, CMD_SEPARATOR, sizeof(CMD_SEPARATOR)); + strcat(cmd, argv[i]); + } + + sock = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (sock == -1) { + fprintf(stderr, "Error \"%s\" creating socket\n", strerror(errno)); + return errno; + } + + ret = connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)); + if (ret == -1) { + fprintf(stderr, "Error \"%s\" connecting to server\n", strerror(errno)); + return errno; + } + + ret = sendmsg(sock, &msg, 0); + if (ret == -1) { + fprintf(stderr, "Error \"%s\" sending message to server\n", strerror(errno)); + return errno; + } + + ret = read(sock, &flag, sizeof(flag)); + if (ret == -1) { + fprintf(stderr, "Error \"%s\" getting answer from server\n", strerror(errno)); + return errno; + } + + /* Check for ACK or monitoring */ + if (flag == MONITOR_START_FLAG) { + do { + ret = read(sock, &flag, sizeof(flag)); + } while (ret > 0 && flag != ACK_FLAG); + } else if (flag != ACK_FLAG) + fprintf(stderr, "Error answer from HAL server: %d. Check logs for more details\n", flag); + + close(sock); + + return 0; +} + +static int run_cmd(int argc, char **argv) +{ + if (strncmp(argv[1], START_CMD, sizeof(START_CMD)) == 0) { + if (argc == 2) + return start_hal(); + return print_usage(); + } + + /* Send user command to HAL server socket */ + return send_cmd(argc, argv); +} + +int main(int argc, char **argv) +{ + if (argc < 2) + return print_usage(); + + return run_cmd(argc, argv); +} diff --git a/activity_event_entry.c b/activity_event_entry.c new file mode 100644 index 0000000..35e24e4 --- /dev/null +++ b/activity_event_entry.c @@ -0,0 +1,788 @@ +/* + * Copyright (C) 2015 Intel Corporation. + * + * This file represents the entry point for the activity recognition HAL module. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "activity_event_utils.h" + +#define MODULE_VERSION 1 +#define HAL_VERSION 0 + +#define MODULE_NAME "Activity recognition HAL" +#define MODULE_AUTHOR "Intel" + +#define CONTROL_FD (-1) +#define EXIT_FD (-2) + +/* + * This table maps syfs entries in scan_elements directories to sensor types, + * and will also be used to determine other sysfs names as well as the iio + * device number associated to a specific sensor. + */ +sensor_catalog_entry_t sensor_catalog[] = { + { + .tag = "activity", + .type = SENSOR_TYPE_SIGNIFICANT_MOTION, + .num_channels = 3, + .is_virtual = 0, + .channel = { + { + DECLARE_VOID_CHANNEL("still") + .num_events = 2, + .event = { + { DECLARE_GENERIC_EVENT("activity", "still", "thresh", "rising") }, + { DECLARE_GENERIC_EVENT("activity", "still", "thresh", "falling") }, + }, + }, + { + DECLARE_VOID_CHANNEL("walking") + .num_events = 2, + .event = { + { DECLARE_GENERIC_EVENT("activity", "walking", "thresh", "rising") }, + { DECLARE_GENERIC_EVENT("activity", "walking", "thresh", "falling") }, + }, + }, + { + DECLARE_VOID_CHANNEL("running") + .num_events = 2, + .event = { + { DECLARE_GENERIC_EVENT("activity", "running", "thresh", "rising") }, + { DECLARE_GENERIC_EVENT("activity", "running", "thresh", "falling") }, + }, + }, + }, + }, +}; + +unsigned int catalog_size = ARRAY_SIZE(sensor_catalog); + +/* All possible activities - see activity_recognition.h */ +static char const* sysfs_activity_names[MAX_ACTIVITIES] = { + "in_vehicle", + "on_bicycle", + "walking", + "running", + "still", + "tilting", +}; + +/* Internal HAL info */ +static struct activity_event_info supported_activities[MAX_ACTIVITIES + 1]; +/* Android framework level description (activities' name). The element on the + * first position (0) is reserved for the FLUSH COMPLETE event, thus we have + * (MAX_ACTIVITIES + 1) possible activities. + */ +static char const* supported_activity_names[MAX_ACTIVITIES + 1]; +/* Supported activities count */ +static unsigned int count; + +static int poll_fd, control_fd, exit_fd; +static pthread_t control_thread; + +static activity_recognition_callback_procs_t activity_dev_callback; +static pthread_mutex_t callback_mutex; + +static int instances_count; + +static void register_activity_callback(const struct activity_recognition_device *dev __attribute((unused)), + const activity_recognition_callback_procs_t* callback) +{ + pthread_mutex_lock(&callback_mutex); + activity_dev_callback.activity_callback = callback->activity_callback; + pthread_mutex_unlock(&callback_mutex); +} + +static bool check_activity_event(uint32_t activity_handle, uint32_t event_type) +{ + if (activity_handle > count) + return false; + + /* Also return false if the handle is 0 - this is reserved for flush + * event, that is currently not supported. */ + if (activity_handle == 0) + return 0; + + switch (event_type) { + case ACTIVITY_EVENT_ENTER: + case ACTIVITY_EVENT_EXIT: + return true; + case ACTIVITY_EVENT_FLUSH_COMPLETE: + /* Not supported yet */ + default: + return false; + } +} + +static int set_activity_event_state(const struct activity_recognition_device *dev __attribute((unused)), + uint32_t activity_handle, uint32_t event_type, + char const *action) +{ + uint64_t control_code; + int ret; + + /* Check received index boundaries */ + if (!check_activity_event(activity_handle, event_type)) { + ALOGE("Received invalid %s request\n", + activity_handle, event_type, action); + return -EINVAL; + } + + control_code = get_control_code((uint8_t) 1, + (uint8_t) activity_handle, + (uint8_t) event_type); + + ret = write(control_fd, &control_code, sizeof(control_code)); + if (ret < 0) { + ALOGE("Error writing to control fd to %s activity event\n", action); + return errno; + } + + ALOGI("Sent %s <%s, %i> request\n", action, supported_activity_names[activity_handle], event_type); + + return 0; +} + +static int enable_activity_event(const struct activity_recognition_device *dev, + uint32_t activity_handle, uint32_t event_type, + int64_t max_batch_report_latency_ns __attribute((unused))) +{ + return set_activity_event_state(dev, activity_handle, event_type, "enable"); +} + +static int disable_activity_event(const struct activity_recognition_device *dev, + uint32_t activity_handle, uint32_t event_type) +{ + return set_activity_event_state(dev, activity_handle, event_type, "disable"); +} + +/** + * For now, just report that the function call has been made, since we yet do + * not have a batch FIFO device. + */ +static int flush(const struct activity_recognition_device *dev __attribute((unused))) +{ + ALOGV("Flushing...\n"); + + return 0; +} + +static void process_disabling_activity_ev(uint8_t activity, uint8_t event); + +static int close_device(struct hw_device_t *device __attribute((unused))) +{ + int j, ret, exit_ping; + unsigned int i; + + if (!instances_count) + return -EINVAL; + + instances_count--; + + if (instances_count) + return 0; + + /* Send exit request to the worker thread and close resources. We can + * write anything to the exit fd, we just need the event. + */ + exit_ping = 1; + write(exit_fd, &exit_ping, sizeof(exit_ping)); + + /* Wait for worker thread to finish in order to release shared + * resources. + */ + pthread_join(control_thread, NULL); + + /* Close exit fd after sending the canceling request. */ + ret = epoll_ctl(poll_fd, EPOLL_CTL_DEL, exit_fd, NULL); + if (ret == -1) + ALOGE("Error deleting exit fd from polling pool\n"); + close(exit_fd); + + /* Clean control data. */ + ret = epoll_ctl(poll_fd, EPOLL_CTL_DEL, control_fd, NULL); + if (ret == -1) + ALOGE("Error deleting control fd from polling pool\n"); + close(control_fd); + + /* Disable all monitored pairs. This step should be + * the last one, after worker thread has ended in order to avoid + * supported_activities data corruption and avoid using another lock. + */ + for (i = 1; i <= count; i++) + for (j = 0; j < MAX_EVENTS_PER_ACTIVITY; j++) + if (supported_activities[i].monitored[j]) + process_disabling_activity_ev( + (uint8_t) i, + supported_activities[i].event[j]->event_type); + + close(poll_fd); + + pthread_mutex_destroy(&callback_mutex); + + ALOGI("Successfully closed device\n"); + + return 0; +} + +/* + * Finds the event given by type in the sensor's channel structure and retrieves + * its index. + * Equivalence (activity HAL naming - sensor HAL naming): + * ACTIVITY_EVENT_ENTER - "rising" + * ACTIVITY_EVENT_EXIT - "falling" + */ +static int get_ev_index(int ev_type, channel_descriptor_t *chann) +{ + int i; + char const *ev_dir; + + switch (ev_type) { + case ACTIVITY_EVENT_ENTER: + ev_dir = "rising"; + break; + case ACTIVITY_EVENT_EXIT: + ev_dir = "falling"; + break; + default: + ev_dir = NULL; + return -1; + } + + for (i = 0; i < chann->num_events; i++) { + if (strcmp(ev_dir, chann->event[i].dir) == 0) + return i; + } + + return -1; +} + +static int set_event_enabling(int dev_num, const char *en_path, int value) +{ + char path[PATH_MAX]; + int ret; + + ret = snprintf(path, sizeof(EVENTS_PATH) + sizeof(en_path), EVENTS_PATH "%s", dev_num, en_path); + if (ret < 0) + return ret; + + return sysfs_write_int(path, value); + +} + +static void process_enabling_activity_ev(uint8_t activity, uint8_t event) +{ + struct activity_event_info *activ = supported_activities + activity; + channel_descriptor_t *chann; + struct activity_event *ev; + char path[PATH_MAX]; + struct epoll_event ev_data; + int dev_fd, ev_index, ret; + unsigned int i; + bool open_now = false; + + /* Allocate event structure and populate it */ + ev = malloc(sizeof(*ev)); + if (!ev) { + ALOGE("Error allocating activity event for enabling\n"); + return; + } + + ev->event_type = (uint32_t) event; + ev->activity = (uint32_t) activity; + ev->timestamp = -1; + + /* The event fd is one per device, so we need to check if we have not + * retrieved it already when monitoring another pair. + * If it has not been retrieved, get it and update all other activities + * associated with the same device. + */ + if (activ->event_fd == -1) { + ret = snprintf(path, sizeof(DEV_FILE_PATH), DEV_FILE_PATH, activ->dev_num); + if (ret < 0) + goto dev_err; + + dev_fd = open(path, O_RDONLY | O_NONBLOCK); + if (dev_fd < 0) + goto dev_err; + + ret = ioctl(dev_fd, IIO_GET_EVENT_FD_IOCTL, &activ->event_fd); + close(dev_fd); + if (ret < 0) + goto dev_err; + + open_now = true; + + ev_data.events = EPOLLIN; + ev_data.data.fd = activ->event_fd; + ret = epoll_ctl(poll_fd, EPOLL_CTL_ADD, activ->event_fd, &ev_data); + if (ret == -1) + goto event_err; + + /* Update all other activities generated by this device */ + for (i = 1; i <= count; i++) + if (supported_activities[i].dev_num == activ->dev_num) + supported_activities[i].event_fd = activ->event_fd; + } + + /* Activate the event */ + chann = sensor_catalog[activ->sensor_catalog_index].channel + activ->channel_index; + ev_index = get_ev_index((int)event, chann); + if (ev_index < 0) { + ALOGE("Invalid event index: %d\n", ev_index); + goto event_err; + } + ret = set_event_enabling(activ->dev_num, chann->event[ev_index].ev_en_path, 1); + if (ret < 0) + goto event_err; + + /* Internally mark that the pair is being monitored. + * We keep the same event index in our activity structure as is in the + * channel descriptor structure. + */ + activ->event[ev_index] = ev; + activ->monitored[ev_index] = true; + activ->event_count++; + + return; + +event_err: + if (open_now) { + close(activ->event_fd); + for (i = 1; i <= count; i++) + if (supported_activities[i].dev_num == activ->dev_num) + supported_activities[i].event_fd = -1; + } +dev_err: + free(ev); +} + +static bool device_not_monitored(int dev_num) +{ + unsigned int i; + + for (i = 1; i <= count; i++) + if (supported_activities[i].dev_num == dev_num && + supported_activities[i].event_count > 0) { + return false; + } + + return true; +} + +static void process_disabling_activity_ev(uint8_t activity, uint8_t event) +{ + struct activity_event_info *activ = supported_activities + activity; + channel_descriptor_t *chann; + int ev_index, ret; + unsigned int i; + + /* Deactivate the event. */ + chann = sensor_catalog[activ->sensor_catalog_index].channel + activ->channel_index; + ev_index = get_ev_index((int)event, chann); + if (ev_index < 0) + ALOGE("Invalid event index: %d\n", ev_index); + else { + ret = set_event_enabling(activ->dev_num, chann->event[ev_index].ev_en_path, 0); + if (ret < 0) + ALOGE("Could not deactivate event - writing error\n"); + } + + /* Mark that the pair is not monitored any longer. */ + activ->monitored[ev_index] = false; + activ->event_count--; + + /* Close the event fd if this is the last pair monitored for the given + * device and remove it from the polling pool. + */ + if (device_not_monitored(activ->dev_num)) { + ret = epoll_ctl(poll_fd, EPOLL_CTL_DEL, activ->event_fd, NULL); + if (ret == -1) { + ALOGE("Error removing event fd from polling pool\n"); + return; + } + + close(activ->event_fd); + for (i = 1; i <= count; i++) + if (supported_activities[i].dev_num == activ->dev_num) + supported_activities[i].event_fd = -1; + } + + /* Free resources. */ + free(activ->event[ev_index]); + activ->event[ev_index] = NULL; +} + +static void process_control_event(void) +{ + struct control_event_data control_data; + uint64_t control_code; + ssize_t ret; + + /* Read control data from the control fd and interpret it */ + ret = read(control_fd, &control_code, sizeof(control_code)); + if (ret < 0) { + ALOGW("Error reading from control fd\n"); + return; + } + + get_control_data(control_code, &control_data); + + if (control_data.enable) + process_enabling_activity_ev(control_data.activity, control_data.event); + else + process_disabling_activity_ev(control_data.activity, control_data.event); +} + +static int get_activity_index(int modifier) +{ + unsigned int i; + + /* Start from 1 since 0 is reserved for FLUSH_COMPLETE event. */ + for (i = 1; i <= count; i++) + if (supported_activities[i].modifier == modifier) + return i; + + return -1; +} + +static void process_activity_event(int fd, struct activity_event events[], int *count) +{ + struct iio_event_data event; + int ret, chann_type, ev_type, ev_dir, ev_modifier, index, activity_index; + + /* Retrieve event. */ + ret = read(fd, &event, sizeof(event)); + if (ret < 0) { + ALOGE("Error reading event\n"); + return; + } + + /* Extract fields we are interested in and check the generated event. */ + chann_type = IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event.id); + if (chann_type != IIO_ACTIVITY) { + ALOGW("Event came from other than an activity channel\n"); + return; + } + + ev_modifier = IIO_EVENT_CODE_EXTRACT_MODIFIER(event.id); + switch (ev_modifier) { + case IIO_MOD_STILL: + case IIO_MOD_WALKING: + case IIO_MOD_RUNNING: + activity_index = get_activity_index(ev_modifier); + if (activity_index >= 0) + break; + default: + ALOGW("Incompatible modifier - none of the supported activities is present\n"); + return; + } + + ev_type = IIO_EVENT_CODE_EXTRACT_TYPE(event.id); + if (ev_type != IIO_EV_TYPE_THRESH) { + ALOGW("Event type is not threshold\n"); + return; + } + + ev_dir = IIO_EVENT_CODE_EXTRACT_DIR(event.id); + switch (ev_dir) { + case IIO_EV_DIR_RISING: + ev_dir = ACTIVITY_EVENT_ENTER; + break; + case IIO_EV_DIR_FALLING: + ev_dir = ACTIVITY_EVENT_EXIT; + break; + default: + ALOGW("Incompatible event direction - only RISING and FALLING supported\n"); + return; + } + + /* Add the activity event to the array for further processing. */ + index = *count; + events[index].event_type = ev_dir; + events[index].activity = activity_index; + events[index].timestamp = event.timestamp; + + index++; + *count = index; +} + +static void* events_routine(void *arg __attribute((unused))) +{ + struct epoll_event events[MAX_ACTIVITIES + 2]; + struct activity_event data_events[MAX_ACTIVITIES]; + int no_events, i, no_activity_events; + + while (1) { + ALOGV("Waiting for sensor events ...\n"); + + no_activity_events = 0; + no_events = epoll_wait(poll_fd, events, MAX_ACTIVITIES + 2, -1); + if (no_events == -1) { + ALOGE("epoll_wait error %s\n", strerror(errno)); + continue; + } + + for (i = 0; i < no_events; i++) + if (events[i].events == EPOLLIN) { + int data = events[i].data.fd; + + if (data >= 0) + process_activity_event(data, + data_events, + &no_activity_events); + else switch (data) { + case CONTROL_FD: + process_control_event(); + break; + case EXIT_FD: + return NULL; + default: + ALOGW("Invalid event user data: %d \n", events[i].data.fd); + break; + } + } else + ALOGW("Epoll events %i not expected\n", events[i].events); + + /* Call the callback function for the retrieved events (if it + * has been set). + */ + pthread_mutex_lock(&callback_mutex); + if (activity_dev_callback.activity_callback) { + activity_dev_callback.activity_callback( + &activity_dev_callback, + data_events, + no_activity_events); + } + pthread_mutex_unlock(&callback_mutex); + } +} + +static int set_up_control_data(void) +{ + struct epoll_event control_ev, exit_ev; + int ret = 0; + + ret = pthread_mutex_init(&callback_mutex, NULL); + if (ret) + return ret; + + /* Maximum fds is maximum activities + 1 control fd + 1 exit fd */ + poll_fd = epoll_create(MAX_ACTIVITIES + 2); + if (poll_fd == -1) + return errno; + if (ret) + goto poll_control_err; + + control_fd = eventfd(0, 0); + if (control_fd == -1) { + ret = errno; + goto poll_control_err; + } + + control_ev.events = EPOLLIN; + /* Set data field to event file descriptor */ + control_ev.data.fd = CONTROL_FD; + ret = epoll_ctl(poll_fd, EPOLL_CTL_ADD, control_fd, &control_ev); + if (ret == -1) + goto control_data_err; + + exit_fd = eventfd(0, 0); + if (exit_fd == -1) { + ALOGE("Error allocating exit fd\n"); + goto exit_control_err; + } + exit_ev.events = EPOLLIN; + exit_ev.data.fd = EXIT_FD; + ret = epoll_ctl(poll_fd, EPOLL_CTL_ADD, exit_fd, &exit_ev); + if (ret == -1) { + ALOGE("Error adding exit fd to the polling pool\n"); + goto exit_err; + } + + /* Start worker thread to wait on all event sources */ + ret = pthread_create(&control_thread, NULL, events_routine, NULL); + if (ret) + goto thread_err; + + return 0; + +thread_err: + epoll_ctl(poll_fd, EPOLL_CTL_DEL, exit_fd, NULL); +exit_err: + close(exit_fd); +exit_control_err: + epoll_ctl(poll_fd, EPOLL_CTL_DEL, control_fd, NULL); +control_data_err: + close(control_fd); +poll_control_err: + close(poll_fd); + + return ret; +} + +/* Returns the IIO_MOD_* equivalent to the given name. */ +static int get_modifier_as_int(const char* mod) +{ + if (strncmp(mod, "still", sizeof("still")) == 0) + return IIO_MOD_STILL; + + if (strncmp(mod, "walking", sizeof("walking")) == 0) + return IIO_MOD_WALKING; + + if (strncmp(mod, "running", sizeof("running")) == 0) + return IIO_MOD_RUNNING; + + return -1; +} + +static void add_activity(int sensor_catalog_index, int channel_index, + int dev_num, const char *name) +{ + int index, i, modifier; + + if (count == MAX_ACTIVITIES) { + ALOGE("Trying to add more than supported activities!\n"); + return; + } + + modifier = get_modifier_as_int(name); + if (modifier < 0) { + ALOGE("Invalid channel name as modifier: %s\n", name); + return; + } + + index = ++count; + + for (i = 0; i < MAX_EVENTS_PER_ACTIVITY; i++) { + supported_activities[index].event[i] = NULL; + supported_activities[index].monitored[i] = false; + } + supported_activities[index].modifier = modifier; + supported_activities[index].event_count = 0; + supported_activities[index].sensor_catalog_index = sensor_catalog_index; + supported_activities[index].channel_index = channel_index; + supported_activities[index].dev_num = dev_num; + supported_activities[index].event_fd = -1; + + supported_activity_names[index] = name; +} + +static bool is_activity_valid(const char *activity_name) +{ + unsigned int i; + + /* Look if this activity has not been already added */ + for (i = 1; i <= count; i++) + if (strcmp(supported_activity_names[i], activity_name) == 0) + return false; + + /* Check that the found activity is recognized by this API */ + for (i = 0; i < MAX_ACTIVITIES; i++) + if (strcmp(sysfs_activity_names[i], activity_name) == 0) + return true; + + return false; +} + +/* Get all possible activities provided by the IIO sensors */ +static void discover_activity_events(void) +{ + channel_descriptor_t *chann; + int i, num_channels, dev_num; + unsigned int index; + char event_sensors[catalog_size]; + + /* Discover event sensors */ + for (dev_num = 0; dev_num < MAX_DEVICES; dev_num++) { + discover_sensors(dev_num, EVENTS_PATH, event_sensors, check_event_sensors); + for (index = 0; index < catalog_size; index++) { + if (!event_sensors[index]) + continue; + + num_channels = sensor_catalog[index].num_channels; + for (i = 0; i < num_channels; i++) { + chann = sensor_catalog[index].channel + i; + + if (is_activity_valid(chann->name)) + add_activity(index, i, dev_num, chann->name); + } + } + } + + ALOGI("Discovered %d activities\n", count); +} + +static int open_module(const struct hw_module_t *module, const char *id, + struct hw_device_t **device) +{ + static struct activity_recognition_device activity_dev; + int ret = 0; + + if (strncmp(id, ACTIVITY_RECOGNITION_HARDWARE_INTERFACE, sizeof(ACTIVITY_RECOGNITION_HARDWARE_INTERFACE)) != 0) + return -EINVAL; + + activity_dev.common.tag = HARDWARE_DEVICE_TAG; + activity_dev.common.version = ACTIVITY_RECOGNITION_API_VERSION_0_1; + activity_dev.common.module = (struct hw_module_t *) module; + activity_dev.common.close = close_device; + + activity_dev.register_activity_callback = register_activity_callback; + activity_dev.enable_activity_event = enable_activity_event; + activity_dev.disable_activity_event = disable_activity_event; + activity_dev.flush = flush; + + *device = &activity_dev.common; + + if (instances_count == 0) { + discover_activity_events(); + ret = set_up_control_data(); + + ALOGI("Initialized activity recognition HAL (exit code %i)\n", ret); + } + + instances_count++; + + return ret; +} + +static struct hw_module_methods_t module_methods = { + .open = open_module +}; + + +static int get_supported_activities_list(struct activity_recognition_module *module __attribute((unused)), + char const* const* *activity_list) +{ + *activity_list = supported_activity_names + 1; + + return count; +} + +/* Module descriptor visible to the Android framework. */ +struct activity_recognition_module __attribute__ ((visibility ("default"))) + HAL_MODULE_INFO_SYM = { + .common = { + .tag = HARDWARE_MODULE_TAG, + .module_api_version = MODULE_VERSION, + .hal_api_version = HAL_VERSION, + .id = ACTIVITY_RECOGNITION_HARDWARE_MODULE_ID, + .name = MODULE_NAME, + .author = MODULE_AUTHOR, + .methods = &module_methods, + }, + .get_supported_activities_list = get_supported_activities_list + +}; diff --git a/activity_event_utils.h b/activity_event_utils.h new file mode 100644 index 0000000..e77d166 --- /dev/null +++ b/activity_event_utils.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2015 Intel Corporation. + */ + +#ifndef __ACTIVITY_EVENT_UTILS_H__ +#define __ACTIVITY_EVENT_UTILS_H__ + +#include + +#include "utils.h" + +#define MAX_ACTIVITIES 6 +#define MAX_EVENTS_PER_ACTIVITY 2 + +typedef unsigned bool; +#define true 1 +#define false 0 + +/* For each activity in activity_recognition.h we can monitor 2 events at most : + * ENTER and EXIT */ +struct activity_event_info { + struct activity_event *event[MAX_EVENTS_PER_ACTIVITY]; + int modifier; + int sensor_catalog_index; + int channel_index; + int dev_num; + int event_fd; + int event_count; + bool monitored[MAX_EVENTS_PER_ACTIVITY]; +}; + +struct control_event_data { + uint8_t enable; + uint8_t activity; + uint8_t event; +}; + +/** + * Creates a control event identifier: + * [unused] EVENT ACTIVITY ENABLE + * 63 ... 24 23 ... 16 15 ... 8 7 ... 0 + * @enable: Says if the pair needs to be enabled or disabled (0 or 1) + * @activity: What activity are we working on - index in the list returned by + * get_supported_activities_list() + * @event: What type of event to asociate with the given activity (one of + * the ACTIVITY_EVENT_* enum) + */ +static inline uint64_t get_control_code(uint8_t enable, uint8_t activity, uint8_t event) +{ + return ((uint64_t)enable << 56) | + ((uint64_t)activity << 48) | + ((uint64_t)event << 40); +} + +/** + * Parses the given control identifier and retrieves the control data. + * @control_code: the unified control data + * @control_data: extracted data from the control code + */ +static inline void get_control_data(uint64_t control_code, + struct control_event_data *control_data) +{ + control_data->enable = (uint8_t)(control_code >> 56); + control_data->activity = (uint8_t)(control_code >> 48 & 0xFF); + control_data->event = (uint8_t)(control_code >> 40 & 0xFF); +} + +#endif diff --git a/common.h b/common.h index 809400b..757eab2 100644 --- a/common.h +++ b/common.h @@ -51,6 +51,107 @@ #define MODE_TRIGGER 2 #define MODE_EVENT 3 + +/* Couple of temporary defines until we get a suitable linux/iio/events.h include */ + +struct iio_event_data { + __u64 id; + __s64 timestamp; +}; + +#define IIO_GET_EVENT_FD_IOCTL _IOR('i', 0x90, int) + +#define IIO_EVENT_CODE_EXTRACT_TYPE(mask) ((mask >> 56) & 0xFF) +#define IIO_EVENT_CODE_EXTRACT_DIR(mask) ((mask >> 48) & 0xCF) +#define IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(mask) ((mask >> 32) & 0xFF) +#define IIO_EVENT_CODE_EXTRACT_MODIFIER(mask) ((mask >> 40) & 0xFF) + +/* Couple of temporary defines until we get a suitable linux/iio/types.h include */ +enum iio_chan_type { + IIO_VOLTAGE, + IIO_CURRENT, + IIO_POWER, + IIO_ACCEL, + IIO_ANGL_VEL, + IIO_MAGN, + IIO_LIGHT, + IIO_INTENSITY, + IIO_PROXIMITY, + IIO_TEMP, + IIO_INCLI, + IIO_ROT, + IIO_ANGL, + IIO_TIMESTAMP, + IIO_CAPACITANCE, + IIO_ALTVOLTAGE, + IIO_CCT, + IIO_PRESSURE, + IIO_HUMIDITYRELATIVE, + IIO_ACTIVITY, + IIO_STEPS, + IIO_CALORIES, + IIO_DISTANCE, + IIO_SPEED, +}; + +enum iio_modifier { + IIO_NO_MOD, + IIO_MOD_X, + IIO_MOD_Y, + IIO_MOD_Z, + IIO_MOD_X_AND_Y, + IIO_MOD_X_AND_Z, + IIO_MOD_Y_AND_Z, + IIO_MOD_X_AND_Y_AND_Z, + IIO_MOD_X_OR_Y, + IIO_MOD_X_OR_Z, + IIO_MOD_Y_OR_Z, + IIO_MOD_X_OR_Y_OR_Z, + IIO_MOD_LIGHT_BOTH, + IIO_MOD_LIGHT_IR, + IIO_MOD_ROOT_SUM_SQUARED_X_Y, + IIO_MOD_SUM_SQUARED_X_Y_Z, + IIO_MOD_LIGHT_CLEAR, + IIO_MOD_LIGHT_RED, + IIO_MOD_LIGHT_GREEN, + IIO_MOD_LIGHT_BLUE, + IIO_MOD_QUATERNION, + IIO_MOD_TEMP_AMBIENT, + IIO_MOD_TEMP_OBJECT, + IIO_MOD_NORTH_MAGN, + IIO_MOD_NORTH_TRUE, + IIO_MOD_NORTH_MAGN_TILT_COMP, + IIO_MOD_NORTH_TRUE_TILT_COMP, + IIO_MOD_RUNNING, + IIO_MOD_JOGGING, + IIO_MOD_WALKING, + IIO_MOD_STILL, +}; + +enum iio_event_type { + IIO_EV_TYPE_THRESH, + IIO_EV_TYPE_MAG, + IIO_EV_TYPE_ROC, + IIO_EV_TYPE_THRESH_ADAPTIVE, + IIO_EV_TYPE_MAG_ADAPTIVE, + IIO_EV_TYPE_INSTANCE, +}; + +enum iio_event_info { + IIO_EV_INFO_ENABLE, + IIO_EV_INFO_VALUE, + IIO_EV_INFO_HYSTERESIS, + IIO_EV_INFO_PERIOD, +}; + +enum iio_event_direction { + IIO_EV_DIR_EITHER, + IIO_EV_DIR_RISING, + IIO_EV_DIR_FALLING, + IIO_EV_DIR_NONE, +}; + + typedef struct { const char *type; /* event type; e.g: transition */ diff --git a/control.c b/control.c index 34a8f06..b8fdeab 100644 --- a/control.c +++ b/control.c @@ -22,15 +22,6 @@ #include "description.h" #include "filtering.h" -/* Couple of temporary defines until we get a suitable linux/iio/events.h include */ - -struct iio_event_data { - __u64 id; - __s64 timestamp; -}; - -#define IIO_GET_EVENT_FD_IOCTL _IOR('i', 0x90, int) - /* Currently active sensors count, per device */ static int poll_sensors_per_dev[MAX_DEVICES]; /* poll-mode sensors */ static int trig_sensors_per_dev[MAX_DEVICES]; /* trigger, event based */ diff --git a/discovery.c b/discovery.c index 640c3f9..7434c33 100644 --- a/discovery.c +++ b/discovery.c @@ -30,7 +30,7 @@ void discover_sensors(int dev_num, char *sysfs_base_path, char map[catalog_size] /* Enumerate entries in this iio device's base folder */ while ((d = readdir(dir))) { - if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) + if (!strncmp(d->d_name, ".", sizeof(".")) || !strncmp(d->d_name, "..", sizeof(".."))) continue; /* If the name matches a catalog entry, flag it */