mm_camera_thread.c revision 6a225c824c49ff9ecdc4b67c802f1af272569e03
1/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. 2 * 3 * Redistribution and use in source and binary forms, with or without 4 * modification, are permitted provided that the following conditions are 5 * met: 6 * * Redistributions of source code must retain the above copyright 7 * notice, this list of conditions and the following disclaimer. 8 * * Redistributions in binary form must reproduce the above 9 * copyright notice, this list of conditions and the following 10 * disclaimer in the documentation and/or other materials provided 11 * with the distribution. 12 * * Neither the name of The Linux Foundation nor the names of its 13 * contributors may be used to endorse or promote products derived 14 * from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 23 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 */ 29 30#include <pthread.h> 31#include <errno.h> 32#include <sys/ioctl.h> 33#include <sys/types.h> 34#include <sys/stat.h> 35#include <sys/prctl.h> 36#include <fcntl.h> 37#include <poll.h> 38#include <cam_semaphore.h> 39 40#include "mm_camera_dbg.h" 41#include "mm_camera_interface.h" 42#include "mm_camera.h" 43 44typedef enum { 45 /* poll entries updated */ 46 MM_CAMERA_PIPE_CMD_POLL_ENTRIES_UPDATED, 47 /* exit */ 48 MM_CAMERA_PIPE_CMD_EXIT, 49 /* max count */ 50 MM_CAMERA_PIPE_CMD_MAX 51} mm_camera_pipe_cmd_type_t; 52 53typedef enum { 54 MM_CAMERA_POLL_TASK_STATE_STOPPED, 55 MM_CAMERA_POLL_TASK_STATE_POLL, /* polling pid in polling state. */ 56 MM_CAMERA_POLL_TASK_STATE_MAX 57} mm_camera_poll_task_state_type_t; 58 59typedef struct { 60 uint8_t cmd; 61 mm_camera_event_t event; 62} mm_camera_sig_evt_t; 63 64/*=========================================================================== 65 * FUNCTION : mm_camera_poll_sig 66 * 67 * DESCRIPTION: synchorinzed call to send a command through pipe. 68 * 69 * PARAMETERS : 70 * @poll_cb : ptr to poll thread object 71 * @cmd : command to be sent 72 * 73 * RETURN : int32_t type of status 74 * 0 -- success 75 * -1 -- failure 76 *==========================================================================*/ 77static int32_t mm_camera_poll_sig(mm_camera_poll_thread_t *poll_cb, 78 uint32_t cmd) 79{ 80 /* send through pipe */ 81 /* get the mutex */ 82 mm_camera_sig_evt_t cmd_evt; 83 int len; 84 85 CDBG("%s: E cmd = %d", __func__,cmd); 86 memset(&cmd_evt, 0, sizeof(cmd_evt)); 87 cmd_evt.cmd = cmd; 88 pthread_mutex_lock(&poll_cb->mutex); 89 /* reset the statue to false */ 90 poll_cb->status = FALSE; 91 /* send cmd to worker */ 92 93 len = write(poll_cb->pfds[1], &cmd_evt, sizeof(cmd_evt)); 94 if(len < 1) { 95 CDBG_ERROR("%s: len = %d, errno = %d", __func__, len, errno); 96 /* Avoid waiting for the signal */ 97 pthread_mutex_unlock(&poll_cb->mutex); 98 return 0; 99 } 100 CDBG("%s: begin IN mutex write done, len = %d", __func__, len); 101 /* wait till worker task gives positive signal */ 102 if (FALSE == poll_cb->status) { 103 CDBG("%s: wait", __func__); 104 pthread_cond_wait(&poll_cb->cond_v, &poll_cb->mutex); 105 } 106 /* done */ 107 pthread_mutex_unlock(&poll_cb->mutex); 108 CDBG("%s: X", __func__); 109 return 0; 110} 111 112/*=========================================================================== 113 * FUNCTION : mm_camera_poll_sig 114 * 115 * DESCRIPTION: signal the status of done 116 * 117 * PARAMETERS : 118 * @poll_cb : ptr to poll thread object 119 * 120 * RETURN : none 121 *==========================================================================*/ 122static void mm_camera_poll_sig_done(mm_camera_poll_thread_t *poll_cb) 123{ 124 pthread_mutex_lock(&poll_cb->mutex); 125 poll_cb->status = TRUE; 126 pthread_cond_signal(&poll_cb->cond_v); 127 CDBG("%s: done, in mutex", __func__); 128 pthread_mutex_unlock(&poll_cb->mutex); 129} 130 131/*=========================================================================== 132 * FUNCTION : mm_camera_poll_set_state 133 * 134 * DESCRIPTION: set a polling state 135 * 136 * PARAMETERS : 137 * @poll_cb : ptr to poll thread object 138 * @state : polling state (stopped/polling) 139 * 140 * RETURN : none 141 *==========================================================================*/ 142static void mm_camera_poll_set_state(mm_camera_poll_thread_t *poll_cb, 143 mm_camera_poll_task_state_type_t state) 144{ 145 poll_cb->state = state; 146} 147 148/*=========================================================================== 149 * FUNCTION : mm_camera_poll_proc_pipe 150 * 151 * DESCRIPTION: polling thread routine to process pipe 152 * 153 * PARAMETERS : 154 * @poll_cb : ptr to poll thread object 155 * 156 * RETURN : none 157 *==========================================================================*/ 158static void mm_camera_poll_proc_pipe(mm_camera_poll_thread_t *poll_cb) 159{ 160 ssize_t read_len; 161 int i; 162 mm_camera_sig_evt_t cmd_evt; 163 read_len = read(poll_cb->pfds[0], &cmd_evt, sizeof(cmd_evt)); 164 CDBG("%s: read_fd = %d, read_len = %d, expect_len = %d cmd = %d", 165 __func__, poll_cb->pfds[0], (int)read_len, (int)sizeof(cmd_evt), cmd_evt.cmd); 166 switch (cmd_evt.cmd) { 167 case MM_CAMERA_PIPE_CMD_POLL_ENTRIES_UPDATED: 168 /* we always have index 0 for pipe read */ 169 poll_cb->num_fds = 0; 170 poll_cb->poll_fds[poll_cb->num_fds].fd = poll_cb->pfds[0]; 171 poll_cb->poll_fds[poll_cb->num_fds].events = POLLIN|POLLRDNORM|POLLPRI; 172 poll_cb->num_fds++; 173 174 if (MM_CAMERA_POLL_TYPE_EVT == poll_cb->poll_type) { 175 if (poll_cb->poll_entries[0].fd > 0) { 176 /* fd is valid, we update poll_fds */ 177 poll_cb->poll_fds[poll_cb->num_fds].fd = poll_cb->poll_entries[0].fd; 178 poll_cb->poll_fds[poll_cb->num_fds].events = POLLIN|POLLRDNORM|POLLPRI; 179 poll_cb->num_fds++; 180 } 181 } else if (MM_CAMERA_POLL_TYPE_DATA == poll_cb->poll_type) { 182 for(i = 0; i < MAX_STREAM_NUM_IN_BUNDLE; i++) { 183 if(poll_cb->poll_entries[i].fd > 0) { 184 /* fd is valid, we update poll_fds to this fd */ 185 poll_cb->poll_fds[poll_cb->num_fds].fd = poll_cb->poll_entries[i].fd; 186 poll_cb->poll_fds[poll_cb->num_fds].events = POLLIN|POLLRDNORM|POLLPRI; 187 poll_cb->num_fds++; 188 } else { 189 /* fd is invalid, we set the entry to -1 to prevent polling. 190 * According to spec, polling will not poll on entry with fd=-1. 191 * If this is not the case, we need to skip these invalid fds 192 * when updating this array. 193 * We still keep fd=-1 in this array because this makes easier to 194 * map cb associated with this fd once incoming data avail by directly 195 * using the index-1(0 is reserved for pipe read, so need to reduce index by 1) */ 196 poll_cb->poll_fds[poll_cb->num_fds].fd = -1; 197 poll_cb->poll_fds[poll_cb->num_fds].events = 0; 198 poll_cb->num_fds++; 199 } 200 } 201 } 202 mm_camera_poll_sig_done(poll_cb); 203 break; 204 205 case MM_CAMERA_PIPE_CMD_EXIT: 206 default: 207 mm_camera_poll_set_state(poll_cb, MM_CAMERA_POLL_TASK_STATE_STOPPED); 208 mm_camera_poll_sig_done(poll_cb); 209 break; 210 } 211} 212 213/*=========================================================================== 214 * FUNCTION : mm_camera_poll_fn 215 * 216 * DESCRIPTION: polling thread routine 217 * 218 * PARAMETERS : 219 * @poll_cb : ptr to poll thread object 220 * 221 * RETURN : none 222 *==========================================================================*/ 223static void *mm_camera_poll_fn(mm_camera_poll_thread_t *poll_cb) 224{ 225 int rc = 0, i; 226 227 CDBG("%s: poll type = %d, num_fd = %d poll_cb = %p\n", 228 __func__, poll_cb->poll_type, poll_cb->num_fds,poll_cb); 229 do { 230 for(i = 0; i < poll_cb->num_fds; i++) { 231 poll_cb->poll_fds[i].events = POLLIN|POLLRDNORM|POLLPRI; 232 } 233 234 rc = poll(poll_cb->poll_fds, poll_cb->num_fds, poll_cb->timeoutms); 235 if(rc > 0) { 236 if ((poll_cb->poll_fds[0].revents & POLLIN) && 237 (poll_cb->poll_fds[0].revents & POLLRDNORM)) { 238 /* if we have data on pipe, we only process pipe in this iteration */ 239 CDBG("%s: cmd received on pipe\n", __func__); 240 mm_camera_poll_proc_pipe(poll_cb); 241 } else { 242 for(i=1; i<poll_cb->num_fds; i++) { 243 /* Checking for ctrl events */ 244 if ((poll_cb->poll_type == MM_CAMERA_POLL_TYPE_EVT) && 245 (poll_cb->poll_fds[i].revents & POLLPRI)) { 246 CDBG("%s: mm_camera_evt_notify\n", __func__); 247 if (NULL != poll_cb->poll_entries[i-1].notify_cb) { 248 poll_cb->poll_entries[i-1].notify_cb(poll_cb->poll_entries[i-1].user_data); 249 } 250 } 251 252 if ((MM_CAMERA_POLL_TYPE_DATA == poll_cb->poll_type) && 253 (poll_cb->poll_fds[i].revents & POLLIN) && 254 (poll_cb->poll_fds[i].revents & POLLRDNORM)) { 255 CDBG("%s: mm_stream_data_notify\n", __func__); 256 if (NULL != poll_cb->poll_entries[i-1].notify_cb) { 257 poll_cb->poll_entries[i-1].notify_cb(poll_cb->poll_entries[i-1].user_data); 258 } 259 } 260 } 261 } 262 } else { 263 /* in error case sleep 10 us and then continue. hard coded here */ 264 usleep(10); 265 continue; 266 } 267 } while (poll_cb->state == MM_CAMERA_POLL_TASK_STATE_POLL); 268 return NULL; 269} 270 271/*=========================================================================== 272 * FUNCTION : mm_camera_poll_thread 273 * 274 * DESCRIPTION: polling thread entry function 275 * 276 * PARAMETERS : 277 * @data : ptr to poll thread object 278 * 279 * RETURN : none 280 *==========================================================================*/ 281static void *mm_camera_poll_thread(void *data) 282{ 283 prctl(PR_SET_NAME, (unsigned long)"mm_cam_poll_th", 0, 0, 0); 284 mm_camera_poll_thread_t *poll_cb = (mm_camera_poll_thread_t *)data; 285 286 /* add pipe read fd into poll first */ 287 poll_cb->poll_fds[poll_cb->num_fds++].fd = poll_cb->pfds[0]; 288 289 mm_camera_poll_sig_done(poll_cb); 290 mm_camera_poll_set_state(poll_cb, MM_CAMERA_POLL_TASK_STATE_POLL); 291 return mm_camera_poll_fn(poll_cb); 292} 293 294/*=========================================================================== 295 * FUNCTION : mm_camera_poll_thread 296 * 297 * DESCRIPTION: notify the polling thread that entries for polling fd have 298 * been updated 299 * 300 * PARAMETERS : 301 * @poll_cb : ptr to poll thread object 302 * 303 * RETURN : none 304 *==========================================================================*/ 305int32_t mm_camera_poll_thread_notify_entries_updated(mm_camera_poll_thread_t * poll_cb) 306{ 307 /* send poll entries updated signal to poll thread */ 308 return mm_camera_poll_sig(poll_cb, MM_CAMERA_PIPE_CMD_POLL_ENTRIES_UPDATED); 309} 310 311/*=========================================================================== 312 * FUNCTION : mm_camera_poll_thread_add_poll_fd 313 * 314 * DESCRIPTION: add a new fd into polling thread 315 * 316 * PARAMETERS : 317 * @poll_cb : ptr to poll thread object 318 * @handler : stream handle if channel data polling thread, 319 * 0 if event polling thread 320 * @fd : file descriptor need to be added into polling thread 321 * @notify_cb : callback function to handle if any notify from fd 322 * @userdata : user data ptr 323 * 324 * RETURN : none 325 *==========================================================================*/ 326int32_t mm_camera_poll_thread_add_poll_fd(mm_camera_poll_thread_t * poll_cb, 327 uint32_t handler, 328 int32_t fd, 329 mm_camera_poll_notify_t notify_cb, 330 void* userdata) 331{ 332 int32_t rc = -1; 333 uint8_t idx = 0; 334 335 if (MM_CAMERA_POLL_TYPE_DATA == poll_cb->poll_type) { 336 /* get stream idx from handler if CH type */ 337 idx = mm_camera_util_get_index_by_handler(handler); 338 } else { 339 /* for EVT type, only idx=0 is valid */ 340 idx = 0; 341 } 342 343 if (MAX_STREAM_NUM_IN_BUNDLE > idx) { 344 poll_cb->poll_entries[idx].fd = fd; 345 poll_cb->poll_entries[idx].handler = handler; 346 poll_cb->poll_entries[idx].notify_cb = notify_cb; 347 poll_cb->poll_entries[idx].user_data = userdata; 348 /* send poll entries updated signal to poll thread */ 349 rc = mm_camera_poll_sig(poll_cb, MM_CAMERA_PIPE_CMD_POLL_ENTRIES_UPDATED); 350 } else { 351 CDBG_ERROR("%s: invalid handler %d (%d)", 352 __func__, handler, idx); 353 } 354 return rc; 355} 356 357/*=========================================================================== 358 * FUNCTION : mm_camera_poll_thread_del_poll_fd 359 * 360 * DESCRIPTION: delete a fd from polling thread 361 * 362 * PARAMETERS : 363 * @poll_cb : ptr to poll thread object 364 * @handler : stream handle if channel data polling thread, 365 * 0 if event polling thread 366 * 367 * RETURN : none 368 *==========================================================================*/ 369int32_t mm_camera_poll_thread_del_poll_fd(mm_camera_poll_thread_t * poll_cb, 370 uint32_t handler) 371{ 372 int32_t rc = -1; 373 uint8_t idx = 0; 374 375 if (MM_CAMERA_POLL_TYPE_DATA == poll_cb->poll_type) { 376 /* get stream idx from handler if CH type */ 377 idx = mm_camera_util_get_index_by_handler(handler); 378 } else { 379 /* for EVT type, only idx=0 is valid */ 380 idx = 0; 381 } 382 383 if ((MAX_STREAM_NUM_IN_BUNDLE > idx) && 384 (handler == poll_cb->poll_entries[idx].handler)) { 385 /* reset poll entry */ 386 poll_cb->poll_entries[idx].fd = -1; /* set fd to invalid */ 387 poll_cb->poll_entries[idx].handler = 0; 388 poll_cb->poll_entries[idx].notify_cb = NULL; 389 390 /* send poll entries updated signal to poll thread */ 391 rc = mm_camera_poll_sig(poll_cb, MM_CAMERA_PIPE_CMD_POLL_ENTRIES_UPDATED); 392 } else { 393 CDBG_ERROR("%s: invalid handler %d (%d)", 394 __func__, handler, idx); 395 } 396 397 return rc; 398} 399 400static pthread_mutex_t constr_destr_lock = PTHREAD_MUTEX_INITIALIZER; 401 402int32_t mm_camera_poll_thread_launch(mm_camera_poll_thread_t * poll_cb, 403 mm_camera_poll_thread_type_t poll_type) 404{ 405 int32_t rc = 0; 406 407 pthread_mutex_lock(&constr_destr_lock); 408 409 poll_cb->poll_type = poll_type; 410 411 poll_cb->pfds[0] = 0; 412 poll_cb->pfds[1] = 0; 413 rc = pipe(poll_cb->pfds); 414 if(rc < 0) { 415 CDBG_ERROR("%s: pipe open rc=%d\n", __func__, rc); 416 pthread_mutex_unlock(&constr_destr_lock); 417 return -1; 418 } 419 420 poll_cb->timeoutms = -1; /* Infinite seconds */ 421 422 CDBG("%s: poll_type = %d, read fd = %d, write fd = %d timeout = %d", 423 __func__, poll_cb->poll_type, 424 poll_cb->pfds[0], poll_cb->pfds[1],poll_cb->timeoutms); 425 426 pthread_mutex_init(&poll_cb->mutex, NULL); 427 pthread_cond_init(&poll_cb->cond_v, NULL); 428 429 /* launch the thread */ 430 pthread_mutex_lock(&poll_cb->mutex); 431 poll_cb->status = 0; 432 pthread_create(&poll_cb->pid, NULL, mm_camera_poll_thread, (void *)poll_cb); 433 if(!poll_cb->status) { 434 pthread_cond_wait(&poll_cb->cond_v, &poll_cb->mutex); 435 } 436 pthread_mutex_unlock(&poll_cb->mutex); 437 CDBG("%s: End",__func__); 438 pthread_mutex_unlock(&constr_destr_lock); 439 return rc; 440} 441 442int32_t mm_camera_poll_thread_release(mm_camera_poll_thread_t *poll_cb) 443{ 444 int32_t rc = 0; 445 446 pthread_mutex_lock(&constr_destr_lock); 447 448 if(MM_CAMERA_POLL_TASK_STATE_STOPPED == poll_cb->state) { 449 CDBG_ERROR("%s: err, poll thread is not running.\n", __func__); 450 goto done; 451 } 452 453 /* send exit signal to poll thread */ 454 mm_camera_poll_sig(poll_cb, MM_CAMERA_PIPE_CMD_EXIT); 455 /* wait until poll thread exits */ 456 if (pthread_join(poll_cb->pid, NULL) != 0) { 457 CDBG_ERROR("%s: pthread dead already\n", __func__); 458 } 459 460 /* close pipe */ 461 if(poll_cb->pfds[0]) { 462 close(poll_cb->pfds[0]); 463 } 464 if(poll_cb->pfds[1]) { 465 close(poll_cb->pfds[1]); 466 } 467 468 pthread_mutex_destroy(&poll_cb->mutex); 469 pthread_cond_destroy(&poll_cb->cond_v); 470 memset(poll_cb, 0, sizeof(mm_camera_poll_thread_t)); 471done: 472 pthread_mutex_unlock(&constr_destr_lock); 473 return rc; 474} 475 476static void *mm_camera_cmd_thread(void *data) 477{ 478 int running = 1; 479 int ret; 480 mm_camera_cmd_thread_t *cmd_thread = 481 (mm_camera_cmd_thread_t *)data; 482 mm_camera_cmdcb_t* node = NULL; 483 484 do { 485 do { 486 ret = cam_sem_wait(&cmd_thread->cmd_sem); 487 if (ret != 0 && errno != EINVAL) { 488 CDBG_ERROR("%s: cam_sem_wait error (%s)", 489 __func__, strerror(errno)); 490 return NULL; 491 } 492 } while (ret != 0); 493 494 /* we got notified about new cmd avail in cmd queue */ 495 node = (mm_camera_cmdcb_t*)cam_queue_deq(&cmd_thread->cmd_queue); 496 while (node != NULL) { 497 switch (node->cmd_type) { 498 case MM_CAMERA_CMD_TYPE_EVT_CB: 499 case MM_CAMERA_CMD_TYPE_DATA_CB: 500 case MM_CAMERA_CMD_TYPE_REQ_DATA_CB: 501 case MM_CAMERA_CMD_TYPE_SUPER_BUF_DATA_CB: 502 case MM_CAMERA_CMD_TYPE_CONFIG_NOTIFY: 503 case MM_CAMERA_CMD_TYPE_FLUSH_QUEUE: 504 if (NULL != cmd_thread->cb) { 505 cmd_thread->cb(node, cmd_thread->user_data); 506 } 507 break; 508 case MM_CAMERA_CMD_TYPE_EXIT: 509 default: 510 running = 0; 511 break; 512 } 513 free(node); 514 node = (mm_camera_cmdcb_t*)cam_queue_deq(&cmd_thread->cmd_queue); 515 } /* (node != NULL) */ 516 } while (running); 517 return NULL; 518} 519 520int32_t mm_camera_cmd_thread_launch(mm_camera_cmd_thread_t * cmd_thread, 521 mm_camera_cmd_cb_t cb, 522 void* user_data) 523{ 524 int32_t rc = 0; 525 526 cam_sem_init(&cmd_thread->cmd_sem, 0); 527 cam_queue_init(&cmd_thread->cmd_queue); 528 cmd_thread->cb = cb; 529 cmd_thread->user_data = user_data; 530 531 /* launch the thread */ 532 pthread_create(&cmd_thread->cmd_pid, 533 NULL, 534 mm_camera_cmd_thread, 535 (void *)cmd_thread); 536 return rc; 537} 538 539int32_t mm_camera_cmd_thread_name(const char* name) 540{ 541 int32_t rc = 0; 542 /* name the thread */ 543 prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0); 544 return rc; 545} 546 547 548int32_t mm_camera_cmd_thread_stop(mm_camera_cmd_thread_t * cmd_thread) 549{ 550 int32_t rc = 0; 551 mm_camera_cmdcb_t* node = (mm_camera_cmdcb_t *)malloc(sizeof(mm_camera_cmdcb_t)); 552 if (NULL == node) { 553 CDBG_ERROR("%s: No memory for mm_camera_cmdcb_t", __func__); 554 return -1; 555 } 556 557 memset(node, 0, sizeof(mm_camera_cmdcb_t)); 558 node->cmd_type = MM_CAMERA_CMD_TYPE_EXIT; 559 560 cam_queue_enq(&cmd_thread->cmd_queue, node); 561 cam_sem_post(&cmd_thread->cmd_sem); 562 563 /* wait until cmd thread exits */ 564 if (pthread_join(cmd_thread->cmd_pid, NULL) != 0) { 565 CDBG("%s: pthread dead already\n", __func__); 566 } 567 return rc; 568} 569 570int32_t mm_camera_cmd_thread_destroy(mm_camera_cmd_thread_t * cmd_thread) 571{ 572 int32_t rc = 0; 573 cam_queue_deinit(&cmd_thread->cmd_queue); 574 cam_sem_destroy(&cmd_thread->cmd_sem); 575 memset(cmd_thread, 0, sizeof(mm_camera_cmd_thread_t)); 576 return rc; 577} 578 579int32_t mm_camera_cmd_thread_release(mm_camera_cmd_thread_t * cmd_thread) 580{ 581 int32_t rc = 0; 582 rc = mm_camera_cmd_thread_stop(cmd_thread); 583 if (0 == rc) { 584 rc = mm_camera_cmd_thread_destroy(cmd_thread); 585 } 586 return rc; 587} 588