/*
* Copyright (C) 2015 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cutils/log.h>
#include <sys/epoll.h>
#include <string.h>
#include <errno.h>
#include <hardware/sensors.h>
#include "AcquisitionThread.hpp"
#include "Utils.hpp"
void* AcquisitionThread::acquisitionRoutine(void *param) {
AcquisitionThread *acquisitionThread = reinterpret_cast<AcquisitionThread *>(param);
Sensor *sensor = nullptr;
int64_t timestamp = 0;
struct timespec target_time;
int rc = 0;
sensors_event_t data;
if (acquisitionThread == nullptr) {
ALOGE("%s: The acquisition thread was not initialized", __func__);
return nullptr;
}
sensor = acquisitionThread->sensor;
if (sensor == nullptr) {
ALOGE("%s: The acquisition thread doesn't have an associated sensor", __func__);
return nullptr;
}
/* initialize sensor data structure */
memset(&data, 0, sizeof(sensors_event_t));
data.version = sizeof(sensors_event_t);
data.sensor = sensor->getHandle();
data.type = sensor->getType();
pthread_mutex_lock(&acquisitionThread->pthreadMutex);
/* get current timestamp */
timestamp = get_timestamp_monotonic();
/* loop until the thread is canceled */
while(acquisitionThread->getWritePipeFd() != -1) {
/* get one event from the sensor */
if (sensor->pollEvents(&data, 1) != 1) {
ALOGE("%s: Sensor %d: Cannot read data", __func__, data.sensor);
goto exit;
}
/* send the data over the pipe to the main thread */
rc = write(acquisitionThread->getWritePipeFd(), &data, sizeof(sensors_event_t));
if (rc != sizeof(sensors_event_t)) {
ALOGE("%s: Sensor %d: Cannot write data to pipe", __func__, data.sensor);
goto exit;
}
if (acquisitionThread->getWritePipeFd() == -1) {
ALOGE("%s: Sensor %d: The write pipe file descriptor is invalid", __func__, data.sensor);
goto exit;
}
timestamp += sensor->getDelay();
set_timestamp(&target_time, timestamp);
pthread_cond_timedwait(&acquisitionThread->pthreadCond, &acquisitionThread->pthreadMutex, &target_time);
}
exit:
pthread_mutex_unlock(&acquisitionThread->pthreadMutex);
return nullptr;
}
AcquisitionThread::AcquisitionThread(int pollFd, Sensor *sensor)
: pollFd(pollFd), sensor(sensor), initialized(false) {
pipeFds[0] = pipeFds[1] = -1;
}
bool AcquisitionThread::init() {
struct epoll_event ev;
int rc;
memset(&ev, 0, sizeof(ev));
if (initialized) {
ALOGE("%s: acquisition thread already initialized", __func__);
return false;
}
/* create condition variable & mutex for quick thread release */
rc = pthread_condattr_init(&pthreadCondAttr);
if (rc != 0) {
ALOGE("%s: Cannot initialize the pthread condattr", __func__);
return false;
}
rc = pthread_condattr_setclock(&pthreadCondAttr, CLOCK_MONOTONIC);
if (rc != 0) {
ALOGE("%s: Cannot set the clock of condattr to monotonic", __func__);
return false;
}
rc = pthread_cond_init(&pthreadCond, &pthreadCondAttr);
if (rc != 0) {
ALOGE("%s: Cannot intialize the pthread structure", __func__);
return false;
}
rc = pthread_mutex_init(&pthreadMutex, nullptr);
if (rc != 0) {
ALOGE("%s: Cannot initialize the mutex associated with the pthread cond", __func__);
goto mutex_err;
}
/* create pipe to signal events to the main thread */
rc = pipe2(pipeFds, O_NONBLOCK);
if (rc != 0) {
ALOGE("%s: Cannot initialize pipe", __func__);
goto pipe_err;
}
ev.events = EPOLLIN;
ev.data.u32 = sensor->getHandle();
/* add read pipe fd to pollFd */
rc = epoll_ctl(pollFd, EPOLL_CTL_ADD, pipeFds[0] , &ev);
if (rc != 0) {
ALOGE("%s: Cannot add the read file descriptor to poll set", __func__);
goto epoll_err;
}
/* launch the thread */
rc = pthread_create(&pthread, nullptr, acquisitionRoutine, (void *)this);
if (rc != 0) {
ALOGE("%s: Cannot create acquisition pthread", __func__);
goto thread_create_err;
}
initialized = true;
return true;
thread_create_err:
epoll_ctl(pollFd, EPOLL_CTL_DEL, pipeFds[0], nullptr);
epoll_err:
close(pipeFds[0]);
close(pipeFds[1]);
pipeFds[0] = pipeFds[1] = -1;
pipe_err:
pthread_mutex_destroy(&pthreadMutex);
mutex_err:
pthread_cond_destroy(&pthreadCond);
return false;
}
bool AcquisitionThread::generateFlushCompleteEvent() {
sensors_event_t data;
int rc;
if (!initialized) {
return false;
}
/* batching is not supported; return one META_DATA_FLUSH_COMPLETE event */
memset(&data, 0, sizeof(sensors_event_t));
data.version = META_DATA_VERSION;
data.type = SENSOR_TYPE_META_DATA;
data.meta_data.sensor = sensor->getHandle();
data.meta_data.what = META_DATA_FLUSH_COMPLETE;
/*
* Send the event via the associated pipe. It doesn't need to be in a loop
* as O_NONBLOCK is enabled and the number of bytes is <= PIPE_BUF.
* If there is room to write n bytes to the pipe, then write succeeds
* immediately, writing all n bytes; otherwise write fails.
*/
rc = write(getWritePipeFd(), &data, sizeof(sensors_event_t));
if (rc != sizeof(sensors_event_t)) {
ALOGE("%s: not all data has been sent over the pipe", __func__);
return false;
}
return true;
}
int AcquisitionThread::wakeup() {
if (initialized) {
return pthread_cond_signal(&pthreadCond);
}
return -EINVAL;
}
AcquisitionThread::~AcquisitionThread() {
int readPipeEnd, writePipeEnd;
if (initialized) {
readPipeEnd = pipeFds[0];
writePipeEnd = pipeFds[1];
epoll_ctl(pollFd, EPOLL_CTL_DEL, readPipeEnd, nullptr);
/* take the mutex to correctly signal the thread */
pthread_mutex_lock(&pthreadMutex);
pipeFds[0] = pipeFds[1] = -1;
close(readPipeEnd);
close(writePipeEnd);
/* wakeup and wait for the thread */
pthread_cond_signal(&pthreadCond);
pthread_mutex_unlock(&pthreadMutex);
pthread_join(pthread, nullptr);
pthread_cond_destroy(&pthreadCond);
pthread_mutex_destroy(&pthreadMutex);
}
}