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