xref: /aosp_15_r20/system/chre/chpp/clients/wwan.c (revision 84e339476a462649f82315436d70fd732297a399)
1 /*
2  * Copyright (C) 2020 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 "chpp/clients/wwan.h"
18 
19 #include <inttypes.h>
20 #include <stdbool.h>
21 #include <stddef.h>
22 #include <stdint.h>
23 #include <string.h>
24 
25 #include "chpp/app.h"
26 #include "chpp/clients/discovery.h"
27 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
28 #include "chpp/clients/timesync.h"
29 #endif
30 #include "chpp/common/standard_uuids.h"
31 #include "chpp/common/wwan.h"
32 #include "chpp/common/wwan_types.h"
33 #include "chpp/log.h"
34 #include "chpp/macros.h"
35 #include "chpp/memory.h"
36 #include "chre/pal/wwan.h"
37 
38 #ifndef CHPP_WWAN_DISCOVERY_TIMEOUT_MS
39 #define CHPP_WWAN_DISCOVERY_TIMEOUT_MS CHPP_DISCOVERY_DEFAULT_TIMEOUT_MS
40 #endif
41 
42 #ifndef CHPP_WWAN_MAX_TIMESYNC_AGE_NS
43 #define CHPP_WWAN_MAX_TIMESYNC_AGE_NS CHPP_TIMESYNC_DEFAULT_MAX_AGE_NS
44 #endif
45 
46 /************************************************
47  *  Prototypes
48  ***********************************************/
49 
50 static enum ChppAppErrorCode chppDispatchWwanResponse(void *clientContext,
51                                                       uint8_t *buf, size_t len);
52 static bool chppWwanClientInit(void *clientContext, uint8_t handle,
53                                struct ChppVersion serviceVersion);
54 static void chppWwanClientDeinit(void *clientContext);
55 static void chppWwanClientNotifyReset(void *clientContext);
56 static void chppWwanClientNotifyMatch(void *clientContext);
57 
58 /************************************************
59  *  Private Definitions
60  ***********************************************/
61 
62 /**
63  * Structure to maintain state for the WWAN client and its Request/Response
64  * (RR) functionality.
65  */
66 struct ChppWwanClientState {
67   struct ChppEndpointState client;   // CHPP client state
68   const struct chrePalWwanApi *api;  // WWAN PAL API
69 
70   struct ChppOutgoingRequestState
71       outReqStates[CHPP_WWAN_CLIENT_REQUEST_MAX + 1];
72 
73   uint32_t capabilities;   // Cached GetCapabilities result
74   bool capabilitiesValid;  // Flag to indicate if the capabilities result
75                            // is valid
76 };
77 
78 // Note: This global definition of gWwanClientContext supports only one
79 // instance of the CHPP WWAN client at a time.
80 struct ChppWwanClientState gWwanClientContext;
81 static const struct chrePalSystemApi *gSystemApi;
82 static const struct chrePalWwanCallbacks *gCallbacks;
83 
84 /**
85  * Configuration parameters for this client
86  */
87 static const struct ChppClient kWwanClientConfig = {
88     .descriptor.uuid = CHPP_UUID_WWAN_STANDARD,
89 
90     // Version
91     .descriptor.version.major = 1,
92     .descriptor.version.minor = 0,
93     .descriptor.version.patch = 0,
94 
95     // Notifies client if CHPP is reset
96     .resetNotifierFunctionPtr = &chppWwanClientNotifyReset,
97 
98     // Notifies client if they are matched to a service
99     .matchNotifierFunctionPtr = &chppWwanClientNotifyMatch,
100 
101     // Service response dispatch function pointer
102     .responseDispatchFunctionPtr = &chppDispatchWwanResponse,
103 
104     // Service notification dispatch function pointer
105     .notificationDispatchFunctionPtr = NULL,  // Not supported
106 
107     // Service response dispatch function pointer
108     .initFunctionPtr = &chppWwanClientInit,
109 
110     // Service notification dispatch function pointer
111     .deinitFunctionPtr = &chppWwanClientDeinit,
112 
113     // Number of request-response states in the outReqStates array.
114     .outReqCount = ARRAY_SIZE(gWwanClientContext.outReqStates),
115 
116     // Min length is the entire header
117     .minLength = sizeof(struct ChppAppHeader),
118 };
119 
120 /************************************************
121  *  Prototypes
122  ***********************************************/
123 
124 static bool chppWwanClientOpen(const struct chrePalSystemApi *systemApi,
125                                const struct chrePalWwanCallbacks *callbacks);
126 static void chppWwanClientClose(void);
127 static uint32_t chppWwanClientGetCapabilities(void);
128 static bool chppWwanClientGetCellInfoAsync(void);
129 static void chppWwanClientReleaseCellInfoResult(
130     struct chreWwanCellInfoResult *result);
131 
132 static void chppWwanCloseResult(struct ChppWwanClientState *clientContext,
133                                 uint8_t *buf, size_t len);
134 static void chppWwanGetCapabilitiesResult(
135     struct ChppWwanClientState *clientContext, uint8_t *buf, size_t len);
136 static void chppWwanGetCellInfoAsyncResult(
137     struct ChppWwanClientState *clientContext, uint8_t *buf, size_t len);
138 
139 /************************************************
140  *  Private Functions
141  ***********************************************/
142 
143 /**
144  * Dispatches a service response from the transport layer that is determined to
145  * be for the WWAN client.
146  *
147  * This function is called from the app layer using its function pointer given
148  * during client registration.
149  *
150  * @param clientContext Maintains status for each client instance.
151  * @param buf Input data. Cannot be null.
152  * @param len Length of input data in bytes.
153  *
154  * @return Indicates the result of this function call.
155  */
chppDispatchWwanResponse(void * clientContext,uint8_t * buf,size_t len)156 static enum ChppAppErrorCode chppDispatchWwanResponse(void *clientContext,
157                                                       uint8_t *buf,
158                                                       size_t len) {
159   struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
160   struct ChppWwanClientState *wwanClientContext =
161       (struct ChppWwanClientState *)clientContext;
162   enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
163 
164   if (rxHeader->command > CHPP_WWAN_CLIENT_REQUEST_MAX) {
165     error = CHPP_APP_ERROR_INVALID_COMMAND;
166 
167   } else if (!chppTimestampIncomingResponse(
168                  wwanClientContext->client.appContext,
169                  &wwanClientContext->outReqStates[rxHeader->command],
170                  rxHeader)) {
171     error = CHPP_APP_ERROR_UNEXPECTED_RESPONSE;
172 
173   } else {
174     switch (rxHeader->command) {
175       case CHPP_WWAN_OPEN: {
176         chppClientProcessOpenResponse(&wwanClientContext->client, buf, len);
177         break;
178       }
179 
180       case CHPP_WWAN_CLOSE: {
181         chppWwanCloseResult(wwanClientContext, buf, len);
182         break;
183       }
184 
185       case CHPP_WWAN_GET_CAPABILITIES: {
186         chppWwanGetCapabilitiesResult(wwanClientContext, buf, len);
187         break;
188       }
189 
190       case CHPP_WWAN_GET_CELLINFO_ASYNC: {
191         chppWwanGetCellInfoAsyncResult(wwanClientContext, buf, len);
192         break;
193       }
194 
195       default: {
196         error = CHPP_APP_ERROR_INVALID_COMMAND;
197         break;
198       }
199     }
200   }
201 
202   return error;
203 }
204 
205 /**
206  * Initializes the client and provides its handle number and the version of the
207  * matched service when/if it the client is matched with a service during
208  * discovery.
209  *
210  * @param clientContext Maintains status for each client instance.
211  * @param handle Handle number for this client.
212  * @param serviceVersion Version of the matched service.
213  *
214  * @return True if client is compatible and successfully initialized.
215  */
chppWwanClientInit(void * clientContext,uint8_t handle,struct ChppVersion serviceVersion)216 static bool chppWwanClientInit(void *clientContext, uint8_t handle,
217                                struct ChppVersion serviceVersion) {
218   UNUSED_VAR(serviceVersion);
219 
220   struct ChppWwanClientState *wwanClientContext =
221       (struct ChppWwanClientState *)clientContext;
222   chppClientInit(&wwanClientContext->client, handle);
223 
224   return true;
225 }
226 
227 /**
228  * Deinitializes the client.
229  *
230  * @param clientContext Maintains status for each client instance.
231  */
chppWwanClientDeinit(void * clientContext)232 static void chppWwanClientDeinit(void *clientContext) {
233   struct ChppWwanClientState *wwanClientContext =
234       (struct ChppWwanClientState *)clientContext;
235   chppClientDeinit(&wwanClientContext->client);
236 }
237 
238 /**
239  * Notifies the client of an incoming reset.
240  *
241  * @param clientContext Maintains status for each client instance.
242  */
chppWwanClientNotifyReset(void * clientContext)243 static void chppWwanClientNotifyReset(void *clientContext) {
244   struct ChppWwanClientState *wwanClientContext =
245       (struct ChppWwanClientState *)clientContext;
246 
247   chppClientCloseOpenRequests(&wwanClientContext->client, &kWwanClientConfig,
248                               false /* clearOnly */);
249 
250   CHPP_LOGI("WWAN client reopening from state=%" PRIu8,
251             wwanClientContext->client.openState);
252   chppClientSendOpenRequest(&wwanClientContext->client,
253                             &wwanClientContext->outReqStates[CHPP_WWAN_OPEN],
254                             CHPP_WWAN_OPEN,
255                             /*blocking=*/false);
256 }
257 
258 /**
259  * Notifies the client of being matched to a service.
260  *
261  * @param clientContext Maintains status for each client instance.
262  */
chppWwanClientNotifyMatch(void * clientContext)263 static void chppWwanClientNotifyMatch(void *clientContext) {
264   struct ChppWwanClientState *wwanClientContext =
265       (struct ChppWwanClientState *)clientContext;
266 
267   if (wwanClientContext->client.pseudoOpen) {
268     CHPP_LOGD("Pseudo-open WWAN client opening");
269     chppClientSendOpenRequest(&wwanClientContext->client,
270                               &wwanClientContext->outReqStates[CHPP_WWAN_OPEN],
271                               CHPP_WWAN_OPEN,
272                               /*blocking=*/false);
273   }
274 }
275 
276 /**
277  * Handles the service response for the close client request.
278  *
279  * This function is called from chppDispatchWwanResponse().
280  *
281  * @param clientContext Maintains status for each client instance.
282  * @param buf Input data. Cannot be null.
283  * @param len Length of input data in bytes.
284  */
chppWwanCloseResult(struct ChppWwanClientState * clientContext,uint8_t * buf,size_t len)285 static void chppWwanCloseResult(struct ChppWwanClientState *clientContext,
286                                 uint8_t *buf, size_t len) {
287   // TODO
288   UNUSED_VAR(clientContext);
289   UNUSED_VAR(buf);
290   UNUSED_VAR(len);
291 }
292 
293 /**
294  * Handles the service response for the get capabilities client request.
295  *
296  * This function is called from chppDispatchWwanResponse().
297  *
298  * @param clientContext Maintains status for each client instance.
299  * @param buf Input data. Cannot be null.
300  * @param len Length of input data in bytes.
301  */
chppWwanGetCapabilitiesResult(struct ChppWwanClientState * clientContext,uint8_t * buf,size_t len)302 static void chppWwanGetCapabilitiesResult(
303     struct ChppWwanClientState *clientContext, uint8_t *buf, size_t len) {
304   if (len < sizeof(struct ChppWwanGetCapabilitiesResponse)) {
305     CHPP_LOGE("Bad WWAN capabilities len=%" PRIuSIZE, len);
306 
307   } else {
308     struct ChppWwanGetCapabilitiesParameters *result =
309         &((struct ChppWwanGetCapabilitiesResponse *)buf)->params;
310 
311     CHPP_LOGD("chppWwanGetCapabilitiesResult received capabilities=0x%" PRIx32,
312               result->capabilities);
313 
314     // TODO(b/229758746): Disable assertion on WWAN side until it's fixed
315     // CHPP_ASSERT((result->capabilities & CHPP_WWAN_DEFAULT_CAPABILITIES) ==
316     //             CHPP_WWAN_DEFAULT_CAPABILITIES);
317     if (result->capabilities != CHPP_WWAN_DEFAULT_CAPABILITIES) {
318       CHPP_LOGE("WWAN capabilities 0x%" PRIx32 " != 0x%" PRIx32,
319                 result->capabilities, CHPP_WWAN_DEFAULT_CAPABILITIES);
320     }
321 
322     clientContext->capabilitiesValid = true;
323     clientContext->capabilities = result->capabilities;
324   }
325 }
326 
327 /**
328  * Handles the service response for the asynchronous get cell info client
329  * request.
330  *
331  * This function is called from chppDispatchWwanResponse().
332  *
333  * @param clientContext Maintains status for each client instance.
334  * @param buf Input data. Cannot be null.
335  * @param len Length of input data in bytes.
336  */
chppWwanGetCellInfoAsyncResult(struct ChppWwanClientState * clientContext,uint8_t * buf,size_t len)337 static void chppWwanGetCellInfoAsyncResult(
338     struct ChppWwanClientState *clientContext, uint8_t *buf, size_t len) {
339   UNUSED_VAR(clientContext);
340   CHPP_LOGD("chppWwanGetCellInfoAsyncResult received data len=%" PRIuSIZE, len);
341 
342   struct chreWwanCellInfoResult *chre = NULL;
343   uint8_t errorCode = CHRE_ERROR;
344 
345   if (len == sizeof(struct ChppAppHeader)) {
346     errorCode = chppAppShortResponseErrorHandler(buf, len, "GetCellInfo");
347 
348   } else {
349     buf += sizeof(struct ChppAppHeader);
350     len -= sizeof(struct ChppAppHeader);
351     chre =
352         chppWwanCellInfoResultToChre((struct ChppWwanCellInfoResult *)buf, len);
353 
354     if (chre == NULL) {
355       CHPP_LOGE("Cell info conversion failed len=%" PRIuSIZE, len);
356     }
357   }
358 
359   if (chre == NULL) {
360     chre = chppMalloc(sizeof(struct chreWwanCellInfoResult));
361     if (chre == NULL) {
362       CHPP_LOG_OOM();
363     } else {
364       chre->version = CHRE_WWAN_CELL_INFO_RESULT_VERSION;
365       chre->errorCode = errorCode;
366       chre->cellInfoCount = 0;
367       chre->reserved = 0;
368       chre->cookie = 0;
369       chre->cells = NULL;
370     }
371 
372   } else {
373 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
374     int64_t offset = chppTimesyncGetOffset(gWwanClientContext.client.appContext,
375                                            CHPP_WWAN_MAX_TIMESYNC_AGE_NS);
376     for (uint8_t i = 0; i < chre->cellInfoCount; i++) {
377       uint64_t *timeStamp =
378           (uint64_t *)(CHPP_CONST_CAST_POINTER(&chre->cells[i].timeStamp));
379       *timeStamp -= (uint64_t)offset;
380     }
381 #endif
382   }
383 
384   if (chre != NULL) {
385     gCallbacks->cellInfoResultCallback(chre);
386   }
387 }
388 
389 /**
390  * Initializes the WWAN client upon an open request from CHRE and responds with
391  * the result.
392  *
393  * @param systemApi CHRE system function pointers.
394  * @param callbacks CHRE entry points.
395  *
396  * @return True if successful. False otherwise.
397  */
chppWwanClientOpen(const struct chrePalSystemApi * systemApi,const struct chrePalWwanCallbacks * callbacks)398 static bool chppWwanClientOpen(const struct chrePalSystemApi *systemApi,
399                                const struct chrePalWwanCallbacks *callbacks) {
400   CHPP_DEBUG_NOT_NULL(systemApi);
401   CHPP_DEBUG_NOT_NULL(callbacks);
402 
403   bool result = false;
404   gSystemApi = systemApi;
405   gCallbacks = callbacks;
406 
407   CHPP_LOGD("WWAN client opening");
408   if (gWwanClientContext.client.appContext == NULL) {
409     CHPP_LOGE("WWAN client app is null");
410   } else {
411     // Wait for discovery to complete for "open" call to succeed
412     if (chppWaitForDiscoveryComplete(gWwanClientContext.client.appContext,
413                                      CHPP_WWAN_DISCOVERY_TIMEOUT_MS)) {
414       result = chppClientSendOpenRequest(
415           &gWwanClientContext.client,
416           &gWwanClientContext.outReqStates[CHPP_WWAN_OPEN], CHPP_WWAN_OPEN,
417           /*blocking=*/true);
418     }
419 
420     // Since CHPP_WWAN_DEFAULT_CAPABILITIES is mandatory, we can always
421     // pseudo-open and return true. Otherwise, these should have been gated.
422     chppClientPseudoOpen(&gWwanClientContext.client);
423     result = true;
424   }
425 
426   return result;
427 }
428 
429 /**
430  * Deinitializes the WWAN client.
431  */
chppWwanClientClose(void)432 static void chppWwanClientClose(void) {
433   // Remote
434   struct ChppAppHeader *request = chppAllocClientRequestCommand(
435       &gWwanClientContext.client, CHPP_WWAN_CLOSE);
436 
437   if (request == NULL) {
438     CHPP_LOG_OOM();
439   } else if (chppClientSendTimestampedRequestAndWait(
440                  &gWwanClientContext.client,
441                  &gWwanClientContext.outReqStates[CHPP_WWAN_CLOSE], request,
442                  sizeof(*request))) {
443     gWwanClientContext.client.openState = CHPP_OPEN_STATE_CLOSED;
444     gWwanClientContext.capabilities = CHRE_WWAN_CAPABILITIES_NONE;
445     gWwanClientContext.capabilitiesValid = false;
446     chppClientCloseOpenRequests(&gWwanClientContext.client, &kWwanClientConfig,
447                                 true /* clearOnly */);
448   }
449 }
450 
451 /**
452  * Retrieves a set of flags indicating the WWAN features supported by the
453  * current implementation.
454  *
455  * @return Capabilities flags.
456  */
chppWwanClientGetCapabilities(void)457 static uint32_t chppWwanClientGetCapabilities(void) {
458   uint32_t capabilities = CHPP_WWAN_DEFAULT_CAPABILITIES;
459 
460   if (gWwanClientContext.capabilitiesValid) {
461     // Result already cached
462     capabilities = gWwanClientContext.capabilities;
463 
464   } else {
465     struct ChppAppHeader *request = chppAllocClientRequestCommand(
466         &gWwanClientContext.client, CHPP_WWAN_GET_CAPABILITIES);
467 
468     if (request == NULL) {
469       CHPP_LOG_OOM();
470     } else {
471       if (chppClientSendTimestampedRequestAndWait(
472               &gWwanClientContext.client,
473               &gWwanClientContext.outReqStates[CHPP_WWAN_GET_CAPABILITIES],
474               request, sizeof(*request))) {
475         // Success. gWwanClientContext.capabilities is now populated
476         if (gWwanClientContext.capabilitiesValid) {
477           capabilities = gWwanClientContext.capabilities;
478         }
479       }
480     }
481   }
482 
483   return capabilities;
484 }
485 
486 /**
487  * Query information about the current serving cell and its neighbors. This does
488  * not perform a network scan, but should return state from the current network
489  * registration data stored in the cellular modem.
490  *
491  * @return True indicates the request was sent off to the service.
492  */
chppWwanClientGetCellInfoAsync(void)493 static bool chppWwanClientGetCellInfoAsync(void) {
494   bool result = false;
495 
496   struct ChppAppHeader *request = chppAllocClientRequestCommand(
497       &gWwanClientContext.client, CHPP_WWAN_GET_CELLINFO_ASYNC);
498 
499   if (request == NULL) {
500     CHPP_LOG_OOM();
501   } else {
502     result = chppClientSendTimestampedRequestOrFail(
503         &gWwanClientContext.client,
504         &gWwanClientContext.outReqStates[CHPP_WWAN_GET_CELLINFO_ASYNC], request,
505         sizeof(*request), CHPP_REQUEST_TIMEOUT_DEFAULT);
506   }
507 
508   return result;
509 }
510 
511 /**
512  * Releases the memory held for the GetCellInfoAsync result.
513  */
chppWwanClientReleaseCellInfoResult(struct chreWwanCellInfoResult * result)514 static void chppWwanClientReleaseCellInfoResult(
515     struct chreWwanCellInfoResult *result) {
516   if (result->cellInfoCount > 0) {
517     void *cells = CHPP_CONST_CAST_POINTER(result->cells);
518     CHPP_FREE_AND_NULLIFY(cells);
519   }
520 
521   CHPP_FREE_AND_NULLIFY(result);
522 }
523 
524 /************************************************
525  *  Public Functions
526  ***********************************************/
527 
chppRegisterWwanClient(struct ChppAppState * appContext)528 void chppRegisterWwanClient(struct ChppAppState *appContext) {
529   memset(&gWwanClientContext, 0, sizeof(gWwanClientContext));
530   chppRegisterClient(appContext, (void *)&gWwanClientContext,
531                      &gWwanClientContext.client,
532                      gWwanClientContext.outReqStates, &kWwanClientConfig);
533 }
534 
chppDeregisterWwanClient(struct ChppAppState * appContext)535 void chppDeregisterWwanClient(struct ChppAppState *appContext) {
536   // TODO
537 
538   UNUSED_VAR(appContext);
539 }
540 
getChppWwanClientState(void)541 struct ChppEndpointState *getChppWwanClientState(void) {
542   return &gWwanClientContext.client;
543 }
544 
545 #ifdef CHPP_CLIENT_ENABLED_WWAN
546 
547 #ifdef CHPP_CLIENT_ENABLED_CHRE_WWAN
chrePalWwanGetApi(uint32_t requestedApiVersion)548 const struct chrePalWwanApi *chrePalWwanGetApi(uint32_t requestedApiVersion) {
549 #else
550 const struct chrePalWwanApi *chppPalWwanGetApi(uint32_t requestedApiVersion) {
551 #endif
552 
553   static const struct chrePalWwanApi api = {
554       .moduleVersion = CHPP_PAL_WWAN_API_VERSION,
555       .open = chppWwanClientOpen,
556       .close = chppWwanClientClose,
557       .getCapabilities = chppWwanClientGetCapabilities,
558       .requestCellInfo = chppWwanClientGetCellInfoAsync,
559       .releaseCellInfoResult = chppWwanClientReleaseCellInfoResult,
560   };
561 
562   CHPP_STATIC_ASSERT(
563       CHRE_PAL_WWAN_API_CURRENT_VERSION == CHPP_PAL_WWAN_API_VERSION,
564       "A newer CHRE PAL API version is available. Please update.");
565 
566   if (!CHRE_PAL_VERSIONS_ARE_COMPATIBLE(api.moduleVersion,
567                                         requestedApiVersion)) {
568     return NULL;
569   } else {
570     return &api;
571   }
572 }
573 
574 #endif
575