wds: add option for specifying profile index
[project/uqmi.git] / 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 "qmi-message.h"
23
24 static struct qmi_nas_set_system_selection_preference_request sel_req;
25 static struct {
26 bool mcc_is_set;
27 bool mnc_is_set;
28 } plmn_code_flag;
29
30 #define cmd_nas_do_set_system_selection_cb no_cb
31 static enum qmi_cmd_result
32 cmd_nas_do_set_system_selection_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
33 {
34 qmi_set_nas_set_system_selection_preference_request(msg, &sel_req);
35 return QMI_CMD_REQUEST;
36 }
37
38 static enum qmi_cmd_result
39 do_sel_network(void)
40 {
41 static bool use_sel_req = false;
42
43 if (!use_sel_req) {
44 use_sel_req = true;
45 uqmi_add_command(NULL, __UQMI_COMMAND_nas_do_set_system_selection);
46 }
47
48 return QMI_CMD_DONE;
49 }
50
51 #define cmd_nas_set_network_modes_cb no_cb
52 static enum qmi_cmd_result
53 cmd_nas_set_network_modes_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
54 {
55 static const struct {
56 const char *name;
57 QmiNasRatModePreference val;
58 } modes[] = {
59 { "cdma", QMI_NAS_RAT_MODE_PREFERENCE_CDMA_1X | QMI_NAS_RAT_MODE_PREFERENCE_CDMA_1XEVDO },
60 { "td-scdma", QMI_NAS_RAT_MODE_PREFERENCE_TD_SCDMA },
61 { "gsm", QMI_NAS_RAT_MODE_PREFERENCE_GSM },
62 { "umts", QMI_NAS_RAT_MODE_PREFERENCE_UMTS },
63 { "lte", QMI_NAS_RAT_MODE_PREFERENCE_LTE },
64 };
65 QmiNasRatModePreference val = 0;
66 char *word;
67 int i;
68
69 for (word = strtok(arg, ",");
70 word;
71 word = strtok(NULL, ",")) {
72 bool found = false;
73
74 for (i = 0; i < ARRAY_SIZE(modes); i++) {
75 if (strcmp(word, modes[i].name) != 0 &&
76 strcmp(word, "all") != 0)
77 continue;
78
79 val |= modes[i].val;
80 found = true;
81 }
82
83 if (!found) {
84 uqmi_add_error("Invalid network mode");
85 return QMI_CMD_EXIT;
86 }
87 }
88
89 qmi_set(&sel_req, mode_preference, val);
90 return do_sel_network();
91 }
92
93 #define cmd_nas_set_network_preference_cb no_cb
94 static enum qmi_cmd_result
95 cmd_nas_set_network_preference_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
96 {
97 QmiNasGsmWcdmaAcquisitionOrderPreference pref = QMI_NAS_GSM_WCDMA_ACQUISITION_ORDER_PREFERENCE_AUTOMATIC;
98
99 if (!strcmp(arg, "gsm"))
100 pref = QMI_NAS_GSM_WCDMA_ACQUISITION_ORDER_PREFERENCE_GSM;
101 else if (!strcmp(arg, "wcdma"))
102 pref = QMI_NAS_GSM_WCDMA_ACQUISITION_ORDER_PREFERENCE_WCDMA;
103
104 qmi_set(&sel_req, gsm_wcdma_acquisition_order_preference, pref);
105 return do_sel_network();
106 }
107
108 #define cmd_nas_set_roaming_cb no_cb
109 static enum qmi_cmd_result
110 cmd_nas_set_roaming_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
111 {
112 QmiNasRoamingPreference pref;
113
114 if (!strcmp(arg, "any"))
115 pref = QMI_NAS_ROAMING_PREFERENCE_ANY;
116 else if (!strcmp(arg, "only"))
117 pref = QMI_NAS_ROAMING_PREFERENCE_NOT_OFF;
118 else if (!strcmp(arg, "off"))
119 pref = QMI_NAS_ROAMING_PREFERENCE_OFF;
120 else
121 return uqmi_add_error("Invalid argument");
122
123 qmi_set(&sel_req, roaming_preference, pref);
124 return do_sel_network();
125 }
126
127 #define cmd_nas_set_mcc_cb no_cb
128 static enum qmi_cmd_result
129 cmd_nas_set_mcc_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
130 {
131 char *err;
132 int value = strtoul(arg, &err, 10);
133 if (err && *err) {
134 uqmi_add_error("Invalid MCC value");
135 return QMI_CMD_EXIT;
136 }
137
138 sel_req.data.network_selection_preference.mcc = value;
139 plmn_code_flag.mcc_is_set = true;
140 return QMI_CMD_DONE;
141 }
142
143 #define cmd_nas_set_mnc_cb no_cb
144 static enum qmi_cmd_result
145 cmd_nas_set_mnc_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
146 {
147 char *err;
148 int value = strtoul(arg, &err, 10);
149 if (err && *err) {
150 uqmi_add_error("Invalid MNC value");
151 return QMI_CMD_EXIT;
152 }
153
154 sel_req.data.network_selection_preference.mnc = value;
155 plmn_code_flag.mnc_is_set = true;
156 return QMI_CMD_DONE;
157 }
158
159 #define cmd_nas_set_plmn_cb no_cb
160 static enum qmi_cmd_result
161 cmd_nas_set_plmn_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
162 {
163 sel_req.set.network_selection_preference = 1;
164 sel_req.data.network_selection_preference.mode = QMI_NAS_NETWORK_SELECTION_PREFERENCE_AUTOMATIC;
165
166 if (!plmn_code_flag.mcc_is_set && plmn_code_flag.mnc_is_set) {
167 uqmi_add_error("No MCC value");
168 return QMI_CMD_EXIT;
169 }
170
171 if (plmn_code_flag.mcc_is_set && sel_req.data.network_selection_preference.mcc) {
172 if (!plmn_code_flag.mnc_is_set) {
173 uqmi_add_error("No MNC value");
174 return QMI_CMD_EXIT;
175 } else {
176 sel_req.data.network_selection_preference.mode = QMI_NAS_NETWORK_SELECTION_PREFERENCE_MANUAL;
177 }
178 }
179
180 return do_sel_network();
181 }
182
183 #define cmd_nas_initiate_network_register_cb no_cb
184 static enum qmi_cmd_result
185 cmd_nas_initiate_network_register_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
186 {
187 static struct qmi_nas_initiate_network_register_request register_req = {
188 QMI_INIT(action, QMI_NAS_NETWORK_REGISTER_TYPE_AUTOMATIC)
189 };
190
191 qmi_set_nas_initiate_network_register_request(msg, &register_req);
192 return QMI_CMD_REQUEST;
193 }
194
195 static void
196 cmd_nas_get_signal_info_cb(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg)
197 {
198 struct qmi_nas_get_signal_info_response res;
199 void *c;
200
201 qmi_parse_nas_get_signal_info_response(msg, &res);
202
203 c = blobmsg_open_table(&status, NULL);
204 if (res.set.cdma_signal_strength) {
205 blobmsg_add_string(&status, "type", "cdma");
206 blobmsg_add_u32(&status, "rssi", (int32_t) res.data.cdma_signal_strength.rssi);
207 blobmsg_add_u32(&status, "ecio", (int32_t) res.data.cdma_signal_strength.ecio);
208 }
209
210 if (res.set.hdr_signal_strength) {
211 blobmsg_add_string(&status, "type", "hdr");
212 blobmsg_add_u32(&status, "rssi", (int32_t) res.data.hdr_signal_strength.rssi);
213 blobmsg_add_u32(&status, "ecio", (int32_t) res.data.hdr_signal_strength.ecio);
214 blobmsg_add_u32(&status, "io", res.data.hdr_signal_strength.io);
215 }
216
217 if (res.set.gsm_signal_strength) {
218 blobmsg_add_string(&status, "type", "gsm");
219 blobmsg_add_u32(&status, "signal", (int32_t) res.data.gsm_signal_strength);
220 }
221
222 if (res.set.wcdma_signal_strength) {
223 blobmsg_add_string(&status, "type", "wcdma");
224 blobmsg_add_u32(&status, "rssi", (int32_t) res.data.wcdma_signal_strength.rssi);
225 blobmsg_add_u32(&status, "ecio", (int32_t) res.data.wcdma_signal_strength.ecio);
226 }
227
228 if (res.set.lte_signal_strength) {
229 blobmsg_add_string(&status, "type", "lte");
230 blobmsg_add_u32(&status, "rssi", (int32_t) res.data.lte_signal_strength.rssi);
231 blobmsg_add_u32(&status, "rsrq", (int32_t) res.data.lte_signal_strength.rsrq);
232 blobmsg_add_u32(&status, "rsrp", (int32_t) res.data.lte_signal_strength.rsrp);
233 blobmsg_add_u32(&status, "snr", (int32_t) res.data.lte_signal_strength.snr);
234 }
235
236 if (res.set.tdma_signal_strength) {
237 blobmsg_add_string(&status, "type", "tdma");
238 blobmsg_add_u32(&status, "signal", (int32_t) res.data.tdma_signal_strength);
239 }
240
241 blobmsg_close_table(&status, c);
242 }
243
244 static enum qmi_cmd_result
245 cmd_nas_get_signal_info_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
246 {
247 qmi_set_nas_get_signal_info_request(msg);
248 return QMI_CMD_REQUEST;
249 }
250
251 static void
252 cmd_nas_get_serving_system_cb(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg)
253 {
254 struct qmi_nas_get_serving_system_response res;
255 static const char *reg_states[] = {
256 [QMI_NAS_REGISTRATION_STATE_NOT_REGISTERED] = "not_registered",
257 [QMI_NAS_REGISTRATION_STATE_REGISTERED] = "registered",
258 [QMI_NAS_REGISTRATION_STATE_NOT_REGISTERED_SEARCHING] = "searching",
259 [QMI_NAS_REGISTRATION_STATE_REGISTRATION_DENIED] = "registering_denied",
260 [QMI_NAS_REGISTRATION_STATE_UNKNOWN] = "unknown",
261 };
262 void *c;
263
264 qmi_parse_nas_get_serving_system_response(msg, &res);
265
266 c = blobmsg_open_table(&status, NULL);
267 if (res.set.serving_system) {
268 int state = res.data.serving_system.registration_state;
269
270 if (state > QMI_NAS_REGISTRATION_STATE_UNKNOWN)
271 state = QMI_NAS_REGISTRATION_STATE_UNKNOWN;
272
273 blobmsg_add_string(&status, "registration", reg_states[state]);
274 }
275 if (res.set.current_plmn) {
276 blobmsg_add_u32(&status, "plmn_mcc", res.data.current_plmn.mcc);
277 blobmsg_add_u32(&status, "plmn_mnc", res.data.current_plmn.mnc);
278 if (res.data.current_plmn.description)
279 blobmsg_add_string(&status, "plmn_description", res.data.current_plmn.description);
280 }
281
282 if (res.set.roaming_indicator)
283 blobmsg_add_u8(&status, "roaming", !res.data.roaming_indicator);
284
285 blobmsg_close_table(&status, c);
286 }
287
288 static enum qmi_cmd_result
289 cmd_nas_get_serving_system_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
290 {
291 qmi_set_nas_get_serving_system_request(msg);
292 return QMI_CMD_REQUEST;
293 }
294
295 static void
296 cmd_nas_network_scan_cb(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg)
297 {
298 static struct qmi_nas_network_scan_response res;
299 const char *network_status[] = {
300 "current_serving",
301 "available",
302 "home",
303 "roaming",
304 "forbidden",
305 "not_forbidden",
306 "preferred",
307 "not_preferred",
308 };
309 const char *radio[] = {
310 [QMI_NAS_RADIO_INTERFACE_NONE] = "none",
311 [QMI_NAS_RADIO_INTERFACE_CDMA_1X] = "cdma-1x",
312 [QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO] = "cdma-1x_evdo",
313 [QMI_NAS_RADIO_INTERFACE_AMPS] = "amps",
314 [QMI_NAS_RADIO_INTERFACE_GSM] = "gsm",
315 [QMI_NAS_RADIO_INTERFACE_UMTS] = "umts",
316 [QMI_NAS_RADIO_INTERFACE_LTE] = "lte",
317 [QMI_NAS_RADIO_INTERFACE_TD_SCDMA] = "td-scdma",
318 };
319 void *t, *c, *info, *stat;
320 int i, j;
321
322 qmi_parse_nas_network_scan_response(msg, &res);
323
324 t = blobmsg_open_table(&status, NULL);
325
326 c = blobmsg_open_array(&status, "network_info");
327 for (i = 0; i < res.data.network_information_n; i++) {
328 info = blobmsg_open_table(&status, NULL);
329 blobmsg_add_u32(&status, "mcc", res.data.network_information[i].mcc);
330 blobmsg_add_u32(&status, "mnc", res.data.network_information[i].mnc);
331 if (res.data.network_information[i].description)
332 blobmsg_add_string(&status, "description", res.data.network_information[i].description);
333 stat = blobmsg_open_array(&status, "status");
334 for (j = 0; j < ARRAY_SIZE(network_status); j++) {
335 if (!(res.data.network_information[i].network_status & (1 << j)))
336 continue;
337
338 blobmsg_add_string(&status, NULL, network_status[j]);
339 }
340 blobmsg_close_array(&status, stat);
341 blobmsg_close_table(&status, info);
342 }
343 blobmsg_close_array(&status, c);
344
345 c = blobmsg_open_array(&status, "radio_access_technology");
346 for (i = 0; i < res.data.radio_access_technology_n; i++) {
347 const char *r = "unknown";
348 int r_i = res.data.radio_access_technology[i].radio_interface;
349
350 info = blobmsg_open_table(&status, NULL);
351 blobmsg_add_u32(&status, "mcc", res.data.radio_access_technology[i].mcc);
352 blobmsg_add_u32(&status, "mnc", res.data.radio_access_technology[i].mnc);
353 if (r_i >= 0 && r_i < ARRAY_SIZE(radio))
354 r = radio[r_i];
355
356 blobmsg_add_string(&status, "radio", r);
357 blobmsg_close_table(&status, info);
358 }
359 blobmsg_close_array(&status, c);
360
361 blobmsg_close_table(&status, t);
362 }
363
364 static enum qmi_cmd_result
365 cmd_nas_network_scan_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
366 {
367 struct qmi_nas_network_scan_request sreq = {};
368
369 qmi_set_nas_network_scan_request(msg, &sreq);
370 return QMI_CMD_REQUEST;
371 }