blob: a3bc9f66a4eeb072ae8a7e855bb57bf330464d37 [file] [log] [blame]
/* Copyright (c) 2012-2016, 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.
*
*/
// Camera dependencies
#include "mm_qcamera_app.h"
#include "mm_qcamera_dbg.h"
/* This callback is received once the complete JPEG encoding is done */
static void jpeg_encode_cb(jpeg_job_status_t status,
uint32_t client_hdl,
uint32_t jobId,
mm_jpeg_output_t *p_buf,
void *userData)
{
uint32_t i = 0;
mm_camera_test_obj_t *pme = NULL;
LOGD(" BEGIN\n");
pme = (mm_camera_test_obj_t *)userData;
if (pme->jpeg_hdl != client_hdl ||
jobId != pme->current_job_id ||
!pme->current_job_frames) {
LOGE(" NULL current job frames or not matching job ID (%d, %d)",
jobId, pme->current_job_id);
return;
}
/* dump jpeg img */
LOGE(" job %d, status=%d", jobId, status);
if (status == JPEG_JOB_STATUS_DONE && p_buf != NULL) {
mm_app_dump_jpeg_frame(p_buf->buf_vaddr, p_buf->buf_filled_len, "jpeg", "jpg", jobId);
}
/* buf done current encoding frames */
pme->current_job_id = 0;
for (i = 0; i < pme->current_job_frames->num_bufs; i++) {
if (MM_CAMERA_OK != pme->cam->ops->qbuf(pme->current_job_frames->camera_handle,
pme->current_job_frames->ch_id,
pme->current_job_frames->bufs[i])) {
LOGE(" Failed in Qbuf\n");
}
mm_app_cache_ops((mm_camera_app_meminfo_t *) pme->current_job_frames->bufs[i]->mem_info,
ION_IOC_INV_CACHES);
}
free(pme->jpeg_buf.buf.buffer);
free(pme->current_job_frames);
pme->current_job_frames = NULL;
/* signal snapshot is done */
mm_camera_app_done();
}
int encodeData(mm_camera_test_obj_t *test_obj, mm_camera_super_buf_t* recvd_frame,
mm_camera_stream_t *m_stream)
{
int rc = -MM_CAMERA_E_GENERAL;
mm_jpeg_job_t job;
/* remember current frames being encoded */
test_obj->current_job_frames =
(mm_camera_super_buf_t *)malloc(sizeof(mm_camera_super_buf_t));
if (!test_obj->current_job_frames) {
LOGE(" No memory for current_job_frames");
return rc;
}
*(test_obj->current_job_frames) = *recvd_frame;
memset(&job, 0, sizeof(job));
job.job_type = JPEG_JOB_TYPE_ENCODE;
job.encode_job.session_id = test_obj->current_jpeg_sess_id;
// TODO: Rotation should be set according to
// sensor&device orientation
job.encode_job.rotation = 0;
/* fill in main src img encode param */
job.encode_job.main_dim.src_dim = m_stream->s_config.stream_info->dim;
job.encode_job.main_dim.dst_dim = m_stream->s_config.stream_info->dim;
job.encode_job.src_index = 0;
job.encode_job.thumb_dim.src_dim = m_stream->s_config.stream_info->dim;
job.encode_job.thumb_dim.dst_dim.width = DEFAULT_PREVIEW_WIDTH;
job.encode_job.thumb_dim.dst_dim.height = DEFAULT_PREVIEW_HEIGHT;
/* fill in sink img param */
job.encode_job.dst_index = 0;
if (test_obj->metadata != NULL) {
job.encode_job.p_metadata = test_obj->metadata;
} else {
LOGE(" Metadata null, not set for jpeg encoding");
}
rc = test_obj->jpeg_ops.start_job(&job, &test_obj->current_job_id);
if ( 0 != rc ) {
free(test_obj->current_job_frames);
test_obj->current_job_frames = NULL;
}
return rc;
}
int createEncodingSession(mm_camera_test_obj_t *test_obj,
mm_camera_stream_t *m_stream,
mm_camera_buf_def_t *m_frame)
{
mm_jpeg_encode_params_t encode_param;
memset(&encode_param, 0, sizeof(mm_jpeg_encode_params_t));
encode_param.jpeg_cb = jpeg_encode_cb;
encode_param.userdata = (void*)test_obj;
encode_param.encode_thumbnail = 0;
encode_param.quality = 85;
encode_param.color_format = MM_JPEG_COLOR_FORMAT_YCRCBLP_H2V2;
encode_param.thumb_color_format = MM_JPEG_COLOR_FORMAT_YCRCBLP_H2V2;
/* fill in main src img encode param */
encode_param.num_src_bufs = 1;
encode_param.src_main_buf[0].index = 0;
encode_param.src_main_buf[0].buf_size = m_frame->frame_len;
encode_param.src_main_buf[0].buf_vaddr = (uint8_t *)m_frame->buffer;
encode_param.src_main_buf[0].fd = m_frame->fd;
encode_param.src_main_buf[0].format = MM_JPEG_FMT_YUV;
encode_param.src_main_buf[0].offset = m_stream->offset;
/* fill in sink img param */
encode_param.num_dst_bufs = 1;
encode_param.dest_buf[0].index = 0;
encode_param.dest_buf[0].buf_size = test_obj->jpeg_buf.buf.frame_len;
encode_param.dest_buf[0].buf_vaddr = (uint8_t *)test_obj->jpeg_buf.buf.buffer;
encode_param.dest_buf[0].fd = test_obj->jpeg_buf.buf.fd;
encode_param.dest_buf[0].format = MM_JPEG_FMT_YUV;
/* main dimension */
encode_param.main_dim.src_dim = m_stream->s_config.stream_info->dim;
encode_param.main_dim.dst_dim = m_stream->s_config.stream_info->dim;
return test_obj->jpeg_ops.create_session(test_obj->jpeg_hdl,
&encode_param,
&test_obj->current_jpeg_sess_id);
}
/** mm_app_snapshot_metadata_notify_cb
* @bufs: Pointer to super buffer
* @user_data: Pointer to user data
*
*
**/
__unused
static void mm_app_snapshot_metadata_notify_cb(mm_camera_super_buf_t *bufs,
void *user_data)
{
uint32_t i = 0;
mm_camera_channel_t *channel = NULL;
mm_camera_stream_t *p_stream = NULL;
mm_camera_test_obj_t *pme = (mm_camera_test_obj_t *)user_data;
mm_camera_buf_def_t *frame;
metadata_buffer_t *pMetadata;
if (NULL == bufs || NULL == user_data) {
LOGE(" bufs or user_data are not valid ");
return;
}
frame = bufs->bufs[0];
/* find channel */
for (i = 0; i < MM_CHANNEL_TYPE_MAX; i++) {
if (pme->channels[i].ch_id == bufs->ch_id) {
channel = &pme->channels[i];
break;
}
}
if (NULL == channel) {
LOGE(" Channel object is null");
return;
}
/* find meta stream */
for (i = 0; i < channel->num_streams; i++) {
if (channel->streams[i].s_config.stream_info->stream_type == CAM_STREAM_TYPE_METADATA) {
p_stream = &channel->streams[i];
break;
}
}
if (NULL == p_stream) {
LOGE(" cannot find metadata stream");
return;
}
/* find meta frame */
for (i = 0; i < bufs->num_bufs; i++) {
if (bufs->bufs[i]->stream_id == p_stream->s_id) {
frame = bufs->bufs[i];
break;
}
}
if (!pme->metadata) {
/* The app will free the metadata, we don't need to bother here */
pme->metadata = malloc(sizeof(metadata_buffer_t));
if (NULL == pme->metadata) {
LOGE(" malloc failed");
return;
}
}
memcpy(pme->metadata , frame->buffer, sizeof(metadata_buffer_t));
pMetadata = (metadata_buffer_t *)frame->buffer;
IF_META_AVAILABLE(cam_auto_focus_data_t, focus_data,
CAM_INTF_META_AUTOFOCUS_DATA, pMetadata) {
if (focus_data->focus_state == CAM_AF_STATE_FOCUSED_LOCKED) {
LOGE(" AutoFocus Done Call Back Received\n");
mm_camera_app_done();
} else if (focus_data->focus_state == CAM_AF_STATE_NOT_FOCUSED_LOCKED) {
LOGE(" AutoFocus failed\n");
mm_camera_app_done();
}
}
if (MM_CAMERA_OK != pme->cam->ops->qbuf(bufs->camera_handle,
bufs->ch_id,
frame)) {
LOGE(" Failed in Preview Qbuf\n");
}
mm_app_cache_ops((mm_camera_app_meminfo_t *)frame->mem_info,
ION_IOC_INV_CACHES);
}
static void mm_app_snapshot_notify_cb_raw(mm_camera_super_buf_t *bufs,
void *user_data)
{
int rc;
uint32_t i = 0;
mm_camera_test_obj_t *pme = (mm_camera_test_obj_t *)user_data;
mm_camera_channel_t *channel = NULL;
mm_camera_stream_t *m_stream = NULL;
mm_camera_buf_def_t *m_frame = NULL;
LOGD(" BEGIN\n");
/* find channel */
for (i = 0; i < MM_CHANNEL_TYPE_MAX; i++) {
if (pme->channels[i].ch_id == bufs->ch_id) {
channel = &pme->channels[i];
break;
}
}
if (NULL == channel) {
LOGE(" Wrong channel id (%d)", bufs->ch_id);
rc = -1;
goto EXIT;
}
/* find snapshot stream */
for (i = 0; i < channel->num_streams; i++) {
if (channel->streams[i].s_config.stream_info->stream_type == CAM_STREAM_TYPE_RAW) {
m_stream = &channel->streams[i];
break;
}
}
if (NULL == m_stream) {
LOGE(" cannot find snapshot stream");
rc = -1;
goto EXIT;
}
/* find snapshot frame */
for (i = 0; i < bufs->num_bufs; i++) {
if (bufs->bufs[i]->stream_id == m_stream->s_id) {
m_frame = bufs->bufs[i];
break;
}
}
if (NULL == m_frame) {
LOGE(" main frame is NULL");
rc = -1;
goto EXIT;
}
mm_app_dump_frame(m_frame, "main", "raw", m_frame->frame_idx);
EXIT:
for (i=0; i<bufs->num_bufs; i++) {
if (MM_CAMERA_OK != pme->cam->ops->qbuf(bufs->camera_handle,
bufs->ch_id,
bufs->bufs[i])) {
LOGE(" Failed in Qbuf\n");
}
}
mm_camera_app_done();
LOGD(" END\n");
}
static void mm_app_snapshot_notify_cb(mm_camera_super_buf_t *bufs,
void *user_data)
{
int rc = 0;
uint32_t i = 0;
mm_camera_test_obj_t *pme = (mm_camera_test_obj_t *)user_data;
mm_camera_channel_t *channel = NULL;
mm_camera_stream_t *p_stream = NULL;
mm_camera_stream_t *m_stream = NULL;
mm_camera_buf_def_t *p_frame = NULL;
mm_camera_buf_def_t *m_frame = NULL;
/* find channel */
for (i = 0; i < MM_CHANNEL_TYPE_MAX; i++) {
if (pme->channels[i].ch_id == bufs->ch_id) {
channel = &pme->channels[i];
break;
}
}
if (NULL == channel) {
LOGE(" Wrong channel id (%d)", bufs->ch_id);
rc = -1;
goto error;
}
/* find snapshot stream */
for (i = 0; i < channel->num_streams; i++) {
if (channel->streams[i].s_config.stream_info->stream_type == CAM_STREAM_TYPE_SNAPSHOT) {
m_stream = &channel->streams[i];
break;
}
}
if (NULL == m_stream) {
LOGE(" cannot find snapshot stream");
rc = -1;
goto error;
}
/* find snapshot frame */
for (i = 0; i < bufs->num_bufs; i++) {
if (bufs->bufs[i]->stream_id == m_stream->s_id) {
m_frame = bufs->bufs[i];
break;
}
}
if (NULL == m_frame) {
LOGE(" main frame is NULL");
rc = -1;
goto error;
}
mm_app_dump_frame(m_frame, "main", "yuv", m_frame->frame_idx);
/* find postview stream */
for (i = 0; i < channel->num_streams; i++) {
if (channel->streams[i].s_config.stream_info->stream_type == CAM_STREAM_TYPE_POSTVIEW) {
p_stream = &channel->streams[i];
break;
}
}
if (NULL != p_stream) {
/* find preview frame */
for (i = 0; i < bufs->num_bufs; i++) {
if (bufs->bufs[i]->stream_id == p_stream->s_id) {
p_frame = bufs->bufs[i];
break;
}
}
if (NULL != p_frame) {
mm_app_dump_frame(p_frame, "postview", "yuv", p_frame->frame_idx);
}
}
mm_app_cache_ops((mm_camera_app_meminfo_t *)m_frame->mem_info,
ION_IOC_CLEAN_INV_CACHES);
pme->jpeg_buf.buf.buffer = (uint8_t *)malloc(m_frame->frame_len);
if ( NULL == pme->jpeg_buf.buf.buffer ) {
LOGE(" error allocating jpeg output buffer");
goto error;
}
pme->jpeg_buf.buf.frame_len = m_frame->frame_len;
/* create a new jpeg encoding session */
rc = createEncodingSession(pme, m_stream, m_frame);
if (0 != rc) {
LOGE(" error creating jpeg session");
free(pme->jpeg_buf.buf.buffer);
goto error;
}
/* start jpeg encoding job */
rc = encodeData(pme, bufs, m_stream);
if (0 != rc) {
LOGE(" error creating jpeg session");
free(pme->jpeg_buf.buf.buffer);
goto error;
}
error:
/* buf done rcvd frames in error case */
if ( 0 != rc ) {
for (i=0; i<bufs->num_bufs; i++) {
if (MM_CAMERA_OK != pme->cam->ops->qbuf(bufs->camera_handle,
bufs->ch_id,
bufs->bufs[i])) {
LOGE(" Failed in Qbuf\n");
}
mm_app_cache_ops((mm_camera_app_meminfo_t *)bufs->bufs[i]->mem_info,
ION_IOC_INV_CACHES);
}
}
LOGD(" END\n");
}
mm_camera_channel_t * mm_app_add_snapshot_channel(mm_camera_test_obj_t *test_obj)
{
mm_camera_channel_t *channel = NULL;
mm_camera_stream_t *stream = NULL;
channel = mm_app_add_channel(test_obj,
MM_CHANNEL_TYPE_SNAPSHOT,
NULL,
NULL,
NULL);
if (NULL == channel) {
LOGE(" add channel failed");
return NULL;
}
stream = mm_app_add_snapshot_stream(test_obj,
channel,
mm_app_snapshot_notify_cb,
(void *)test_obj,
1,
1);
if (NULL == stream) {
LOGE(" add snapshot stream failed\n");
mm_app_del_channel(test_obj, channel);
return NULL;
}
return channel;
}
mm_camera_stream_t * mm_app_add_postview_stream(mm_camera_test_obj_t *test_obj,
mm_camera_channel_t *channel,
mm_camera_buf_notify_t stream_cb,
void *userdata,
uint8_t num_bufs,
uint8_t num_burst)
{
int rc = MM_CAMERA_OK;
mm_camera_stream_t *stream = NULL;
cam_capability_t *cam_cap = (cam_capability_t *)(test_obj->cap_buf.buf.buffer);
stream = mm_app_add_stream(test_obj, channel);
if (NULL == stream) {
LOGE(" add stream failed\n");
return NULL;
}
stream->s_config.mem_vtbl.get_bufs = mm_app_stream_initbuf;
stream->s_config.mem_vtbl.put_bufs = mm_app_stream_deinitbuf;
stream->s_config.mem_vtbl.clean_invalidate_buf =
mm_app_stream_clean_invalidate_buf;
stream->s_config.mem_vtbl.invalidate_buf = mm_app_stream_invalidate_buf;
stream->s_config.mem_vtbl.user_data = (void *)stream;
stream->s_config.stream_cb = stream_cb;
stream->s_config.stream_cb_sync = NULL;
stream->s_config.userdata = userdata;
stream->num_of_bufs = num_bufs;
stream->s_config.stream_info = (cam_stream_info_t *)stream->s_info_buf.buf.buffer;
memset(stream->s_config.stream_info, 0, sizeof(cam_stream_info_t));
stream->s_config.stream_info->stream_type = CAM_STREAM_TYPE_POSTVIEW;
if (num_burst == 0) {
stream->s_config.stream_info->streaming_mode = CAM_STREAMING_MODE_CONTINUOUS;
} else {
stream->s_config.stream_info->streaming_mode = CAM_STREAMING_MODE_BURST;
stream->s_config.stream_info->num_of_burst = num_burst;
}
stream->s_config.stream_info->fmt = DEFAULT_PREVIEW_FORMAT;
stream->s_config.stream_info->dim.width = DEFAULT_PREVIEW_WIDTH;
stream->s_config.stream_info->dim.height = DEFAULT_PREVIEW_HEIGHT;
stream->s_config.stream_info->num_bufs = num_bufs;
stream->s_config.padding_info = cam_cap->padding_info;
rc = mm_app_config_stream(test_obj, channel, stream, &stream->s_config);
if (MM_CAMERA_OK != rc) {
LOGE("config postview stream err=%d\n", rc);
return NULL;
}
return stream;
}
int mm_app_start_capture_raw(mm_camera_test_obj_t *test_obj, uint8_t num_snapshots)
{
int32_t rc = MM_CAMERA_OK;
mm_camera_channel_t *channel = NULL;
mm_camera_stream_t *s_main = NULL;
mm_camera_channel_attr_t attr;
memset(&attr, 0, sizeof(mm_camera_channel_attr_t));
attr.notify_mode = MM_CAMERA_SUPER_BUF_NOTIFY_BURST;
attr.max_unmatched_frames = 3;
channel = mm_app_add_channel(test_obj,
MM_CHANNEL_TYPE_CAPTURE,
&attr,
mm_app_snapshot_notify_cb_raw,
test_obj);
if (NULL == channel) {
LOGE(" add channel failed");
return -MM_CAMERA_E_GENERAL;
}
test_obj->buffer_format = DEFAULT_RAW_FORMAT;
s_main = mm_app_add_raw_stream(test_obj,
channel,
mm_app_snapshot_notify_cb_raw,
test_obj,
num_snapshots,
num_snapshots);
if (NULL == s_main) {
LOGE(" add main snapshot stream failed\n");
mm_app_del_channel(test_obj, channel);
return rc;
}
rc = mm_app_start_channel(test_obj, channel);
if (MM_CAMERA_OK != rc) {
LOGE("start zsl failed rc=%d\n", rc);
mm_app_del_stream(test_obj, channel, s_main);
mm_app_del_channel(test_obj, channel);
return rc;
}
return rc;
}
int mm_app_stop_capture_raw(mm_camera_test_obj_t *test_obj)
{
int rc = MM_CAMERA_OK;
mm_camera_channel_t *ch = NULL;
int i;
cam_stream_size_info_t abc ;
memset (&abc , 0, sizeof (cam_stream_size_info_t));
ch = mm_app_get_channel_by_type(test_obj, MM_CHANNEL_TYPE_CAPTURE);
rc = mm_app_stop_channel(test_obj, ch);
if (MM_CAMERA_OK != rc) {
LOGE("stop recording failed rc=%d\n", rc);
}
for ( i = 0 ; i < ch->num_streams ; i++ ) {
mm_app_del_stream(test_obj, ch, &ch->streams[i]);
}
rc = setmetainfoCommand(test_obj, &abc);
if (rc != MM_CAMERA_OK) {
LOGE(" meta info command failed\n");
}
mm_app_del_channel(test_obj, ch);
return rc;
}
int mm_app_start_capture(mm_camera_test_obj_t *test_obj,
uint8_t num_snapshots)
{
int32_t rc = MM_CAMERA_OK;
mm_camera_channel_t *channel = NULL;
mm_camera_stream_t *s_main = NULL;
mm_camera_stream_t *s_post = NULL;
mm_camera_channel_attr_t attr;
memset(&attr, 0, sizeof(mm_camera_channel_attr_t));
attr.notify_mode = MM_CAMERA_SUPER_BUF_NOTIFY_CONTINUOUS;
attr.max_unmatched_frames = 3;
channel = mm_app_add_channel(test_obj,
MM_CHANNEL_TYPE_CAPTURE,
&attr,
mm_app_snapshot_notify_cb,
test_obj);
if (NULL == channel) {
LOGE(" add channel failed");
return -MM_CAMERA_E_GENERAL;
}
s_main = mm_app_add_snapshot_stream(test_obj,
channel,
mm_app_snapshot_notify_cb,
(void *)test_obj,
CAPTURE_BUF_NUM,
num_snapshots);
if (NULL == s_main) {
LOGE(" add main snapshot stream failed\n");
mm_app_del_channel(test_obj, channel);
return rc;
}
s_post = mm_app_add_postview_stream(test_obj,
channel,
NULL,
NULL,
CAPTURE_BUF_NUM,
num_snapshots);
if (NULL == s_main) {
LOGE(" add main postview stream failed\n");
mm_app_del_channel(test_obj, channel);
return rc;
}
rc = mm_app_start_channel(test_obj, channel);
if (MM_CAMERA_OK != rc) {
LOGE("start zsl failed rc=%d\n", rc);
mm_app_del_stream(test_obj, channel, s_main);
mm_app_del_channel(test_obj, channel);
return rc;
}
return rc;
}
int mm_app_stop_capture(mm_camera_test_obj_t *test_obj)
{
int rc = MM_CAMERA_OK;
mm_camera_channel_t *ch = NULL;
ch = mm_app_get_channel_by_type(test_obj, MM_CHANNEL_TYPE_CAPTURE);
rc = mm_app_stop_and_del_channel(test_obj, ch);
if (MM_CAMERA_OK != rc) {
LOGE("stop capture channel failed rc=%d\n", rc);
}
return rc;
}
int mm_app_take_picture(mm_camera_test_obj_t *test_obj, uint8_t is_burst_mode)
{
LOGH("\nEnter %s!!\n");
int rc = MM_CAMERA_OK;
uint8_t num_snapshot = 1;
int num_rcvd_snapshot = 0;
if (is_burst_mode)
num_snapshot = 6;
//stop preview before starting capture.
rc = mm_app_stop_preview(test_obj);
if (rc != MM_CAMERA_OK) {
LOGE(" stop preview failed before capture!!, err=%d\n", rc);
return rc;
}
rc = mm_app_start_capture(test_obj, num_snapshot);
if (rc != MM_CAMERA_OK) {
LOGE(" mm_app_start_capture(), err=%d\n", rc);
return rc;
}
while (num_rcvd_snapshot < num_snapshot) {
LOGH("\nWaiting mm_camera_app_wait !!\n");
mm_camera_app_wait();
num_rcvd_snapshot++;
}
rc = mm_app_stop_capture(test_obj);
if (rc != MM_CAMERA_OK) {
LOGE(" mm_app_stop_capture(), err=%d\n", rc);
return rc;
}
//start preview after capture.
rc = mm_app_start_preview(test_obj);
if (rc != MM_CAMERA_OK) {
LOGE(" start preview failed after capture!!, err=%d\n",rc);
}
return rc;
}