uqmi: print radio interfaces in serving system command
[project/uqmi.git] / commands-wds.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 <stdlib.h>
23 #include <arpa/inet.h>
24
25 #include "qmi-message.h"
26
27 static struct qmi_wds_start_network_request wds_sn_req = {
28 QMI_INIT(authentication_preference,
29 QMI_WDS_AUTHENTICATION_PAP | QMI_WDS_AUTHENTICATION_CHAP),
30 };
31 static struct qmi_wds_stop_network_request wds_stn_req;
32
33 #define cmd_wds_set_apn_cb no_cb
34 static enum qmi_cmd_result
35 cmd_wds_set_apn_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
36 {
37 qmi_set_ptr(&wds_sn_req, apn, arg);
38 return QMI_CMD_DONE;
39 }
40
41 #define cmd_wds_set_auth_cb no_cb
42 static enum qmi_cmd_result
43 cmd_wds_set_auth_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
44 {
45 static const struct {
46 const char *name;
47 QmiWdsAuthentication auth;
48 } modes[] = {
49 { "pap", QMI_WDS_AUTHENTICATION_PAP },
50 { "chap", QMI_WDS_AUTHENTICATION_CHAP },
51 { "both", QMI_WDS_AUTHENTICATION_PAP | QMI_WDS_AUTHENTICATION_CHAP },
52 { "none", QMI_WDS_AUTHENTICATION_NONE },
53 };
54 int i;
55
56 for (i = 0; i < ARRAY_SIZE(modes); i++) {
57 if (strcasecmp(modes[i].name, arg) != 0)
58 continue;
59
60 qmi_set(&wds_sn_req, authentication_preference, modes[i].auth);
61 return QMI_CMD_DONE;
62 }
63
64 uqmi_add_error("Invalid auth mode (valid: pap, chap, both, none)");
65 return QMI_CMD_EXIT;
66 }
67
68 #define cmd_wds_set_username_cb no_cb
69 static enum qmi_cmd_result
70 cmd_wds_set_username_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
71 {
72 qmi_set_ptr(&wds_sn_req, username, arg);
73 return QMI_CMD_DONE;
74 }
75
76 #define cmd_wds_set_password_cb no_cb
77 static enum qmi_cmd_result
78 cmd_wds_set_password_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
79 {
80 qmi_set_ptr(&wds_sn_req, password, arg);
81 return QMI_CMD_DONE;
82 }
83
84 #define cmd_wds_set_autoconnect_cb no_cb
85 static enum qmi_cmd_result
86 cmd_wds_set_autoconnect_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
87 {
88 qmi_set(&wds_sn_req, enable_autoconnect, true);
89 qmi_set(&wds_stn_req, disable_autoconnect, true);
90 return QMI_CMD_DONE;
91 }
92
93 #define cmd_wds_set_ip_family_pref_cb no_cb
94 static enum qmi_cmd_result
95 cmd_wds_set_ip_family_pref_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
96 {
97 static const struct {
98 const char *name;
99 const QmiWdsIpFamily mode;
100 } modes[] = {
101 { "ipv4", QMI_WDS_IP_FAMILY_IPV4 },
102 { "ipv6", QMI_WDS_IP_FAMILY_IPV6 },
103 { "unspecified", QMI_WDS_IP_FAMILY_UNSPECIFIED },
104 };
105 int i;
106
107 for (i = 0; i < ARRAY_SIZE(modes); i++) {
108 if (strcasecmp(modes[i].name, arg) != 0)
109 continue;
110
111 qmi_set(&wds_sn_req, ip_family_preference, modes[i].mode);
112 return QMI_CMD_DONE;
113 }
114
115 uqmi_add_error("Invalid value (valid: ipv4, ipv6, unspecified)");
116 return QMI_CMD_EXIT;
117 }
118
119 #define cmd_wds_set_profile_cb no_cb
120 static enum qmi_cmd_result
121 cmd_wds_set_profile_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
122 {
123 uint32_t idx = strtoul(arg, NULL, 10);
124
125 qmi_set(&wds_sn_req, profile_index_3gpp, idx);
126 return QMI_CMD_DONE;
127 }
128
129 static void
130 cmd_wds_start_network_cb(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg)
131 {
132 struct qmi_wds_start_network_response res;
133
134 qmi_parse_wds_start_network_response(msg, &res);
135 if (res.set.packet_data_handle)
136 blobmsg_add_u32(&status, NULL, res.data.packet_data_handle);
137 }
138
139 static enum qmi_cmd_result
140 cmd_wds_start_network_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
141 {
142 qmi_set_wds_start_network_request(msg, &wds_sn_req);
143 return QMI_CMD_REQUEST;
144 }
145
146 #define cmd_wds_stop_network_cb no_cb
147 static enum qmi_cmd_result
148 cmd_wds_stop_network_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
149 {
150 uint32_t pdh = strtoul(arg, NULL, 0);
151
152 qmi_set(&wds_stn_req, packet_data_handle, pdh);
153 qmi_set_wds_stop_network_request(msg, &wds_stn_req);
154 return QMI_CMD_REQUEST;
155 }
156
157 static void
158 cmd_wds_get_packet_service_status_cb(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg)
159 {
160 struct qmi_wds_get_packet_service_status_response res;
161 const char *data_status[] = {
162 [QMI_WDS_CONNECTION_STATUS_UNKNOWN] = "unknown",
163 [QMI_WDS_CONNECTION_STATUS_DISCONNECTED] = "disconnected",
164 [QMI_WDS_CONNECTION_STATUS_CONNECTED] = "connected",
165 [QMI_WDS_CONNECTION_STATUS_SUSPENDED] = "suspended",
166 [QMI_WDS_CONNECTION_STATUS_AUTHENTICATING] = "authenticating",
167 };
168 int s = 0;
169
170 qmi_parse_wds_get_packet_service_status_response(msg, &res);
171 if (res.set.connection_status &&
172 res.data.connection_status < ARRAY_SIZE(data_status))
173 s = res.data.connection_status;
174
175 blobmsg_add_string(&status, NULL, data_status[s]);
176 }
177
178 static enum qmi_cmd_result
179 cmd_wds_get_packet_service_status_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
180 {
181 qmi_set_wds_get_packet_service_status_request(msg);
182 return QMI_CMD_REQUEST;
183 }
184
185 #define cmd_wds_set_autoconnect_setting_cb no_cb
186 static enum qmi_cmd_result
187 cmd_wds_set_autoconnect_setting_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
188 {
189 struct qmi_wds_set_autoconnect_setting_request ac_req;
190 const char *modes[] = {
191 [QMI_WDS_AUTOCONNECT_DISABLED] = "disabled",
192 [QMI_WDS_AUTOCONNECT_ENABLED] = "enabled",
193 [QMI_WDS_AUTOCONNECT_PAUSED] = "paused",
194 };
195 int i;
196
197 for (i = 0; i < ARRAY_SIZE(modes); i++) {
198 if (strcasecmp(modes[i], arg) != 0)
199 continue;
200
201 qmi_set(&ac_req, setting, i);
202 qmi_set_wds_set_autoconnect_setting_request(msg, &ac_req);
203 return QMI_CMD_DONE;
204 }
205
206 uqmi_add_error("Invalid value (valid: disabled, enabled, paused)");
207 return QMI_CMD_EXIT;
208 }
209
210 #define cmd_wds_reset_cb no_cb
211 static enum qmi_cmd_result
212 cmd_wds_reset_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
213 {
214 qmi_set_wds_reset_request(msg);
215 return QMI_CMD_REQUEST;
216 }
217
218 #define cmd_wds_set_ip_family_cb no_cb
219 static enum qmi_cmd_result
220 cmd_wds_set_ip_family_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
221 {
222 struct qmi_wds_set_ip_family_request ipf_req;
223 const struct ip_modes {
224 const char *name;
225 const QmiWdsIpFamily mode;
226 } modes[] = {
227 { "ipv4", QMI_WDS_IP_FAMILY_IPV4 },
228 { "ipv6", QMI_WDS_IP_FAMILY_IPV6 },
229 { "unspecified", QMI_WDS_IP_FAMILY_UNSPECIFIED },
230 };
231 int i;
232
233 for (i = 0; i < ARRAY_SIZE(modes); i++) {
234 if (strcasecmp(modes[i].name, arg) != 0)
235 continue;
236
237 qmi_set(&ipf_req, preference, modes[i].mode);
238 qmi_set_wds_set_ip_family_request(msg, &ipf_req);
239 return QMI_CMD_REQUEST;
240 }
241
242 uqmi_add_error("Invalid value (valid: ipv4, ipv6, unspecified)");
243 return QMI_CMD_EXIT;
244 }
245
246 static void wds_to_ipv4(const char *name, const uint32_t addr)
247 {
248 struct in_addr ip_addr;
249 char buf[INET_ADDRSTRLEN];
250
251 ip_addr.s_addr = htonl(addr);
252 blobmsg_add_string(&status, name, inet_ntop(AF_INET, &ip_addr, buf, sizeof(buf)));
253 }
254
255 static void wds_to_ipv6(const char *name, const uint16_t *addr)
256 {
257 char buf[INET6_ADDRSTRLEN];
258 uint16_t ip_addr[8];
259 int i;
260
261 for (i = 0; i < ARRAY_SIZE(ip_addr); i++)
262 ip_addr[i] = htons(addr[i]);
263
264 blobmsg_add_string(&status, name, inet_ntop(AF_INET6, &ip_addr, buf, sizeof(buf)));
265 }
266
267 static void
268 cmd_wds_get_current_settings_cb(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg)
269 {
270 void *v4, *v6, *d, *t;
271 struct qmi_wds_get_current_settings_response res;
272 const char *pdptypes[] = {
273 [QMI_WDS_PDP_TYPE_IPV4] = "ipv4",
274 [QMI_WDS_PDP_TYPE_PPP] = "ppp",
275 [QMI_WDS_PDP_TYPE_IPV6] = "ipv6",
276 [QMI_WDS_PDP_TYPE_IPV4_OR_IPV6] = "ipv4-or-ipv6",
277 };
278 const struct ip_modes {
279 const char *name;
280 const QmiWdsIpFamily mode;
281 } modes[] = {
282 { "ipv4", QMI_WDS_IP_FAMILY_IPV4 },
283 { "ipv6", QMI_WDS_IP_FAMILY_IPV6 },
284 { "unspecified", QMI_WDS_IP_FAMILY_UNSPECIFIED },
285 };
286 int i;
287
288 qmi_parse_wds_get_current_settings_response(msg, &res);
289
290 t = blobmsg_open_table(&status, NULL);
291
292 if (res.set.pdp_type && (int) res.data.pdp_type < ARRAY_SIZE(pdptypes))
293 blobmsg_add_string(&status, "pdp-type", pdptypes[res.data.pdp_type]);
294
295 if (res.set.ip_family) {
296 for (i = 0; i < ARRAY_SIZE(modes); i++) {
297 if (modes[i].mode != res.data.ip_family)
298 continue;
299 blobmsg_add_string(&status, "ip-family", modes[i].name);
300 break;
301 }
302 }
303
304 if (res.set.mtu)
305 blobmsg_add_u32(&status, "mtu", res.data.mtu);
306
307 /* IPV4 */
308 v4 = blobmsg_open_table(&status, "ipv4");
309
310 if (res.set.ipv4_address)
311 wds_to_ipv4("ip", res.data.ipv4_address);
312 if (res.set.primary_ipv4_dns_address)
313 wds_to_ipv4("dns1", res.data.primary_ipv4_dns_address);
314 if (res.set.secondary_ipv4_dns_address)
315 wds_to_ipv4("dns2", res.data.secondary_ipv4_dns_address);
316 if (res.set.ipv4_gateway_address)
317 wds_to_ipv4("gateway", res.data.ipv4_gateway_address);
318 if (res.set.ipv4_gateway_subnet_mask)
319 wds_to_ipv4("subnet", res.data.ipv4_gateway_subnet_mask);
320 blobmsg_close_table(&status, v4);
321
322 /* IPV6 */
323 v6 = blobmsg_open_table(&status, "ipv6");
324
325 if (res.set.ipv6_address) {
326 wds_to_ipv6("ip", res.data.ipv6_address.address);
327 blobmsg_add_u32(&status, "ip-prefix-length", res.data.ipv6_address.prefix_length);
328 }
329 if (res.set.ipv6_gateway_address) {
330 wds_to_ipv6("gateway", res.data.ipv6_gateway_address.address);
331 blobmsg_add_u32(&status, "gw-prefix-length", res.data.ipv6_gateway_address.prefix_length);
332 }
333 if (res.set.ipv6_primary_dns_address)
334 wds_to_ipv6("dns1", res.data.ipv6_primary_dns_address);
335 if (res.set.ipv6_secondary_dns_address)
336 wds_to_ipv6("dns2", res.data.ipv6_secondary_dns_address);
337
338 blobmsg_close_table(&status, v6);
339
340 d = blobmsg_open_table(&status, "domain-names");
341 for (i = 0; i < res.data.domain_name_list_n; i++) {
342 blobmsg_add_string(&status, NULL, res.data.domain_name_list[i]);
343 }
344 blobmsg_close_table(&status, d);
345
346 blobmsg_close_table(&status, t);
347 }
348
349 static enum qmi_cmd_result
350 cmd_wds_get_current_settings_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
351 {
352 struct qmi_wds_get_current_settings_request gcs_req;
353 memset(&gcs_req, '\0', sizeof(struct qmi_wds_get_current_settings_request));
354 qmi_set(&gcs_req, requested_settings,
355 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_PDP_TYPE |
356 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_DNS_ADDRESS |
357 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_GRANTED_QOS |
358 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_IP_ADDRESS |
359 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_GATEWAY_INFO |
360 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_MTU |
361 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_DOMAIN_NAME_LIST |
362 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_IP_FAMILY);
363 qmi_set_wds_get_current_settings_request(msg, &gcs_req);
364 return QMI_CMD_REQUEST;
365 }