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 #include "chre/platform/slpi/smgr/smr_helper.h"
18
19 #include <inttypes.h>
20
21 #include "chre/platform/assert.h"
22 #include "chre/platform/log.h"
23 #include "chre/platform/slpi/power_control_util.h"
24 #include "chre/util/lock_guard.h"
25 #include "chre/util/memory.h"
26
27 namespace chre {
28
releaseSync(smr_client_hndl clientHandle,Nanoseconds timeout)29 smr_err SmrHelper::releaseSync(smr_client_hndl clientHandle,
30 Nanoseconds timeout) {
31 // smr_client_release is synchronous for SMR services in the current
32 // implementation, so we can't hold the lock while calling it otherwise we'll
33 // deadlock in the callback.
34 prepareForWait();
35
36 smr_err result = smr_client_release(
37 clientHandle, SmrHelper::smrReleaseCb, this);
38 if (result != SMR_NO_ERR) {
39 LOGE("SMR client release failed: %d", result);
40
41 // Don't need to re-acquire the mutex for this, as we are assured that the
42 // callback won't be invoked in the error case
43 mWaiting = false;
44 } else {
45 LockGuard<Mutex> lock(mMutex);
46 bool waitSuccess = true;
47 while (mWaiting && waitSuccess) {
48 waitSuccess = mCond.wait_for(mMutex, timeout);
49 }
50
51 if (!waitSuccess) {
52 LOGE("Releasing SMR client timed out");
53 result = SMR_TIMEOUT_ERR;
54 mWaiting = false;
55 }
56 }
57
58 return result;
59 }
60
waitForService(qmi_idl_service_object_type serviceObj,Microseconds timeout)61 smr_err SmrHelper::waitForService(qmi_idl_service_object_type serviceObj,
62 Microseconds timeout) {
63 // smr_client_check_ext is synchronous if the service already exists,
64 // so don't hold the lock while calling to prevent deadlock in the callback.
65 prepareForWait();
66
67 smr_err result = smr_client_check_ext(serviceObj, SMR_CLIENT_INSTANCE_ANY,
68 timeout.getMicroseconds(),
69 SmrHelper::smrWaitForServiceCb, this);
70 if (result != SMR_NO_ERR) {
71 LOGE("Failed to wait for service: %d", result);
72
73 // Don't need to re-acquire the mutex for this, as we are assured that the
74 // callback won't be invoked in the error case
75 mWaiting = false;
76 } else {
77 LockGuard<Mutex> lock(mMutex);
78 while (mWaiting) {
79 mCond.wait(mMutex);
80 }
81
82 if (mServiceTimedOut) {
83 LOGE("Wait for SMR service timed out");
84 result = SMR_TIMEOUT_ERR;
85 mServiceTimedOut = false;
86 }
87 }
88
89 return result;
90 }
91
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)92 smr_err SmrHelper::sendReqAsyncUntyped(
93 smr_client_hndl client_handle, unsigned int msg_id,
94 void *req_c_struct, unsigned int req_c_struct_len,
95 void *resp_c_struct, unsigned int resp_c_struct_len,
96 void *resp_cb_data, smr_client_resp_cb resp_cb) {
97 // Force big image since smr_client_send_req is not supported in micro-image
98 slpiForceBigImage();
99
100 smr_err result = smr_client_send_req(client_handle, msg_id, req_c_struct,
101 req_c_struct_len, resp_c_struct,
102 resp_c_struct_len, resp_cb, resp_cb_data,
103 nullptr /* txn_handle */);
104
105 if (result != SMR_NO_ERR) {
106 LOGE("Failed to send request (msg_id 0x%02x): %d", msg_id, result);
107 }
108
109 return result;
110 }
111
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)112 bool SmrHelper::sendReqSyncUntyped(
113 smr_client_hndl client_handle, unsigned int msg_id,
114 void *req_c_struct, unsigned int req_c_struct_len,
115 void *resp_c_struct, unsigned int resp_c_struct_len,
116 Nanoseconds timeout, smr_err *result) {
117 // Set to false only in the event of timeout
118 bool waitSuccess = true;
119
120 // Force big image since smr_client_send_req is not supported in micro-image
121 slpiForceBigImage();
122
123 UniquePtr<SmrTransaction> txn = MakeUnique<SmrTransaction>();
124 if (txn.isNull()) {
125 FATAL_ERROR_OOM();
126 }
127 txn->parent = this;
128 txn->reqBuf = req_c_struct;
129 txn->rspBuf = resp_c_struct;
130 txn->transactionId = mCurrentTransactionId;
131
132 // Avoid holding the mutex while calling smr_client_send_req() - deadlock
133 // could arise if we did, due to acquisition order for SMR's client_mutex in
134 // the response flow
135 prepareForWait();
136
137 // Note that null txn_handle means we can't abandon the transaction, but it's
138 // only supported for QMI (non-SMR) services, and we don't expect that anyway.
139 // SMR itself does not support canceling transactions made to SMR services.
140 *result = smr_client_send_req(
141 client_handle, msg_id, req_c_struct, req_c_struct_len, resp_c_struct,
142 resp_c_struct_len, SmrHelper::smrSyncRespCb, txn.get(),
143 nullptr /* txn_handle */);
144 if (*result != SMR_NO_ERR) {
145 LOGE("Failed to send request (msg_id 0x%02x): %d", msg_id, *result);
146
147 // Don't need to re-acquire the mutex for this, as we are assured that the
148 // callback won't be invoked in the error case
149 mWaiting = false;
150 } else {
151 LockGuard<Mutex> lock(mMutex);
152 while (mWaiting && waitSuccess) {
153 waitSuccess = mCond.wait_for(mMutex, timeout);
154 }
155
156 if (waitSuccess) {
157 *result = mTranspErr;
158 } else {
159 LOGE("SMR request for msg_id 0x%02x timed out after %" PRIu64 " ms",
160 msg_id, Milliseconds(timeout).getMilliseconds());
161 *result = SMR_TIMEOUT_ERR;
162
163 // If the callback comes later, we'll recognize the mismatched transaction
164 // ID and drop the response. Note that we don't increment this in the
165 // successful case, as it's not necessary in that case, and this gives us
166 // a way to track the number of timeouts we've hit.
167 mCurrentTransactionId++;
168 mWaiting = false;
169 txn.release();
170 }
171 }
172
173 return waitSuccess;
174 }
175
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)176 void SmrHelper::handleResp(smr_client_hndl client_handle, unsigned int msg_id,
177 void *resp_c_struct, unsigned int resp_c_struct_len,
178 smr_err transp_err, SmrTransaction *txn) {
179 LockGuard<Mutex> lock(mMutex);
180
181 if (!mWaiting || txn->transactionId != mCurrentTransactionId) {
182 LOGE("Got expired SMR response (my ID %" PRIu32 " vs current %" PRIu32 ")",
183 txn->transactionId, mCurrentTransactionId);
184
185 // If this happens, it means the requestor timed out, so it's depending on
186 // us to release the memory
187 memoryFree(txn->reqBuf);
188 memoryFree(txn->rspBuf);
189 memoryFree(txn);
190 } else {
191 // SMR will handle copying the response into the buffer passed in to
192 // smr_client_send_req(), so we just need to unblock the waiting thread
193 mTranspErr = transp_err;
194 mWaiting = false;
195 mCond.notify_one();
196 }
197 }
198
prepareForWait()199 void SmrHelper::prepareForWait() {
200 LockGuard<Mutex> lock(mMutex);
201 CHRE_ASSERT(!mWaiting);
202 mWaiting = true;
203 }
204
smrReleaseCb(void * release_cb_data)205 void SmrHelper::smrReleaseCb(void *release_cb_data) {
206 SmrHelper *obj = static_cast<SmrHelper *>(release_cb_data);
207 LockGuard<Mutex> lock(obj->mMutex);
208 obj->mWaiting = false;
209 obj->mCond.notify_one();
210 }
211
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)212 void SmrHelper::smrSyncRespCb(
213 smr_client_hndl client_handle, unsigned int msg_id, void *resp_c_struct,
214 unsigned int resp_c_struct_len, void *resp_cb_data, smr_err transp_err) {
215 auto *txn = static_cast<SmrTransaction *>(resp_cb_data);
216 txn->parent->handleResp(
217 client_handle, msg_id, resp_c_struct, resp_c_struct_len, transp_err, txn);
218 }
219
smrWaitForServiceCb(qmi_idl_service_object_type,qmi_service_instance,bool timeout_expired,void * wait_for_service_cb_data)220 void SmrHelper::smrWaitForServiceCb(
221 qmi_idl_service_object_type /* service_obj */,
222 qmi_service_instance /* instance_id */, bool timeout_expired,
223 void *wait_for_service_cb_data) {
224 SmrHelper *obj = static_cast<SmrHelper *>(wait_for_service_cb_data);
225 LockGuard<Mutex> lock(obj->mMutex);
226 obj->mServiceTimedOut = timeout_expired;
227 obj->mWaiting = false;
228 obj->mCond.notify_one();
229 }
230
231 } // namespace chre
232