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/services/wwan.h"
18 
19 #include <inttypes.h>
20 #include <stdbool.h>
21 #include <stddef.h>
22 #include <stdint.h>
23 
24 #include "chpp/common/standard_uuids.h"
25 #include "chpp/common/wwan.h"
26 #include "chpp/common/wwan_types.h"
27 #include "chpp/log.h"
28 #include "chpp/macros.h"
29 #include "chpp/memory.h"
30 #include "chpp/services.h"
31 #include "chre/pal/wwan.h"
32 
33 /************************************************
34  *  Prototypes
35  ***********************************************/
36 
37 static enum ChppAppErrorCode chppDispatchWwanRequest(void *serviceContext,
38                                                      uint8_t *buf, size_t len);
39 static void chppWwanServiceNotifyReset(void *serviceContext);
40 
41 /************************************************
42  *  Private Definitions
43  ***********************************************/
44 
45 /**
46  * Configuration parameters for this service
47  */
48 static const struct ChppService kWwanServiceConfig = {
49     .descriptor.uuid = CHPP_UUID_WWAN_STANDARD,
50 
51     // Human-readable name
52     .descriptor.name = "WWAN",
53 
54     // Version
55     .descriptor.version.major = 1,
56     .descriptor.version.minor = 0,
57     .descriptor.version.patch = 0,
58 
59     // Notifies service if CHPP is reset
60     .resetNotifierFunctionPtr = &chppWwanServiceNotifyReset,
61 
62     // Client request dispatch function pointer
63     .requestDispatchFunctionPtr = &chppDispatchWwanRequest,
64 
65     // Client notification dispatch function pointer
66     .notificationDispatchFunctionPtr = NULL,  // Not supported
67 
68     // Min length is the entire header
69     .minLength = sizeof(struct ChppAppHeader),
70 };
71 
72 /**
73  * Structure to maintain state for the WWAN service and its Request/Response
74  * (RR) functionality.
75  */
76 struct ChppWwanServiceState {
77   struct ChppEndpointState service;  // CHPP service state
78   const struct chrePalWwanApi *api;  // WWAN PAL API
79 
80   struct ChppIncomingRequestState open;              // Service init state
81   struct ChppIncomingRequestState close;             // Service deinit state
82   struct ChppIncomingRequestState getCapabilities;   // Get Capabilities state
83   struct ChppIncomingRequestState getCellInfoAsync;  // Get CellInfo Async state
84 };
85 
86 // Note: This global definition of gWwanServiceContext supports only one
87 // instance of the CHPP WWAN service at a time. This limitation is primarily due
88 // to the PAL API.
89 // It would be possible to generate different API and callback pointers to
90 // support multiple instances of the service or modify the PAL API to pass a
91 // void* for context, but this is not necessary in the current version of CHPP.
92 // In such case, wwanServiceContext would be allocated dynamically as part of
93 // chppRegisterWwanService(), e.g.
94 //   struct ChppWwanServiceState *wwanServiceContext = chppMalloc(...);
95 // instead of globally here.
96 struct ChppWwanServiceState gWwanServiceContext;
97 
98 /************************************************
99  *  Prototypes
100  ***********************************************/
101 
102 static enum ChppAppErrorCode chppWwanServiceOpen(
103     struct ChppWwanServiceState *wwanServiceContext,
104     struct ChppAppHeader *requestHeader);
105 static enum ChppAppErrorCode chppWwanServiceClose(
106     struct ChppWwanServiceState *wwanServiceContext,
107     struct ChppAppHeader *requestHeader);
108 static enum ChppAppErrorCode chppWwanServiceGetCapabilities(
109     struct ChppWwanServiceState *wwanServiceContext,
110     struct ChppAppHeader *requestHeader);
111 static enum ChppAppErrorCode chppWwanServiceGetCellInfoAsync(
112     struct ChppWwanServiceState *wwanServiceContext,
113     struct ChppAppHeader *requestHeader);
114 
115 static void chppWwanServiceCellInfoResultCallback(
116     struct chreWwanCellInfoResult *result);
117 
118 /************************************************
119  *  Private Functions
120  ***********************************************/
121 
122 /**
123  * Dispatches a client request from the transport layer that is determined to be
124  * for the WWAN service. If the result of the dispatch is an error, this
125  * function responds to the client with the same error.
126  *
127  * This function is called from the app layer using its function pointer given
128  * during service registration.
129  *
130  * @param serviceContext Maintains status for each service instance.
131  * @param buf Input data. Cannot be null.
132  * @param len Length of input data in bytes.
133  *
134  * @return Indicates the result of this function call.
135  */
chppDispatchWwanRequest(void * serviceContext,uint8_t * buf,size_t len)136 static enum ChppAppErrorCode chppDispatchWwanRequest(void *serviceContext,
137                                                      uint8_t *buf, size_t len) {
138   struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
139   struct ChppWwanServiceState *wwanServiceContext =
140       (struct ChppWwanServiceState *)serviceContext;
141   struct ChppIncomingRequestState *inReqState = NULL;
142   enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
143   bool dispatched = true;
144 
145   UNUSED_VAR(len);
146 
147   switch (rxHeader->command) {
148     case CHPP_WWAN_OPEN: {
149       inReqState = &wwanServiceContext->open;
150       chppTimestampIncomingRequest(inReqState, rxHeader);
151       error = chppWwanServiceOpen(wwanServiceContext, rxHeader);
152       break;
153     }
154 
155     case CHPP_WWAN_CLOSE: {
156       inReqState = &wwanServiceContext->close;
157       chppTimestampIncomingRequest(inReqState, rxHeader);
158       error = chppWwanServiceClose(wwanServiceContext, rxHeader);
159       break;
160     }
161 
162     case CHPP_WWAN_GET_CAPABILITIES: {
163       inReqState = &wwanServiceContext->getCapabilities;
164       chppTimestampIncomingRequest(inReqState, rxHeader);
165       error = chppWwanServiceGetCapabilities(wwanServiceContext, rxHeader);
166       break;
167     }
168 
169     case CHPP_WWAN_GET_CELLINFO_ASYNC: {
170       inReqState = &wwanServiceContext->getCellInfoAsync;
171       chppTimestampIncomingRequest(inReqState, rxHeader);
172       error = chppWwanServiceGetCellInfoAsync(wwanServiceContext, rxHeader);
173       break;
174     }
175 
176     default: {
177       dispatched = false;
178       error = CHPP_APP_ERROR_INVALID_COMMAND;
179       break;
180     }
181   }
182 
183   if (dispatched == true && error != CHPP_APP_ERROR_NONE) {
184     // Request was dispatched but an error was returned. Close out
185     // chppTimestampIncomingRequest()
186     chppTimestampOutgoingResponse(inReqState);
187   }
188 
189   return error;
190 }
191 
192 /**
193  * Initializes the WWAN service upon an open request from the client and
194  * responds to the client with the result.
195  *
196  * @param serviceContext Maintains status for each service instance.
197  * @param requestHeader App layer header of the request.
198  *
199  * @return Indicates the result of this function call.
200  */
chppWwanServiceOpen(struct ChppWwanServiceState * wwanServiceContext,struct ChppAppHeader * requestHeader)201 static enum ChppAppErrorCode chppWwanServiceOpen(
202     struct ChppWwanServiceState *wwanServiceContext,
203     struct ChppAppHeader *requestHeader) {
204   static const struct chrePalWwanCallbacks palCallbacks = {
205       .cellInfoResultCallback = chppWwanServiceCellInfoResultCallback,
206   };
207 
208   enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
209 
210   if (wwanServiceContext->service.openState == CHPP_OPEN_STATE_OPENED) {
211     CHPP_DEBUG_ASSERT_LOG(false, "WWAN service already open");
212     error = CHPP_APP_ERROR_INVALID_COMMAND;
213 
214   } else if (!wwanServiceContext->api->open(
215                  wwanServiceContext->service.appContext->systemApi,
216                  &palCallbacks)) {
217     CHPP_DEBUG_ASSERT_LOG(false, "WWAN PAL open failed");
218     error = CHPP_APP_ERROR_BEYOND_CHPP;
219 
220   } else {
221     CHPP_LOGD("WWAN service opened");
222     wwanServiceContext->service.openState = CHPP_OPEN_STATE_OPENED;
223 
224     struct ChppAppHeader *response =
225         chppAllocResponseFixed(requestHeader, struct ChppAppHeader);
226     size_t responseLen = sizeof(*response);
227 
228     if (response == NULL) {
229       CHPP_LOG_OOM();
230       error = CHPP_APP_ERROR_OOM;
231     } else {
232       chppSendTimestampedResponseOrFail(wwanServiceContext->service.appContext,
233                                         &wwanServiceContext->open, response,
234                                         responseLen);
235     }
236   }
237 
238   return error;
239 }
240 
241 /**
242  * Deinitializes the WWAN service.
243  *
244  * @param serviceContext Maintains status for each service instance.
245  * @param requestHeader App layer header of the request.
246  *
247  * @return Indicates the result of this function call.
248  */
chppWwanServiceClose(struct ChppWwanServiceState * wwanServiceContext,struct ChppAppHeader * requestHeader)249 static enum ChppAppErrorCode chppWwanServiceClose(
250     struct ChppWwanServiceState *wwanServiceContext,
251     struct ChppAppHeader *requestHeader) {
252   enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
253 
254   wwanServiceContext->api->close();
255   wwanServiceContext->service.openState = CHPP_OPEN_STATE_CLOSED;
256 
257   CHPP_LOGD("WWAN service closed");
258 
259   struct ChppAppHeader *response =
260       chppAllocResponseFixed(requestHeader, struct ChppAppHeader);
261   size_t responseLen = sizeof(*response);
262 
263   if (response == NULL) {
264     CHPP_LOG_OOM();
265     error = CHPP_APP_ERROR_OOM;
266   } else {
267     chppSendTimestampedResponseOrFail(wwanServiceContext->service.appContext,
268                                       &wwanServiceContext->close, response,
269                                       responseLen);
270   }
271 
272   return error;
273 }
274 
275 /**
276  * Notifies the service of an incoming reset.
277  *
278  * @param serviceContext Maintains status for each service instance.
279  */
chppWwanServiceNotifyReset(void * serviceContext)280 static void chppWwanServiceNotifyReset(void *serviceContext) {
281   struct ChppWwanServiceState *wwanServiceContext =
282       (struct ChppWwanServiceState *)serviceContext;
283 
284   if (wwanServiceContext->service.openState != CHPP_OPEN_STATE_OPENED) {
285     CHPP_LOGW("WWAN service reset but wasn't open");
286   } else {
287     CHPP_LOGD("WWAN service reset. Closing");
288     wwanServiceContext->service.openState = CHPP_OPEN_STATE_CLOSED;
289     wwanServiceContext->api->close();
290   }
291 }
292 
293 /**
294  * Retrieves a set of flags indicating the WWAN features supported by the
295  * current implementation.
296  *
297  * @param serviceContext Maintains status for each service instance.
298  * @param requestHeader App layer header of the request.
299  *
300  * @return Indicates the result of this function call.
301  */
chppWwanServiceGetCapabilities(struct ChppWwanServiceState * wwanServiceContext,struct ChppAppHeader * requestHeader)302 static enum ChppAppErrorCode chppWwanServiceGetCapabilities(
303     struct ChppWwanServiceState *wwanServiceContext,
304     struct ChppAppHeader *requestHeader) {
305   enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
306 
307   struct ChppWwanGetCapabilitiesResponse *response = chppAllocResponseFixed(
308       requestHeader, struct ChppWwanGetCapabilitiesResponse);
309   size_t responseLen = sizeof(*response);
310 
311   if (response == NULL) {
312     CHPP_LOG_OOM();
313     error = CHPP_APP_ERROR_OOM;
314   } else {
315     response->params.capabilities = wwanServiceContext->api->getCapabilities();
316 
317     CHPP_LOGD("chppWwanServiceGetCapabilities returning 0x%" PRIx32
318               ", %" PRIuSIZE " bytes",
319               response->params.capabilities, responseLen);
320     chppSendTimestampedResponseOrFail(wwanServiceContext->service.appContext,
321                                       &wwanServiceContext->getCapabilities,
322                                       response, responseLen);
323   }
324 
325   return error;
326 }
327 
328 /**
329  * Query information about the current serving cell and its neighbors in
330  * response to a client request. This does not perform a network scan, but
331  * should return state from the current network registration data stored in the
332  * cellular modem.
333  *
334  * This function returns an error code synchronously. The requested cellular
335  * information shall be returned asynchronously to the client via the
336  * chppPlatformWwanCellInfoResultEvent() service response.
337  *
338  * @param serviceContext Maintains status for each service instance.
339  * @param requestHeader App layer header of the request.
340  *
341  * @return Indicates the result of this function call.
342  */
chppWwanServiceGetCellInfoAsync(struct ChppWwanServiceState * wwanServiceContext,struct ChppAppHeader * requestHeader)343 static enum ChppAppErrorCode chppWwanServiceGetCellInfoAsync(
344     struct ChppWwanServiceState *wwanServiceContext,
345     struct ChppAppHeader *requestHeader) {
346   UNUSED_VAR(requestHeader);
347 
348   enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
349 
350   if (!wwanServiceContext->api->requestCellInfo()) {
351     CHPP_LOGE(
352         "WWAN requestCellInfo PAL API failed. Unable to register for callback");
353     error = CHPP_APP_ERROR_UNSPECIFIED;
354   }
355 
356   return error;
357 }
358 
359 /**
360  * PAL callback to provide the result of a prior Request Cell Info
361  * (cellInfoResultCallback).
362  *
363  * @param result Scan results.
364  */
chppWwanServiceCellInfoResultCallback(struct chreWwanCellInfoResult * result)365 static void chppWwanServiceCellInfoResultCallback(
366     struct chreWwanCellInfoResult *result) {
367   // Recover state
368   struct ChppIncomingRequestState *inReqState =
369       &gWwanServiceContext.getCellInfoAsync;
370   struct ChppWwanServiceState *wwanServiceContext =
371       container_of(inReqState, struct ChppWwanServiceState, getCellInfoAsync);
372 
373   // Craft response per parser script
374   struct ChppWwanCellInfoResultWithHeader *response = NULL;
375   size_t responseLen = 0;
376   if (!chppWwanCellInfoResultFromChre(result, &response, &responseLen)) {
377     CHPP_LOGE("CellInfo conversion failed (OOM?) ID=%" PRIu8,
378               inReqState->transaction);
379 
380     response = chppMalloc(sizeof(struct ChppAppHeader));
381     if (response == NULL) {
382       CHPP_LOG_OOM();
383     } else {
384       responseLen = sizeof(struct ChppAppHeader);
385     }
386   }
387 
388   if (response != NULL) {
389     response->header.handle = wwanServiceContext->service.handle;
390     response->header.type = CHPP_MESSAGE_TYPE_SERVICE_RESPONSE;
391     response->header.transaction = inReqState->transaction;
392     response->header.error = (responseLen > sizeof(struct ChppAppHeader))
393                                  ? CHPP_APP_ERROR_NONE
394                                  : CHPP_APP_ERROR_CONVERSION_FAILED;
395     response->header.command = CHPP_WWAN_GET_CELLINFO_ASYNC;
396 
397     chppSendTimestampedResponseOrFail(wwanServiceContext->service.appContext,
398                                       inReqState, response, responseLen);
399   }
400 
401   gWwanServiceContext.api->releaseCellInfoResult(result);
402 }
403 
404 /************************************************
405  *  Public Functions
406  ***********************************************/
407 
chppRegisterWwanService(struct ChppAppState * appContext)408 void chppRegisterWwanService(struct ChppAppState *appContext) {
409   gWwanServiceContext.api = chrePalWwanGetApi(CHPP_PAL_WWAN_API_VERSION);
410 
411   if (gWwanServiceContext.api == NULL) {
412     CHPP_DEBUG_ASSERT_LOG(false,
413                           "WWAN PAL API incompatible. Cannot register service");
414 
415   } else {
416     chppRegisterService(appContext, (void *)&gWwanServiceContext,
417                         &gWwanServiceContext.service, NULL /*outReqStates*/,
418                         &kWwanServiceConfig);
419     CHPP_DEBUG_ASSERT(gWwanServiceContext.service.handle);
420   }
421 }
422 
chppDeregisterWwanService(struct ChppAppState * appContext)423 void chppDeregisterWwanService(struct ChppAppState *appContext) {
424   // TODO
425 
426   UNUSED_VAR(appContext);
427 }
428