/* * Copyright (C) 2017 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. */ #include "chre/platform/slpi/smgr/smr_helper.h" #include #include "chre/platform/assert.h" #include "chre/platform/log.h" #include "chre/platform/slpi/power_control_util.h" #include "chre/util/lock_guard.h" #include "chre/util/memory.h" namespace chre { smr_err SmrHelper::releaseSync(smr_client_hndl clientHandle, Nanoseconds timeout) { // smr_client_release is synchronous for SMR services in the current // implementation, so we can't hold the lock while calling it otherwise we'll // deadlock in the callback. prepareForWait(); smr_err result = smr_client_release( clientHandle, SmrHelper::smrReleaseCb, this); if (result != SMR_NO_ERR) { LOGE("SMR client release failed: %d", result); // Don't need to re-acquire the mutex for this, as we are assured that the // callback won't be invoked in the error case mWaiting = false; } else { LockGuard lock(mMutex); bool waitSuccess = true; while (mWaiting && waitSuccess) { waitSuccess = mCond.wait_for(mMutex, timeout); } if (!waitSuccess) { LOGE("Releasing SMR client timed out"); result = SMR_TIMEOUT_ERR; mWaiting = false; } } return result; } smr_err SmrHelper::waitForService(qmi_idl_service_object_type serviceObj, Microseconds timeout) { // smr_client_check_ext is synchronous if the service already exists, // so don't hold the lock while calling to prevent deadlock in the callback. prepareForWait(); smr_err result = smr_client_check_ext(serviceObj, SMR_CLIENT_INSTANCE_ANY, timeout.getMicroseconds(), SmrHelper::smrWaitForServiceCb, this); if (result != SMR_NO_ERR) { LOGE("Failed to wait for service: %d", result); // Don't need to re-acquire the mutex for this, as we are assured that the // callback won't be invoked in the error case mWaiting = false; } else { LockGuard lock(mMutex); while (mWaiting) { mCond.wait(mMutex); } if (mServiceTimedOut) { LOGE("Wait for SMR service timed out"); result = SMR_TIMEOUT_ERR; mServiceTimedOut = false; } } return result; } smr_err SmrHelper::sendReqAsyncUntyped( smr_client_hndl client_handle, unsigned int msg_id, void *req_c_struct, unsigned int req_c_struct_len, void *resp_c_struct, unsigned int resp_c_struct_len, void *resp_cb_data, smr_client_resp_cb resp_cb) { // Force big image since smr_client_send_req is not supported in micro-image slpiForceBigImage(); smr_err result = smr_client_send_req(client_handle, msg_id, req_c_struct, req_c_struct_len, resp_c_struct, resp_c_struct_len, resp_cb, resp_cb_data, nullptr /* txn_handle */); if (result != SMR_NO_ERR) { LOGE("Failed to send request (msg_id 0x%02x): %d", msg_id, result); } return result; } bool SmrHelper::sendReqSyncUntyped( smr_client_hndl client_handle, unsigned int msg_id, void *req_c_struct, unsigned int req_c_struct_len, void *resp_c_struct, unsigned int resp_c_struct_len, Nanoseconds timeout, smr_err *result) { // Set to false only in the event of timeout bool waitSuccess = true; // Force big image since smr_client_send_req is not supported in micro-image slpiForceBigImage(); UniquePtr txn = MakeUnique(); if (txn.isNull()) { FATAL_ERROR_OOM(); } txn->parent = this; txn->reqBuf = req_c_struct; txn->rspBuf = resp_c_struct; txn->transactionId = mCurrentTransactionId; // Avoid holding the mutex while calling smr_client_send_req() - deadlock // could arise if we did, due to acquisition order for SMR's client_mutex in // the response flow prepareForWait(); // Note that null txn_handle means we can't abandon the transaction, but it's // only supported for QMI (non-SMR) services, and we don't expect that anyway. // SMR itself does not support canceling transactions made to SMR services. *result = smr_client_send_req( client_handle, msg_id, req_c_struct, req_c_struct_len, resp_c_struct, resp_c_struct_len, SmrHelper::smrSyncRespCb, txn.get(), nullptr /* txn_handle */); if (*result != SMR_NO_ERR) { LOGE("Failed to send request (msg_id 0x%02x): %d", msg_id, *result); // Don't need to re-acquire the mutex for this, as we are assured that the // callback won't be invoked in the error case mWaiting = false; } else { LockGuard lock(mMutex); while (mWaiting && waitSuccess) { waitSuccess = mCond.wait_for(mMutex, timeout); } if (waitSuccess) { *result = mTranspErr; } else { LOGE("SMR request for msg_id 0x%02x timed out after %" PRIu64 " ms", msg_id, Milliseconds(timeout).getMilliseconds()); *result = SMR_TIMEOUT_ERR; // If the callback comes later, we'll recognize the mismatched transaction // ID and drop the response. Note that we don't increment this in the // successful case, as it's not necessary in that case, and this gives us // a way to track the number of timeouts we've hit. mCurrentTransactionId++; mWaiting = false; txn.release(); } } return waitSuccess; } void SmrHelper::handleResp(smr_client_hndl client_handle, unsigned int msg_id, void *resp_c_struct, unsigned int resp_c_struct_len, smr_err transp_err, SmrTransaction *txn) { LockGuard lock(mMutex); if (!mWaiting || txn->transactionId != mCurrentTransactionId) { LOGE("Got expired SMR response (my ID %" PRIu32 " vs current %" PRIu32 ")", txn->transactionId, mCurrentTransactionId); // If this happens, it means the requestor timed out, so it's depending on // us to release the memory memoryFree(txn->reqBuf); memoryFree(txn->rspBuf); memoryFree(txn); } else { // SMR will handle copying the response into the buffer passed in to // smr_client_send_req(), so we just need to unblock the waiting thread mTranspErr = transp_err; mWaiting = false; mCond.notify_one(); } } void SmrHelper::prepareForWait() { LockGuard lock(mMutex); CHRE_ASSERT(!mWaiting); mWaiting = true; } void SmrHelper::smrReleaseCb(void *release_cb_data) { SmrHelper *obj = static_cast(release_cb_data); LockGuard lock(obj->mMutex); obj->mWaiting = false; obj->mCond.notify_one(); } void SmrHelper::smrSyncRespCb( 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) { auto *txn = static_cast(resp_cb_data); txn->parent->handleResp( client_handle, msg_id, resp_c_struct, resp_c_struct_len, transp_err, txn); } void SmrHelper::smrWaitForServiceCb( qmi_idl_service_object_type /* service_obj */, qmi_service_instance /* instance_id */, bool timeout_expired, void *wait_for_service_cb_data) { SmrHelper *obj = static_cast(wait_for_service_cb_data); LockGuard lock(obj->mMutex); obj->mServiceTimedOut = timeout_expired; obj->mWaiting = false; obj->mCond.notify_one(); } } // namespace chre