| /* |
| * 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 <inttypes.h> |
| #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; |
| } |