コード例 #1
0
ファイル: semdbg.c プロジェクト: tomsparrow25/wifisdk_for_wm
void sem_debug_add(const xSemaphoreHandle handle, const char *name,
		   bool is_semaphore)
{
	int temp, i;
	if (sem_mutex) {
		temp = os_mutex_get(&sem_mutex, OS_WAIT_FOREVER);
		if (temp == -WM_FAIL) {
			wmprintf("[sem-dbg] Failed to get sem-mutex\r\n");
			return;
		}
	}
	for (i = 0;  i < MAX_SEM_INFO_BUF; i++) {
		if (semdbg[i].x_queue == 0) {
			if (name && strlen(name)) {
				semdbg[i].q_name =
					os_mem_alloc(strlen(name)+1);
				if (semdbg[i].q_name == NULL)
					break;
				strcpy(semdbg[i].q_name, name);
			}
			semdbg[i].x_queue = handle;
			semdbg[i].is_semaphore = is_semaphore;
			semcnt++;
			break;
		}
	}
	if (sem_mutex)
		os_mutex_put(&sem_mutex);
	return;
}
コード例 #2
0
/***********************************************************
*  Function: get_gw_status
*  Input: 
*  Output: 
*  Return: GW_STAT_E
***********************************************************/
GW_STAT_E get_gw_status(VOID)
{
    GW_STAT_E stat;
    os_mutex_get(&gw_mutex, OS_WAIT_FOREVER);
    stat = gw_cntl.stat;
    os_mutex_put(&gw_mutex);

    return stat;
}
コード例 #3
0
int ssp_drv_close(mdev_t *dev)
{
	sspdev_data_t *ssp_data_p;
	ssp_data_p = (sspdev_data_t *) dev->private_data;
	os_mem_free(ssp_data_p->rx_ringbuf.buf_p);
	SSP_Disable(dev->port_id);

	return os_mutex_put(&ssp_mutex[dev->port_id]);
}
コード例 #4
0
/***********************************************************
*  Function: get_wf_gw_status
*  Input: 
*  Output: 
*  Return: GW_WIFI_STAT_E
***********************************************************/
GW_WIFI_STAT_E get_wf_gw_status(VOID)
{
    GW_WIFI_STAT_E wf_stat;

    os_mutex_get(&gw_mutex, OS_WAIT_FOREVER);
    wf_stat = gw_cntl.wf_stat;
    os_mutex_put(&gw_mutex);

    return wf_stat;
}
コード例 #5
0
ファイル: semdbg.c プロジェクト: tomsparrow25/wifisdk_for_wm
static void os_dump_seminfo()
{
	int i = 0, temp;
	const unsigned int *px_queue = NULL;
	unsigned int *list;
	char *pc_write_buffer = os_mem_alloc(MAX_WAITER_BUF);
	if (pc_write_buffer == NULL)
		return;

	if (sem_mutex) {
		temp = os_mutex_get(&sem_mutex, OS_WAIT_FOREVER);
		if (temp == -WM_FAIL) {
			wmprintf("[sem-dbg] Failed to get sem-mutex\r\n");
			os_mem_free(pc_write_buffer);
			return;
		}
	}

	if (semcnt > 0) {
		wmprintf("%-32s%-8s%-50s%s", "Name", "Count", "Waiters",
			 "Type\r\n");
	}

	for (i = 0; i < MAX_SEM_INFO_BUF; i++) {
		if (semdbg[i].x_queue == 0)
			continue;
		if (semdbg[i].q_name && isascii(semdbg[i].q_name[0])) {
			wmprintf("%-32s", semdbg[i].q_name);
		} else {
			wmprintf("%-32s", "-");
		}
		px_queue = semdbg[i].x_queue;
		wmprintf("%-8d",
			 os_semaphore_getcount(&semdbg[i].x_queue));
		vGetWaiterListFromHandle(px_queue, &list);
		vGetTaskNamesInList((signed char *) pc_write_buffer,
				    MAX_WAITER_BUF - 1,
				    (const unsigned int *)list);
		pc_write_buffer[MAX_WAITER_BUF - 1] = '\0';
		wmprintf("%-50s ", pc_write_buffer);
		(semdbg[i].is_semaphore == 0) ?
			wmprintf("q") :
			wmprintf("s");
		wmprintf("\r\n");
	}
	if (sem_mutex)
		os_mutex_put(&sem_mutex);
	os_mem_free(pc_write_buffer);
}
コード例 #6
0
ファイル: cli.c プロジェクト: tomsparrow25/wifisdk_for_wm
/* Main CLI processing thread
 *
 * Waits to receive a command buffer pointer from an input collector, and
 * then processes.  Note that it must cleanup the buffer when done with it.
 *
 * Input collectors handle their own lexical analysis and must pass complete
 * command lines to CLI.
 */
static void cli_main(os_thread_arg_t data)
{
	os_mutex_get(&cli_mutex, OS_WAIT_FOREVER);
	while (1) {
		int ret;
		char *msg;

		msg = NULL;
		ret = os_queue_recv(&cli.input_queue, &msg, RX_WAIT);
		if (ret != WM_SUCCESS) {
			if (ret == WM_E_BADF) {
				wmprintf("Error: CLI fatal queue error."
					       "\r\n");
/* Special case fatal errors.  Shouldn't happen. If it does
 * it means CLI is fatally corrupted, so end the thread.
 */
				return;
			}
/* A number of other non-fatal conditions can cause us to get here]
 * without a message to process, if so, just go back and wait.
 */
			continue;
		}

/* HALT message indicates that this thread will be deleted
 * shortly. Hence this function need to do necessary actions
 * required before getting deleted.
 * HALT message is not dynamically allocated,
 * hence msg doesn't need to be freed up in that case.
 */
		if (msg != NULL) {
			if (strcmp(msg, HALT_MSG) == 0)
				break;
			ret = handle_input(msg);
			if (ret == 1)
				print_bad_command(msg);
			else if (ret == 2)
				wmprintf("syntax error\r\n");
			wmprintf(PROMPT);
			/* done with it, clean up the message (we own it) */
			cli_mem_free(&msg);
		}
	}
	os_mutex_put(&cli_mutex);
	os_thread_self_complete(NULL);
}
コード例 #7
0
ファイル: lcd.c プロジェクト: tomsparrow25/wifisdk_for_wm
int lcd_drv_write(mdev_t *dev, const char *line1, const char *line2)
{
	int ret;
	ret = wakelock_get(WL_ID_LCD_WRITE);
	if (ret != WM_SUCCESS)
		return ret;

	ret = os_mutex_get(&lcd_mutex, OS_WAIT_FOREVER);
	if (ret == -WM_FAIL) {
		lcd_e("failed to get mutex");
		wakelock_put(WL_ID_LCD_WRITE);
		return ret;
	}
	__write_to_lcd(dev, line1, line2);
	os_mutex_put(&lcd_mutex);

	wakelock_put(WL_ID_LCD_WRITE);

	return 0;
}
コード例 #8
0
static void gw_actv_timer_cb(os_timer_arg_t arg)
{
    GW_WIFI_STAT_E wf_stat;
    wf_stat = get_wf_gw_status();
    if(wf_stat != STAT_STA_CONN) {
        // PR_DEBUG("we can not active gw,because the wifi state is :%d",wf_stat);
        return;
    }
    else {
        PR_DEBUG("now,we'll go to active gateway");
    }

    OPERATE_RET op_ret;
    #if NO_DEF_GW_DEV_ACTV_IF
    op_ret = httpc_gw_active();
    if(OPRT_OK != op_ret) {
        PR_ERR("op_ret:%d",op_ret);
        return;
    }
    #else
    op_ret = httpc_gw_dev_active(&(gw_cntl.dev->dev_if));
    if(OPRT_OK != op_ret) {
        PR_ERR("httpc_gw_dev_active error:%d",op_ret);
        return;
    }

    os_mutex_get(&gw_mutex, OS_WAIT_FOREVER);
    gw_cntl.dev->dev_if.bind = TRUE;
    gw_cntl.dev->dev_if.sync = FALSE;
    os_mutex_put(&gw_mutex);
    
    op_ret = ws_db_set_dev_if(&gw_cntl.dev->dev_if);
    if(OPRT_OK != op_ret) {
        PR_ERR("ws_db_set_dev_if error,op_ret:%d",op_ret);
    }
    #endif
    check_all_dev_if_update();

    os_timer_deactivate(&gw_actv_timer);
}
コード例 #9
0
VOID update_dev_ol_status(IN CONST CHAR *id,IN CONST BOOL online)
{
    if(id == NULL) {
        return;
    }

    DEV_CNTL_N_S *dev_cntl = get_dev_cntl(id);
    if(NULL == dev_cntl) {
        PR_ERR("do not get dev cntl");
        return;
    }

    if((get_gw_status() <= UN_ACTIVE) || \
        FALSE == dev_cntl->dev_if.bind) {
        return;
    }

    os_mutex_get(&gw_mutex, OS_WAIT_FOREVER);
    dev_cntl->online = online;
    os_mutex_put(&gw_mutex);

    check_dev_need_update(dev_cntl);
}
コード例 #10
0
ファイル: semdbg.c プロジェクト: tomsparrow25/wifisdk_for_wm
void sem_debug_delete(const xSemaphoreHandle handle)
{
	int temp, i;
	if (sem_mutex) {
		temp = os_mutex_get(&sem_mutex, OS_WAIT_FOREVER);
		if (temp == -WM_FAIL) {
			wmprintf("[sem-dbg] Failed to get sem-mutex\r\n");
			return;
		}
	}
	for (i = 0; i < MAX_SEM_INFO_BUF; i++) {
		if (semdbg[i].x_queue == handle) {
			semdbg[i].x_queue  = 0;
			os_mem_free(semdbg[i].q_name);
			semdbg[i].q_name = NULL;
			semcnt--;
			break;
		}
	}
	if (sem_mutex)
		os_mutex_put(&sem_mutex);
	return;
}
コード例 #11
0
bool CO2_dataRecieve(void)
{
    uint8_t data[9];
    int i, len, sz;

    /* transmit command data */
    os_mutex_get(&co2uart_mutex, OS_WAIT_FOREVER);
    uart_drv_write(uart2_dev, cmd_get_sensor, 9); //sizeof(cmd_get_sensor));
    os_mutex_put(&co2uart_mutex);

    os_thread_sleep(10); /* delay of 10 milisecs */

    /* begin reveiceing data */
    for(i = 0; i < 9; i++) {
        len = 0;
        while(len++ < 100) { /* Ready each byte until it is received, timeout 100msec*/
            sz = uart_drv_read(uart2_dev, &data[i], 1);
            if (sz == 1) {
                /* If a bute is read then store and move to next */
                break;
            } else
                os_thread_sleep(1); /* delay of 1 milisecs */
        }
    }

    if((i != 9) || (1 + (0xFF ^ (uint8_t)(data[1] + data[2] + data[3]
                                          + data[4] + data[5] + data[6] + data[7]))) != data[8]) {
        wmprintf("Received Data Checksum Error\r\n");
        return false;
    }

    CO2PPM = (int)data[2] * 256 + (int)data[3];
    temperature = (int)data[4] - 40;

    wmprintf("Tempr: %d, CORPPM: %d\r\n", temperature, CO2PPM);
    return true;
}
コード例 #12
0
/***********************************************************
*  Function: check_and_update_dev_desc_if
*  Input: 
*  Output: 
*  Return: 
***********************************************************/
VOID check_and_update_dev_desc_if(IN CONST CHAR *id,\
                                  IN CONST CHAR *name,\
                                  IN CONST CHAR *sw_ver,\
                                  IN CONST CHAR *schema_id,\
                                  IN CONST CHAR *ui_id)
{
    if(id == NULL) {
        return;
    }

    DEV_CNTL_N_S *dev_cntl = get_dev_cntl(id);
    if(NULL == dev_cntl) {
        PR_ERR("do not get dev cntl");
        return;
    }

    CONST CHAR *str[] = {name,sw_ver,schema_id,ui_id};
    CHAR *oldstr[] = {dev_cntl->dev_if.name,dev_cntl->dev_if.sw_ver,\
                      dev_cntl->dev_if.schema_id,dev_cntl->dev_if.ui_id};
    INT i;

    os_mutex_get(&gw_mutex, OS_WAIT_FOREVER);
    for(i = 0;i < CNTSOF(str);i++) {
        if(NULL == str[i]) {
            continue;
        }
        
        if(strcmp(str[i],oldstr[i]) || \
           (strlen(str[i]) != strlen(oldstr[i]))) {
            dev_cntl->dev_if.sync = TRUE;
            strcpy(oldstr[i],str[i]);
        }
    }
    os_mutex_put(&gw_mutex);

    check_dev_need_update(dev_cntl);
}
コード例 #13
0
/**
 * @brief Unlock the provided mutex
 *
 * Call this function to unlock the mutex before performing a state change
 *
 * @param IoT_Mutex_t - pointer to the mutex to be unlocked
 * @return IoT_Error_t - error code indicating result of operation
 */
IoT_Error_t aws_iot_thread_mutex_unlock(IoT_Mutex_t *pMutex) {
	if (os_mutex_put(&pMutex->lock) != WM_SUCCESS) {
		return MUTEX_LOCK_ERROR;
	}
	return AWS_SUCCESS;
}
コード例 #14
0
/***********************************************************
*  Function: set_gw_status
*  Input: 
*  Output: 
*  Return: 
***********************************************************/
VOID set_gw_status(IN CONST GW_STAT_E stat)
{
    os_mutex_get(&gw_mutex, OS_WAIT_FOREVER);
    gw_cntl.stat = stat;
    os_mutex_put(&gw_mutex);
}
コード例 #15
0
static void dev_ul_timer_cb(os_timer_arg_t arg)
{
    if(get_gw_status() <= ACTIVE_RD) {
        os_timer_deactivate(&dev_ul_timer);
    }

    GW_WIFI_STAT_E wf_stat;
    wf_stat = get_wf_gw_status();
    if(wf_stat != STAT_STA_CONN) {
        // PR_DEBUG("we can not update info,because the wifi state is :%d",wf_stat);
        return;
    }
    else {
        PR_DEBUG("now,we'll go to update device info");
    }

    // TODO 1 http device bind 
    //      2 device info update
    GW_CNTL_S *gw_cntl = get_gw_cntl();
    DEV_CNTL_N_S *dev_cntl = NULL;
    BOOL close_timer = TRUE;
    OPERATE_RET op_ret = OPRT_OK;

    for(dev_cntl = gw_cntl->dev;dev_cntl;dev_cntl = dev_cntl->next) {        
        // device bind
        if(FALSE == dev_cntl->dev_if.bind) {
            PR_DEBUG("bind");
            op_ret = httpc_dev_bind(&dev_cntl->dev_if);
            if(OPRT_OK != op_ret) {
                close_timer = FALSE;
                continue;
            }

            os_mutex_get(&gw_mutex, OS_WAIT_FOREVER);
            dev_cntl->dev_if.bind = TRUE;
            dev_cntl->dev_if.sync = FALSE;
            os_mutex_put(&gw_mutex);

            op_ret = ws_db_set_dev_if(&dev_cntl->dev_if);
            if(OPRT_OK != op_ret) {
                PR_ERR("ws_db_set_dev_if error,op_ret:%d",op_ret);
            }
        }

        // device info update
        if(TRUE == dev_cntl->dev_if.sync && \
           TRUE == dev_cntl->dev_if.bind) {
            PR_DEBUG("update");
            op_ret = httpc_dev_update(&dev_cntl->dev_if);
            if(OPRT_OK != op_ret) {
                close_timer = FALSE;
                continue;
            }

            os_mutex_get(&gw_mutex, OS_WAIT_FOREVER);
            dev_cntl->dev_if.sync = FALSE;
            os_mutex_put(&gw_mutex);

            op_ret = ws_db_set_dev_if(&dev_cntl->dev_if);
            if(OPRT_OK != op_ret) {
                PR_ERR("ws_db_set_dev_if error,op_ret:%d",op_ret);
            }
        }
    }

    if(TRUE == close_timer) {
        int ret = os_timer_deactivate(&dev_ul_timer);;
        PR_DEBUG("close timer ret:%d",ret);
    }
}
コード例 #16
0
/***********************************************************
*  Function: set_wf_gw_status
*  Input: 
*  Output: 
*  Return: 
***********************************************************/
VOID set_wf_gw_status(IN CONST GW_WIFI_STAT_E wf_stat)
{
    os_mutex_get(&gw_mutex, OS_WAIT_FOREVER);
    gw_cntl.wf_stat = wf_stat;
    os_mutex_put(&gw_mutex);
}
コード例 #17
0
/***********************************************************
*  Function: gw_lc_bind_device
*  Input: 
*  Output: 
*  Return: 
***********************************************************/
OPERATE_RET gw_lc_bind_device(IN CONST DEV_DESC_IF_S *dev_if,\
                              IN CONST CHAR *sch_json_arr)
{
    if(NULL == dev_if || \
       NULL == sch_json_arr) {
        return OPRT_INVALID_PARM;
    }

    if(get_dev_cntl(dev_if->id)) {
        return OPRT_OK;
    }

    cJSON *root = NULL;
    OPERATE_RET op_ret = OPRT_OK;
    root = cJSON_Parse(sch_json_arr);
    if(NULL == root) {
        return OPRT_CJSON_PARSE_ERR;
    }
    
    INT dp_num;
    DEV_CNTL_N_S *dev_cntl = NULL;
    dp_num = cJSON_GetArraySize(root);
    if(0 == dp_num) {
        op_ret = OPRT_INVALID_PARM;
        goto ERR_EXIT;
    }

    dev_cntl = (DEV_CNTL_N_S *)Malloc(sizeof(DEV_CNTL_N_S)+dp_num*sizeof(DP_CNTL_S));
    if(NULL == dev_cntl) {
        op_ret = OPRT_MALLOC_FAILED;
        goto ERR_EXIT;
    }
    memset(dev_cntl,0,sizeof(DEV_CNTL_N_S)+dp_num*sizeof(DP_CNTL_S));

    memcpy(&dev_cntl->dev_if,dev_if,sizeof(DEV_DESC_IF_S));
    dev_cntl->dp_num = dp_num;

    // dp schema parse
    INT i;
    DP_DESC_IF_S *dp_desc;
    DP_PROP_VALUE_U *prop;
    cJSON *cjson;
    cJSON *next;
    
    for(i = 0;i < dev_cntl->dp_num;i++) {        
        dp_desc = &(dev_cntl->dp[i].dp_desc);
        prop = &(dev_cntl->dp[i].prop);

        cjson = cJSON_GetArrayItem(root,i);
        if(NULL == cjson) {
            op_ret = OPRT_CJSON_GET_ERR;
            goto ERR_EXIT;
        }

        // id
        next = cJSON_GetObjectItem(cjson,"id");
        if(NULL == next) {
            PR_ERR("get id null");
            op_ret = OPRT_CJSON_GET_ERR;
            goto ERR_EXIT;
        }
        if(next->type == cJSON_String) {
            dp_desc->dp_id = atoi(next->valuestring);
        }else {
            dp_desc->dp_id = next->valueint;
        }

        // mode
        next = cJSON_GetObjectItem(cjson,"mode");
        if(NULL == next) {
            PR_ERR("get mode null");
            op_ret = OPRT_CJSON_GET_ERR;
            goto ERR_EXIT;
        }
        if(!strcmp(next->valuestring,"rw")) {
            dp_desc->mode = M_RW;
        }else if(!strcmp(next->valuestring,"ro")) {
            dp_desc->mode = M_RO;
        }else {
            dp_desc->mode = M_WR;
        }

        // passive
        next = cJSON_GetObjectItem(cjson,"passive");
        if(next == NULL) {
            dp_desc->passive = FALSE;
        }else {
            dp_desc->passive = next->type;
            dev_cntl->preprocess = TRUE;
        }
        
        // trigger
        next = cJSON_GetObjectItem(cjson,"trigger");
        if(NULL == next) {
            dp_desc->trig_t = TRIG_PULSE;
        }else {
            if(!strcmp(next->valuestring,"pulse")) {
                dp_desc->trig_t = TRIG_PULSE;
            }else {
                dp_desc->trig_t = TRIG_DIRECT;
            }
        }

        // type
        next = cJSON_GetObjectItem(cjson,"type");
        if(NULL == next) {
            PR_ERR("get type null");
            op_ret = OPRT_CJSON_GET_ERR;
            goto ERR_EXIT;
        }
        if(!strcmp(next->valuestring,"obj")) {
            dp_desc->type = T_OBJ;
        }else if(!strcmp(next->valuestring,"raw")) {
            dp_desc->type = T_RAW;
            continue;
        }else {
            dp_desc->type = T_FILE;
            continue;
        }

        // property
        next = cJSON_GetObjectItem(cjson,"property");
        if(NULL == next) {
            PR_ERR("get property null");
            op_ret = OPRT_CJSON_GET_ERR;
            goto ERR_EXIT;
        }
        cJSON *child;
        child = cJSON_GetObjectItem(next,"type");
        if(NULL == next) {
            PR_ERR("get type null");
            op_ret = OPRT_CJSON_GET_ERR;
            goto ERR_EXIT;
        }
        if(!strcmp(child->valuestring,"bool")) {
            dp_desc->prop_tp = PROP_BOOL;
        }else if(!strcmp(child->valuestring,"value")) {
            dp_desc->prop_tp = PROP_VALUE;

            CHAR *str[] = {"max","min","scale"};
            INT i;
            for(i = 0; i < CNTSOF(str);i++) {
                child = cJSON_GetObjectItem(next,str[i]);
                if(NULL == child && (i != CNTSOF(str)-1)) {
                    PR_ERR("get property null");
                    op_ret = OPRT_CJSON_GET_ERR;
                    goto ERR_EXIT;
                }else if(NULL == child && (i == CNTSOF(str)-1)) {
                    prop->prop_value.scale = 0;
                }else {
                    switch(i) {
                        case 0: prop->prop_value.max = child->valueint; break;
                        case 1: prop->prop_value.min = child->valueint; break;
                        //case 2: prop->prop_value.step = child->valueint; break;
                        case 2: prop->prop_value.scale = child->valueint; break;
                    }
                }
            }
        }else if(!strcmp(child->valuestring,"string")) {
            dp_desc->prop_tp = PROP_STR;
            child = cJSON_GetObjectItem(next,"maxlen");
            if(NULL == child) {
                PR_ERR("get maxlen null");
                op_ret = OPRT_CJSON_GET_ERR;
                goto ERR_EXIT;
            }
            prop->prop_str.max_len = child->valueint;
            prop->prop_str.value = Malloc(prop->prop_str.max_len+1);
            if(NULL == prop->prop_str.value) {
                PR_ERR("malloc error");
                op_ret = OPRT_MALLOC_FAILED;
                goto ERR_EXIT;
            }
        }else {
            dp_desc->prop_tp = PROP_ENUM;
            child = cJSON_GetObjectItem(next,"range");
            if(NULL == child) {
                PR_ERR("get range null");
                op_ret = OPRT_CJSON_GET_ERR;
                goto ERR_EXIT;
            }
            
            INT i,num;
            num = cJSON_GetArraySize(child);
            if(num == 0) {
                PR_ERR("get array size error");
                op_ret = OPRT_CJSON_GET_ERR;
                goto ERR_EXIT;
            }
            prop->prop_enum.pp_enum = Malloc(num*sizeof(CHAR *));
            if(NULL == prop->prop_enum.pp_enum) {
                PR_ERR("malloc error");
                op_ret = OPRT_MALLOC_FAILED;
                goto ERR_EXIT;
            }

            prop->prop_enum.cnt = num;
            for(i = 0;i < num;i++) {
                cJSON *c_child = cJSON_GetArrayItem(child,i);
                if(NULL == c_child) {
                    PR_ERR("get array null");
                    op_ret = OPRT_CJSON_GET_ERR;
                    goto ERR_EXIT;
                }

                prop->prop_enum.pp_enum[i] = cJSON_strdup(c_child->valuestring);
                if(NULL == prop->prop_enum.pp_enum[i]) {
                    PR_ERR("malloc error");
                    op_ret = OPRT_MALLOC_FAILED;
                    goto ERR_EXIT;
                }
            }
        }
    }

    os_mutex_get(&gw_mutex, OS_WAIT_FOREVER);
    if(NULL == gw_cntl.dev) {
        gw_cntl.dev = dev_cntl;
    }else {
        DEV_CNTL_N_S *tmp_dev_cntl = gw_cntl.dev;
        while(tmp_dev_cntl->next) {
            tmp_dev_cntl = tmp_dev_cntl->next;
        }
        tmp_dev_cntl->next = dev_cntl;
    }
    gw_cntl.dev_num++;
    os_mutex_put(&gw_mutex);

    if(root) {
        cJSON_Delete(root);
    }

    return OPRT_OK;

ERR_EXIT:
    if(dev_cntl) {
        Free(dev_cntl);
    }

    if(root) {
        cJSON_Delete(root);
    }

    return op_ret;
}