From: Denis Kenzior <denkenz@gmail.com>
To: ofono@lists.linux.dev
Cc: Denis Kenzior <denkenz@gmail.com>
Subject: [PATCH 11/11] qmi: gprs-context: Dual-Stack context activation support
Date: Tue, 9 Jul 2024 17:50:30 -0500 [thread overview]
Message-ID: <20240709225047.1427626-11-denkenz@gmail.com> (raw)
In-Reply-To: <20240709225047.1427626-1-denkenz@gmail.com>
Support dual-stack contexts by utilizing two WDS services, one for each
family type. For IPv4 only contexts, the WDS service 'ipv4' is used.
For IPv6 only contexts, the WDS service 'ipv6' is used. For dual stack
contexts, both WDS services are used simultaneously.
When a default bearer (using read_settings) or primary context is
activated (using activate_primary), the Start Network command is issued
on one or both of the WDS services. Once the Start Network command
succeeds, the returned packet handle is stored in 'packet_handle_ipv4'
or 'packet_handle_ipv6', depending on the selected IP family.
Subsequently Get Current Settings command is issued and settings are
read from the QMI response and applied to the context. Success is
signaled to the core if at least one Start Network command succeeded.
Otherwise, an error is signaled.
---
drivers/qmimodem/gprs-context.c | 364 ++++++++++++++++++++------------
1 file changed, 228 insertions(+), 136 deletions(-)
diff --git a/drivers/qmimodem/gprs-context.c b/drivers/qmimodem/gprs-context.c
index f05f10a277fd..fa6c5fa1c85e 100644
--- a/drivers/qmimodem/gprs-context.c
+++ b/drivers/qmimodem/gprs-context.c
@@ -26,6 +26,8 @@ struct gprs_context_data {
unsigned int active_context;
uint32_t packet_handle_ipv4;
uint32_t packet_handle_ipv6;
+ uint32_t start_network_ipv4_id;
+ uint32_t start_network_ipv6_id;
uint8_t mux_id;
};
@@ -67,23 +69,49 @@ static void pkt_status_notify(struct qmi_result *result, void *user_data)
switch (status->status) {
case QMI_WDS_CONNECTION_STATUS_DISCONNECTED:
- if (data->packet_handle_ipv4) {
+ if (ip_family == QMI_WDS_IP_FAMILY_IPV4 &&
+ data->packet_handle_ipv4) {
data->packet_handle_ipv4 = 0;
check_all_deactivated(gc);
}
+ if (ip_family == QMI_WDS_IP_FAMILY_IPV6 &&
+ data->packet_handle_ipv6) {
+ data->packet_handle_ipv6 = 0;
+ check_all_deactivated(gc);
+ }
+
break;
}
}
-static void get_settings_ipv6(struct ofono_gprs_context *gc,
- struct qmi_result *result)
+static void check_all_activated(struct gprs_context_data *data,
+ ofono_gprs_context_cb_t cb,
+ void *user_data)
+{
+ if (data->start_network_ipv4_id || data->start_network_ipv6_id)
+ return;
+
+ if (!data->packet_handle_ipv4 && !data->packet_handle_ipv6) {
+ data->active_context = 0;
+ CALLBACK_WITH_FAILURE(cb, user_data);
+ return;
+ }
+
+ CALLBACK_WITH_SUCCESS(cb, user_data);
+}
+
+static void get_settings_ipv6_cb(struct qmi_result *result, void *user_data)
{
static const uint8_t RESULT_IP_ADDRESS = 0x25;
static const uint8_t RESULT_GATEWAY = 0x26;
static const uint8_t RESULT_PRIMARY_DNS = 0x27;
static const uint8_t RESULT_SECONDARY_DNS = 0x28;
static const uint8_t RESULT_MTU = 0x29;
+ struct cb_data *cbd = user_data;
+ struct ofono_gprs_context *gc = cbd->user;
+ struct gprs_context_data *data = ofono_gprs_context_get_data(gc);
+ uint16_t error;
const char *dns[3] = { NULL, NULL, NULL };
char dns1str[INET6_ADDRSTRLEN];
char dns2str[INET6_ADDRSTRLEN];
@@ -92,6 +120,13 @@ static void get_settings_ipv6(struct ofono_gprs_context *gc,
uint16_t len;
uint32_t mtu;
+ data->start_network_ipv6_id = 0;
+
+ if (qmi_result_set_error(result, &error)) {
+ DBG("error: %u", error);
+ goto done;
+ }
+
tlv = qmi_result_get(result, RESULT_IP_ADDRESS, &len);
if (tlv && len == sizeof(struct in6_addr) + 1) {
const struct in6_addr *ip = tlv;
@@ -131,22 +166,35 @@ static void get_settings_ipv6(struct ofono_gprs_context *gc,
if (qmi_result_get_uint32(result, RESULT_MTU, &mtu))
DBG("MTU: %u", mtu);
+
+done:
+ check_all_activated(data, cbd->cb, cbd->data);
}
-static void get_settings_ipv4(struct ofono_gprs_context *gc,
- struct qmi_result *result)
+static void get_settings_ipv4_cb(struct qmi_result *result, void *user_data)
{
static const uint8_t RESULT_PRIMARY_DNS = 0x15;
static const uint8_t RESULT_SECONDARY_DNS = 0x16;
static const uint8_t RESULT_IP_ADDRESS = 0x1e;
static const uint8_t RESULT_GATEWAY = 0x20;
static const uint8_t RESULT_GATEWAY_NETMASK = 0x21;
+ struct cb_data *cbd = user_data;
+ struct ofono_gprs_context *gc = cbd->user;
+ struct gprs_context_data *data = ofono_gprs_context_get_data(gc);
+ uint16_t error;
uint32_t ip_addr;
struct in_addr addr;
char* straddr;
const char *dns[3] = { NULL, NULL, NULL };
char dns_buf[2][INET_ADDRSTRLEN];
+ data->start_network_ipv4_id = 0;
+
+ if (qmi_result_set_error(result, &error)) {
+ DBG("error: %u", error);
+ goto done;
+ }
+
if (qmi_result_get_uint32(result, RESULT_IP_ADDRESS, &ip_addr)) {
addr.s_addr = htonl(ip_addr);
straddr = inet_ntoa(addr);
@@ -182,65 +230,20 @@ static void get_settings_ipv4(struct ofono_gprs_context *gc,
if (dns[0])
ofono_gprs_context_set_ipv4_dns_servers(gc, dns);
-}
-
-static void get_settings_cb(struct qmi_result *result, void *user_data)
-{
- static const uint8_t RESULT_IP_FAMILY = 0x2b; /* uint8 */
- struct cb_data *cbd = user_data;
- ofono_gprs_context_cb_t cb = cbd->cb;
- struct ofono_gprs_context *gc = cbd->user;
- uint8_t ip_family;
-
- DBG("");
-
- if (qmi_result_set_error(result, NULL))
- goto done;
-
- if (!qmi_result_get_uint8(result, RESULT_IP_FAMILY, &ip_family)) {
- ofono_error("No IP family in results");
- goto done;
- }
-
- switch (ip_family) {
- case QMI_WDS_IP_FAMILY_IPV4:
- get_settings_ipv4(gc, result);
- break;
- case QMI_WDS_IP_FAMILY_IPV6:
- get_settings_ipv6(gc, result);
- break;
- default:
- break;
- }
done:
- CALLBACK_WITH_SUCCESS(cb, cbd->data);
+ check_all_activated(data, cbd->cb, cbd->data);
}
-static void start_net_cb(struct qmi_result *result, void *user_data)
+static uint32_t send_get_current_settings(struct qmi_service *wds,
+ qmi_service_result_func_t func,
+ void *user_data,
+ qmi_destroy_func_t destroy)
{
- static const uint8_t RESULT_PACKET_HANDLE = 0x01;
static const uint8_t PARAM_REQUESTED_SETTINGS = 0x10;
- struct cb_data *cbd = user_data;
- ofono_gprs_context_cb_t cb = cbd->cb;
- struct ofono_gprs_context *gc = cbd->user;
- struct gprs_context_data *data = ofono_gprs_context_get_data(gc);
- uint32_t handle;
uint32_t requested_settings = 0;
struct qmi_param *param;
-
- DBG("");
-
- if (qmi_result_set_error(result, NULL))
- goto error;
-
- if (!qmi_result_get_uint32(result, RESULT_PACKET_HANDLE, &handle))
- goto error;
-
- DBG("packet handle %d", handle);
-
- data->packet_handle_ipv4 = handle;
-
+ uint32_t id;
/*
* Explicitly request certain information to be provided. The requested
* settings is a bit field, with each bit representing whether the
@@ -254,18 +257,149 @@ static void start_net_cb(struct qmi_result *result, void *user_data)
L_BITS_SET(&requested_settings, 2, 3, 4, 5, 6, 7, 8, 9, 13, 14, 15, 17);
param = qmi_param_new_uint32(PARAM_REQUESTED_SETTINGS,
requested_settings);
+ id = qmi_service_send(wds, QMI_WDS_GET_CURRENT_SETTINGS, param,
+ func, user_data, destroy);
+
+ if (!id)
+ qmi_param_free(param);
+
+ return id;
+}
+
+static void start_network_common_cb(int family, struct qmi_result *result,
+ struct cb_data *cbd,
+ struct qmi_service *wds,
+ qmi_service_result_func_t func,
+ uint32_t *packet_handle,
+ uint32_t *family_start_id)
+{
+ static const uint8_t RESULT_PACKET_HANDLE = 0x01;
+ struct ofono_gprs_context *gc = cbd->user;
+ struct gprs_context_data *data = ofono_gprs_context_get_data(gc);
+ uint16_t error;
+
+ *family_start_id = 0;
- if (qmi_service_send(data->ipv4, QMI_WDS_GET_CURRENT_SETTINGS, param,
- get_settings_cb, cbd, cb_data_unref) > 0) {
- cb_data_ref(cbd);
+ if (!qmi_result_set_error(result, &error))
+ error = 0;
+
+ DBG("family: %d, error: %u", family, error);
+
+ if (error)
+ goto error;
+
+ if (!qmi_result_get_uint32(result, RESULT_PACKET_HANDLE, packet_handle))
+ goto error;
+
+ *family_start_id =
+ send_get_current_settings(wds, func,
+ cb_data_ref(cbd), cb_data_unref);
+ if (*family_start_id)
return;
+
+ *packet_handle = 0;
+ cb_data_unref(cbd);
+error:
+ check_all_activated(data, cbd->cb, cbd->data);
+}
+
+static void start_network_ipv4_cb(struct qmi_result *result, void *user_data)
+{
+ struct cb_data *cbd = user_data;
+ struct ofono_gprs_context *gc = cbd->user;
+ struct gprs_context_data *data = ofono_gprs_context_get_data(gc);
+
+ start_network_common_cb(4, result, user_data,
+ data->ipv4, get_settings_ipv4_cb,
+ &data->packet_handle_ipv4,
+ &data->start_network_ipv4_id);
+}
+
+static void start_network_ipv6_cb(struct qmi_result *result, void *user_data)
+{
+ struct cb_data *cbd = user_data;
+ struct ofono_gprs_context *gc = cbd->user;
+ struct gprs_context_data *data = ofono_gprs_context_get_data(gc);
+
+ start_network_common_cb(6, result, user_data,
+ data->ipv6, get_settings_ipv6_cb,
+ &data->packet_handle_ipv6,
+ &data->start_network_ipv6_id);
+}
+
+static struct qmi_param *param_from_context(uint8_t ip_family,
+ const struct ofono_gprs_primary_context *ctx)
+{
+ struct qmi_param *param = qmi_param_new();
+ uint8_t auth;
+
+ qmi_param_append_uint8(param, QMI_WDS_PARAM_IP_FAMILY, ip_family);
+
+ if (!ctx)
+ goto done;
+
+ qmi_param_append(param, QMI_WDS_PARAM_APN,
+ strlen(ctx->apn), ctx->apn);
+
+ auth = qmi_wds_auth_from_ofono(ctx->auth_method);
+ qmi_param_append_uint8(param, QMI_WDS_PARAM_AUTHENTICATION_PREFERENCE,
+ auth);
+
+ if (auth && ctx->username[0] != '\0')
+ qmi_param_append(param, QMI_WDS_PARAM_USERNAME,
+ strlen(ctx->username), ctx->username);
+
+ if (auth && ctx->password[0] != '\0')
+ qmi_param_append(param, QMI_WDS_PARAM_PASSWORD,
+ strlen(ctx->password), ctx->password);
+done:
+ return param;
+}
+
+static int start_network(uint8_t iptype, struct gprs_context_data *data,
+ const struct ofono_gprs_primary_context *ctx,
+ struct cb_data *cbd)
+{
+ struct qmi_param *param;
+
+ if (!L_IN_SET(iptype, QMI_WDS_IP_SUPPORT_IPV4, QMI_WDS_IP_SUPPORT_IPV6,
+ QMI_WDS_IP_SUPPORT_IPV4V6))
+ return -EINVAL;
+
+ if (iptype == QMI_WDS_IP_SUPPORT_IPV4 ||
+ iptype == QMI_WDS_IP_SUPPORT_IPV4V6) {
+ param = param_from_context(QMI_WDS_IP_FAMILY_IPV4, ctx);
+
+ data->start_network_ipv4_id =
+ qmi_service_send(data->ipv4, QMI_WDS_START_NETWORK,
+ param, start_network_ipv4_cb,
+ cb_data_ref(cbd), cb_data_unref);
+
+ if (!data->start_network_ipv4_id) {
+ cb_data_unref(cbd);
+ qmi_param_free(param);
+ }
}
- qmi_param_free(param);
+ if (iptype == QMI_WDS_IP_SUPPORT_IPV6 ||
+ iptype == QMI_WDS_IP_SUPPORT_IPV4V6) {
+ param = param_from_context(QMI_WDS_IP_FAMILY_IPV6, ctx);
-error:
- data->active_context = 0;
- CALLBACK_WITH_FAILURE(cb, cbd->data);
+ data->start_network_ipv6_id =
+ qmi_service_send(data->ipv6, QMI_WDS_START_NETWORK,
+ param, start_network_ipv6_cb,
+ cb_data_ref(cbd), cb_data_unref);
+
+ if (!data->start_network_ipv6_id) {
+ cb_data_unref(cbd);
+ qmi_param_free(param);
+ }
+ }
+
+ if (data->start_network_ipv4_id || data->start_network_ipv6_id)
+ return 0;
+
+ return -EIO;
}
static void get_lte_attach_param_cb(struct qmi_result *result, void *user_data)
@@ -277,40 +411,20 @@ static void get_lte_attach_param_cb(struct qmi_result *result, void *user_data)
struct gprs_context_data *data = ofono_gprs_context_get_data(gc);
uint16_t error;
uint8_t iptype;
- struct qmi_param *param;
- uint8_t ip_family;
- DBG("");
+ if (!qmi_result_set_error(result, &error))
+ error = 0;
- if (qmi_result_set_error(result, &error))
- goto error;
+ DBG("error: %u", error);
- if (!qmi_result_get_uint8(result, RESULT_IP_SUPPORT_TYPE, &iptype))
+ if (error)
goto error;
- switch (iptype) {
- case QMI_WDS_IP_SUPPORT_IPV4:
- ip_family = QMI_WDS_IP_FAMILY_IPV4;
- break;
- case QMI_WDS_IP_SUPPORT_IPV6:
- ip_family = QMI_WDS_IP_FAMILY_IPV6;
- break;
- case QMI_WDS_IP_SUPPORT_IPV4V6:
- ip_family = QMI_WDS_IP_FAMILY_IPV4;
- break;
- default:
+ if (!qmi_result_get_uint8(result, RESULT_IP_SUPPORT_TYPE, &iptype))
goto error;
- }
-
- param = qmi_param_new_uint8(QMI_WDS_PARAM_IP_FAMILY, ip_family);
- if (qmi_service_send(data->ipv4, QMI_WDS_START_NETWORK, param,
- start_net_cb, cbd, cb_data_unref) > 0) {
- cb_data_ref(cbd);
+ if (!start_network(iptype, data, NULL, cbd))
return;
- }
-
- qmi_param_free(param);
error:
data->active_context = 0;
@@ -320,7 +434,7 @@ error:
/*
* This function gets called for "automatic" contexts, those which are
* not activated via activate_primary. For these, we will still need
- * to call start_net in order to get the packet handle for the context.
+ * to call start_network in order to get the packet handle for the context.
* The process for automatic contexts is essentially identical to that
* for others.
*/
@@ -334,14 +448,15 @@ static void qmi_gprs_read_settings(struct ofono_gprs_context* gc,
DBG("cid %u", cid);
+ data->active_context = cid;
+ cbd->user = gc;
+
if (qmi_service_send(data->ipv4, QMI_WDS_GET_LTE_ATTACH_PARAMETERS,
NULL, get_lte_attach_param_cb, cbd,
- cb_data_unref) > 0) {
- data->active_context = cid;
- cbd->user = gc;
+ cb_data_unref) > 0)
return;
- }
+ data->active_context = 0;
CALLBACK_WITH_FAILURE(cb, cbd->data);
l_free(cbd);
}
@@ -351,60 +466,37 @@ static void qmi_activate_primary(struct ofono_gprs_context *gc,
ofono_gprs_context_cb_t cb, void *user_data)
{
struct gprs_context_data *data = ofono_gprs_context_get_data(gc);
- struct cb_data *cbd = cb_data_new(cb, user_data);
- struct qmi_param *param;
- uint8_t ip_family;
- uint8_t auth;
+ enum ofono_gprs_proto proto = ctx->proto;
+ struct cb_data *cbd;
+ int ip_type;
+ int r;
DBG("cid %u", ctx->cid);
- cbd->user = gc;
-
- data->active_context = ctx->cid;
-
- switch (ctx->proto) {
- case OFONO_GPRS_PROTO_IP:
- ip_family = QMI_WDS_IP_FAMILY_IPV4;
- break;
- case OFONO_GPRS_PROTO_IPV6:
- ip_family = QMI_WDS_IP_FAMILY_IPV6;
- break;
- default:
+ if (!L_IN_SET(proto, OFONO_GPRS_PROTO_IP, OFONO_GPRS_PROTO_IPV6,
+ OFONO_GPRS_PROTO_IPV4V6))
goto error;
- }
- param = qmi_param_new();
-
- qmi_param_append(param, QMI_WDS_PARAM_APN,
- strlen(ctx->apn), ctx->apn);
-
- qmi_param_append_uint8(param, QMI_WDS_PARAM_IP_FAMILY, ip_family);
-
- auth = qmi_wds_auth_from_ofono(ctx->auth_method);
+ ip_type = qmi_wds_pdp_type_from_ofono(ctx->proto);
+ if (ip_type < 0)
+ goto error;
- qmi_param_append_uint8(param, QMI_WDS_PARAM_AUTHENTICATION_PREFERENCE,
- auth);
+ if (qmi_wds_auth_from_ofono(ctx->auth_method) < 0)
+ goto error;
- if (auth && ctx->username[0] != '\0')
- qmi_param_append(param, QMI_WDS_PARAM_USERNAME,
- strlen(ctx->username), ctx->username);
+ data->active_context = ctx->cid;
+ cbd = cb_data_new(cb, user_data);
+ cbd->user = gc;
- if (auth && ctx->password[0] != '\0')
- qmi_param_append(param, QMI_WDS_PARAM_PASSWORD,
- strlen(ctx->password), ctx->password);
+ r = start_network(ip_type, data, ctx, cbd);
+ cb_data_unref(cbd);
- if (qmi_service_send(data->ipv4, QMI_WDS_START_NETWORK, param,
- start_net_cb, cbd, cb_data_unref) > 0)
+ if (!r)
return;
- qmi_param_free(param);
-
-error:
data->active_context = 0;
-
- CALLBACK_WITH_FAILURE(cb, cbd->data);
-
- l_free(cbd);
+error:
+ CALLBACK_WITH_FAILURE(cb, user_data);
}
static uint32_t send_stop_net(struct qmi_service *wds, uint32_t packet_handle,
--
2.45.2
next prev parent reply other threads:[~2024-07-09 22:50 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-07-09 22:50 [PATCH 01/11] gobi: Limit number of premultiplexed contexts to 4 Denis Kenzior
2024-07-09 22:50 ` [PATCH 02/11] gobi: Request dedicated WDS services for contexts Denis Kenzior
2024-07-09 22:50 ` [PATCH 03/11] qmi: Enable _auto_ use for some classes Denis Kenzior
2024-07-09 22:50 ` [PATCH 04/11] qmi: gprs-context: Declare qmi_service objects using _auto_ Denis Kenzior
2024-07-09 22:50 ` [PATCH 05/11] qmi: Pass two dedicated WDS services to gprs-context Denis Kenzior
2024-07-09 22:50 ` [PATCH 06/11] qmi: gprs-context: Remove parsing of unused TLVs Denis Kenzior
2024-07-09 22:50 ` [PATCH 07/11] qmi: gprs-context: Treat IP family TLV as mandatory Denis Kenzior
2024-07-09 22:50 ` [PATCH 08/11] qmi: gprs-context: Set up WDS service for IPv6 Denis Kenzior
2024-07-09 22:50 ` [PATCH 09/11] qmi: gprs-context: refactor detach_shutdown path Denis Kenzior
2024-07-09 22:50 ` [PATCH 10/11] qmi: gprs-context: refactor deactivate_primary Denis Kenzior
2024-07-09 22:50 ` Denis Kenzior [this message]
2024-07-11 17:53 ` [PATCH 01/11] gobi: Limit number of premultiplexed contexts to 4 patchwork-bot+ofono
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240709225047.1427626-11-denkenz@gmail.com \
--to=denkenz@gmail.com \
--cc=ofono@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.