1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef CHRE_PLATFORM_SLPI_SMGR_SMR_HELPER_H_ 18 #define CHRE_PLATFORM_SLPI_SMGR_SMR_HELPER_H_ 19 20 #include <type_traits> 21 22 extern "C" { 23 24 #include "qurt.h" 25 #include "sns_usmr.h" 26 27 } 28 29 #include "chre/platform/condition_variable.h" 30 #include "chre/platform/mutex.h" 31 #include "chre/util/non_copyable.h" 32 #include "chre/util/time.h" 33 #include "chre/util/unique_ptr.h" 34 35 namespace chre { 36 37 //! Default timeout for sendReqSync 38 constexpr Nanoseconds kDefaultSmrTimeout = Seconds(2); 39 40 //! Default timeout for waitForService. Have a longer timeout since there may be 41 //! external dependencies blocking SMGR initialization. 42 constexpr Nanoseconds kDefaultSmrWaitTimeout = Seconds(5); 43 44 /** 45 * A helper class for making synchronous requests to SMR (Sensors Message 46 * Router). Not safe to use from multiple threads. 47 */ 48 class SmrHelper : public NonCopyable { 49 public: 50 /** 51 * Wrapper to convert the async smr_client_release() to a synchronous call. 52 * 53 * @param clientHandle SMR handle to release 54 * @param timeout How long to wait for the response before abandoning it 55 * 56 * @return Result code returned by smr_client_release(), or SMR_TIMEOUT_ERR if 57 * the timeout was reached 58 */ 59 smr_err releaseSync(smr_client_hndl clientHandle, 60 Nanoseconds timeout = kDefaultSmrTimeout); 61 62 /** 63 * Wrapper to convert the async smr_client_send_req() to a synchronous call. 64 * 65 * Only one request can be pending at a time per instance of SmrHelper. 66 * 67 * @param ReqStruct QMI IDL-generated request structure 68 * @param RespStruct QMI IDL-generated response structure 69 * @param clientHandle SMR handle previously given by smr_client_init() 70 * @param msgId QMI message ID of the request to send 71 * @param req Pointer to populated request structure 72 * @param resp Pointer to structure to receive the response 73 * @param timeout How long to wait for the response before abandoning it 74 * 75 * @return Result code returned by smr_client_send_req(), or SMR_TIMEOUT_ERR 76 * if the supplied timeout was reached 77 */ 78 template<typename ReqStruct, typename RespStruct> 79 smr_err sendReqSync( 80 smr_client_hndl clientHandle, unsigned int msgId, 81 UniquePtr<ReqStruct> *req, UniquePtr<RespStruct> *resp, 82 Nanoseconds timeout = kDefaultSmrTimeout) { 83 // Try to catch copy/paste errors at compile time - QMI always has a 84 // different struct definition for request and response 85 static_assert(!std::is_same<ReqStruct, RespStruct>::value, 86 "Request and response structures must be different"); 87 88 smr_err result; 89 bool timedOut = !sendReqSyncUntyped( 90 clientHandle, msgId, req->get(), sizeof(ReqStruct), 91 resp->get(), sizeof(RespStruct), timeout, &result); 92 93 // Unlike QMI, SMR does not support canceling an in-flight transaction. 94 // SMR's internal request structure maintains a pointer to the client 95 // request and response buffers, so in the event of a timeout, it is unsafe 96 // for us to free the memory because the service may try to send the 97 // response later on - we'll try to free it if that ever happens, but 98 // otherwise we need to leave the memory allocation open. 99 if (timedOut) { 100 req->release(); 101 resp->release(); 102 } 103 104 return result; 105 } 106 107 /** 108 * Wrapper to convert the async smr_client_check_ext() to a synchronous call. 109 * Waits for an SMR service to become available. 110 * 111 * @param serviceObj The SMR service object to wait for. 112 * @param timeout The wait timeout in microseconds. 113 * 114 * @return Result code returned by smr_client_check_ext, or SMR_TIMEOUT_ERR if 115 * the timeout was reached 116 */ 117 smr_err waitForService(qmi_idl_service_object_type serviceObj, 118 Microseconds timeout = kDefaultSmrWaitTimeout); 119 120 private: 121 /** 122 * Used to track asynchronous SMR requests from sendReqSyncUntyped() to 123 * smrRespCb() 124 */ 125 struct SmrTransaction { 126 //! Value of SmrHelper::mCurrentTransactionId when this instance was 127 //! created - if it does not match at the time the transaction is given in 128 //! the callback, this transaction is invalid (it has timed out) 129 uint32_t transactionId; 130 131 //! Pointer to the SmrHelper instance that created this transaction 132 SmrHelper *parent; 133 134 // SMR request and response buffers given by the client; only used to free 135 // memory in the event of a late (post-timeout) callback 136 void *reqBuf; 137 void *rspBuf; 138 }; 139 140 /** 141 * Implements sendReqSync(), but with accepting untyped (void*) buffers. 142 * snake_case parameters exactly match those given to smr_client_send_req(). 143 * 144 * @param timeout How long to wait for the response before abandoning it 145 * @param result If smr_client_send_req() returns an error, then that error 146 * code, otherwise the transport error code given in the SMR response 147 * callback (assuming there was no timeout) 148 * @return false on timeout, otherwise true (includes the case where 149 * smr_client_send_req() returns an immediate error) 150 * 151 * @see sendReqSync() 152 * @see smr_client_send_req() 153 */ 154 bool sendReqSyncUntyped( 155 smr_client_hndl client_handle, unsigned int msg_id, 156 void *req_c_struct, unsigned int req_c_struct_len, 157 void *resp_c_struct, unsigned int resp_c_struct_len, 158 Nanoseconds timeout, smr_err *result); 159 160 /** 161 * Processes an SMR response callback 162 * 163 * @see smr_client_resp_cb 164 */ 165 void handleResp(smr_client_hndl client_handle, unsigned int msg_id, 166 void *resp_c_struct, unsigned int resp_c_struct_len, 167 smr_err transp_err, SmrTransaction *txn); 168 169 /** 170 * Sets mWaiting to true in advance of calling an async SMR function. 171 * Preconditions: mMutex not held, mWaiting false 172 */ 173 void prepareForWait(); 174 175 /** 176 * SMR release complete callback used with releaseSync() 177 * 178 * @see smr_client_release_cb 179 */ 180 static void smrReleaseCb(void *release_cb_data); 181 182 /** 183 * Extracts "this" from resp_cb_data and calls through to handleResp() 184 * 185 * @see smr_client_resp_cb 186 */ 187 static void smrRespCb(smr_client_hndl client_handle, unsigned int msg_id, 188 void *resp_c_struct, unsigned int resp_c_struct_len, 189 void *resp_cb_data, smr_err transp_err); 190 191 /** 192 * SMR wait for service callback used with waitForService() 193 * 194 * @see smr_client_init_ext_cb 195 */ 196 static void smrWaitForServiceCb(qmi_idl_service_object_type service_obj, 197 qmi_service_instance instance_id, 198 bool timeout_expired, 199 void *wait_for_service_cb_data); 200 201 //! Used to synchronize responses 202 ConditionVariable mCond; 203 204 //! Used with mCond, and to protect access to member variables from other 205 //! threads 206 Mutex mMutex; 207 208 //! true if we are waiting on an async response 209 bool mWaiting = false; 210 211 //! The transaction ID we're expecting in the next response callback 212 uint32_t mCurrentTransactionId = 0; 213 214 //! true if timed out while waiting for a service to become available 215 bool mServiceTimedOut = false; 216 217 //! The (transport) error code given in the response callback 218 smr_err mTranspErr; 219 }; 220 221 } // namespace chre 222 223 #endif // CHRE_PLATFORM_SLPI_SMGR_SMR_HELPER_H_ 224