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