blob: aee0a587430629f7e9b970b41539dd8d921400b0 [file] [log] [blame]
/*
Copyright (c) 2011, The Linux Foundation. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of The Linux Foundation nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <pthread.h>
#include "mm_camera_dbg.h"
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>
#include "mm_camera_interface2.h"
#include "mm_camera.h"
typedef enum {
/* ask the channel to flash out the queued frames. */
MM_CAMERA_PIPE_CMD_FLASH_QUEUED_FRAME,
/* ask ctrl fd to generate ch event to HAL */
MM_CAMERA_PIPE_CMD_CH_EVENT,
/*start*/
MM_CAMERA_PIPE_CMD_ADD_CH,
/*stop*/
MM_CAMERA_PIPE_CMD_DEL_CH,
/* exit */
MM_CAMERA_PIPE_CMD_EXIT,
/* max count */
MM_CAMERA_PIPE_CMD_MAX
} mm_camera_pipe_cmd_type_t;
typedef enum {
MM_CAMERA_POLL_TASK_STATE_POLL, /* polling pid in polling state. */
MM_CAMERA_POLL_TASK_STATE_MAX
} mm_camera_poll_task_state_type_t;
typedef struct {
uint8_t cmd;
mm_camera_event_t event;
} mm_camera_sig_evt_t;
static int32_t mm_camera_poll_sig(mm_camera_poll_thread_t *poll_cb,
uint32_t cmd)
{
/* send through pipe */
/* get the mutex */
mm_camera_sig_evt_t cmd_evt;
memset(&cmd_evt, 0, sizeof(cmd_evt));
cmd_evt.cmd = cmd;
int len;
CDBG("%s: begin", __func__);
pthread_mutex_lock(&poll_cb->mutex);
/* reset the statue to false */
poll_cb->status = FALSE;
/* send cmd to worker */
len = write(poll_cb->data.pfds[1], &cmd_evt, sizeof(cmd_evt));
if(len < 1) {
CDBG("%s: len = %d, errno = %d", __func__, len, errno);
//pthread_mutex_unlock(&poll_cb->mutex);
//return -1;
}
CDBG("%s: begin IN mutex write done, len = %d", __func__, len);
/* wait till worker task gives positive signal */
while (!poll_cb->status) {
int rc;
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 2;
CDBG("%s: wait", __func__);
rc = pthread_cond_timedwait(&poll_cb->cond_v, &poll_cb->mutex, &ts);
if (rc) {
ALOGV("%s: error on pthread_cond_timedwait: %s", __func__, strerror(rc));
break;
}
}
/* done */
pthread_mutex_unlock(&poll_cb->mutex);
CDBG("%s: end, len = %d, size = %d", __func__, len, sizeof(cmd_evt));
return MM_CAMERA_OK;
}
static void mm_camera_poll_sig_done(mm_camera_poll_thread_t *poll_cb)
{
pthread_mutex_lock(&poll_cb->mutex);
poll_cb->status = TRUE;
pthread_cond_signal(&poll_cb->cond_v);
CDBG("%s: done, in mutex", __func__);
pthread_mutex_unlock(&poll_cb->mutex);
}
static int32_t mm_camera_poll_proc_msm(mm_camera_poll_thread_t *poll_cb, struct pollfd *fds)
{
int i;
for(i = 0; i < poll_cb->data.num_fds-1; i++) {
/*Checking for data events*/
if((poll_cb->data.poll_type == MM_CAMERA_POLL_TYPE_CH) &&
(fds[i].revents & POLLIN) &&
(fds[i].revents & POLLRDNORM)) {
if(poll_cb->data.used) {
mm_camera_msm_data_notify(poll_cb->data.my_obj,
fds[i].fd,
poll_cb->data.poll_streams[i]->stream_type);
}
}
/*Checking for ctrl events*/
if((poll_cb->data.poll_type == MM_CAMERA_POLL_TYPE_EVT) &&
(fds[i].revents & POLLPRI)) {
CDBG("%s: mm_camera_msm_evt_notify\n", __func__);
mm_camera_msm_evt_notify(poll_cb->data.my_obj, fds[i].fd);
}
}
return 0;
}
static void cm_camera_poll_set_state(mm_camera_poll_thread_t *poll_cb,
mm_camera_poll_task_state_type_t state)
{
poll_cb->data.state = state;
}
static void mm_camera_poll_proc_pipe(mm_camera_poll_thread_t *poll_cb)
{
ssize_t read_len;
int i;
mm_camera_sig_evt_t cmd_evt;
read_len = read(poll_cb->data.pfds[0], &cmd_evt, sizeof(cmd_evt));
CDBG("%s: read_fd = %d, read_len = %d, expect_len = %d",
__func__, poll_cb->data.pfds[0], (int)read_len, (int)sizeof(cmd_evt));
switch(cmd_evt.cmd) {
case MM_CAMERA_PIPE_CMD_FLASH_QUEUED_FRAME:
mm_camera_dispatch_buffered_frames(poll_cb->data.my_obj,
poll_cb->data.ch_type);
break;
case MM_CAMERA_PIPE_CMD_CH_EVENT: {
mm_camera_event_t *event = &cmd_evt.event;
CDBG("%s: ch event, type=0x%x, ch=%d, evt=%d",
__func__, event->event_type, event->e.ch.ch, event->e.ch.evt);
mm_camera_dispatch_app_event(poll_cb->data.my_obj, event);
break;
}
case MM_CAMERA_PIPE_CMD_ADD_CH:
if(poll_cb->data.poll_type == MM_CAMERA_POLL_TYPE_CH) {
for(i = 0; i < MM_CAMERA_CH_STREAM_MAX; i++) {
if(poll_cb->data.poll_streams[i]) {
poll_cb->data.poll_fd[poll_cb->data.num_fds + i] = poll_cb->data.poll_streams[i]->fd;
}
}
}
poll_cb->data.num_fds += mm_camera_ch_util_get_num_stream(poll_cb->data.my_obj,
poll_cb->data.ch_type);
poll_cb->data.used = 1;
CDBG("Num fds after MM_CAMERA_PIPE_CMD_ADD_CH = %d",poll_cb->data.num_fds);
break;
case MM_CAMERA_PIPE_CMD_DEL_CH:
poll_cb->data.num_fds -= mm_camera_ch_util_get_num_stream(poll_cb->data.my_obj,
poll_cb->data.ch_type);
poll_cb->data.used = 0;
CDBG("Num fds after MM_CAMERA_PIPE_CMD_DEL_CH = %d",poll_cb->data.num_fds);
break;
case MM_CAMERA_PIPE_CMD_EXIT:
default:
cm_camera_poll_set_state(poll_cb, MM_CAMERA_POLL_TASK_STATE_MAX);
mm_camera_poll_sig_done(poll_cb);
break;
}
}
static int mm_camera_poll_ch_busy(mm_camera_obj_t * my_obj, int ch_type)
{
int i;
int used = 0;
mm_camera_poll_thread_t *poll_cb = &my_obj->poll_threads[ch_type];
pthread_mutex_lock(&poll_cb->mutex);
used = poll_cb->data.used;
pthread_mutex_unlock(&poll_cb->mutex);
if(used)
return 1;
else
return 0;
}
int32_t mm_camera_poll_dispatch_buffered_frames(mm_camera_obj_t * my_obj, int ch_type)
{
mm_camera_poll_thread_t *poll_cb = &my_obj->poll_threads[ch_type];
mm_camera_sig_evt_t cmd;
int len;
cmd.cmd = MM_CAMERA_PIPE_CMD_FLASH_QUEUED_FRAME;
memset(&cmd.event, 0, sizeof(cmd.event));
pthread_mutex_lock(&poll_cb->mutex);
len = write(poll_cb->data.pfds[1], &cmd, sizeof(cmd));
pthread_mutex_unlock(&poll_cb->mutex);
return MM_CAMERA_OK;
}
int mm_camera_poll_busy(mm_camera_obj_t * my_obj)
{
int i;
mm_camera_poll_thread_t *poll_cb;
for(i = 0; i < (MM_CAMERA_POLL_THRAED_MAX - 1); i++) {
if(mm_camera_poll_ch_busy(my_obj, i) > 0)
return 1;
}
return 0;
}
int mm_camera_poll_send_ch_event(mm_camera_obj_t * my_obj, mm_camera_event_t *event)
{
mm_camera_poll_thread_t *poll_cb = &my_obj->poll_threads[MM_CAMERA_CH_MAX];
mm_camera_sig_evt_t cmd;
int len;
cmd.cmd = MM_CAMERA_PIPE_CMD_CH_EVENT;
memcpy(&cmd.event, event, sizeof(cmd.event));
CDBG("%s: ch event, type=0x%x, ch=%d, evt=%d, poll_type = %d, read_fd=%d, write_fd=%d",
__func__, event->event_type, event->e.ch.ch, event->e.ch.evt, poll_cb->data.poll_type,
poll_cb->data.pfds[0], poll_cb->data.pfds[1]);
pthread_mutex_lock(&poll_cb->mutex);
len = write(poll_cb->data.pfds[1], &cmd, sizeof(cmd));
pthread_mutex_unlock(&poll_cb->mutex);
return MM_CAMERA_OK;
}
static void *mm_camera_poll_fn(mm_camera_poll_thread_t *poll_cb)
{
int rc = 0, i;
struct pollfd fds[MM_CAMERA_CH_STREAM_MAX+1];
int timeoutms;
CDBG("%s: poll type = %d, num_fd = %d\n",
__func__, poll_cb->data.poll_type, poll_cb->data.num_fds);
do {
for(i = 0; i < poll_cb->data.num_fds; i++) {
fds[i].fd = poll_cb->data.poll_fd[i];
fds[i].events = POLLIN|POLLRDNORM|POLLPRI;
}
timeoutms = poll_cb->data.timeoutms;
rc = poll(fds, poll_cb->data.num_fds, timeoutms);
if(rc > 0) {
if((fds[0].revents & POLLIN) && (fds[0].revents & POLLRDNORM))
mm_camera_poll_proc_pipe(poll_cb);
else
mm_camera_poll_proc_msm(poll_cb, &fds[1]);
} else {
/* in error case sleep 10 us and then continue. hard coded here */
usleep(10);
continue;
}
} while (poll_cb->data.state == MM_CAMERA_POLL_TASK_STATE_POLL);
return NULL;
}
static void *mm_camera_poll_thread(void *data)
{
int rc = 0;
int i;
void *ret = NULL;
mm_camera_poll_thread_t *poll_cb = data;
poll_cb->data.poll_fd[poll_cb->data.num_fds++] = poll_cb->data.pfds[0];
switch(poll_cb->data.poll_type) {
case MM_CAMERA_POLL_TYPE_EVT:
poll_cb->data.poll_fd[poll_cb->data.num_fds++] =
((mm_camera_obj_t *)(poll_cb->data.my_obj))->ctrl_fd;
break;
case MM_CAMERA_POLL_TYPE_CH:
default:
break;
}
mm_camera_poll_sig_done(poll_cb);
ret = mm_camera_poll_fn(poll_cb);
return ret;
}
int mm_camera_poll_start(mm_camera_obj_t * my_obj, mm_camera_poll_thread_t *poll_cb)
{
pthread_mutex_lock(&poll_cb->mutex);
poll_cb->status = 0;
pthread_create(&poll_cb->data.pid, NULL, mm_camera_poll_thread, (void *)poll_cb);
while (!poll_cb->status) {
int rc;
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 2;
rc = pthread_cond_timedwait(&poll_cb->cond_v, &poll_cb->mutex, &ts);
if (rc) {
ALOGV("%s: error on pthread_cond_timedwait: %s", __func__, strerror(rc));
break;
}
}
pthread_mutex_unlock(&poll_cb->mutex);
return MM_CAMERA_OK;
}
int mm_camera_poll_stop(mm_camera_obj_t * my_obj, mm_camera_poll_thread_t *poll_cb)
{
CDBG("%s, my_obj=0x%x\n", __func__, (uint32_t)my_obj);
mm_camera_poll_sig(poll_cb, MM_CAMERA_PIPE_CMD_EXIT);
if (pthread_join(poll_cb->data.pid, NULL) != 0) {
CDBG("%s: pthread dead already\n", __func__);
}
return MM_CAMERA_OK;
}
int mm_camera_poll_thread_add_ch(mm_camera_obj_t * my_obj, int ch_type)
{
mm_camera_poll_thread_t *poll_cb = &my_obj->poll_threads[ch_type];
mm_camera_sig_evt_t cmd;
int len;
if(poll_cb->data.used == 1){
CDBG_ERROR("%s : Thread is Active",__func__);
return MM_CAMERA_OK;
}
CDBG("Run thread for ch_type = %d ",ch_type);
cmd.cmd = MM_CAMERA_PIPE_CMD_ADD_CH;
poll_cb->data.ch_type = ch_type;
pthread_mutex_lock(&poll_cb->mutex);
len = write(poll_cb->data.pfds[1], &cmd, sizeof(cmd));
pthread_mutex_unlock(&poll_cb->mutex);
poll_cb->data.used = 1;
return MM_CAMERA_OK;
}
int mm_camera_poll_thread_del_ch(mm_camera_obj_t * my_obj, int ch_type)
{
mm_camera_poll_thread_t *poll_cb = &my_obj->poll_threads[ch_type];
mm_camera_sig_evt_t cmd;
int len;
if(poll_cb->data.used == 0){
CDBG_ERROR("%s : Thread is Not Active",__func__);
return MM_CAMERA_OK;
}
CDBG("Stop thread for ch_type = %d ",ch_type);
cmd.cmd = MM_CAMERA_PIPE_CMD_DEL_CH;
poll_cb->data.ch_type = (mm_camera_channel_type_t)ch_type;
pthread_mutex_lock(&poll_cb->mutex);
len = write(poll_cb->data.pfds[1], &cmd, sizeof(cmd));
pthread_mutex_unlock(&poll_cb->mutex);
poll_cb->data.used = 0;
return MM_CAMERA_OK;
}
int mm_camera_poll_thread_launch(mm_camera_obj_t * my_obj, int ch_type)
{
int rc = MM_CAMERA_OK;
mm_camera_poll_thread_t *poll_cb = &my_obj->poll_threads[ch_type];
if(mm_camera_poll_ch_busy(my_obj, ch_type) > 0) {
CDBG_ERROR("%s: err, poll thread of channel %d already running. cam_id=%d\n",
__func__, ch_type, my_obj->my_id);
return -MM_CAMERA_E_INVALID_OPERATION;
}
poll_cb->data.ch_type = ch_type;
rc = pipe(poll_cb->data.pfds);
if(rc < 0) {
CDBG_ERROR("%s: camera_id = %d, pipe open rc=%d\n", __func__, my_obj->my_id, rc);
rc = - MM_CAMERA_E_GENERAL;
}
CDBG("%s: ch = %d, poll_type = %d, read fd = %d, write fd = %d",
__func__, ch_type, poll_cb->data.poll_type,
poll_cb->data.pfds[0], poll_cb->data.pfds[1]);
poll_cb->data.my_obj = my_obj;
poll_cb->data.used = 0;
poll_cb->data.timeoutms = -1; /* Infinite seconds */
if(ch_type < MM_CAMERA_CH_MAX) {
poll_cb->data.poll_type = MM_CAMERA_POLL_TYPE_CH;
mm_camera_ch_util_get_stream_objs(my_obj, ch_type,
&poll_cb->data.poll_streams[0],
&poll_cb->data.poll_streams[1]);
} else{
poll_cb->data.poll_type = MM_CAMERA_POLL_TYPE_EVT;
}
ALOGV("%s: ch_type = %d, poll_type = %d, read fd = %d, write fd = %d",
__func__, ch_type, poll_cb->data.poll_type,
poll_cb->data.pfds[0], poll_cb->data.pfds[1]);
/* launch the thread */
rc = mm_camera_poll_start(my_obj, poll_cb);
return rc;
}
int mm_camera_poll_thread_release(mm_camera_obj_t * my_obj, int ch_type)
{
int rc = MM_CAMERA_OK;
mm_camera_poll_thread_t *poll_cb = &my_obj->poll_threads[ch_type];
if(MM_CAMERA_POLL_TASK_STATE_MAX == poll_cb->data.state) {
CDBG("%s: err, poll thread of channel % is not running. cam_id=%d\n",
__func__, ch_type, my_obj->my_id);
return -MM_CAMERA_E_INVALID_OPERATION;
}
rc = mm_camera_poll_stop(my_obj, poll_cb);
if(poll_cb->data.pfds[0]) {
close(poll_cb->data.pfds[0]);
}
if(poll_cb->data.pfds[1]) {
close(poll_cb->data.pfds[1]);
}
memset(&poll_cb->data, 0, sizeof(poll_cb->data));
return MM_CAMERA_OK;
}
void mm_camera_poll_threads_init(mm_camera_obj_t * my_obj)
{
int i;
mm_camera_poll_thread_t *poll_cb;
for(i = 0; i < MM_CAMERA_POLL_THRAED_MAX; i++) {
poll_cb = &my_obj->poll_threads[i];
pthread_mutex_init(&poll_cb->mutex, NULL);
pthread_cond_init(&poll_cb->cond_v, NULL);
}
}
void mm_camera_poll_threads_deinit(mm_camera_obj_t * my_obj)
{
int i;
mm_camera_poll_thread_t *poll_cb;
for(i = 0; i < MM_CAMERA_POLL_THRAED_MAX; i++) {
poll_cb = &my_obj->poll_threads[i];
if(poll_cb->data.used)
mm_camera_poll_stop(my_obj, poll_cb);
pthread_mutex_destroy(&poll_cb->mutex);
pthread_cond_destroy(&poll_cb->cond_v);
memset(poll_cb, 0, sizeof(mm_camera_poll_thread_t));
}
}