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