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"
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
),
31 static struct qmi_wds_stop_network_request wds_stn_req
;
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
)
37 qmi_set_ptr(&wds_sn_req
, apn
, arg
);
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
)
47 QmiWdsAuthentication auth
;
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
},
56 for (i
= 0; i
< ARRAY_SIZE(modes
); i
++) {
57 if (strcasecmp(modes
[i
].name
, arg
) != 0)
60 qmi_set(&wds_sn_req
, authentication_preference
, modes
[i
].auth
);
64 uqmi_add_error("Invalid auth mode (valid: pap, chap, both, none)");
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
)
72 qmi_set_ptr(&wds_sn_req
, username
, arg
);
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
)
80 qmi_set_ptr(&wds_sn_req
, password
, arg
);
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
)
88 qmi_set(&wds_sn_req
, enable_autoconnect
, true);
89 qmi_set(&wds_stn_req
, disable_autoconnect
, true);
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
)
99 const QmiWdsIpFamily mode
;
101 { "ipv4", QMI_WDS_IP_FAMILY_IPV4
},
102 { "ipv6", QMI_WDS_IP_FAMILY_IPV6
},
103 { "unspecified", QMI_WDS_IP_FAMILY_UNSPECIFIED
},
107 for (i
= 0; i
< ARRAY_SIZE(modes
); i
++) {
108 if (strcasecmp(modes
[i
].name
, arg
) != 0)
111 qmi_set(&wds_sn_req
, ip_family_preference
, modes
[i
].mode
);
115 uqmi_add_error("Invalid value (valid: ipv4, ipv6, unspecified)");
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
)
123 uint32_t idx
= strtoul(arg
, NULL
, 10);
125 qmi_set(&wds_sn_req
, profile_index_3gpp
, idx
);
130 cmd_wds_start_network_cb(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
)
132 struct qmi_wds_start_network_response res
;
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
);
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
)
142 qmi_set_wds_start_network_request(msg
, &wds_sn_req
);
143 return QMI_CMD_REQUEST
;
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
)
150 uint32_t pdh
= strtoul(arg
, NULL
, 0);
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
;
158 cmd_wds_get_packet_service_status_cb(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
)
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",
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
;
175 blobmsg_add_string(&status
, NULL
, data_status
[s
]);
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
)
181 qmi_set_wds_get_packet_service_status_request(msg
);
182 return QMI_CMD_REQUEST
;
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
)
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",
197 for (i
= 0; i
< ARRAY_SIZE(modes
); i
++) {
198 if (strcasecmp(modes
[i
], arg
) != 0)
201 qmi_set(&ac_req
, setting
, i
);
202 qmi_set_wds_set_autoconnect_setting_request(msg
, &ac_req
);
206 uqmi_add_error("Invalid value (valid: disabled, enabled, paused)");
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
)
214 qmi_set_wds_reset_request(msg
);
215 return QMI_CMD_REQUEST
;
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
)
222 struct qmi_wds_set_ip_family_request ipf_req
;
223 const struct ip_modes
{
225 const QmiWdsIpFamily mode
;
227 { "ipv4", QMI_WDS_IP_FAMILY_IPV4
},
228 { "ipv6", QMI_WDS_IP_FAMILY_IPV6
},
229 { "unspecified", QMI_WDS_IP_FAMILY_UNSPECIFIED
},
233 for (i
= 0; i
< ARRAY_SIZE(modes
); i
++) {
234 if (strcasecmp(modes
[i
].name
, arg
) != 0)
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
;
242 uqmi_add_error("Invalid value (valid: ipv4, ipv6, unspecified)");
246 static void wds_to_ipv4(const char *name
, const uint32_t addr
)
248 struct in_addr ip_addr
;
249 char buf
[INET_ADDRSTRLEN
];
251 ip_addr
.s_addr
= htonl(addr
);
252 blobmsg_add_string(&status
, name
, inet_ntop(AF_INET
, &ip_addr
, buf
, sizeof(buf
)));
255 static void wds_to_ipv6(const char *name
, const uint16_t *addr
)
257 char buf
[INET6_ADDRSTRLEN
];
261 for (i
= 0; i
< ARRAY_SIZE(ip_addr
); i
++)
262 ip_addr
[i
] = htons(addr
[i
]);
264 blobmsg_add_string(&status
, name
, inet_ntop(AF_INET6
, &ip_addr
, buf
, sizeof(buf
)));
268 cmd_wds_get_current_settings_cb(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
)
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",
278 const struct ip_modes
{
280 const QmiWdsIpFamily mode
;
282 { "ipv4", QMI_WDS_IP_FAMILY_IPV4
},
283 { "ipv6", QMI_WDS_IP_FAMILY_IPV6
},
284 { "unspecified", QMI_WDS_IP_FAMILY_UNSPECIFIED
},
288 qmi_parse_wds_get_current_settings_response(msg
, &res
);
290 t
= blobmsg_open_table(&status
, NULL
);
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
]);
295 if (res
.set
.ip_family
) {
296 for (i
= 0; i
< ARRAY_SIZE(modes
); i
++) {
297 if (modes
[i
].mode
!= res
.data
.ip_family
)
299 blobmsg_add_string(&status
, "ip-family", modes
[i
].name
);
305 blobmsg_add_u32(&status
, "mtu", res
.data
.mtu
);
308 v4
= blobmsg_open_table(&status
, "ipv4");
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
);
323 v6
= blobmsg_open_table(&status
, "ipv6");
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
);
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
);
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
);
338 blobmsg_close_table(&status
, v6
);
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
]);
344 blobmsg_close_table(&status
, d
);
346 blobmsg_close_table(&status
, t
);
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
)
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
;