2 * uqmi -- tiny QMI support implementation
4 * Copyright (C) 2014-2015 Felix Fietkau <nbd@openwrt.org>
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.
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.
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.
23 #include <arpa/inet.h>
25 #include "qmi-message.h"
28 const char *auth_name
;
29 QmiWdsAuthentication auth
;
31 { "none", QMI_WDS_AUTHENTICATION_NONE
},
32 { "pap", QMI_WDS_AUTHENTICATION_PAP
},
33 { "chap", QMI_WDS_AUTHENTICATION_CHAP
},
34 { "both", QMI_WDS_AUTHENTICATION_PAP
| QMI_WDS_AUTHENTICATION_CHAP
},
38 const char *ipfam_name
;
39 const QmiWdsIpFamily mode
;
41 { "ipv4", QMI_WDS_IP_FAMILY_IPV4
},
42 { "ipv6", QMI_WDS_IP_FAMILY_IPV6
},
43 { "unspecified", QMI_WDS_IP_FAMILY_UNSPECIFIED
},
48 const QmiWdsPdpType type
;
50 { "ipv4", QMI_WDS_PDP_TYPE_IPV4
},
51 { "ppp", QMI_WDS_PDP_TYPE_PPP
},
52 { "ipv6", QMI_WDS_PDP_TYPE_IPV6
},
53 { "ipv4v6", QMI_WDS_PDP_TYPE_IPV4_OR_IPV6
},
57 const char *profile_name
;
58 const QmiWdsProfileType profile
;
60 { "3gpp", QMI_WDS_PROFILE_TYPE_3GPP
},
61 { "3gpp2", QMI_WDS_PROFILE_TYPE_3GPP2
},
64 static struct qmi_wds_start_network_request wds_sn_req
= {
65 QMI_INIT(authentication_preference
,
66 QMI_WDS_AUTHENTICATION_PAP
| QMI_WDS_AUTHENTICATION_CHAP
),
69 static struct qmi_wds_stop_network_request wds_stn_req
;
71 static struct qmi_wds_modify_profile_request wds_mp_req
= {
72 QMI_INIT_SEQUENCE(profile_identifier
,
73 .profile_type
= QMI_WDS_PROFILE_TYPE_3GPP
,
76 QMI_INIT(apn_disabled_flag
, false),
79 static struct qmi_wds_create_profile_request wds_cp_req
= {
80 QMI_INIT(profile_type
,QMI_WDS_PROFILE_TYPE_3GPP
),
81 QMI_INIT(apn_disabled_flag
, false),
84 #define cmd_wds_set_apn_cb no_cb
85 static enum qmi_cmd_result
86 cmd_wds_set_apn_prepare(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, char *arg
)
88 qmi_set_ptr(&wds_sn_req
, apn
, arg
);
89 qmi_set_ptr(&wds_mp_req
, apn_name
, arg
);
90 qmi_set_ptr(&wds_cp_req
, apn_name
, arg
);
94 #define cmd_wds_set_auth_cb no_cb
95 static enum qmi_cmd_result
96 cmd_wds_set_auth_prepare(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, char *arg
)
99 for (i
= 0; i
< ARRAY_SIZE(auth_modes
); i
++) {
100 if (strcasecmp(auth_modes
[i
].auth_name
, arg
) != 0)
103 qmi_set(&wds_sn_req
, authentication_preference
, auth_modes
[i
].auth
);
104 qmi_set(&wds_mp_req
, authentication
, auth_modes
[i
].auth
);
105 qmi_set(&wds_cp_req
, authentication
, auth_modes
[i
].auth
);
109 uqmi_add_error("Invalid auth mode (valid: pap, chap, both, none)");
113 #define cmd_wds_set_username_cb no_cb
114 static enum qmi_cmd_result
115 cmd_wds_set_username_prepare(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, char *arg
)
117 qmi_set_ptr(&wds_sn_req
, username
, arg
);
118 qmi_set_ptr(&wds_mp_req
, username
, arg
);
119 qmi_set_ptr(&wds_cp_req
, username
, arg
);
123 #define cmd_wds_set_password_cb no_cb
124 static enum qmi_cmd_result
125 cmd_wds_set_password_prepare(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, char *arg
)
127 qmi_set_ptr(&wds_sn_req
, password
, arg
);
128 qmi_set_ptr(&wds_mp_req
, password
, arg
);
129 qmi_set_ptr(&wds_cp_req
, password
, arg
);
133 #define cmd_wds_set_autoconnect_cb no_cb
134 static enum qmi_cmd_result
135 cmd_wds_set_autoconnect_prepare(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, char *arg
)
137 qmi_set(&wds_sn_req
, enable_autoconnect
, true);
138 qmi_set(&wds_stn_req
, disable_autoconnect
, true);
142 #define cmd_wds_set_ip_family_pref_cb no_cb
143 static enum qmi_cmd_result
144 cmd_wds_set_ip_family_pref_prepare(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, char *arg
)
147 for (i
= 0; i
< ARRAY_SIZE(ipfam_modes
); i
++) {
148 if (strcasecmp(ipfam_modes
[i
].ipfam_name
, arg
) != 0)
151 qmi_set(&wds_sn_req
, ip_family_preference
, ipfam_modes
[i
].mode
);
155 uqmi_add_error("Invalid value (valid: ipv4, ipv6, unspecified)");
159 #define cmd_wds_set_pdp_type_cb no_cb
160 static enum qmi_cmd_result
161 cmd_wds_set_pdp_type_prepare(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, char *arg
)
165 for (i
= 0; i
< ARRAY_SIZE(pdp_types
); i
++) {
166 if (strcasecmp(pdp_types
[i
].pdp_name
, arg
) != 0)
169 qmi_set(&wds_mp_req
, pdp_type
, pdp_types
[i
].type
);
170 qmi_set(&wds_cp_req
, pdp_type
, pdp_types
[i
].type
);
174 uqmi_add_error("Invalid value (valid: ipv4, ipv6, ipv4v6)");
178 #define cmd_wds_no_roaming_cb no_cb
179 static enum qmi_cmd_result
180 cmd_wds_no_roaming_prepare(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, char *arg
)
182 if (strcmp(arg
, "true") == 0) {
183 qmi_set(&wds_mp_req
, roaming_disallowed_flag
, true);
184 qmi_set(&wds_cp_req
, roaming_disallowed_flag
, true);
185 } else if (strcmp(arg
, "false") == 0) {
186 qmi_set(&wds_mp_req
, roaming_disallowed_flag
, false);
187 qmi_set(&wds_cp_req
, roaming_disallowed_flag
, false);
189 uqmi_add_error("Invalid value (true or false)");
195 #define cmd_wds_set_profile_cb no_cb
196 static enum qmi_cmd_result
197 cmd_wds_set_profile_prepare(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, char *arg
)
199 uint32_t idx
= strtoul(arg
, NULL
, 10);
201 qmi_set(&wds_sn_req
, profile_index_3gpp
, idx
);
206 cmd_wds_start_network_cb(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
)
208 struct qmi_wds_start_network_response res
;
210 qmi_parse_wds_start_network_response(msg
, &res
);
211 if (res
.set
.packet_data_handle
)
212 blobmsg_add_u32(&status
, NULL
, res
.data
.packet_data_handle
);
215 static enum qmi_cmd_result
216 cmd_wds_start_network_prepare(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, char *arg
)
218 qmi_set_wds_start_network_request(msg
, &wds_sn_req
);
219 return QMI_CMD_REQUEST
;
222 #define cmd_wds_stop_network_cb no_cb
223 static enum qmi_cmd_result
224 cmd_wds_stop_network_prepare(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, char *arg
)
226 uint32_t pdh
= strtoul(arg
, NULL
, 0);
228 qmi_set(&wds_stn_req
, packet_data_handle
, pdh
);
229 qmi_set_wds_stop_network_request(msg
, &wds_stn_req
);
230 return QMI_CMD_REQUEST
;
234 cmd_wds_modify_profile_cb(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
)
236 struct qmi_wds_modify_profile_response res
;
237 qmi_parse_wds_modify_profile_response(msg
, &res
);
240 static enum qmi_cmd_result
241 cmd_wds_modify_profile_prepare(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, char *arg
)
247 s
= strchr(arg
, ',');
249 fprintf(stderr
, "Invalid argument\n");
255 id
= strtoul(s
, &s
, 0);
257 fprintf(stderr
, "Invalid argument\n");
261 p_type
= strtok(arg
, ",");
264 for (i
= 0; i
< ARRAY_SIZE(profile_types
); i
++) {
265 if (strcasecmp(profile_types
[i
].profile_name
, p_type
) != 0)
268 qmi_set_ptr(&wds_mp_req
, profile_identifier
.profile_type
, profile_types
[i
].profile
);
269 qmi_set_ptr(&wds_mp_req
, profile_identifier
.profile_index
, id
);
270 qmi_set_wds_modify_profile_request(msg
, &wds_mp_req
);
271 return QMI_CMD_REQUEST
;
274 uqmi_add_error("Invalid value (valid: 3gpp or 3gpp2)");
279 cmd_wds_create_profile_cb(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
)
281 struct qmi_wds_create_profile_response res
;
284 qmi_parse_wds_create_profile_response(msg
, &res
);
286 if (res
.set
.profile_identifier
) {
287 p
= blobmsg_open_table(&status
, NULL
);
288 blobmsg_add_u32(&status
, "created-profile", res
.data
.profile_identifier
.profile_index
);
289 blobmsg_close_table(&status
, p
);
293 static enum qmi_cmd_result
294 cmd_wds_create_profile_prepare(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, char *arg
)
297 for (i
= 0; i
< ARRAY_SIZE(profile_types
); i
++) {
298 if (strcasecmp(profile_types
[i
].profile_name
, arg
) != 0)
301 qmi_set_ptr(&wds_cp_req
, profile_type
, profile_types
[i
].profile
);
303 qmi_set_wds_create_profile_request(msg
, &wds_cp_req
);
304 return QMI_CMD_REQUEST
;
307 uqmi_add_error("Invalid value (valid: 3gpp or 3gpp2)");
312 cmd_wds_get_packet_service_status_cb(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
)
314 struct qmi_wds_get_packet_service_status_response res
;
315 const char *data_status
[] = {
316 [QMI_WDS_CONNECTION_STATUS_UNKNOWN
] = "unknown",
317 [QMI_WDS_CONNECTION_STATUS_DISCONNECTED
] = "disconnected",
318 [QMI_WDS_CONNECTION_STATUS_CONNECTED
] = "connected",
319 [QMI_WDS_CONNECTION_STATUS_SUSPENDED
] = "suspended",
320 [QMI_WDS_CONNECTION_STATUS_AUTHENTICATING
] = "authenticating",
324 qmi_parse_wds_get_packet_service_status_response(msg
, &res
);
325 if (res
.set
.connection_status
&&
326 res
.data
.connection_status
< ARRAY_SIZE(data_status
))
327 s
= res
.data
.connection_status
;
329 blobmsg_add_string(&status
, NULL
, data_status
[s
]);
332 static enum qmi_cmd_result
333 cmd_wds_get_packet_service_status_prepare(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, char *arg
)
335 qmi_set_wds_get_packet_service_status_request(msg
);
336 return QMI_CMD_REQUEST
;
339 #define cmd_wds_set_autoconnect_settings_cb no_cb
340 static enum qmi_cmd_result
341 cmd_wds_set_autoconnect_settings_prepare(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, char *arg
)
343 struct qmi_wds_set_autoconnect_settings_request ac_req
;
344 const char *modes
[] = {
345 [QMI_WDS_AUTOCONNECT_SETTING_DISABLED
] = "disabled",
346 [QMI_WDS_AUTOCONNECT_SETTING_ENABLED
] = "enabled",
347 [QMI_WDS_AUTOCONNECT_SETTING_PAUSED
] = "paused",
351 for (i
= 0; i
< ARRAY_SIZE(modes
); i
++) {
352 if (strcasecmp(modes
[i
], arg
) != 0)
355 qmi_set(&ac_req
, status
, i
);
356 qmi_set_wds_set_autoconnect_settings_request(msg
, &ac_req
);
360 uqmi_add_error("Invalid value (valid: disabled, enabled, paused)");
364 #define cmd_wds_reset_cb no_cb
365 static enum qmi_cmd_result
366 cmd_wds_reset_prepare(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, char *arg
)
368 qmi_set_wds_reset_request(msg
);
369 return QMI_CMD_REQUEST
;
372 #define cmd_wds_set_ip_family_cb no_cb
373 static enum qmi_cmd_result
374 cmd_wds_set_ip_family_prepare(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, char *arg
)
376 struct qmi_wds_set_ip_family_request ipf_req
;
379 for (i
= 0; i
< ARRAY_SIZE(ipfam_modes
); i
++) {
380 if (strcasecmp(ipfam_modes
[i
].ipfam_name
, arg
) != 0)
383 qmi_set(&ipf_req
, preference
, ipfam_modes
[i
].mode
);
384 qmi_set_wds_set_ip_family_request(msg
, &ipf_req
);
385 return QMI_CMD_REQUEST
;
388 uqmi_add_error("Invalid value (valid: ipv4, ipv6, unspecified)");
392 static void wds_to_ipv4(const char *name
, const uint32_t addr
)
394 struct in_addr ip_addr
;
395 char buf
[INET_ADDRSTRLEN
];
397 ip_addr
.s_addr
= htonl(addr
);
398 blobmsg_add_string(&status
, name
, inet_ntop(AF_INET
, &ip_addr
, buf
, sizeof(buf
)));
401 static void wds_to_ipv6(const char *name
, const uint16_t *addr
)
403 char buf
[INET6_ADDRSTRLEN
];
407 for (i
= 0; i
< ARRAY_SIZE(ip_addr
); i
++)
408 ip_addr
[i
] = htons(addr
[i
]);
410 blobmsg_add_string(&status
, name
, inet_ntop(AF_INET6
, &ip_addr
, buf
, sizeof(buf
)));
413 static enum qmi_cmd_result
414 cmd_wds_get_profile_settings_prepare(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, char *arg
)
420 s
= strchr(arg
, ',');
422 fprintf(stderr
, "Invalid argument\n");
428 id
= strtoul(s
, &s
, 0);
430 fprintf(stderr
, "Invalid argument\n");
434 p_type
= strtok(arg
, ",");
437 for (i
= 0; i
< ARRAY_SIZE(profile_types
); i
++) {
438 if (strcasecmp(profile_types
[i
].profile_name
, p_type
) != 0)
441 struct qmi_wds_get_profile_settings_request p_num
= {
442 QMI_INIT_SEQUENCE(profile_id
,
443 .profile_type
= profile_types
[i
].profile
,
447 qmi_set_wds_get_profile_settings_request(msg
, &p_num
);
448 return QMI_CMD_REQUEST
;
451 uqmi_add_error("Invalid value (valid: 3gpp or 3gpp2)");
456 cmd_wds_get_profile_settings_cb(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
)
458 struct qmi_wds_get_profile_settings_response res
;
462 qmi_parse_wds_get_profile_settings_response(msg
, &res
);
464 p
= blobmsg_open_table(&status
, NULL
);
466 blobmsg_add_string(&status
, "apn", res
.data
.apn_name
);
467 if (res
.set
.pdp_type
&& (int) res
.data
.pdp_type
< ARRAY_SIZE(pdp_types
))
468 blobmsg_add_string(&status
, "pdp-type", pdp_types
[res
.data
.pdp_type
].pdp_name
);
469 blobmsg_add_string(&status
, "username", res
.data
.username
);
470 blobmsg_add_string(&status
, "password", res
.data
.password
);
471 if (res
.set
.authentication
&& (int) res
.data
.authentication
< ARRAY_SIZE(auth_modes
))
472 blobmsg_add_string(&status
, "auth", auth_modes
[res
.data
.authentication
].auth_name
);
473 blobmsg_add_u8(&status
, "no-roaming", res
.data
.roaming_disallowed_flag
);
474 blobmsg_add_u8(&status
, "apn-disabled", res
.data
.apn_disabled_flag
);
475 blobmsg_close_table(&status
, p
);
479 cmd_wds_get_current_settings_cb(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
)
481 void *v4
, *v6
, *d
, *t
;
482 struct qmi_wds_get_current_settings_response res
;
485 qmi_parse_wds_get_current_settings_response(msg
, &res
);
487 t
= blobmsg_open_table(&status
, NULL
);
489 if (res
.set
.pdp_type
&& (int) res
.data
.pdp_type
< ARRAY_SIZE(pdp_types
))
490 blobmsg_add_string(&status
, "pdp-type", pdp_types
[res
.data
.pdp_type
].pdp_name
);
492 if (res
.set
.ip_family
) {
493 for (i
= 0; i
< ARRAY_SIZE(ipfam_modes
); i
++) {
494 if (ipfam_modes
[i
].mode
!= res
.data
.ip_family
)
496 blobmsg_add_string(&status
, "ip-family", ipfam_modes
[i
].ipfam_name
);
502 blobmsg_add_u32(&status
, "mtu", res
.data
.mtu
);
505 v4
= blobmsg_open_table(&status
, "ipv4");
507 if (res
.set
.ipv4_address
)
508 wds_to_ipv4("ip", res
.data
.ipv4_address
);
509 if (res
.set
.primary_ipv4_dns_address
)
510 wds_to_ipv4("dns1", res
.data
.primary_ipv4_dns_address
);
511 if (res
.set
.secondary_ipv4_dns_address
)
512 wds_to_ipv4("dns2", res
.data
.secondary_ipv4_dns_address
);
513 if (res
.set
.ipv4_gateway_address
)
514 wds_to_ipv4("gateway", res
.data
.ipv4_gateway_address
);
515 if (res
.set
.ipv4_gateway_subnet_mask
)
516 wds_to_ipv4("subnet", res
.data
.ipv4_gateway_subnet_mask
);
517 blobmsg_close_table(&status
, v4
);
520 v6
= blobmsg_open_table(&status
, "ipv6");
522 if (res
.set
.ipv6_address
) {
523 wds_to_ipv6("ip", res
.data
.ipv6_address
.address
);
524 blobmsg_add_u32(&status
, "ip-prefix-length", res
.data
.ipv6_address
.prefix_length
);
526 if (res
.set
.ipv6_gateway_address
) {
527 wds_to_ipv6("gateway", res
.data
.ipv6_gateway_address
.address
);
528 blobmsg_add_u32(&status
, "gw-prefix-length", res
.data
.ipv6_gateway_address
.prefix_length
);
530 if (res
.set
.ipv6_primary_dns_address
)
531 wds_to_ipv6("dns1", res
.data
.ipv6_primary_dns_address
);
532 if (res
.set
.ipv6_secondary_dns_address
)
533 wds_to_ipv6("dns2", res
.data
.ipv6_secondary_dns_address
);
535 blobmsg_close_table(&status
, v6
);
537 d
= blobmsg_open_table(&status
, "domain-names");
538 for (i
= 0; i
< res
.data
.domain_name_list_n
; i
++) {
539 blobmsg_add_string(&status
, NULL
, res
.data
.domain_name_list
[i
]);
541 blobmsg_close_table(&status
, d
);
543 blobmsg_close_table(&status
, t
);
546 static enum qmi_cmd_result
547 cmd_wds_get_current_settings_prepare(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, char *arg
)
549 struct qmi_wds_get_current_settings_request gcs_req
;
550 memset(&gcs_req
, '\0', sizeof(struct qmi_wds_get_current_settings_request
));
551 qmi_set(&gcs_req
, requested_settings
,
552 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_PDP_TYPE
|
553 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_DNS_ADDRESS
|
554 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_GRANTED_QOS
|
555 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_IP_ADDRESS
|
556 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_GATEWAY_INFO
|
557 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_MTU
|
558 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_DOMAIN_NAME_LIST
|
559 QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_IP_FAMILY
);
560 qmi_set_wds_get_current_settings_request(msg
, &gcs_req
);
561 return QMI_CMD_REQUEST
;
564 static enum qmi_cmd_result
565 cmd_wds_get_default_profile_prepare(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, char *arg
)
568 for (i
= 0; i
< ARRAY_SIZE(profile_types
); i
++) {
569 if (strcasecmp(profile_types
[i
].profile_name
, arg
) != 0)
572 struct qmi_wds_get_default_profile_number_request type_family
= {
573 QMI_INIT_SEQUENCE(profile_type
,
574 .type
= profile_types
[i
].profile
,
575 .family
= QMI_WDS_PROFILE_FAMILY_TETHERED
,
579 qmi_set_wds_get_default_profile_number_request(msg
, &type_family
);
580 return QMI_CMD_REQUEST
;
583 uqmi_add_error("Invalid value (valid: 3gpp or 3gpp2)");
588 cmd_wds_get_default_profile_cb(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
)
590 struct qmi_wds_get_default_profile_number_response res
;
592 qmi_parse_wds_get_default_profile_number_response(msg
, &res
);
594 p
= blobmsg_open_table(&status
, NULL
);
596 blobmsg_add_u32(&status
, "default-profile", res
.data
.index
);
598 blobmsg_close_table(&status
, p
);