Home | History | Annotate | Download | only in smgr
      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/util/non_copyable.h"
     32 #include "chre/util/time.h"
     33 #include "chre/util/unique_ptr.h"
     34 
     35 namespace chre {
     36 
     37 //! Default timeout for sendReqSync
     38 constexpr Nanoseconds kDefaultSmrTimeout = Seconds(2);
     39 
     40 //! Default timeout for waitForService. Have a longer timeout since there may be
     41 //! external dependencies blocking SMGR initialization.
     42 constexpr Nanoseconds kDefaultSmrWaitTimeout = Seconds(5);
     43 
     44 /**
     45  * A helper class for making synchronous requests to SMR (Sensors Message
     46  * Router). Not safe to use from multiple threads.
     47  */
     48 class SmrHelper : public NonCopyable {
     49  public:
     50   /**
     51    * Wrapper to convert the async smr_client_release() to a synchronous call.
     52    *
     53    * @param clientHandle SMR handle to release
     54    * @param timeout How long to wait for the response before abandoning it
     55    *
     56    * @return Result code returned by smr_client_release(), or SMR_TIMEOUT_ERR if
     57    *         the timeout was reached
     58    */
     59   smr_err releaseSync(smr_client_hndl clientHandle,
     60                       Nanoseconds timeout = kDefaultSmrTimeout);
     61 
     62   /**
     63    * Wrapper to convert the async smr_client_send_req() to a synchronous call.
     64    *
     65    * Only one request can be pending at a time per instance of SmrHelper.
     66    *
     67    * @param ReqStruct QMI IDL-generated request structure
     68    * @param RespStruct QMI IDL-generated response structure
     69    * @param clientHandle SMR handle previously given by smr_client_init()
     70    * @param msgId QMI message ID of the request to send
     71    * @param req Pointer to populated request structure
     72    * @param resp Pointer to structure to receive the response
     73    * @param timeout How long to wait for the response before abandoning it
     74    *
     75    * @return Result code returned by smr_client_send_req(), or SMR_TIMEOUT_ERR
     76    *         if the supplied timeout was reached
     77    */
     78   template<typename ReqStruct, typename RespStruct>
     79   smr_err sendReqSync(
     80       smr_client_hndl clientHandle, unsigned int msgId,
     81       UniquePtr<ReqStruct> *req, UniquePtr<RespStruct> *resp,
     82       Nanoseconds timeout = kDefaultSmrTimeout) {
     83     // Try to catch copy/paste errors at compile time - QMI always has a
     84     // different struct definition for request and response
     85     static_assert(!std::is_same<ReqStruct, RespStruct>::value,
     86                   "Request and response structures must be different");
     87 
     88     smr_err result;
     89     bool timedOut = !sendReqSyncUntyped(
     90         clientHandle, msgId, req->get(), sizeof(ReqStruct),
     91         resp->get(), sizeof(RespStruct), timeout, &result);
     92 
     93     // Unlike QMI, SMR does not support canceling an in-flight transaction.
     94     // SMR's internal request structure maintains a pointer to the client
     95     // request and response buffers, so in the event of a timeout, it is unsafe
     96     // for us to free the memory because the service may try to send the
     97     // response later on - we'll try to free it if that ever happens, but
     98     // otherwise we need to leave the memory allocation open.
     99     if (timedOut) {
    100       req->release();
    101       resp->release();
    102     }
    103 
    104     return result;
    105   }
    106 
    107   /**
    108    * Wrapper to convert the async smr_client_check_ext() to a synchronous call.
    109    * Waits for an SMR service to become available.
    110    *
    111    * @param serviceObj The SMR service object to wait for.
    112    * @param timeout The wait timeout in microseconds.
    113    *
    114    * @return Result code returned by smr_client_check_ext, or SMR_TIMEOUT_ERR if
    115    *         the timeout was reached
    116    */
    117   smr_err waitForService(qmi_idl_service_object_type serviceObj,
    118                          Microseconds timeout = kDefaultSmrWaitTimeout);
    119 
    120  private:
    121   /**
    122    * Used to track asynchronous SMR requests from sendReqSyncUntyped() to
    123    * smrRespCb()
    124    */
    125   struct SmrTransaction {
    126     //! Value of SmrHelper::mCurrentTransactionId when this instance was
    127     //! created - if it does not match at the time the transaction is given in
    128     //! the callback, this transaction is invalid (it has timed out)
    129     uint32_t transactionId;
    130 
    131     //! Pointer to the SmrHelper instance that created this transaction
    132     SmrHelper *parent;
    133 
    134     // SMR request and response buffers given by the client; only used to free
    135     // memory in the event of a late (post-timeout) callback
    136     void *reqBuf;
    137     void *rspBuf;
    138   };
    139 
    140   /**
    141    * Implements sendReqSync(), but with accepting untyped (void*) buffers.
    142    * snake_case parameters exactly match those given to smr_client_send_req().
    143    *
    144    * @param timeout How long to wait for the response before abandoning it
    145    * @param result If smr_client_send_req() returns an error, then that error
    146    *        code, otherwise the transport error code given in the SMR response
    147    *        callback (assuming there was no timeout)
    148    * @return false on timeout, otherwise true (includes the case where
    149    *         smr_client_send_req() returns an immediate error)
    150    *
    151    * @see sendReqSync()
    152    * @see smr_client_send_req()
    153    */
    154   bool sendReqSyncUntyped(
    155       smr_client_hndl client_handle, unsigned int msg_id,
    156       void *req_c_struct, unsigned int req_c_struct_len,
    157       void *resp_c_struct, unsigned int resp_c_struct_len,
    158       Nanoseconds timeout, smr_err *result);
    159 
    160   /**
    161    * Processes an SMR response callback
    162    *
    163    * @see smr_client_resp_cb
    164    */
    165   void handleResp(smr_client_hndl client_handle, unsigned int msg_id,
    166                   void *resp_c_struct, unsigned int resp_c_struct_len,
    167                   smr_err transp_err, SmrTransaction *txn);
    168 
    169   /**
    170    * Sets mWaiting to true in advance of calling an async SMR function.
    171    * Preconditions: mMutex not held, mWaiting false
    172    */
    173   void prepareForWait();
    174 
    175   /**
    176    * SMR release complete callback used with releaseSync()
    177    *
    178    * @see smr_client_release_cb
    179    */
    180   static void smrReleaseCb(void *release_cb_data);
    181 
    182   /**
    183    * Extracts "this" from resp_cb_data and calls through to handleResp()
    184    *
    185    * @see smr_client_resp_cb
    186    */
    187   static void smrRespCb(smr_client_hndl client_handle, unsigned int msg_id,
    188                         void *resp_c_struct, unsigned int resp_c_struct_len,
    189                         void *resp_cb_data, smr_err transp_err);
    190 
    191   /**
    192    * SMR wait for service callback used with waitForService()
    193    *
    194    * @see smr_client_init_ext_cb
    195    */
    196   static void smrWaitForServiceCb(qmi_idl_service_object_type service_obj,
    197                                   qmi_service_instance instance_id,
    198                                   bool timeout_expired,
    199                                   void *wait_for_service_cb_data);
    200 
    201   //! Used to synchronize responses
    202   ConditionVariable mCond;
    203 
    204   //! Used with mCond, and to protect access to member variables from other
    205   //! threads
    206   Mutex mMutex;
    207 
    208   //! true if we are waiting on an async response
    209   bool mWaiting = false;
    210 
    211   //! The transaction ID we're expecting in the next response callback
    212   uint32_t mCurrentTransactionId = 0;
    213 
    214   //! true if timed out while waiting for a service to become available
    215   bool mServiceTimedOut = false;
    216 
    217   //! The (transport) error code given in the response callback
    218   smr_err mTranspErr;
    219 };
    220 
    221 }  // namespace chre
    222 
    223 #endif  // CHRE_PLATFORM_SLPI_SMGR_SMR_HELPER_H_
    224