| /* Copyright (c) 2013, 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. |
| */ |
| |
| #define LOG_NDEBUG 0 |
| #define LOG_TAG "LocSvc_ds_client" |
| |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdint.h> |
| #include <stdbool.h> |
| #include <stddef.h> |
| #include <wireless_data_service_v01.h> |
| #include <utils/Log.h> |
| #include <log_util.h> |
| #include <loc_log.h> |
| #include <qmi_client.h> |
| #include <qmi_idl_lib.h> |
| #include <qmi_cci_target_ext.h> |
| #include <qmi_cci_target.h> |
| #include <qmi_cci_common.h> |
| #include <dsi_netctrl.h> |
| #include <ds_client.h> |
| |
| #include<sys/time.h> |
| |
| //Timeout to wait for wds service notification from qmi |
| #define DS_CLIENT_SERVICE_TIMEOUT (4000) |
| //Max timeout for the service to come up |
| #define DS_CLIENT_SERVICE_TIMEOUT_TOTAL (40000) |
| //Timeout for the service to respond to sync msg |
| #define DS_CLIENT_SYNC_MSG_TIMEOUT (5000) |
| /*Request messages the WDS client can send to the WDS service*/ |
| typedef union |
| { |
| /*Requests the service for a list of all profiles present*/ |
| wds_get_profile_list_req_msg_v01 *p_get_profile_list_req; |
| /*Requests the service for a profile's settings*/ |
| wds_get_profile_settings_req_msg_v01 *p_get_profile_settings_req; |
| }ds_client_req_union_type; |
| |
| /*Response indications that are sent by the WDS service*/ |
| typedef union |
| { |
| wds_get_profile_list_resp_msg_v01 *p_get_profile_list_resp; |
| wds_get_profile_settings_resp_msg_v01 *p_get_profile_setting_resp; |
| }ds_client_resp_union_type; |
| |
| struct event_strings_s |
| { |
| char * str; |
| dsi_net_evt_t evt; |
| }; |
| |
| struct event_strings_s event_string_tbl[DSI_EVT_MAX] = |
| { |
| NAME_VAL(DSI_EVT_INVALID), |
| NAME_VAL(DSI_EVT_NET_IS_CONN), |
| NAME_VAL(DSI_EVT_NET_NO_NET), |
| NAME_VAL(DSI_EVT_PHYSLINK_DOWN_STATE), |
| NAME_VAL(DSI_EVT_PHYSLINK_UP_STATE), |
| NAME_VAL(DSI_EVT_NET_RECONFIGURED), |
| #ifndef GPS_PDK |
| NAME_VAL(DSI_EVT_WDS_CONNECTED) |
| #endif |
| }; |
| |
| typedef struct |
| { |
| ds_client_event_ind_cb_type event_cb; |
| void *caller_cookie; |
| }ds_caller_data; |
| |
| typedef struct { |
| //Global dsi handle |
| dsi_hndl_t dsi_net_handle; |
| //Handle to caller's data |
| ds_caller_data caller_data; |
| } ds_client_session_data; |
| |
| void net_ev_cb(dsi_hndl_t handle, void* user_data, |
| dsi_net_evt_t evt, dsi_evt_payload_t *payload_ptr) |
| { |
| int i; |
| (void)handle; |
| (void)user_data; |
| (void)payload_ptr; |
| ds_caller_data *callback_data = (ds_caller_data *)user_data; |
| |
| LOC_LOGD("%s:%d]: Enter. Callback data: %p\n", __func__, __LINE__, callback_data); |
| if(evt > DSI_EVT_INVALID && evt < DSI_EVT_MAX) |
| { |
| for(i=0;i<DSI_EVT_MAX;i++) |
| { |
| if(event_string_tbl[i].evt == evt) |
| LOC_LOGE("%s:%d]: Callback received: %s", |
| __func__, __LINE__, event_string_tbl[i].str); |
| } |
| switch(evt) { |
| case DSI_EVT_NET_IS_CONN: |
| #ifndef GPS_PDK |
| case DSI_EVT_WDS_CONNECTED: |
| #endif |
| { |
| LOC_LOGD("%s:%d]: Emergency call started\n", __func__, __LINE__); |
| callback_data->event_cb(E_DS_CLIENT_DATA_CALL_CONNECTED, |
| callback_data->caller_cookie); |
| break; |
| } |
| case DSI_EVT_NET_NO_NET: |
| { |
| LOC_LOGD("%s:%d]: Emergency call stopped\n", __func__, __LINE__); |
| callback_data->event_cb(E_DS_CLIENT_DATA_CALL_DISCONNECTED, |
| callback_data->caller_cookie); |
| break; |
| } |
| default: |
| LOC_LOGD("%s:%d]: uninteresting event\n", __func__, __LINE__); |
| } |
| } |
| LOC_LOGD("%s:%d]:Exit\n", __func__, __LINE__); |
| } |
| |
| /*This function is called to obtain a handle to the QMI WDS service*/ |
| static ds_client_status_enum_type |
| ds_client_qmi_ctrl_point_init(qmi_client_type *p_wds_qmi_client) |
| { |
| qmi_client_type wds_qmi_client, notifier = NULL; |
| ds_client_status_enum_type status = E_DS_CLIENT_SUCCESS; |
| qmi_service_info *p_service_info = NULL; |
| uint32_t num_services = 0, num_entries = 0; |
| qmi_client_error_type ret = QMI_NO_ERR; |
| unsigned char no_signal = 0; |
| qmi_client_os_params os_params; |
| int timeout = 0; |
| |
| LOC_LOGD("%s:%d]:Enter\n", __func__, __LINE__); |
| |
| //Get service object for QMI_WDS service |
| qmi_idl_service_object_type ds_client_service_object = |
| wds_get_service_object_v01(); |
| if(ds_client_service_object == NULL) { |
| LOC_LOGE("%s:%d]: wds_get_service_object_v01 failed\n" , |
| __func__, __LINE__); |
| status = E_DS_CLIENT_FAILURE_INTERNAL; |
| goto err; |
| } |
| |
| //get service addressing information |
| ret = qmi_client_get_service_list(ds_client_service_object, NULL, NULL, |
| &num_services); |
| LOC_LOGD("%s:%d]: qmi_client_get_service_list() first try ret %d, " |
| "num_services %d]\n", __func__, __LINE__, ret, num_services); |
| if(ret != QMI_NO_ERR) { |
| //Register for service notification |
| ret = qmi_client_notifier_init(ds_client_service_object, &os_params, ¬ifier); |
| if (ret != QMI_NO_ERR) { |
| LOC_LOGE("%s:%d]: qmi_client_notifier_init failed %d\n", |
| __func__, __LINE__, ret); |
| status = E_DS_CLIENT_FAILURE_INTERNAL; |
| goto err; |
| } |
| |
| do { |
| QMI_CCI_OS_SIGNAL_CLEAR(&os_params); |
| ret = qmi_client_get_service_list(ds_client_service_object, NULL, |
| NULL, &num_services); |
| if(ret != QMI_NO_ERR) { |
| QMI_CCI_OS_SIGNAL_WAIT(&os_params, DS_CLIENT_SERVICE_TIMEOUT); |
| no_signal = QMI_CCI_OS_SIGNAL_TIMED_OUT(&os_params); |
| if(!no_signal) |
| ret = qmi_client_get_service_list(ds_client_service_object, NULL, |
| NULL, &num_services); |
| } |
| timeout += DS_CLIENT_SERVICE_TIMEOUT; |
| LOC_LOGV("%s:%d]: qmi_client_get_service_list() returned ret: %d," |
| "no_signal: %d, total timeout: %d\n", __func__, __LINE__, |
| ret, no_signal, timeout); |
| } while( (timeout < DS_CLIENT_SERVICE_TIMEOUT_TOTAL) && |
| no_signal && |
| (ret != QMI_NO_ERR) ); |
| } |
| |
| //Handle failure cases |
| if(num_services == 0 || ret != QMI_NO_ERR) { |
| if(!no_signal) { |
| LOC_LOGE("%s:%d]: qmi_client_get_service_list failed even though" |
| "service is up! Error: %d \n", __func__, __LINE__, ret); |
| status = E_DS_CLIENT_FAILURE_INTERNAL; |
| } |
| else { |
| LOC_LOGE("%s:%d]: qmi_client_get_service_list failed after retries" |
| "Error: %d \n", __func__, __LINE__, ret); |
| status = E_DS_CLIENT_FAILURE_TIMEOUT; |
| } |
| goto err; |
| } |
| |
| LOC_LOGD("%s:%d]: qmi_client_get_service_list succeeded\n", __func__, __LINE__); |
| |
| //Success |
| p_service_info = (qmi_service_info *)malloc(num_services * sizeof(qmi_service_info)); |
| if(p_service_info == NULL) { |
| LOC_LOGE("%s:%d]: could not allocate memory for serviceInfo !!\n", |
| __func__, __LINE__); |
| status = E_DS_CLIENT_FAILURE_INTERNAL; |
| goto err; |
| } |
| num_entries = num_services; |
| |
| //Populate service info |
| ret = qmi_client_get_service_list(ds_client_service_object, p_service_info, |
| &num_entries, &num_services); |
| if(ret != QMI_NO_ERR) { |
| LOC_LOGE("%s:%d]: qmi_client_get_service_list failed. ret: %d \n", |
| __func__, __LINE__, ret); |
| status = E_DS_CLIENT_FAILURE_INTERNAL; |
| goto err; |
| } |
| |
| //Initialize wds_qmi_client |
| LOC_LOGD("%s:%d]: Initializing WDS client with qmi_client_init\n", __func__, |
| __LINE__); |
| ret = qmi_client_init(&p_service_info[0], ds_client_service_object, |
| NULL, NULL, NULL, &wds_qmi_client); |
| if(ret != QMI_NO_ERR) { |
| LOC_LOGE("%s:%d]: qmi_client_init Error. ret: %d\n", __func__, __LINE__, ret); |
| status = E_DS_CLIENT_FAILURE_INTERNAL; |
| goto err; |
| } |
| LOC_LOGD("%s:%d]: WDS client initialized with qmi_client_init\n", __func__, |
| __LINE__); |
| |
| //Store WDS QMI client handle in the parameter passed in |
| *p_wds_qmi_client = wds_qmi_client; |
| |
| status = E_DS_CLIENT_SUCCESS; |
| LOC_LOGD("%s:%d]: init success\n", __func__, __LINE__); |
| |
| if(notifier) |
| qmi_client_release(notifier); |
| |
| err: |
| if(p_service_info) |
| free(p_service_info); |
| |
| LOC_LOGD("%s:%d]:Exit\n", __func__, __LINE__); |
| return status; |
| } |
| |
| /*This function reads the error code from within the response struct*/ |
| static ds_client_status_enum_type ds_client_convert_qmi_response( |
| uint32_t req_id, |
| ds_client_resp_union_type *resp_union) |
| { |
| ds_client_status_enum_type ret = E_DS_CLIENT_FAILURE_GENERAL; |
| LOC_LOGD("%s:%d]:Enter\n", __func__, __LINE__); |
| switch(req_id) |
| { |
| case QMI_WDS_GET_PROFILE_LIST_REQ_V01 : |
| { |
| if(resp_union->p_get_profile_list_resp->resp.error != |
| QMI_ERR_NONE_V01) { |
| LOC_LOGE("%s:%d]: Response error: %d", __func__, __LINE__, |
| resp_union->p_get_profile_list_resp->resp.error); |
| } |
| else |
| ret = E_DS_CLIENT_SUCCESS; |
| } |
| break; |
| |
| case QMI_WDS_GET_PROFILE_SETTINGS_REQ_V01 : |
| { |
| if(resp_union->p_get_profile_setting_resp->resp.error != |
| QMI_ERR_NONE_V01) { |
| LOC_LOGE("%s:%d]: Response error: %d", __func__, __LINE__, |
| resp_union->p_get_profile_setting_resp->resp.error); |
| } |
| else |
| ret = E_DS_CLIENT_SUCCESS; |
| } |
| break; |
| |
| default: |
| LOC_LOGE("%s:%d]: Unknown request ID\n", __func__, __LINE__); |
| } |
| LOC_LOGD("%s:%d]:Exit\n", __func__, __LINE__); |
| return ret; |
| } |
| |
| |
| static ds_client_status_enum_type ds_client_send_qmi_sync_req( |
| qmi_client_type *ds_client_handle, |
| uint32_t req_id, |
| ds_client_resp_union_type *resp_union, |
| ds_client_req_union_type *req_union) |
| { |
| uint32_t req_len = 0; |
| uint32_t resp_len = 0; |
| ds_client_status_enum_type ret = E_DS_CLIENT_SUCCESS; |
| qmi_client_error_type qmi_ret = QMI_NO_ERR; |
| LOC_LOGD("%s:%d]:Enter\n", __func__, __LINE__); |
| switch(req_id) |
| { |
| case QMI_WDS_GET_PROFILE_LIST_REQ_V01 : |
| { |
| req_len = sizeof(wds_get_profile_list_req_msg_v01); |
| resp_len = sizeof(wds_get_profile_list_resp_msg_v01); |
| LOC_LOGD("%s:%d]: req_id = GET_PROFILE_LIST_REQ\n", |
| __func__, __LINE__); |
| } |
| break; |
| |
| case QMI_WDS_GET_PROFILE_SETTINGS_REQ_V01 : |
| { |
| req_len = sizeof(wds_get_profile_settings_req_msg_v01); |
| resp_len = sizeof(wds_get_profile_settings_resp_msg_v01); |
| LOC_LOGD("%s:%d]: req_id = GET_PROFILE_SETTINGS_REQ\n", |
| __func__, __LINE__); |
| } |
| break; |
| |
| default: |
| LOC_LOGE("%s:%d]: Error unknown req_id=%d\n", __func__, __LINE__, |
| req_id); |
| ret = E_DS_CLIENT_FAILURE_INVALID_PARAMETER; |
| goto err; |
| } |
| |
| LOC_LOGD("%s:%d]: req_id=%d, len = %d; resp_len= %d\n", __func__, __LINE__, |
| req_id, req_len, resp_len); |
| //Send msg through QCCI |
| qmi_ret = qmi_client_send_msg_sync( |
| *ds_client_handle, |
| req_id, |
| (void *)req_union->p_get_profile_list_req, |
| req_len, |
| (void *)resp_union->p_get_profile_list_resp, |
| resp_len, |
| DS_CLIENT_SYNC_MSG_TIMEOUT); |
| LOC_LOGD("%s:%d]: qmi_client_send_msg_sync returned: %d", __func__, __LINE__, qmi_ret); |
| |
| if(qmi_ret != QMI_NO_ERR) { |
| ret = E_DS_CLIENT_FAILURE_INTERNAL; |
| goto err; |
| } |
| |
| ret = ds_client_convert_qmi_response(req_id, resp_union); |
| |
| err: |
| LOC_LOGD("%s:%d]:Exit\n", __func__, __LINE__); |
| return ret; |
| } |
| |
| /*This function obtains the list of supported profiles*/ |
| static ds_client_status_enum_type ds_client_get_profile_list( |
| qmi_client_type *ds_client_handle, |
| ds_client_resp_union_type *profile_list_resp_msg, |
| wds_profile_type_enum_v01 profile_type) |
| { |
| ds_client_status_enum_type ret = E_DS_CLIENT_SUCCESS; |
| ds_client_req_union_type req_union; |
| LOC_LOGD("%s:%d]:Enter\n", __func__, __LINE__); |
| |
| req_union.p_get_profile_list_req = NULL; |
| req_union.p_get_profile_list_req = (wds_get_profile_list_req_msg_v01 *) |
| calloc(1, sizeof(wds_get_profile_list_req_msg_v01)); |
| if(req_union.p_get_profile_list_req == NULL) { |
| LOC_LOGE("%s:%d]: Could not allocate memory for" |
| "wds_get_profile_list_req_msg_v01\n", __func__, __LINE__); |
| goto err; |
| } |
| //Populate required members of the request structure |
| req_union.p_get_profile_list_req->profile_type_valid = 1; |
| req_union.p_get_profile_list_req->profile_type = profile_type; |
| ret = ds_client_send_qmi_sync_req(ds_client_handle, |
| QMI_WDS_GET_PROFILE_LIST_REQ_V01, |
| profile_list_resp_msg, &req_union); |
| if(ret != E_DS_CLIENT_SUCCESS) { |
| LOC_LOGE("%s:%d]: ds_client_send_qmi_req failed. ret: %d\n", |
| __func__, __LINE__, ret); |
| goto err; |
| } |
| err: |
| LOC_LOGD("%s:%d]:Exit\n", __func__, __LINE__); |
| if(req_union.p_get_profile_list_req) |
| free(req_union.p_get_profile_list_req); |
| return ret; |
| } |
| |
| /*This function obtains settings for the profile specified by |
| the profile_identifier*/ |
| static ds_client_status_enum_type ds_client_get_profile_settings( |
| qmi_client_type *ds_client_handle, |
| ds_client_resp_union_type *profile_settings_resp_msg, |
| wds_profile_identifier_type_v01 *profile_identifier) |
| { |
| ds_client_status_enum_type ret = E_DS_CLIENT_SUCCESS; |
| ds_client_req_union_type req_union; |
| |
| LOC_LOGD("%s:%d]:Enter\n", __func__, __LINE__); |
| //Since it's a union containing a pointer to a structure, |
| //following entities have the same address |
| //- req_union |
| //- req_union.p_get_profile_settings_req |
| //- req_union.p_get_profile_settings_req->profile |
| //so we can very well assign req_union = profile_identifier |
| req_union.p_get_profile_settings_req = |
| (wds_get_profile_settings_req_msg_v01 *)profile_identifier; |
| ret = ds_client_send_qmi_sync_req(ds_client_handle, |
| QMI_WDS_GET_PROFILE_SETTINGS_REQ_V01, |
| profile_settings_resp_msg, &req_union); |
| if(ret != E_DS_CLIENT_SUCCESS) { |
| LOC_LOGE("%s:%d]: ds_client_send_qmi_req failed. ret: %d\n", |
| __func__, __LINE__, ret); |
| goto err; |
| } |
| err: |
| LOC_LOGD("%s:%d]:Exit\n", __func__, __LINE__); |
| return ret; |
| } |
| |
| /* |
| Starts data call using the handle and the profile index |
| */ |
| ds_client_status_enum_type |
| ds_client_start_call(dsClientHandleType client_handle, int profile_index, int pdp_type) |
| { |
| ds_client_status_enum_type ret = E_DS_CLIENT_FAILURE_GENERAL; |
| dsi_call_param_value_t param_info; |
| dsi_hndl_t dsi_handle; |
| ds_client_session_data *ds_global_data = (ds_client_session_data *)client_handle; |
| LOC_LOGD("%s:%d]:Enter\n", __func__, __LINE__); |
| if(ds_global_data == NULL) { |
| LOC_LOGE("%s:%d]: Null callback parameter\n", __func__, __LINE__); |
| goto err; |
| } |
| dsi_handle = ds_global_data->dsi_net_handle; |
| //Set profile index as call parameter |
| param_info.buf_val = NULL; |
| param_info.num_val = profile_index; |
| dsi_set_data_call_param(dsi_handle, |
| DSI_CALL_INFO_UMTS_PROFILE_IDX, |
| ¶m_info); |
| |
| //Set IP Version as call parameter |
| param_info.buf_val = NULL; |
| param_info.num_val = pdp_type; |
| dsi_set_data_call_param(dsi_handle, |
| DSI_CALL_INFO_IP_VERSION, |
| ¶m_info); |
| LOC_LOGD("%s:%d]: Starting emergency call with profile index %d; pdp_type:%d\n", |
| __func__, __LINE__, profile_index, pdp_type); |
| if(dsi_start_data_call(dsi_handle) == DSI_SUCCESS) { |
| LOC_LOGD("%s:%d]: Sent request to start data call\n", |
| __func__, __LINE__); |
| ret = E_DS_CLIENT_SUCCESS; |
| } |
| else { |
| LOC_LOGE("%s:%d]: Could not send req to start data call \n", __func__, __LINE__); |
| ret = E_DS_CLIENT_FAILURE_GENERAL; |
| goto err; |
| } |
| |
| err: |
| LOC_LOGD("%s:%d]:Exit\n", __func__, __LINE__); |
| return ret; |
| |
| } |
| |
| /*Function to open an emergency call. Does the following things: |
| - Obtains a handle to the WDS service |
| - Obtains a list of profiles configured in the modem |
| - Queries each profile and obtains settings to check if emergency calls |
| are supported |
| - Returns the profile index that supports emergency calls |
| - Returns handle to dsi_netctrl*/ |
| ds_client_status_enum_type |
| ds_client_open_call(dsClientHandleType *client_handle, |
| ds_client_cb_data *callback, |
| void *caller_cookie, |
| int *profile_index, |
| int *pdp_type) |
| { |
| ds_client_status_enum_type ret = E_DS_CLIENT_FAILURE_GENERAL; |
| ds_client_resp_union_type profile_list_resp_msg; |
| ds_client_resp_union_type profile_settings_resp_msg; |
| wds_profile_identifier_type_v01 profile_identifier; |
| uint32_t i=0; |
| dsi_hndl_t dsi_handle; |
| ds_client_session_data **ds_global_data = (ds_client_session_data **)client_handle; |
| unsigned char call_profile_index_found = 0; |
| uint32_t emergency_profile_index=0; |
| qmi_client_type wds_qmi_client; |
| |
| profile_list_resp_msg.p_get_profile_list_resp = NULL; |
| profile_settings_resp_msg.p_get_profile_setting_resp = NULL; |
| |
| LOC_LOGD("%s:%d]:Enter\n", __func__, __LINE__); |
| if(callback == NULL || ds_global_data == NULL) { |
| LOC_LOGE("%s:%d]: Null callback parameter\n", __func__, __LINE__); |
| goto err; |
| } |
| |
| ret = ds_client_qmi_ctrl_point_init(&wds_qmi_client); |
| if(ret != E_DS_CLIENT_SUCCESS) { |
| LOC_LOGE("%s:%d]: ds_client_qmi_ctrl_point_init failed. ret: %d\n", |
| __func__, __LINE__, ret); |
| goto err; |
| } |
| |
| //Allocate memory for the response msg to obtain a list of profiles |
| profile_list_resp_msg.p_get_profile_list_resp = (wds_get_profile_list_resp_msg_v01 *) |
| calloc(1, sizeof(wds_get_profile_list_resp_msg_v01)); |
| if(profile_list_resp_msg.p_get_profile_list_resp == NULL) { |
| LOC_LOGE("%s:%d]: Could not allocate memory for" |
| "p_get_profile_list_resp\n", __func__, __LINE__); |
| ret = E_DS_CLIENT_FAILURE_NOT_ENOUGH_MEMORY; |
| goto err; |
| } |
| |
| LOC_LOGD("%s:%d]: Getting profile list\n", __func__, __LINE__); |
| ret = ds_client_get_profile_list(&wds_qmi_client, |
| &profile_list_resp_msg, |
| WDS_PROFILE_TYPE_3GPP_V01); |
| if(ret != E_DS_CLIENT_SUCCESS) { |
| LOC_LOGE("%s:%d]: ds_client_get_profile_list failed. ret: %d\n", |
| __func__, __LINE__, ret); |
| goto err; |
| } |
| LOC_LOGD("%s:%d]: Got profile list; length = %d\n", __func__, __LINE__, |
| profile_list_resp_msg.p_get_profile_list_resp->profile_list_len); |
| |
| //Allocate memory for the response msg to obtain profile settings |
| //We allocate memory for only one response msg and keep re-using it |
| profile_settings_resp_msg.p_get_profile_setting_resp = |
| (wds_get_profile_settings_resp_msg_v01 *) |
| calloc(1, sizeof(wds_get_profile_settings_resp_msg_v01)); |
| if(profile_settings_resp_msg.p_get_profile_setting_resp == NULL) { |
| LOC_LOGE("%s:%d]: Could not allocate memory for" |
| "p_get_profile_setting_resp\n", __func__, __LINE__); |
| ret = E_DS_CLIENT_FAILURE_NOT_ENOUGH_MEMORY; |
| goto err; |
| } |
| |
| //Loop over the list of profiles to find a profile that supports |
| //emergency calls |
| for(i=0; i < profile_list_resp_msg.p_get_profile_list_resp->profile_list_len; i++) { |
| /*QMI_WDS_GET_PROFILE_SETTINGS_REQ requires an input data |
| structure that is of type wds_profile_identifier_type_v01 |
| We have to fill that structure for each profile from the |
| info obtained from the profile list*/ |
| //copy profile type |
| profile_identifier.profile_type = |
| profile_list_resp_msg.p_get_profile_list_resp->profile_list[i].profile_type; |
| //copy profile index |
| profile_identifier.profile_index = |
| profile_list_resp_msg.p_get_profile_list_resp->profile_list[i].profile_index; |
| |
| ret = ds_client_get_profile_settings(&wds_qmi_client, |
| &profile_settings_resp_msg, |
| &profile_identifier); |
| if(ret != E_DS_CLIENT_SUCCESS) { |
| LOC_LOGE("%s:%d]: ds_client_get_profile_settings failed. ret: %d\n", |
| __func__, __LINE__, ret); |
| goto err; |
| } |
| LOC_LOGD("%s:%d]: Got profile setting for profile %d; name: %s\n", |
| __func__, __LINE__, i, |
| profile_settings_resp_msg.p_get_profile_setting_resp->profile_name); |
| |
| if(profile_settings_resp_msg.p_get_profile_setting_resp->support_emergency_calls_valid) { |
| if(profile_settings_resp_msg.p_get_profile_setting_resp->support_emergency_calls) { |
| LOC_LOGD("%s:%d]: Found emergency profile in profile %d" |
| , __func__, __LINE__, i); |
| call_profile_index_found = 1; |
| emergency_profile_index = profile_identifier.profile_index; |
| |
| if(profile_settings_resp_msg.p_get_profile_setting_resp->pdp_type_valid) { |
| *pdp_type = (int)profile_settings_resp_msg.p_get_profile_setting_resp->pdp_type; |
| LOC_LOGD("%s:%d]: pdp_type: %d\n", __func__, __LINE__, *pdp_type); |
| switch(*pdp_type) { |
| case WDS_PDP_TYPE_PDP_IPV4_V01: |
| *pdp_type = DSI_IP_VERSION_4; |
| break; |
| case WDS_PDP_TYPE_PDP_IPV6_V01: |
| *pdp_type = DSI_IP_VERSION_6; |
| break; |
| case WDS_PDP_TYPE_PDP_IPV4V6_V01: |
| *pdp_type = DSI_IP_VERSION_4_6; |
| break; |
| default: |
| LOC_LOGE("%s:%d]: pdp_type unknown. Setting default as ipv4/v6\n", |
| __func__, __LINE__); |
| *pdp_type = DSI_IP_VERSION_4; |
| |
| } |
| } |
| else { |
| LOC_LOGD("%s:%d]: pdp type not valid in profile setting. Default ipv4\n", |
| __func__, __LINE__); |
| *pdp_type = DSI_IP_VERSION_4; |
| } |
| //Break out of for loop since we found the emergency profile |
| break; |
| } |
| else |
| LOC_LOGE("%s:%d]: Emergency profile valid but not supported in profile: %d " |
| , __func__, __LINE__, i); |
| } |
| //Since this struct is loaded with settings for the next profile, |
| //it is important to clear out the memory to avoid values/flags |
| //from being carried over |
| memset((void *)profile_settings_resp_msg.p_get_profile_setting_resp, |
| 0, sizeof(wds_get_profile_settings_resp_msg_v01)); |
| } |
| |
| //Release qmi client handle |
| if(qmi_client_release(wds_qmi_client) != QMI_NO_ERR) { |
| LOC_LOGE("%s:%d]: Could not release qmi client handle\n", |
| __func__, __LINE__); |
| ret = E_DS_CLIENT_FAILURE_GENERAL; |
| } |
| |
| if(call_profile_index_found) { |
| *profile_index = emergency_profile_index; |
| *ds_global_data = (ds_client_session_data *)calloc(1, sizeof(ds_client_session_data)); |
| if(*ds_global_data == NULL) { |
| LOC_LOGE("%s:%d]: Could not allocate memory for ds_global_data. Failing\n", |
| __func__, __LINE__); |
| ret = E_DS_CLIENT_FAILURE_NOT_ENOUGH_MEMORY; |
| goto err; |
| } |
| |
| (*ds_global_data)->caller_data.event_cb = callback->event_cb; |
| (*ds_global_data)->caller_data.caller_cookie = caller_cookie; |
| dsi_handle = dsi_get_data_srvc_hndl(net_ev_cb, &(*ds_global_data)->caller_data); |
| if(dsi_handle == NULL) { |
| LOC_LOGE("%s:%d]: Could not get data handle. Retry Later\n", |
| __func__, __LINE__); |
| ret = E_DS_CLIENT_RETRY_LATER; |
| goto err; |
| } |
| else |
| (*ds_global_data)->dsi_net_handle = dsi_handle; |
| } |
| else { |
| LOC_LOGE("%s:%d]: Could not find a profile that supports emergency calls", |
| __func__, __LINE__); |
| ret = E_DS_CLIENT_FAILURE_GENERAL; |
| } |
| err: |
| if(profile_list_resp_msg.p_get_profile_list_resp) |
| free(profile_list_resp_msg.p_get_profile_list_resp); |
| if(profile_settings_resp_msg.p_get_profile_setting_resp) |
| free(profile_settings_resp_msg.p_get_profile_setting_resp); |
| LOC_LOGD("%s:%d]:Exit\n", __func__, __LINE__); |
| return ret; |
| } |
| |
| ds_client_status_enum_type ds_client_stop_call(dsClientHandleType client_handle) |
| { |
| ds_client_status_enum_type ret = E_DS_CLIENT_SUCCESS; |
| ds_client_session_data *p_ds_global_data = (ds_client_session_data *)client_handle; |
| LOC_LOGD("%s:%d]:Enter\n", __func__, __LINE__); |
| |
| if(client_handle == NULL) { |
| LOC_LOGE("%s:%d]: Null argument received. Failing\n", __func__, __LINE__); |
| ret = E_DS_CLIENT_FAILURE_GENERAL; |
| goto err; |
| } |
| |
| if(dsi_stop_data_call(p_ds_global_data->dsi_net_handle) == DSI_SUCCESS) { |
| LOC_LOGD("%s:%d]: Sent request to stop data call\n", __func__, __LINE__); |
| } |
| else { |
| LOC_LOGE("%s:%d]: Could not send request to stop data call\n", |
| __func__, __LINE__); |
| ret = E_DS_CLIENT_FAILURE_GENERAL; |
| goto err; |
| } |
| |
| err: |
| LOC_LOGD("%s:%d]:Exit\n", __func__, __LINE__); |
| return ret; |
| } |
| |
| /* |
| Stops data call associated with the data handle |
| */ |
| void ds_client_close_call(dsClientHandleType *client_handle) |
| { |
| ds_client_session_data **ds_global_data = (ds_client_session_data **)client_handle; |
| LOC_LOGD("%s:%d]:Enter\n", __func__, __LINE__); |
| if(client_handle == NULL || *client_handle == NULL) { |
| LOC_LOGE("%s:%d]: Null argument received. Failing\n", __func__, __LINE__); |
| goto err; |
| } |
| dsi_rel_data_srvc_hndl((*ds_global_data)->dsi_net_handle); |
| (*ds_global_data)->dsi_net_handle = NULL; |
| free(*ds_global_data); |
| *ds_global_data = NULL; |
| LOC_LOGD("%s:%d]: Released Data handle\n", __func__, __LINE__); |
| err: |
| LOC_LOGD("%s:%d]:Exit\n", __func__, __LINE__); |
| return; |
| } |
| |
| int ds_client_init() |
| { |
| int ret = 0; |
| LOC_LOGD("%s:%d]:Enter\n", __func__, __LINE__); |
| if(DSI_SUCCESS != dsi_init(DSI_MODE_GENERAL)) |
| { |
| LOC_LOGE("%s:%d]:dsi_init failed\n", __func__, __LINE__); |
| ret = -1; |
| } |
| LOC_LOGD("%s:%d]:Exit\n", __func__, __LINE__); |
| return ret; |
| } |