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/platform/slpi/power_control_util.h" 32 #include "chre/util/non_copyable.h" 33 #include "chre/util/time.h" 34 #include "chre/util/unique_ptr.h" 35 36 namespace chre { 37 38 //! Default timeout for sendReqSync 39 constexpr Nanoseconds kDefaultSmrTimeout = Seconds(2); 40 41 //! Default timeout for waitForService. Have a longer timeout since there may be 42 //! external dependencies blocking SMGR initialization. 43 constexpr Nanoseconds kDefaultSmrWaitTimeout = Seconds(5); 44 45 template<typename RespStruct> 46 using SmrReqCallback = void (*)(UniquePtr<RespStruct> resp, void *callbackData, 47 smr_err transpErr); 48 49 /** 50 * A helper class for making synchronous requests to SMR (Sensors Message 51 * Router). Not safe to use from multiple threads. 52 */ 53 class SmrHelper : public NonCopyable { 54 public: 55 /** 56 * Wrapper to convert the async smr_client_release() to a synchronous call. 57 * 58 * @param clientHandle SMR handle to release 59 * @param timeout How long to wait for the response before abandoning it 60 * 61 * @return Result code returned by smr_client_release(), or SMR_TIMEOUT_ERR if 62 * the timeout was reached 63 */ 64 smr_err releaseSync(smr_client_hndl clientHandle, 65 Nanoseconds timeout = kDefaultSmrTimeout); 66 67 /** 68 * Wrapper to send the async smr_client_send_req() call. 69 * 70 * @param ReqStruct QMI IDL-generated request structure 71 * @param RespStruct QMI IDL-generated response structure 72 * @param clientHandle SMR handle previously given by smr_client_init() 73 * @param msgId QMI message ID of the request to send 74 * @param req Pointer to populated request structure 75 * @param resp Pointer to structure to receive the response. 76 * @param callback Callback to be invoked upon completion of the request. 77 * NOTE: This callback will be invoked on the CHRE thread. 78 * @param callbackData Data to be sent to the callback when invoked. 79 * 80 * @return Result code returned by smr_client_send_req() 81 */ 82 template<typename ReqStruct, typename RespStruct> sendReqAsync(smr_client_hndl clientHandle,unsigned int msgId,UniquePtr<ReqStruct> * req,UniquePtr<RespStruct> * resp,SmrReqCallback<RespStruct> callback,void * callbackData)83 smr_err sendReqAsync( 84 smr_client_hndl clientHandle, unsigned int msgId, 85 UniquePtr<ReqStruct> *req, UniquePtr<RespStruct> *resp, 86 SmrReqCallback<RespStruct> callback, void *callbackData) { 87 // Try to catch copy/paste errors at compile time - QMI always has a 88 // different struct definition for request and response 89 static_assert(!std::is_same<ReqStruct, RespStruct>::value, 90 "Request and response structures must be different"); 91 92 smr_err result; 93 auto reqData = MakeUnique<AsyncCallbackData<ReqStruct, RespStruct>>(); 94 if (reqData.isNull()) { 95 LOG_OOM(); 96 result = SMR_OUT_OF_MEMORY; 97 } else { 98 reqData->callback = callback; 99 reqData->reqCStruct = std::move(*req); 100 reqData->respCStruct = std::move(*resp); 101 reqData->data = callbackData; 102 103 result = sendReqAsyncUntyped( 104 clientHandle, msgId, reqData->reqCStruct.get(), sizeof(ReqStruct), 105 reqData->respCStruct.get(), sizeof(RespStruct), reqData.get(), 106 SmrHelper::smrAsyncRespCb<ReqStruct, RespStruct>); 107 108 if (result == SMR_NO_ERR) { 109 // Release ownership of the request callback data since it will be used 110 // by SMGR and the async callback after this function returns. 111 reqData.release(); 112 } 113 } 114 115 return result; 116 } 117 118 /** 119 * Wrapper to convert the async smr_client_send_req() to a synchronous call. 120 * 121 * Only one request can be pending at a time per instance of SmrHelper. 122 * 123 * @param ReqStruct QMI IDL-generated request structure 124 * @param RespStruct QMI IDL-generated response structure 125 * @param clientHandle SMR handle previously given by smr_client_init() 126 * @param msgId QMI message ID of the request to send 127 * @param req Pointer to populated request structure 128 * @param resp Pointer to structure to receive the response 129 * @param timeout How long to wait for the response before abandoning it 130 * 131 * @return Result code returned by smr_client_send_req(), or SMR_TIMEOUT_ERR 132 * if the supplied timeout was reached 133 */ 134 template<typename ReqStruct, typename RespStruct> 135 smr_err sendReqSync( 136 smr_client_hndl clientHandle, unsigned int msgId, 137 UniquePtr<ReqStruct> *req, UniquePtr<RespStruct> *resp, 138 Nanoseconds timeout = kDefaultSmrTimeout) { 139 // Try to catch copy/paste errors at compile time - QMI always has a 140 // different struct definition for request and response 141 static_assert(!std::is_same<ReqStruct, RespStruct>::value, 142 "Request and response structures must be different"); 143 144 smr_err result; 145 bool timedOut = !sendReqSyncUntyped( 146 clientHandle, msgId, req->get(), sizeof(ReqStruct), 147 resp->get(), sizeof(RespStruct), timeout, &result); 148 149 // Unlike QMI, SMR does not support canceling an in-flight transaction. 150 // SMR's internal request structure maintains a pointer to the client 151 // request and response buffers, so in the event of a timeout, it is unsafe 152 // for us to free the memory because the service may try to send the 153 // response later on - we'll try to free it if that ever happens, but 154 // otherwise we need to leave the memory allocation open. 155 if (timedOut) { 156 req->release(); 157 resp->release(); 158 } 159 160 return result; 161 } 162 163 /** 164 * Wrapper to convert the async smr_client_check_ext() to a synchronous call. 165 * Waits for an SMR service to become available. 166 * 167 * @param serviceObj The SMR service object to wait for. 168 * @param timeout The wait timeout in microseconds. 169 * 170 * @return Result code returned by smr_client_check_ext, or SMR_TIMEOUT_ERR if 171 * the timeout was reached 172 */ 173 smr_err waitForService(qmi_idl_service_object_type serviceObj, 174 Microseconds timeout = kDefaultSmrWaitTimeout); 175 176 private: 177 /** 178 * Used to track asynchronous SMR requests from sendReqSyncUntyped() to 179 * smrRespCb() 180 */ 181 struct SmrTransaction { 182 //! Value of SmrHelper::mCurrentTransactionId when this instance was 183 //! created - if it does not match at the time the transaction is given in 184 //! the callback, this transaction is invalid (it has timed out) 185 uint32_t transactionId; 186 187 //! Pointer to the SmrHelper instance that created this transaction 188 SmrHelper *parent; 189 190 // SMR request and response buffers given by the client; only used to free 191 // memory in the event of a late (post-timeout) callback 192 void *reqBuf; 193 void *rspBuf; 194 }; 195 196 /** 197 * Struct used to store data needed once smr_client_send_req invokes the async 198 * request callback. 199 */ 200 template<typename ReqStruct, typename RespStruct> 201 struct AsyncCallbackData { 202 //! Callback given by the client issuing the request. 203 SmrReqCallback<RespStruct> callback; 204 205 //! Error received from the SMGR response callback. 206 smr_err transpErr; 207 208 //! Arbitrary data to be given to the callback. 209 void *data; 210 211 //! ReqStruct info from the initial SMGR request. 212 UniquePtr<ReqStruct> reqCStruct; 213 214 //! RespStruct info from the SMGR response callback. 215 UniquePtr<RespStruct> respCStruct; 216 }; 217 218 /** 219 * Implements sendReqAsync(), but accepts untyped (void*) buffers. 220 * snake_case parameters exactly match those given to smr_client_send_req(). 221 * 222 * @return The error code returned by smr_client_send_req(). 223 * 224 * @see sendReqAsync() 225 * @see smr_client_send_req() 226 */ 227 static smr_err sendReqAsyncUntyped( 228 smr_client_hndl client_handle, unsigned int msg_id, 229 void *req_c_struct, unsigned int req_c_struct_len, 230 void *resp_c_struct, unsigned int resp_c_struct_len, 231 void *resp_cb_data, smr_client_resp_cb resp_cb); 232 233 /** 234 * Implements sendReqSync(), but with accepting untyped (void*) buffers. 235 * snake_case parameters exactly match those given to smr_client_send_req(). 236 * 237 * @param timeout How long to wait for the response before abandoning it 238 * @param result If smr_client_send_req() returns an error, then that error 239 * code, otherwise the transport error code given in the SMR response 240 * callback (assuming there was no timeout) 241 * @return false on timeout, otherwise true (includes the case where 242 * smr_client_send_req() returns an immediate error) 243 * 244 * @see sendReqSync() 245 * @see smr_client_send_req() 246 */ 247 bool sendReqSyncUntyped( 248 smr_client_hndl client_handle, unsigned int msg_id, 249 void *req_c_struct, unsigned int req_c_struct_len, 250 void *resp_c_struct, unsigned int resp_c_struct_len, 251 Nanoseconds timeout, smr_err *result); 252 253 /** 254 * Processes an SMR response callback 255 * 256 * @see smr_client_resp_cb 257 */ 258 void handleResp(smr_client_hndl client_handle, unsigned int msg_id, 259 void *resp_c_struct, unsigned int resp_c_struct_len, 260 smr_err transp_err, SmrTransaction *txn); 261 262 /** 263 * Sets mWaiting to true in advance of calling an async SMR function. 264 * Preconditions: mMutex not held, mWaiting false 265 */ 266 void prepareForWait(); 267 268 /** 269 * SMR release complete callback used with releaseSync() 270 * 271 * @see smr_client_release_cb 272 */ 273 static void smrReleaseCb(void *release_cb_data); 274 275 /** 276 * Posts the asynchronous response back to the CHRE thread and then invokes 277 * the callback given by the client when the request was made with the 278 * appropriate parameters from the response. 279 * 280 * @see smr_client_resp_cb 281 */ 282 template<typename ReqStruct, typename RespStruct> smrAsyncRespCb(smr_client_hndl client_handle,unsigned int msg_id,void * resp_c_struct,unsigned int resp_c_struct_len,void * resp_cb_data,smr_err transp_err)283 static void smrAsyncRespCb(smr_client_hndl client_handle, 284 unsigned int msg_id, void *resp_c_struct, 285 unsigned int resp_c_struct_len, 286 void *resp_cb_data, smr_err transp_err) { 287 auto callback = [](uint16_t /* type */, void *data) { 288 UniquePtr<AsyncCallbackData<ReqStruct, RespStruct>> cbData( 289 static_cast<AsyncCallbackData<ReqStruct, RespStruct> *>(data)); 290 cbData->callback(std::move(cbData->respCStruct), cbData->data, 291 cbData->transpErr); 292 }; 293 294 auto *cbData = 295 static_cast<AsyncCallbackData<ReqStruct, RespStruct> *>(resp_cb_data); 296 cbData->transpErr = transp_err; 297 298 // Schedule a deferred callback to handle sensor status change on the 299 // main thread. 300 EventLoopManagerSingleton::get()->deferCallback( 301 SystemCallbackType::SensorStatusInfoResponse, resp_cb_data, callback); 302 } 303 304 /** 305 * Extracts "this" from resp_cb_data and calls through to handleResp() 306 * 307 * @see smr_client_resp_cb 308 */ 309 static void smrSyncRespCb(smr_client_hndl client_handle, 310 unsigned int msg_id, void *resp_c_struct, 311 unsigned int resp_c_struct_len, 312 void *resp_cb_data, smr_err transp_err); 313 314 /** 315 * SMR wait for service callback used with waitForService() 316 * 317 * @see smr_client_init_ext_cb 318 */ 319 static void smrWaitForServiceCb(qmi_idl_service_object_type service_obj, 320 qmi_service_instance instance_id, 321 bool timeout_expired, 322 void *wait_for_service_cb_data); 323 324 //! Used to synchronize responses 325 ConditionVariable mCond; 326 327 //! Used with mCond, and to protect access to member variables from other 328 //! threads 329 Mutex mMutex; 330 331 //! true if we are waiting on an async response 332 bool mWaiting = false; 333 334 //! The transaction ID we're expecting in the next response callback 335 uint32_t mCurrentTransactionId = 0; 336 337 //! true if timed out while waiting for a service to become available 338 bool mServiceTimedOut = false; 339 340 //! The (transport) error code given in the response callback 341 smr_err mTranspErr; 342 }; 343 344 } // namespace chre 345 346 #endif // CHRE_PLATFORM_SLPI_SMGR_SMR_HELPER_H_ 347