| /* |
| * Copyright (C) 2014-2017 Intel Corporation |
| * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd |
| * |
| * 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. |
| */ |
| |
| #define LOG_TAG "PollerThread" |
| |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include "PollerThread.h" |
| #include "LogHelper.h" |
| |
| NAMESPACE_DECLARATION { |
| |
| PollerThread::PollerThread(const char* name): |
| mName(name), |
| mCameraThread(mName.c_str()), |
| mListener (nullptr), |
| mEvents(POLLPRI | POLLIN | POLLERR) |
| { |
| HAL_TRACE_CALL(CAMERA_DEBUG_LOG_LEVEL1); |
| |
| mFlushFd[0] = -1; |
| mFlushFd[1] = -1; |
| mPid = getpid(); |
| if (!mCameraThread.Start()) { |
| LOGE("Camera thread failed to start"); |
| } |
| } |
| |
| PollerThread::~PollerThread() |
| { |
| HAL_TRACE_CALL(CAMERA_DEBUG_LOG_LEVEL1); |
| |
| close(mFlushFd[0]); |
| close(mFlushFd[1]); |
| mFlushFd[0] = -1; |
| mFlushFd[1] = -1; |
| |
| // detach Listener |
| mListener = nullptr; |
| |
| mCameraThread.Stop(); |
| } |
| |
| /** |
| * init() |
| * initialize flush file descriptors and other class members |
| * |
| * \param devices to poll. |
| * \param observer event listener |
| * \param events the poll events (bits) |
| * \param makeRealtime deprecated do not use, will be removed |
| * \return status |
| * |
| */ |
| status_t PollerThread::init(std::vector<std::shared_ptr<V4L2DeviceBase>> &devices, |
| IPollEventListener *observer, |
| int events, |
| bool makeRealtime) |
| { |
| HAL_TRACE_CALL(CAMERA_DEBUG_LOG_LEVEL1); |
| MessageInit msg; |
| |
| msg.devices = devices; // copy the vector |
| msg.observer = observer; |
| msg.events = events; |
| msg.makeRealtime = makeRealtime; |
| |
| status_t status = NO_ERROR; |
| base::Callback<status_t()> closure = |
| base::Bind(&PollerThread::handleInit, base::Unretained(this), |
| base::Passed(std::move(msg))); |
| mCameraThread.PostTaskSync<status_t>(FROM_HERE, closure, &status); |
| return status; |
| } |
| |
| status_t PollerThread::handleInit(MessageInit msg) |
| { |
| HAL_TRACE_CALL(CAMERA_DEBUG_LOG_LEVEL1); |
| status_t status = NO_ERROR; |
| |
| if (mFlushFd[1] != -1 || mFlushFd[0] != -1) { |
| close(mFlushFd[0]); |
| close(mFlushFd[1]); |
| mFlushFd[0] = -1; |
| mFlushFd[1] = -1; |
| } |
| |
| status = pipe(mFlushFd); |
| if (status < 0) { |
| LOGE("Failed to create Flush pipe: %s", strerror(errno)); |
| return NO_INIT; |
| } |
| |
| /** |
| * make the reading end of the pipe non blocking. |
| * This helps during flush to read any information left there without |
| * blocking |
| */ |
| status = fcntl(mFlushFd[0],F_SETFL,O_NONBLOCK); |
| if (status < 0) { |
| LOGE("Fail to set flush pipe flag: %s", strerror(errno)); |
| return NO_INIT; |
| } |
| |
| if (msg.makeRealtime) { |
| // Request to change request asynchronously |
| LOGW("Real time thread priority change is not supported"); |
| } |
| |
| if (msg.devices.size() == 0) { |
| LOGE("%s, No devices provided", __FUNCTION__); |
| return BAD_VALUE; |
| } |
| |
| if (msg.observer == nullptr) |
| { |
| LOGE("%s, No observer provided", __FUNCTION__); |
| return BAD_VALUE; |
| } |
| |
| mPollingDevices = msg.devices; |
| mEvents = msg.events; |
| |
| //attach listener. |
| mListener = msg.observer; |
| return status; |
| } |
| |
| /** |
| * pollRequest() |
| * this method enqueue the poll request. |
| * params: request ID |
| * |
| */ |
| status_t PollerThread::pollRequest(int reqId, int timeout, |
| std::vector<std::shared_ptr<V4L2DeviceBase>> *devices) |
| { |
| HAL_TRACE_CALL(CAMERA_DEBUG_LOG_LEVEL2); |
| MessagePollRequest msg; |
| msg.reqId = reqId; |
| msg.timeout = timeout; |
| if (devices) |
| msg.devices = *devices; |
| |
| base::Callback<status_t()> closure = |
| base::Bind(&PollerThread::handlePollRequest, base::Unretained(this), |
| base::Passed(std::move(msg))); |
| mCameraThread.PostTaskAsync<status_t>(FROM_HERE, closure); |
| return OK; |
| } |
| |
| status_t PollerThread::handlePollRequest(MessagePollRequest msg) |
| { |
| HAL_TRACE_CALL(CAMERA_DEBUG_LOG_LEVEL2); |
| status_t status = NO_ERROR; |
| int ret; |
| IPollEventListener::PollEventMessage outMsg; |
| |
| if (msg.devices.size() > 0) { |
| mPollingDevices = msg.devices; |
| } else { |
| // Return error for empty poll request |
| outMsg.id = IPollEventListener::POLL_EVENT_ID_ERROR; |
| outMsg.data.reqId = msg.reqId; |
| outMsg.data.activeDevices = &msg.devices; |
| outMsg.data.inactiveDevices = &msg.devices; |
| outMsg.data.polledDevices = &msg.devices; |
| outMsg.data.pollStatus = 0; |
| |
| return notifyListener(&outMsg); |
| } |
| |
| do { |
| ret = V4L2DeviceBase::pollDevices(mPollingDevices, mActiveDevices, |
| mInactiveDevices, |
| msg.timeout, mFlushFd[0], |
| mEvents); |
| if (ret <= 0) { |
| outMsg.id = IPollEventListener::POLL_EVENT_ID_ERROR; |
| } else { |
| outMsg.id = IPollEventListener::POLL_EVENT_ID_EVENT; |
| } |
| outMsg.data.reqId = msg.reqId; |
| outMsg.data.activeDevices = &mActiveDevices; |
| outMsg.data.inactiveDevices = &mInactiveDevices; |
| outMsg.data.polledDevices = &mPollingDevices; |
| outMsg.data.pollStatus = ret; |
| status = notifyListener(&outMsg); |
| } while (status == -EAGAIN); |
| return status; |
| } |
| |
| /** |
| * flush() |
| * this method is done to interrupt the polling. |
| * We first empty the Q for any polling request and then |
| * a value is written to a polled fd, which will make the poll returning |
| * |
| * There are 2 variants an asyncrhonous one that will not wait for the thread |
| * to complete the current request and the synchronous one that will send |
| * a message to the Q |
| * |
| * This can be called on an uninitialized Poller also, but the flush will then |
| * only empty the message queue and the vectors. |
| * |
| */ |
| status_t PollerThread::flush(bool sync, bool clear) |
| { |
| HAL_TRACE_CALL(CAMERA_DEBUG_LOG_LEVEL1); |
| MessageFlush msg; |
| msg.clearVectors = clear; |
| |
| if (mFlushFd[1] != -1) { |
| char buf = 0xf; // random value to write to flush fd. |
| unsigned int size = write(mFlushFd[1], &buf, sizeof(char)); |
| if (size != sizeof(char)) |
| LOGW("Flush write not completed"); |
| } |
| |
| if (sync) { |
| status_t status = NO_ERROR; |
| base::Callback<status_t()> closure = |
| base::Bind(&PollerThread::handleFlush, base::Unretained(this), |
| base::Passed(std::move(msg))); |
| mCameraThread.PostTaskSync<status_t>(FROM_HERE, closure, &status); |
| return status; |
| } else { |
| base::Callback<status_t()> closure = |
| base::Bind(&PollerThread::handleFlush, base::Unretained(this), |
| base::Passed(std::move(msg))); |
| mCameraThread.PostTaskAsync<status_t>(FROM_HERE, closure); |
| return OK; |
| } |
| } |
| |
| status_t PollerThread::handleFlush(MessageFlush msg) |
| { |
| /** |
| * read the pipe just in case there was nothing to flush. |
| * this ensures that the pipe is empty for the next try. |
| * this is safe because the reading end is non blocking. |
| */ |
| if (msg.clearVectors) { |
| mPollingDevices.clear(); |
| mActiveDevices.clear(); |
| mInactiveDevices.clear(); |
| } |
| char readbuf; |
| if (mFlushFd[0] != -1) { |
| unsigned int size = read(mFlushFd[0], (void*) &readbuf, sizeof(char)); |
| if (size != sizeof(char)) |
| LOGW("Flush read not completed."); |
| } |
| |
| return OK; |
| } |
| |
| status_t PollerThread::requestExitAndWait(void) |
| { |
| mCameraThread.Stop(); |
| return NO_ERROR; |
| } |
| |
| /** Listener Methods **/ |
| status_t PollerThread::notifyListener(IPollEventListener::PollEventMessage *msg) |
| { |
| HAL_TRACE_CALL(CAMERA_DEBUG_LOG_LEVEL2); |
| status_t status = OK; |
| |
| if (mListener == nullptr) |
| return BAD_VALUE; |
| |
| status = mListener->notifyPollEvent((IPollEventListener::PollEventMessage*)msg); |
| |
| return status; |
| } |
| |
| } NAMESPACE_DECLARATION_END |
| |