/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /*============================================================================= @file sns_qmi_client.c ===========================================================================*/ /*============================================================================= Include Files ===========================================================================*/ #include #include "pb_encode.h" #include "qmi_client.h" #include "sns_client.h" #include "sns_client.pb.h" #include "sns_client_api_v01.h" #include "sns_osa.h" /*============================================================================= Type Definitions ===========================================================================*/ /* An outgoing request which is awaiting the corresponding response message. */ typedef struct sns_request { struct sns_client *client; /* Client registered callback */ sns_client_resp resp_cb; void *resp_cb_data; } sns_request; /* A client connection; a client process may open multiple QMI connections. */ typedef struct sns_client { /* Client registered callbacks */ sns_client_ind ind_cb; void *ind_cb_data; sns_client_error error_cb; void *error_cb_data; /* QMI Client Handle */ qmi_client_type qmi_handle; } sns_client; /*============================================================================= Static Function Definitions ===========================================================================*/ /* QMI indication callback. See qmi_client_ind_cb. */ static void client_ind_cb(qmi_client_type user_handle, unsigned int msg_id, void *ind_buf, unsigned int ind_buf_len, void *ind_cb_data) { UNUSED_VAR(user_handle); sns_client *client = ind_cb_data; size_t ind_len = sizeof(sns_client_report_ind_msg_v01); sns_client_report_ind_msg_v01 *ind = sns_malloc(ind_len); int32_t qmi_err; SNS_ASSERT(NULL != ind); // Extract the Protocol Buffer encoded message from the outer QMI/IDL message qmi_err = qmi_idl_message_decode(SNS_CLIENT_SVC_get_service_object_v01(), QMI_IDL_INDICATION, msg_id, ind_buf, ind_buf_len, ind, ind_len); if (QMI_IDL_LIB_NO_ERR != qmi_err) { SNS_LOG(ERROR, "QMI decode error %i", qmi_err); } else { SNS_LOG(VERBOSE, "Indication from client ID %" PRIu64, ind->client_id); client->ind_cb(client, ind->payload, ind->payload_len, client->ind_cb_data); } sns_free(ind); } /** * Allocate and initialize the response callback handler state for a request. * * @param[i] client * @param[i] resp_cb Callback to be called upon response receipt * @param[i] resp_cb_data Optional callback to be delivered * * @return Newly create request objct */ static sns_request *create_request(sns_client *client, sns_client_resp resp_cb, void *resp_cb_data) { sns_request *request; request = sns_malloc(sizeof(*request)); SNS_ASSERT(NULL != request); request->resp_cb = resp_cb; request->resp_cb_data = resp_cb_data; request->client = client; return request; } /** * Handle an incoming QMI response message from the Sensors Service. */ static void client_resp_cb(qmi_client_type user_handle, unsigned int msg_id, void *resp_c_struct, unsigned int resp_c_struct_len, void *resp_cb_data, qmi_client_error_type transp_err) { UNUSED_VAR(user_handle); UNUSED_VAR(msg_id); UNUSED_VAR(resp_c_struct_len); sns_request *request = resp_cb_data; sns_client_resp_msg_v01 *resp = resp_c_struct; sns_std_error err = SNS_STD_ERROR_NO_ERROR; if (NULL != resp && resp->result_valid) { err = resp->result; SNS_LOG(VERBOSE, "Response from client %" PRIu64, resp->client_id); } else if (QMI_NO_ERR != transp_err) { err = SNS_STD_ERROR_FAILED; } if (NULL != request->resp_cb) request->resp_cb(request->client, err, request->resp_cb_data); sns_free(request); sns_free(resp_c_struct); } /** * An error occurred; typically means the SLPI has restarted, and the client * should attempt to reconnect. */ static void client_error_cb(qmi_client_type user_handle, qmi_client_error_type error, void *err_cb_data) { UNUSED_VAR(user_handle); UNUSED_VAR(error); sns_client *client = err_cb_data; SNS_LOG(VERBOSE, "Error from client"); // PEND: Convert QMI transport error client->error_cb(client, SNS_STD_ERROR_INVALID_STATE, client->error_cb_data); } /*============================================================================= Public Function Definitions ===========================================================================*/ int sns_client_init(sns_client **client_out, uint32_t timeout, sns_client_ind ind_cb, void *ind_cb_data, sns_client_error error_cb, void *error_cb_data) { qmi_idl_service_object_type service_obj = SNS_CLIENT_SVC_get_service_object_v01(); qmi_service_instance instance_id = 0; qmi_client_error_type qmi_err; qmi_cci_os_signal_type os_params; int rv = -1; sns_client *client; client = sns_malloc(sizeof(*client)); SNS_ASSERT(NULL != client); client->ind_cb = ind_cb; client->ind_cb_data = ind_cb_data; client->error_cb = error_cb; client->error_cb_data = error_cb_data; qmi_err = qmi_client_init_instance(service_obj, instance_id, client_ind_cb, client, &os_params, timeout, &client->qmi_handle); if (QMI_NO_ERR != qmi_err) { SNS_LOG(ERROR, "qmi_client_init_instance error %i", qmi_err); } else { qmi_err = qmi_client_register_error_cb(client->qmi_handle, client_error_cb, NULL); if (QMI_NO_ERR != qmi_err) SNS_LOG(ERROR, "qmi_client_register_error_cb error %i", qmi_err); else rv = 0; } if (0 != rv) { if (NULL != client->qmi_handle) qmi_client_release(client->qmi_handle); sns_free(client); client = NULL; } SNS_LOG(VERBOSE, "sns_client_init %p", client); *client_out = client; return rv; } int sns_client_deinit(sns_client *client) { qmi_client_release(client->qmi_handle); sns_free(client); SNS_LOG(VERBOSE, "sns_client_deinit complete %p", client); return 0; } int sns_client_send(sns_client *client, sns_client_request_msg *msg, sns_client_resp resp_cb, void *resp_cb_data) { int rv = 0; pb_ostream_t stream; sns_client_req_msg_v01 *req_msg; req_msg = sns_malloc(sizeof(*req_msg)); SNS_ASSERT(NULL != req_msg); stream = pb_ostream_from_buffer(req_msg->payload, sizeof(req_msg->payload)); if (!pb_encode(&stream, sns_client_request_msg_fields, msg)) { SNS_LOG(ERROR, "pb_encode error: %s", PB_GET_ERROR(&stream)); rv = -1; } else { qmi_txn_handle txn_handle; qmi_client_error_type qmi_err; sns_request *request; size_t resp_len = sizeof(sns_client_resp_msg_v01); void *resp; resp = sns_malloc(resp_len); SNS_ASSERT(NULL != resp); request = create_request(client, resp_cb, resp_cb_data); req_msg->payload_len = stream.bytes_written; qmi_err = qmi_client_send_msg_async( client->qmi_handle, SNS_CLIENT_REQ_V01, req_msg, sizeof(*req_msg), resp, resp_len, client_resp_cb, request, &txn_handle); if (QMI_NO_ERR != qmi_err) { SNS_LOG(ERROR, "qmi_client_send_msg_async error %i", qmi_err); sns_free(resp); sns_free(request); rv = -2; } } sns_free(req_msg); return rv; }