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