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