uqmi: Add basic 5G NR support
[project/uqmi.git] / uqmi / commands-nas.c
1 /*
2 * uqmi -- tiny QMI support implementation
3 *
4 * Copyright (C) 2014-2015 Felix Fietkau <nbd@openwrt.org>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301 USA.
20 */
21
22 #include "uqmi.h"
23 #include "qmi-message.h"
24 #include "commands.h"
25
26 #include <libubox/blobmsg.h>
27
28 /* According to libqmi, a value of -32768 in 5G
29 * indicates that the modem is not connected. */
30 #define _5GNR_NOT_CONNECTED_VALUE -32768
31
32 static struct qmi_nas_get_tx_rx_info_request tx_rx_req;
33 static struct qmi_nas_set_system_selection_preference_request sel_req;
34 static struct {
35 bool mcc_is_set;
36 bool mnc_is_set;
37 } plmn_code_flag;
38
39 static void
40 print_earfcn_info(uint32_t earfcn)
41 {
42 /* https://www.sqimway.com/lte_band.php */
43 static const struct {
44 uint32_t min;
45 uint32_t max;
46 uint16_t band;
47 uint16_t freq;
48 const char *duplex;
49 } earfcn_ranges[] = {
50 { 0, 599, 1, 2100, "FDD" },
51 { 600, 1199, 2, 1800, "FDD" },
52 { 1200, 1949, 3, 1800, "FDD" },
53 { 1950, 2399, 4, 1700, "FDD" },
54 { 2400, 2649, 5, 850, "FDD" },
55 { 2650, 2749, 6, 800, "FDD" },
56 { 2750, 3449, 7, 2600, "FDD" },
57 { 3450, 3799, 8, 900, "FDD" },
58 { 3800, 4149, 9, 1800, "FDD" },
59 { 4150, 4749, 10, 1700, "FDD" },
60 { 4750, 4999, 11, 1500, "FDD" },
61 { 5000, 5179, 12, 700, "FDD" },
62 { 5180, 5279, 13, 700, "FDD" },
63 { 5280, 5379, 14, 700, "FDD" },
64 { 5730, 5849, 17, 700, "FDD" },
65 { 5850, 5999, 18, 850, "FDD" },
66 { 6000, 6149, 19, 850, "FDD" },
67 { 6150, 6449, 20, 800, "FDD" },
68 { 6450, 6599, 21, 1500, "FDD" },
69 { 6600, 7399, 22, 3500, "FDD" },
70 { 7500, 7699, 23, 2000, "FDD" },
71 { 7700, 8039, 24, 1600, "FDD" },
72 { 8040, 8689, 25, 1900, "FDD" },
73 { 8690, 9039, 26, 850, "FDD" },
74 { 9040, 9209, 27, 800, "FDD" },
75 { 9210, 9659, 28, 700, "FDD" },
76 { 9660, 9769, 29, 700, "SDL" },
77 { 9770, 9869, 30, 2300, "FDD" },
78 { 9870, 9919, 31, 450, "FDD" },
79 { 9920, 10359, 32, 1500, "SDL" },
80 { 36000, 36199, 33, 1900, "TDD" },
81 { 36200, 36349, 34, 2000, "TDD" },
82 { 36350, 36949, 35, 1900, "TDD" },
83 { 36950, 37549, 36, 1900, "TDD" },
84 { 37550, 37749, 37, 1900, "TDD" },
85 { 37750, 38249, 38, 2600, "TDD" },
86 { 38250, 38649, 39, 1900, "TDD" },
87 { 38650, 39649, 40, 2300, "TDD" },
88 { 39650, 41589, 41, 2500, "TDD" },
89 { 41590, 43589, 42, 3500, "TDD" },
90 { 43590, 45589, 43, 3700, "TDD" },
91 { 45590, 46589, 44, 700, "TDD" },
92 };
93
94 for (int i = 0; i < (sizeof(earfcn_ranges) / sizeof(*earfcn_ranges)); i++) {
95 if (earfcn <= earfcn_ranges[i].max && earfcn >= earfcn_ranges[i].min) {
96 blobmsg_add_u32(&status, "band", earfcn_ranges[i].band);
97 blobmsg_add_u32(&status, "frequency", earfcn_ranges[i].freq);
98 blobmsg_add_string(&status, "duplex", earfcn_ranges[i].duplex);
99 return;
100 }
101 }
102 }
103
104 #define cmd_nas_do_set_system_selection_cb no_cb
105 static enum qmi_cmd_result
106 cmd_nas_do_set_system_selection_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
107 {
108 qmi_set_nas_set_system_selection_preference_request(msg, &sel_req);
109 return QMI_CMD_REQUEST;
110 }
111
112 static enum qmi_cmd_result
113 do_sel_network(void)
114 {
115 static bool use_sel_req = false;
116
117 if (!use_sel_req) {
118 use_sel_req = true;
119 uqmi_add_command(NULL, __UQMI_COMMAND_nas_do_set_system_selection);
120 }
121
122 return QMI_CMD_DONE;
123 }
124
125 #define cmd_nas_set_network_modes_cb no_cb
126 static enum qmi_cmd_result
127 cmd_nas_set_network_modes_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
128 {
129 static const struct {
130 const char *name;
131 QmiNasRatModePreference val;
132 } modes[] = {
133 { "cdma", QMI_NAS_RAT_MODE_PREFERENCE_CDMA_1X | QMI_NAS_RAT_MODE_PREFERENCE_CDMA_1XEVDO },
134 { "td-scdma", QMI_NAS_RAT_MODE_PREFERENCE_TD_SCDMA },
135 { "gsm", QMI_NAS_RAT_MODE_PREFERENCE_GSM },
136 { "umts", QMI_NAS_RAT_MODE_PREFERENCE_UMTS },
137 { "lte", QMI_NAS_RAT_MODE_PREFERENCE_LTE },
138 { "5gnr", QMI_NAS_RAT_MODE_PREFERENCE_5GNR },
139 };
140 QmiNasRatModePreference val = 0;
141 char *word;
142 int i;
143
144 for (word = strtok(arg, ",");
145 word;
146 word = strtok(NULL, ",")) {
147 bool found = false;
148
149 for (i = 0; i < ARRAY_SIZE(modes); i++) {
150 if (strcmp(word, modes[i].name) != 0 &&
151 strcmp(word, "all") != 0)
152 continue;
153
154 val |= modes[i].val;
155 found = true;
156 }
157
158 if (!found) {
159 uqmi_add_error("Invalid network mode");
160 return QMI_CMD_EXIT;
161 }
162 }
163
164 qmi_set(&sel_req, mode_preference, val);
165 return do_sel_network();
166 }
167
168 #define cmd_nas_set_network_preference_cb no_cb
169 static enum qmi_cmd_result
170 cmd_nas_set_network_preference_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
171 {
172 QmiNasGsmWcdmaAcquisitionOrderPreference pref = QMI_NAS_GSM_WCDMA_ACQUISITION_ORDER_PREFERENCE_AUTOMATIC;
173
174 if (!strcmp(arg, "gsm"))
175 pref = QMI_NAS_GSM_WCDMA_ACQUISITION_ORDER_PREFERENCE_GSM;
176 else if (!strcmp(arg, "wcdma"))
177 pref = QMI_NAS_GSM_WCDMA_ACQUISITION_ORDER_PREFERENCE_WCDMA;
178
179 qmi_set(&sel_req, gsm_wcdma_acquisition_order_preference, pref);
180 return do_sel_network();
181 }
182
183 #define cmd_nas_set_roaming_cb no_cb
184 static enum qmi_cmd_result
185 cmd_nas_set_roaming_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
186 {
187 QmiNasRoamingPreference pref;
188
189 if (!strcmp(arg, "any"))
190 pref = QMI_NAS_ROAMING_PREFERENCE_ANY;
191 else if (!strcmp(arg, "only"))
192 pref = QMI_NAS_ROAMING_PREFERENCE_NOT_OFF;
193 else if (!strcmp(arg, "off"))
194 pref = QMI_NAS_ROAMING_PREFERENCE_OFF;
195 else
196 return uqmi_add_error("Invalid argument");
197
198 qmi_set(&sel_req, roaming_preference, pref);
199 return do_sel_network();
200 }
201
202 #define cmd_nas_set_mcc_cb no_cb
203 static enum qmi_cmd_result
204 cmd_nas_set_mcc_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
205 {
206 char *err;
207 int value = strtoul(arg, &err, 10);
208 if (err && *err) {
209 uqmi_add_error("Invalid MCC value");
210 return QMI_CMD_EXIT;
211 }
212
213 sel_req.data.network_selection_preference.mcc = value;
214 plmn_code_flag.mcc_is_set = true;
215 return QMI_CMD_DONE;
216 }
217
218 #define cmd_nas_set_mnc_cb no_cb
219 static enum qmi_cmd_result
220 cmd_nas_set_mnc_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
221 {
222 char *err;
223 int value = strtoul(arg, &err, 10);
224 if (err && *err) {
225 uqmi_add_error("Invalid MNC value");
226 return QMI_CMD_EXIT;
227 }
228
229 sel_req.data.network_selection_preference.mnc = value;
230 plmn_code_flag.mnc_is_set = true;
231 return QMI_CMD_DONE;
232 }
233
234 #define cmd_nas_set_plmn_cb no_cb
235 static enum qmi_cmd_result
236 cmd_nas_set_plmn_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
237 {
238 sel_req.set.network_selection_preference = 1;
239 sel_req.data.network_selection_preference.mode = QMI_NAS_NETWORK_SELECTION_PREFERENCE_AUTOMATIC;
240
241 if (!plmn_code_flag.mcc_is_set && plmn_code_flag.mnc_is_set) {
242 uqmi_add_error("No MCC value");
243 return QMI_CMD_EXIT;
244 }
245
246 if (plmn_code_flag.mcc_is_set && sel_req.data.network_selection_preference.mcc) {
247 if (!plmn_code_flag.mnc_is_set) {
248 uqmi_add_error("No MNC value");
249 return QMI_CMD_EXIT;
250 } else {
251 sel_req.data.network_selection_preference.mode = QMI_NAS_NETWORK_SELECTION_PREFERENCE_MANUAL;
252 }
253 }
254
255 return do_sel_network();
256 }
257
258 #define cmd_nas_initiate_network_register_cb no_cb
259 static enum qmi_cmd_result
260 cmd_nas_initiate_network_register_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
261 {
262 static struct qmi_nas_initiate_network_register_request register_req = {
263 QMI_INIT(action, QMI_NAS_NETWORK_REGISTER_TYPE_AUTOMATIC)
264 };
265
266 qmi_set_nas_initiate_network_register_request(msg, &register_req);
267 return QMI_CMD_REQUEST;
268 }
269
270 static void
271 cmd_nas_get_signal_info_cb(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg)
272 {
273 struct qmi_nas_get_signal_info_response res;
274 void *c, *a;
275 bool is_5gnr_connected = false;
276 bool is_5gnr_endc = false;
277
278 qmi_parse_nas_get_signal_info_response(msg, &res);
279
280 /* If 5G NR EN-DC (dual connectivity) is enabled, the mobile device has two connections,
281 * one with the LTE base station, and one with the NR base station.
282 * Therefore an array of signals has to be reported in this case. */
283 is_5gnr_connected = ((res.set._5g_signal_strength &&
284 ((res.data._5g_signal_strength.rsrp != _5GNR_NOT_CONNECTED_VALUE) ||
285 (res.data._5g_signal_strength.snr != _5GNR_NOT_CONNECTED_VALUE))) ||
286 (res.set._5g_signal_strength_extended &&
287 (res.data._5g_signal_strength_extended != _5GNR_NOT_CONNECTED_VALUE)));
288 is_5gnr_endc = (res.set.lte_signal_strength && is_5gnr_connected);
289
290 if (is_5gnr_endc) {
291 a = blobmsg_open_array(&status, NULL);
292 }
293
294 c = blobmsg_open_table(&status, NULL);
295 if (res.set.cdma_signal_strength) {
296 blobmsg_add_string(&status, "type", "cdma");
297 blobmsg_add_u32(&status, "rssi", (int32_t) res.data.cdma_signal_strength.rssi);
298 blobmsg_add_u32(&status, "ecio", (int32_t) res.data.cdma_signal_strength.ecio);
299 }
300
301 if (res.set.hdr_signal_strength) {
302 blobmsg_add_string(&status, "type", "hdr");
303 blobmsg_add_u32(&status, "rssi", (int32_t) res.data.hdr_signal_strength.rssi);
304 blobmsg_add_u32(&status, "ecio", (int32_t) res.data.hdr_signal_strength.ecio);
305 blobmsg_add_u32(&status, "io", res.data.hdr_signal_strength.io);
306 }
307
308 if (res.set.gsm_signal_strength) {
309 blobmsg_add_string(&status, "type", "gsm");
310 blobmsg_add_u32(&status, "signal", (int32_t) res.data.gsm_signal_strength);
311 }
312
313 if (res.set.wcdma_signal_strength) {
314 blobmsg_add_string(&status, "type", "wcdma");
315 blobmsg_add_u32(&status, "rssi", (int32_t) res.data.wcdma_signal_strength.rssi);
316 blobmsg_add_u32(&status, "ecio", (int32_t) res.data.wcdma_signal_strength.ecio);
317 }
318
319 if (res.set.lte_signal_strength) {
320 blobmsg_add_string(&status, "type", "lte");
321 blobmsg_add_u32(&status, "rssi", (int32_t) res.data.lte_signal_strength.rssi);
322 blobmsg_add_u32(&status, "rsrq", (int32_t) res.data.lte_signal_strength.rsrq);
323 blobmsg_add_u32(&status, "rsrp", (int32_t) res.data.lte_signal_strength.rsrp);
324 blobmsg_add_double(&status, "snr", (double) res.data.lte_signal_strength.snr*0.1);
325 }
326
327 if (res.set.tdma_signal_strength) {
328 blobmsg_add_string(&status, "type", "tdma");
329 blobmsg_add_u32(&status, "signal", (int32_t) res.data.tdma_signal_strength);
330 }
331
332 if (is_5gnr_connected) {
333 if (is_5gnr_endc) {
334 blobmsg_close_table(&status, c);
335 c = blobmsg_open_table(&status, NULL);
336 }
337 blobmsg_add_string(&status, "type", "5gnr");
338 if (res.set._5g_signal_strength) {
339 if (res.data._5g_signal_strength.rsrp != _5GNR_NOT_CONNECTED_VALUE)
340 blobmsg_add_u32(&status, "rsrp", (int32_t) res.data._5g_signal_strength.rsrp);
341 if (res.data._5g_signal_strength.snr != _5GNR_NOT_CONNECTED_VALUE)
342 blobmsg_add_double(&status, "snr", (double) res.data._5g_signal_strength.snr*0.1);
343 }
344
345 if (res.set._5g_signal_strength_extended &&
346 (res.data._5g_signal_strength_extended != _5GNR_NOT_CONNECTED_VALUE)) {
347 blobmsg_add_u32(&status, "rsrq", (int32_t) res.data._5g_signal_strength_extended);
348 }
349 }
350
351 blobmsg_close_table(&status, c);
352
353 if (is_5gnr_endc) {
354 blobmsg_close_array(&status, a);
355 }
356 }
357
358 static void
359 print_system_info(uint8_t svc_status, uint8_t tsvc_status, bool preferred, bool system_info,
360 bool domain_valid, uint8_t domain,
361 bool service_cap_valid, uint8_t service_cap,
362 bool roaming_status_valid, uint8_t roaming_status,
363 bool forbidden_valid, bool forbidden,
364 bool network_id_valid, char *mcc, char *mnc,
365 bool lac_valid, uint16_t lac)
366 {
367 static const char *map_service[] = {
368 [QMI_NAS_SERVICE_STATUS_NONE] = "none",
369 [QMI_NAS_SERVICE_STATUS_LIMITED] = "limited",
370 [QMI_NAS_SERVICE_STATUS_AVAILABLE] = "available",
371 [QMI_NAS_SERVICE_STATUS_LIMITED_REGIONAL] = "limited regional",
372 [QMI_NAS_SERVICE_STATUS_POWER_SAVE] = "power save",
373 };
374
375 static const char *map_roaming[] = {
376 [QMI_NAS_ROAMING_STATUS_OFF] = "off",
377 [QMI_NAS_ROAMING_STATUS_ON] = "on",
378 [QMI_NAS_ROAMING_STATUS_BLINK] = "blink",
379 [QMI_NAS_ROAMING_STATUS_OUT_OF_NEIGHBORHOOD] = "out of neighborhood",
380 [QMI_NAS_ROAMING_STATUS_OUT_OF_BUILDING] = "out of building",
381 [QMI_NAS_ROAMING_STATUS_PREFERRED_SYSTEM] = "preferred system",
382 [QMI_NAS_ROAMING_STATUS_AVAILABLE_SYSTEM] = "available system",
383 [QMI_NAS_ROAMING_STATUS_ALLIANCE_PARTNER] = "alliance partner",
384 [QMI_NAS_ROAMING_STATUS_PREMIUM_PARTNER] = "premium partner",
385 [QMI_NAS_ROAMING_STATUS_FULL_SERVICE] = "full service",
386 [QMI_NAS_ROAMING_STATUS_PARTIAL_SERVICE] = "partial service",
387 [QMI_NAS_ROAMING_STATUS_BANNER_ON] = "banner on",
388 [QMI_NAS_ROAMING_STATUS_BANNER_OFF] = "banner off",
389 };
390
391 static const char *map_network[] = {
392 [QMI_NAS_NETWORK_SERVICE_DOMAIN_NONE] = "none",
393 [QMI_NAS_NETWORK_SERVICE_DOMAIN_CS] = "cs",
394 [QMI_NAS_NETWORK_SERVICE_DOMAIN_PS] = "ps",
395 [QMI_NAS_NETWORK_SERVICE_DOMAIN_CS_PS] = "cs-ps",
396 [QMI_NAS_NETWORK_SERVICE_DOMAIN_UNKNOWN] = "unknown",
397 };
398
399 blobmsg_add_string(&status, "service_status", map_service[svc_status]);
400 blobmsg_add_string(&status, "true_service_status", map_service[tsvc_status]);
401 blobmsg_add_u8(&status, "preferred_data_path", preferred);
402
403 if (system_info) {
404 if (domain_valid)
405 blobmsg_add_string(&status, "domain", map_network[domain]);
406 if (service_cap_valid)
407 blobmsg_add_string(&status, "service", map_network[service_cap]);
408 if (roaming_status_valid)
409 blobmsg_add_string(&status, "roaming_status", map_roaming[roaming_status]);
410 if (forbidden_valid)
411 blobmsg_add_u8(&status, "forbidden", forbidden);
412 if (network_id_valid) {
413 blobmsg_add_string(&status, "mcc", mcc);
414 if ((uint8_t)mnc[2] == 255)
415 mnc[2] = 0;
416 blobmsg_add_string(&status, "mnc", mnc);
417 }
418 if (lac_valid)
419 blobmsg_add_u32(&status, "location_area_code", (int32_t) lac);
420 }
421 }
422
423 static void
424 cmd_nas_get_system_info_cb(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg)
425 {
426 static const char *cell_status[] = {
427 [QMI_NAS_CELL_ACCESS_STATUS_NORMAL_ONLY] = "normal",
428 [QMI_NAS_CELL_ACCESS_STATUS_EMERGENCY_ONLY] = "emergency",
429 [QMI_NAS_CELL_ACCESS_STATUS_NO_CALLS] = "no calls",
430 [QMI_NAS_CELL_ACCESS_STATUS_ALL_CALLS] = "all calls",
431 [QMI_NAS_CELL_ACCESS_STATUS_UNKNOWN] = "unknown",
432 };
433
434 struct qmi_nas_get_system_info_response res;
435 void *c, *t;
436
437 qmi_parse_nas_get_system_info_response(msg, &res);
438 t = blobmsg_open_table(&status, NULL);
439 if (res.set.gsm_service_status) {
440 c = blobmsg_open_table(&status, "gsm");
441 print_system_info(res.data.gsm_service_status.service_status,
442 res.data.gsm_service_status.true_service_status,
443 res.data.gsm_service_status.preferred_data_path,
444 res.set.gsm_system_info_v2,
445 res.data.gsm_system_info_v2.domain_valid,
446 res.data.gsm_system_info_v2.domain,
447 res.data.gsm_system_info_v2.service_capability_valid,
448 res.data.gsm_system_info_v2.service_capability,
449 res.data.gsm_system_info_v2.roaming_status_valid,
450 res.data.gsm_system_info_v2.roaming_status,
451 res.data.gsm_system_info_v2.forbidden_valid,
452 res.data.gsm_system_info_v2.forbidden,
453 res.data.gsm_system_info_v2.network_id_valid,
454 res.data.gsm_system_info_v2.mcc,
455 res.data.gsm_system_info_v2.mnc,
456 res.data.gsm_system_info_v2.lac_valid,
457 res.data.gsm_system_info_v2.lac);
458 if (res.set.gsm_system_info_v2 && res.data.gsm_system_info_v2.cid_valid)
459 blobmsg_add_u32(&status, "cell_id",
460 res.data.gsm_system_info_v2.cid);
461 if (res.set.additional_gsm_system_info &&
462 res.data.additional_gsm_system_info.geo_system_index != 0xFFFF)
463 blobmsg_add_u32(&status, "geo_system_index",
464 res.data.additional_gsm_system_info.geo_system_index);
465 blobmsg_close_table(&status, c);
466 }
467
468 if (res.set.wcdma_service_status) {
469 c = blobmsg_open_table(&status, "wcdma");
470 print_system_info(res.data.wcdma_service_status.service_status,
471 res.data.wcdma_service_status.true_service_status,
472 res.data.wcdma_service_status.preferred_data_path,
473 res.set.wcdma_system_info_v2,
474 res.data.wcdma_system_info_v2.domain_valid,
475 res.data.wcdma_system_info_v2.domain,
476 res.data.wcdma_system_info_v2.service_capability_valid,
477 res.data.wcdma_system_info_v2.service_capability,
478 res.data.wcdma_system_info_v2.roaming_status_valid,
479 res.data.wcdma_system_info_v2.roaming_status,
480 res.data.wcdma_system_info_v2.forbidden_valid,
481 res.data.wcdma_system_info_v2.forbidden,
482 res.data.wcdma_system_info_v2.network_id_valid,
483 res.data.wcdma_system_info_v2.mcc,
484 res.data.wcdma_system_info_v2.mnc,
485 res.data.wcdma_system_info_v2.lac_valid,
486 res.data.wcdma_system_info_v2.lac);
487 if (res.set.wcdma_system_info_v2 && res.data.wcdma_system_info_v2.cid_valid) {
488 blobmsg_add_u32(&status, "rnc_id",res.data.wcdma_system_info_v2.cid/65536);
489 blobmsg_add_u32(&status, "cell_id",res.data.wcdma_system_info_v2.cid%65536);
490 }
491 if (res.set.additional_wcdma_system_info &&
492 res.data.additional_wcdma_system_info.geo_system_index != 0xFFFF)
493 blobmsg_add_u32(&status, "geo_system_index",
494 res.data.additional_wcdma_system_info.geo_system_index);
495 blobmsg_close_table(&status, c);
496 }
497
498 if (res.set.lte_service_status) {
499 c = blobmsg_open_table(&status, "lte");
500 print_system_info(res.data.lte_service_status.service_status,
501 res.data.lte_service_status.true_service_status,
502 res.data.lte_service_status.preferred_data_path,
503 res.set.lte_system_info_v2,
504 res.data.lte_system_info_v2.domain_valid,
505 res.data.lte_system_info_v2.domain,
506 res.data.lte_system_info_v2.service_capability_valid,
507 res.data.lte_system_info_v2.service_capability,
508 res.data.lte_system_info_v2.roaming_status_valid,
509 res.data.lte_system_info_v2.roaming_status,
510 res.data.lte_system_info_v2.forbidden_valid,
511 res.data.lte_system_info_v2.forbidden,
512 res.data.lte_system_info_v2.network_id_valid,
513 res.data.lte_system_info_v2.mcc,
514 res.data.lte_system_info_v2.mnc,
515 res.data.lte_system_info_v2.lac_valid,
516 res.data.lte_system_info_v2.lac);
517 if (res.set.lte_system_info_v2 && res.data.lte_system_info_v2.tac_valid)
518 blobmsg_add_u32(&status, "tracking_area_code",
519 res.data.lte_system_info_v2.tac);
520 if (res.set.lte_system_info_v2 && res.data.lte_system_info_v2.cid_valid) {
521 blobmsg_add_u32(&status, "enodeb_id",res.data.lte_system_info_v2.cid/256);
522 blobmsg_add_u32(&status, "cell_id",res.data.lte_system_info_v2.cid%256);
523 }
524 if (res.set.additional_lte_system_info &&
525 res.data.additional_lte_system_info.geo_system_index != 0xFFFF)
526 blobmsg_add_u32(&status, "geo_system_index",
527 res.data.additional_lte_system_info.geo_system_index);
528 if (res.set.lte_voice_support)
529 blobmsg_add_u8(&status, "voice_support", res.data.lte_voice_support);
530 if (res.set.ims_voice_support)
531 blobmsg_add_u8(&status, "ims_voice_support", res.data.ims_voice_support);
532 if (res.set.lte_cell_access_status)
533 blobmsg_add_string(&status, "cell_access_status",
534 cell_status[res.data.lte_cell_access_status]);
535 if (res.set.network_selection_registration_restriction)
536 blobmsg_add_u32(&status, "registration_restriction",
537 res.data.network_selection_registration_restriction);
538 if (res.set.lte_registration_domain)
539 blobmsg_add_u32(&status, "registration_domain",
540 res.data.lte_registration_domain);
541 if (res.set.eutra_with_nr5g_availability)
542 blobmsg_add_u8(&status, "5g_nsa_available",
543 res.data.eutra_with_nr5g_availability);
544 if (res.set.dcnr_restriction_info)
545 blobmsg_add_u8(&status, "dcnr_restriction", res.data.dcnr_restriction_info);
546
547 blobmsg_close_table(&status, c);
548 }
549
550 if (res.set.nr5g_service_status_info) {
551 c = blobmsg_open_table(&status, "5gnr");
552 print_system_info(res.data.nr5g_service_status_info.service_status,
553 res.data.nr5g_service_status_info.true_service_status,
554 res.data.nr5g_service_status_info.preferred_data_path,
555 res.set.nr5g_system_info,
556 res.data.nr5g_system_info.domain_valid,
557 res.data.nr5g_system_info.domain,
558 res.data.nr5g_system_info.service_capability_valid,
559 res.data.nr5g_system_info.service_capability,
560 res.data.nr5g_system_info.roaming_status_valid,
561 res.data.nr5g_system_info.roaming_status,
562 res.data.nr5g_system_info.forbidden_valid,
563 res.data.nr5g_system_info.forbidden,
564 res.data.nr5g_system_info.network_id_valid,
565 res.data.nr5g_system_info.mcc,
566 res.data.nr5g_system_info.mnc,
567 res.data.nr5g_system_info.lac_valid,
568 res.data.nr5g_system_info.lac);
569 if (res.set.nr5g_system_info && res.data.nr5g_system_info.tac_valid)
570 blobmsg_add_u32(&status, "tracking_area_code",
571 res.data.nr5g_system_info.tac);
572 if (res.set.nr5g_system_info && res.data.nr5g_system_info.cid_valid) {
573 blobmsg_add_u32(&status, "enodeb_id",res.data.nr5g_system_info.cid/256);
574 blobmsg_add_u32(&status, "cell_id",res.data.nr5g_system_info.cid%256);
575 }
576
577 blobmsg_close_table(&status, c);
578 }
579
580 blobmsg_close_table(&status, t);
581 }
582
583 static enum qmi_cmd_result
584 cmd_nas_get_system_info_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
585 {
586 qmi_set_nas_get_system_info_request(msg);
587 return QMI_CMD_REQUEST;
588 }
589
590 static void
591 print_channel_info(int32_t cell_id, int32_t channel, uint32_t bw)
592 {
593 static const char *map_bandwidth[] = {
594 [QMI_NAS_DL_BANDWIDTH_1_4] = "1.4",
595 [QMI_NAS_DL_BANDWIDTH_3] = "3",
596 [QMI_NAS_DL_BANDWIDTH_5] = "5",
597 [QMI_NAS_DL_BANDWIDTH_10] = "10",
598 [QMI_NAS_DL_BANDWIDTH_15] = "15",
599 [QMI_NAS_DL_BANDWIDTH_20] = "20",
600 [QMI_NAS_DL_BANDWIDTH_INVALID] = "invalid",
601 [QMI_NAS_DL_BANDWIDTH_UNKNOWN] = "unknown",
602 };
603
604 blobmsg_add_u32(&status, "cell_id", cell_id);
605 blobmsg_add_u32(&status, "channel", channel);
606 print_earfcn_info(channel);
607 blobmsg_add_string(&status, "bandwidth", map_bandwidth[bw]);
608 }
609
610 static void
611 cmd_nas_get_lte_cphy_ca_info_cb(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg)
612 {
613 struct qmi_nas_get_lte_cphy_ca_info_response res;
614 static const char *scell_state[] = {
615 [QMI_NAS_SCELL_STATE_DECONFIGURED] = "deconfigured",
616 [QMI_NAS_SCELL_STATE_DEACTIVATED] = "deactivated",
617 [QMI_NAS_SCELL_STATE_ACTIVATED] = "activated",
618 };
619 char idx_buf[16];
620 void *t, *c;
621 int i;
622
623 qmi_parse_nas_get_lte_cphy_ca_info_response(msg, &res);
624 t = blobmsg_open_table(&status, NULL);
625 if (res.set.phy_ca_agg_pcell_info) {
626 c = blobmsg_open_table(&status, "primary");
627 print_channel_info(res.data.phy_ca_agg_pcell_info.physical_cell_id,
628 res.data.phy_ca_agg_pcell_info.rx_channel,
629 res.data.phy_ca_agg_pcell_info.dl_bandwidth);
630 blobmsg_close_table(&status, c);
631 }
632 if (res.set.phy_ca_agg_scell_info && res.data.phy_ca_agg_secondary_cells_n) {
633 for (i = 0; i < res.data.phy_ca_agg_secondary_cells_n; i++) {
634 if (res.data.phy_ca_agg_secondary_cells[i].rx_channel == 0)
635 break;
636 sprintf(idx_buf, "secondary_%d",
637 res.data.phy_ca_agg_secondary_cells[i].cell_index);
638 c = blobmsg_open_table(&status, idx_buf);
639 print_channel_info(res.data.phy_ca_agg_secondary_cells[i].physical_cell_id,
640 res.data.phy_ca_agg_secondary_cells[i].rx_channel,
641 res.data.phy_ca_agg_secondary_cells[i].dl_bandwidth);
642 blobmsg_add_string(&status, "state",
643 scell_state[res.data.phy_ca_agg_secondary_cells[i].state]);
644 blobmsg_close_table(&status, c);
645 }
646 } else {
647 if (res.set.scell_index)
648 sprintf(idx_buf, "secondary_%d", res.data.scell_index);
649 else
650 sprintf(idx_buf, "secondary");
651 if (res.set.phy_ca_agg_scell_info && res.data.phy_ca_agg_scell_info.rx_channel != 0) {
652 c = blobmsg_open_table(&status, idx_buf);
653 print_channel_info(res.data.phy_ca_agg_scell_info.physical_cell_id,
654 res.data.phy_ca_agg_scell_info.rx_channel,
655 res.data.phy_ca_agg_scell_info.dl_bandwidth);
656 blobmsg_add_string(&status, "state",
657 scell_state[res.data.phy_ca_agg_scell_info.state]);
658 blobmsg_close_table(&status, c);
659 }
660 }
661 blobmsg_close_table(&status, t);
662 }
663
664 static enum qmi_cmd_result
665 cmd_nas_get_lte_cphy_ca_info_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
666 {
667 qmi_set_nas_get_lte_cphy_ca_info_request(msg);
668 return QMI_CMD_REQUEST;
669 }
670
671 static void
672 print_chain_info(int8_t radio, bool tuned, int32_t rssi, int32_t ecio, int32_t rsrp, int32_t rscp, uint32_t phase)
673 {
674 blobmsg_add_u8(&status, "tuned", tuned);
675 blobmsg_add_double(&status, "rssi", (double) rssi*0.1);
676 if (radio == QMI_NAS_RADIO_INTERFACE_5GNR) {
677 blobmsg_add_double(&status, "rsrp", (double) rsrp*-0.1);
678 }
679 else if (radio == QMI_NAS_RADIO_INTERFACE_LTE) {
680 blobmsg_add_double(&status, "rsrq", (double) ecio*-0.1);
681 blobmsg_add_double(&status, "rsrp", (double) rsrp*-0.1);
682 }
683 else if (radio == QMI_NAS_RADIO_INTERFACE_UMTS) {
684 blobmsg_add_double(&status, "ecio", (double) ecio*-0.1);
685 blobmsg_add_double(&status, "rscp", (double) rscp*-0.1);
686 }
687 if (phase != 0xFFFFFFFF)
688 blobmsg_add_double(&status, "phase", (double) phase*0.01);
689 }
690
691 static void
692 cmd_nas_get_tx_rx_info_cb(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg)
693 {
694 struct qmi_nas_get_tx_rx_info_response res;
695 void *c, *t;
696
697 qmi_parse_nas_get_tx_rx_info_response(msg, &res);
698 t = blobmsg_open_table(&status, NULL);
699 if (res.set.rx_chain_0_info) {
700 c = blobmsg_open_table(&status, "rx_chain_0");
701 print_chain_info(tx_rx_req.data.radio_interface,
702 res.data.rx_chain_0_info.is_radio_tuned,
703 res.data.rx_chain_0_info.rx_power,
704 res.data.rx_chain_0_info.ecio,
705 res.data.rx_chain_0_info.rsrp,
706 res.data.rx_chain_0_info.rscp,
707 res.data.rx_chain_0_info.phase);
708 blobmsg_close_table(&status, c);
709 }
710 if (res.set.rx_chain_1_info) {
711 c = blobmsg_open_table(&status, "rx_chain_1");
712 print_chain_info(tx_rx_req.data.radio_interface,
713 res.data.rx_chain_1_info.is_radio_tuned,
714 res.data.rx_chain_1_info.rx_power,
715 res.data.rx_chain_1_info.ecio,
716 res.data.rx_chain_1_info.rsrp,
717 res.data.rx_chain_1_info.rscp,
718 res.data.rx_chain_1_info.phase);
719 blobmsg_close_table(&status, c);
720 }
721 if (res.set.rx_chain_2_info) {
722 c = blobmsg_open_table(&status, "rx_chain_2");
723 print_chain_info(tx_rx_req.data.radio_interface,
724 res.data.rx_chain_2_info.is_radio_tuned,
725 res.data.rx_chain_2_info.rx_power,
726 res.data.rx_chain_2_info.ecio,
727 res.data.rx_chain_2_info.rsrp,
728 res.data.rx_chain_2_info.rscp,
729 res.data.rx_chain_2_info.phase);
730 blobmsg_close_table(&status, c);
731 }
732 if (res.set.rx_chain_3_info) {
733 c = blobmsg_open_table(&status, "rx_chain_3");
734 print_chain_info(tx_rx_req.data.radio_interface,
735 res.data.rx_chain_3_info.is_radio_tuned,
736 res.data.rx_chain_3_info.rx_power,
737 res.data.rx_chain_3_info.ecio,
738 res.data.rx_chain_3_info.rsrp,
739 res.data.rx_chain_3_info.rscp,
740 res.data.rx_chain_3_info.phase);
741 blobmsg_close_table(&status, c);
742 }
743 if (res.set.tx_info) {
744 c = blobmsg_open_table(&status, "tx");
745 blobmsg_add_u8(&status, "traffic", res.data.tx_info.is_in_traffic);
746 if (res.data.tx_info.is_in_traffic)
747 blobmsg_add_double(&status, "tx_power",
748 (double) res.data.tx_info.tx_power*0.1);
749 blobmsg_close_table(&status, c);
750 }
751 blobmsg_close_table(&status, t);
752 }
753
754
755 static enum qmi_cmd_result
756 cmd_nas_get_tx_rx_info_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
757 {
758 int radio = 0;
759
760 if (!strcmp(arg, "5gnr"))
761 radio = QMI_NAS_RADIO_INTERFACE_5GNR;
762 else if (!strcmp(arg, "lte"))
763 radio = QMI_NAS_RADIO_INTERFACE_LTE;
764 else if (!strcmp(arg, "umts"))
765 radio = QMI_NAS_RADIO_INTERFACE_UMTS;
766 else if (!strcmp(arg, "gsm"))
767 radio = QMI_NAS_RADIO_INTERFACE_GSM;
768 else
769 return uqmi_add_error("Invalid argument");
770
771 qmi_set(&tx_rx_req, radio_interface, radio);
772 qmi_set_nas_get_tx_rx_info_request(msg, &tx_rx_req);
773 return QMI_CMD_REQUEST;
774 }
775
776 static void
777 print_lte_info(int32_t cell_id, int16_t rsrp, int16_t rsrq, int16_t rssi)
778 {
779 blobmsg_add_u32(&status, "physical_cell_id", cell_id);
780 blobmsg_add_double(&status, "rsrq", ((double)rsrq)/10);
781 blobmsg_add_double(&status, "rsrp", ((double)rsrp)/10);
782 blobmsg_add_double(&status, "rssi", ((double)rssi)/10);
783 }
784
785 static void
786 print_sel_info(int32_t priority, int32_t high, int32_t low)
787 {
788 blobmsg_add_u32(&status, "cell_reselection_priority", priority);
789 blobmsg_add_u32(&status, "cell_reselection_low", low);
790 blobmsg_add_u32(&status, "cell_reselection_high", high);
791 }
792
793 static void
794 cmd_nas_get_cell_location_info_cb(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg)
795 {
796 struct qmi_nas_get_cell_location_info_response res;
797 void *c = NULL, *t, *cell, *freq;
798 int i, j;
799
800 qmi_parse_nas_get_cell_location_info_response(msg, &res);
801 t = blobmsg_open_table(&status, NULL);
802
803 if (res.set.umts_info_v2) {
804 c = blobmsg_open_table(&status, "umts_info");
805 blobmsg_add_u32(&status, "location_area_code", res.data.umts_info_v2.lac);
806 blobmsg_add_u32(&status, "cell_id", res.data.umts_info_v2.cell_id);
807 blobmsg_add_u32(&status, "channel",
808 res.data.umts_info_v2.utra_absolute_rf_channel_number);
809 blobmsg_add_u32(&status, "primary_scrambling_code",
810 res.data.umts_info_v2.primary_scrambling_code);
811 blobmsg_add_u32(&status, "rscp", res.data.umts_info_v2.rscp);
812 blobmsg_add_u32(&status, "ecio", res.data.umts_info_v2.ecio);
813 for (j = 0; j < res.data.umts_info_v2.cell_n; j++) {
814 cell = blobmsg_open_table(&status, NULL);
815 blobmsg_add_u32(&status, "channel",
816 res.data.umts_info_v2.cell[j].utra_absolute_rf_channel_number);
817 blobmsg_add_u32(&status, "primary_scrambling_code",
818 res.data.umts_info_v2.cell[j].primary_scrambling_code);
819 blobmsg_add_u32(&status, "rscp", res.data.umts_info_v2.cell[j].rscp);
820 blobmsg_add_u32(&status, "ecio", res.data.umts_info_v2.cell[j].ecio);
821 blobmsg_close_table(&status, cell);
822 }
823 for (j = 0; j < res.data.umts_info_v2.neighboring_geran_n; j++) {
824 cell = blobmsg_open_table(&status, "neighboring_geran");
825 blobmsg_add_u32(&status, "channel",
826 res.data.umts_info_v2.neighboring_geran[j].geran_absolute_rf_channel_number);
827 blobmsg_add_u8(&status, "network_color_code",
828 res.data.umts_info_v2.neighboring_geran[j].network_color_code);
829 blobmsg_add_u8(&status, "base_station_color_code",
830 res.data.umts_info_v2.neighboring_geran[j].base_station_color_code);
831 blobmsg_add_u32(&status, "rssi",
832 res.data.umts_info_v2.neighboring_geran[j].rssi);
833 blobmsg_close_table(&status, cell);
834 }
835 blobmsg_close_table(&status, c);
836 }
837 if (res.set.intrafrequency_lte_info_v2) {
838 c = blobmsg_open_table(&status, "intrafrequency_lte_info");
839 blobmsg_add_u32(&status, "tracking_area_code",
840 res.data.intrafrequency_lte_info_v2.tracking_area_code);
841 blobmsg_add_u32(&status, "enodeb_id",
842 res.data.intrafrequency_lte_info_v2.global_cell_id/256);
843 blobmsg_add_u32(&status, "cell_id",
844 res.data.intrafrequency_lte_info_v2.global_cell_id%256);
845 blobmsg_add_u32(&status, "channel",
846 res.data.intrafrequency_lte_info_v2.eutra_absolute_rf_channel_number);
847 print_earfcn_info(res.data.intrafrequency_lte_info_v2.eutra_absolute_rf_channel_number);
848 blobmsg_add_u32(&status, "serving_cell_id",
849 res.data.intrafrequency_lte_info_v2.serving_cell_id);
850 if (res.data.intrafrequency_lte_info_v2.ue_in_idle) {
851 blobmsg_add_u32(&status, "cell_reselection_priority",
852 res.data.intrafrequency_lte_info_v2.cell_reselection_priority);
853 blobmsg_add_u32(&status, "s_non_intra_search_threshold",
854 res.data.intrafrequency_lte_info_v2.s_non_intra_search_threshold);
855 blobmsg_add_u32(&status, "serving_cell_low_threshold",
856 res.data.intrafrequency_lte_info_v2.serving_cell_low_threshold);
857 blobmsg_add_u32(&status, "s_intra_search_threshold",
858 res.data.intrafrequency_lte_info_v2.s_intra_search_threshold);
859 }
860 for (i = 0; i < res.data.intrafrequency_lte_info_v2.cell_n; i++) {
861 cell = blobmsg_open_table(&status, NULL);
862 print_lte_info(res.data.intrafrequency_lte_info_v2.cell[i].physical_cell_id,
863 res.data.intrafrequency_lte_info_v2.cell[i].rsrq,
864 res.data.intrafrequency_lte_info_v2.cell[i].rsrp,
865 res.data.intrafrequency_lte_info_v2.cell[i].rssi);
866 if (res.data.intrafrequency_lte_info_v2.ue_in_idle)
867 blobmsg_add_u32(&status, "cell_selection_rx_level",
868 res.data.intrafrequency_lte_info_v2.cell[i].cell_selection_rx_level);
869 blobmsg_close_table(&status, cell);
870 }
871 blobmsg_close_table(&status, c);
872 }
873 if (res.set.interfrequency_lte_info) {
874 if (res.data.interfrequency_lte_info.frequency_n > 0)
875 c = blobmsg_open_table(&status, "interfrequency_lte_info");
876 for (i = 0; i < res.data.interfrequency_lte_info.frequency_n; i++) {
877 freq = blobmsg_open_table(&status, NULL);
878 blobmsg_add_u32(&status, "channel",
879 res.data.interfrequency_lte_info.frequency[i].eutra_absolute_rf_channel_number);
880 print_earfcn_info(res.data.interfrequency_lte_info.frequency[i].eutra_absolute_rf_channel_number);
881 if (res.data.interfrequency_lte_info.ue_in_idle) {
882 print_sel_info(res.data.interfrequency_lte_info.frequency[i].cell_reselection_priority,
883 res.data.interfrequency_lte_info.frequency[i].cell_selection_rx_level_high_threshold,
884 res.data.interfrequency_lte_info.frequency[i].cell_selection_rx_level_low_threshold);
885 }
886 for (j = 0; j < res.data.interfrequency_lte_info.frequency[i].cell_n; j++) {
887 cell = blobmsg_open_table(&status, NULL);
888 print_lte_info(res.data.interfrequency_lte_info.frequency[i].cell[j].physical_cell_id,
889 res.data.interfrequency_lte_info.frequency[i].cell[j].rsrq,
890 res.data.interfrequency_lte_info.frequency[i].cell[j].rsrp,
891 res.data.interfrequency_lte_info.frequency[i].cell[j].rssi);
892 if (res.data.interfrequency_lte_info.ue_in_idle)
893 blobmsg_add_u32(&status, "cell_selection_rx_level",
894 res.data.interfrequency_lte_info.frequency[i].cell[j].cell_selection_rx_level);
895 blobmsg_close_table(&status, cell);
896 }
897 blobmsg_close_table(&status, freq);
898 }
899 if (res.data.interfrequency_lte_info.frequency_n > 0)
900 blobmsg_close_table(&status, c);
901 }
902 if (res.set.lte_info_neighboring_gsm) {
903 if (res.data.lte_info_neighboring_gsm.frequency_n > 0)
904 c = blobmsg_open_table(&status, "lte_info_neighboring_gsm");
905 for (i = 0; i < res.data.lte_info_neighboring_gsm.frequency_n; i++) {
906 freq = blobmsg_open_table(&status, NULL);
907 blobmsg_add_u32(&status, "ncc_permitted",
908 res.data.lte_info_neighboring_gsm.frequency[i].ncc_permitted);
909 if (res.data.lte_info_neighboring_gsm.ue_in_idle) {
910 print_sel_info(res.data.lte_info_neighboring_gsm.frequency[i].cell_reselection_priority,
911 res.data.lte_info_neighboring_gsm.frequency[i].cell_reselection_high_threshold,
912 res.data.lte_info_neighboring_gsm.frequency[i].cell_reselection_low_threshold);
913 }
914 for (j = 0; j < res.data.lte_info_neighboring_gsm.frequency[i].cell_n; j++) {
915 cell = blobmsg_open_table(&status, NULL);
916 blobmsg_add_u32(&status, "channel",
917 res.data.lte_info_neighboring_gsm.frequency[i].cell[j].geran_absolute_rf_channel_number);
918 blobmsg_add_u32(&status, "base_station_identity_code",
919 res.data.lte_info_neighboring_gsm.frequency[i].cell[j].base_station_identity_code);
920 blobmsg_add_double(&status, "rssi",
921 ((double)res.data.lte_info_neighboring_gsm.frequency[i].cell[j].rssi)/10);
922 if (res.data.lte_info_neighboring_gsm.ue_in_idle)
923 blobmsg_add_u32(&status, "cell_selection_rx_level",
924 res.data.lte_info_neighboring_gsm.frequency[i].cell[j].cell_selection_rx_level);
925 blobmsg_close_table(&status, cell);
926 }
927 blobmsg_close_table(&status, freq);
928 }
929 if (res.data.lte_info_neighboring_gsm.frequency_n > 0)
930 blobmsg_close_table(&status, c);
931 }
932 if (res.set.lte_info_neighboring_wcdma) {
933 if (res.data.lte_info_neighboring_wcdma.frequency_n > 0)
934 c = blobmsg_open_table(&status, "lte_info_neighboring_wcdma");
935 for (i = 0; i < res.data.lte_info_neighboring_wcdma.frequency_n; i++) {
936 freq = blobmsg_open_table(&status, NULL);
937 blobmsg_add_u32(&status, "channel",
938 res.data.lte_info_neighboring_wcdma.frequency[i].utra_absolute_rf_channel_number);
939 if (res.data.lte_info_neighboring_wcdma.ue_in_idle) {
940 print_sel_info(res.data.lte_info_neighboring_wcdma.frequency[i].cell_reselection_priority,
941 res.data.lte_info_neighboring_wcdma.frequency[i].cell_reselection_high_threshold,
942 res.data.lte_info_neighboring_wcdma.frequency[i].cell_reselection_low_threshold);
943 }
944 for (j = 0; j < res.data.lte_info_neighboring_wcdma.frequency[i].cell_n; j++) {
945 cell = blobmsg_open_table(&status, NULL);
946 blobmsg_add_u32(&status, "primary_scrambling_code",
947 res.data.lte_info_neighboring_wcdma.frequency[i].cell[j].primary_scrambling_code);
948 blobmsg_add_double(&status, "rscp",
949 ((double)res.data.lte_info_neighboring_wcdma.frequency[i].cell[j].cpich_rscp)/10);
950 blobmsg_add_double(&status, "ecno",
951 ((double)res.data.lte_info_neighboring_wcdma.frequency[i].cell[j].cpich_ecno)/10);
952 if (res.data.lte_info_neighboring_wcdma.ue_in_idle)
953 blobmsg_add_u32(&status, "cell_selection_rx_level",
954 res.data.lte_info_neighboring_wcdma.frequency[i].cell[j].cell_selection_rx_level);
955 blobmsg_close_table(&status, cell);
956 }
957 blobmsg_close_table(&status, freq);
958 }
959 if (res.data.lte_info_neighboring_wcdma.frequency_n > 0)
960 blobmsg_close_table(&status, c);
961 }
962 if (res.set.umts_info_neighboring_lte) {
963 if (res.data.umts_info_neighboring_lte.frequency_n > 0)
964 c = blobmsg_open_table(&status, "umts_info_neighboring_lte");
965 for (i = 0; i < res.data.umts_info_neighboring_lte.frequency_n; i++) {
966 freq = blobmsg_open_table(&status, NULL);
967 blobmsg_add_u32(&status, "channel",
968 res.data.umts_info_neighboring_lte.frequency[i].eutra_absolute_rf_channel_number);
969 print_earfcn_info(res.data.umts_info_neighboring_lte.frequency[i].eutra_absolute_rf_channel_number);
970 blobmsg_add_u32(&status, "physical_cell_id",
971 res.data.umts_info_neighboring_lte.frequency[i].physical_cell_id);
972 blobmsg_add_double(&status, "rsrp",
973 (double) res.data.umts_info_neighboring_lte.frequency[i].rsrp);
974 blobmsg_add_double(&status, "rsrq",
975 (double) res.data.umts_info_neighboring_lte.frequency[i].rsrq);
976 blobmsg_add_u32(&status, "cell_selection_rx_level",
977 res.data.umts_info_neighboring_lte.frequency[i].cell_selection_rx_level);
978 blobmsg_close_table(&status, freq);
979 }
980 if (res.data.umts_info_neighboring_lte.frequency_n > 0)
981 blobmsg_close_table(&status, c);
982 }
983 if (res.set.nr5g_cell_information) {
984 c = blobmsg_open_table(&status, "nr5g_cell_information");
985 blobmsg_add_u32(&status, "enodeb_id",
986 res.data.nr5g_cell_information.global_cell_id/256);
987 blobmsg_add_u32(&status, "cell_id",
988 res.data.nr5g_cell_information.global_cell_id%256);
989 blobmsg_add_u32(&status, "physical_cell_id",
990 res.data.nr5g_cell_information.physical_cell_id);
991 blobmsg_add_double(&status, "rsrq", ((double)res.data.nr5g_cell_information.rsrq)/10);
992 blobmsg_add_double(&status, "rsrp", ((double)res.data.nr5g_cell_information.rsrp)/10);
993 blobmsg_add_double(&status, "snr", ((double)res.data.nr5g_cell_information.snr)/10);
994 blobmsg_close_table(&status, c);
995 }
996 if (res.set.nr5g_arfcn) {
997 c = blobmsg_open_table(&status, "nr5g_arfcn");
998 blobmsg_add_u32(&status, "arfcn",
999 res.data.nr5g_arfcn);
1000 blobmsg_close_table(&status, c);
1001 }
1002 blobmsg_close_table(&status, t);
1003 }
1004
1005 static enum qmi_cmd_result
1006 cmd_nas_get_cell_location_info_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
1007 {
1008 qmi_set_nas_get_cell_location_info_request(msg);
1009 return QMI_CMD_REQUEST;
1010 }
1011
1012 static enum qmi_cmd_result
1013 cmd_nas_get_signal_info_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
1014 {
1015 qmi_set_nas_get_signal_info_request(msg);
1016 return QMI_CMD_REQUEST;
1017 }
1018
1019 static void
1020 cmd_nas_get_serving_system_cb(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg)
1021 {
1022 struct qmi_nas_get_serving_system_response res;
1023 static const char *reg_states[] = {
1024 [QMI_NAS_REGISTRATION_STATE_NOT_REGISTERED] = "not_registered",
1025 [QMI_NAS_REGISTRATION_STATE_REGISTERED] = "registered",
1026 [QMI_NAS_REGISTRATION_STATE_NOT_REGISTERED_SEARCHING] = "searching",
1027 [QMI_NAS_REGISTRATION_STATE_REGISTRATION_DENIED] = "registering_denied",
1028 [QMI_NAS_REGISTRATION_STATE_UNKNOWN] = "unknown",
1029 };
1030 void *c;
1031
1032 qmi_parse_nas_get_serving_system_response(msg, &res);
1033
1034 c = blobmsg_open_table(&status, NULL);
1035 if (res.set.serving_system) {
1036 int state = res.data.serving_system.registration_state;
1037
1038 if (state > QMI_NAS_REGISTRATION_STATE_UNKNOWN)
1039 state = QMI_NAS_REGISTRATION_STATE_UNKNOWN;
1040
1041 blobmsg_add_string(&status, "registration", reg_states[state]);
1042 }
1043 if (res.set.current_plmn) {
1044 blobmsg_add_u32(&status, "plmn_mcc", res.data.current_plmn.mcc);
1045 blobmsg_add_u32(&status, "plmn_mnc", res.data.current_plmn.mnc);
1046 if (res.data.current_plmn.description)
1047 blobmsg_add_string(&status, "plmn_description", res.data.current_plmn.description);
1048 }
1049
1050 if (res.set.roaming_indicator)
1051 blobmsg_add_u8(&status, "roaming", !res.data.roaming_indicator);
1052
1053 blobmsg_close_table(&status, c);
1054 }
1055
1056 static enum qmi_cmd_result
1057 cmd_nas_get_serving_system_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
1058 {
1059 qmi_set_nas_get_serving_system_request(msg);
1060 return QMI_CMD_REQUEST;
1061 }
1062
1063 static void
1064 cmd_nas_get_plmn_cb(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg)
1065 {
1066 struct qmi_nas_get_system_selection_preference_response res;
1067 static const char *modes[] = {
1068 [QMI_NAS_NETWORK_SELECTION_PREFERENCE_AUTOMATIC] = "automatic",
1069 [QMI_NAS_NETWORK_SELECTION_PREFERENCE_MANUAL] = "manual",
1070 };
1071 void *c;
1072
1073 qmi_parse_nas_get_system_selection_preference_response(msg, &res);
1074
1075 c = blobmsg_open_table(&status, NULL);
1076 if (res.set.network_selection_preference) {
1077 blobmsg_add_string(&status, "mode", modes[res.data.network_selection_preference]);
1078 }
1079 if (res.set.manual_network_selection) {
1080 blobmsg_add_u32(&status, "mcc", res.data.manual_network_selection.mcc);
1081 blobmsg_add_u32(&status, "mnc", res.data.manual_network_selection.mnc);
1082 }
1083
1084 blobmsg_close_table(&status, c);
1085 }
1086
1087 static enum qmi_cmd_result
1088 cmd_nas_get_plmn_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
1089 {
1090 qmi_set_nas_get_system_selection_preference_request(msg);
1091 return QMI_CMD_REQUEST;
1092 }
1093
1094 static void
1095 cmd_nas_network_scan_cb(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg)
1096 {
1097 static struct qmi_nas_network_scan_response res;
1098 const char *network_status[] = {
1099 "current_serving",
1100 "available",
1101 "home",
1102 "roaming",
1103 "forbidden",
1104 "not_forbidden",
1105 "preferred",
1106 "not_preferred",
1107 };
1108 const char *radio[] = {
1109 [QMI_NAS_RADIO_INTERFACE_NONE] = "none",
1110 [QMI_NAS_RADIO_INTERFACE_CDMA_1X] = "cdma-1x",
1111 [QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO] = "cdma-1x_evdo",
1112 [QMI_NAS_RADIO_INTERFACE_AMPS] = "amps",
1113 [QMI_NAS_RADIO_INTERFACE_GSM] = "gsm",
1114 [QMI_NAS_RADIO_INTERFACE_UMTS] = "umts",
1115 [QMI_NAS_RADIO_INTERFACE_LTE] = "lte",
1116 [QMI_NAS_RADIO_INTERFACE_TD_SCDMA] = "td-scdma",
1117 [QMI_NAS_RADIO_INTERFACE_5GNR] = "5gnr",
1118 };
1119 void *t, *c, *info, *stat;
1120 int i, j;
1121
1122 qmi_parse_nas_network_scan_response(msg, &res);
1123
1124 t = blobmsg_open_table(&status, NULL);
1125
1126 c = blobmsg_open_array(&status, "network_info");
1127 for (i = 0; i < res.data.network_information_n; i++) {
1128 info = blobmsg_open_table(&status, NULL);
1129 blobmsg_add_u32(&status, "mcc", res.data.network_information[i].mcc);
1130 blobmsg_add_u32(&status, "mnc", res.data.network_information[i].mnc);
1131 if (res.data.network_information[i].description)
1132 blobmsg_add_string(&status, "description", res.data.network_information[i].description);
1133 stat = blobmsg_open_array(&status, "status");
1134 for (j = 0; j < ARRAY_SIZE(network_status); j++) {
1135 if (!(res.data.network_information[i].network_status & (1 << j)))
1136 continue;
1137
1138 blobmsg_add_string(&status, NULL, network_status[j]);
1139 }
1140 blobmsg_close_array(&status, stat);
1141 blobmsg_close_table(&status, info);
1142 }
1143 blobmsg_close_array(&status, c);
1144
1145 c = blobmsg_open_array(&status, "radio_access_technology");
1146 for (i = 0; i < res.data.radio_access_technology_n; i++) {
1147 const char *r = "unknown";
1148 int r_i = res.data.radio_access_technology[i].radio_interface;
1149
1150 info = blobmsg_open_table(&status, NULL);
1151 blobmsg_add_u32(&status, "mcc", res.data.radio_access_technology[i].mcc);
1152 blobmsg_add_u32(&status, "mnc", res.data.radio_access_technology[i].mnc);
1153 if (r_i >= 0 && r_i < ARRAY_SIZE(radio))
1154 r = radio[r_i];
1155
1156 blobmsg_add_string(&status, "radio", r);
1157 blobmsg_close_table(&status, info);
1158 }
1159 blobmsg_close_array(&status, c);
1160
1161 blobmsg_close_table(&status, t);
1162 }
1163
1164 static enum qmi_cmd_result
1165 cmd_nas_network_scan_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
1166 {
1167 struct qmi_nas_network_scan_request sreq = {
1168 QMI_INIT(network_type,
1169 QMI_NAS_NETWORK_SCAN_TYPE_GSM |
1170 QMI_NAS_NETWORK_SCAN_TYPE_UMTS |
1171 QMI_NAS_NETWORK_SCAN_TYPE_LTE |
1172 QMI_NAS_NETWORK_SCAN_TYPE_TD_SCDMA),
1173 };
1174
1175 qmi_set_nas_network_scan_request(msg, &sreq);
1176 return QMI_CMD_REQUEST;
1177 }