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