diff options
author | Aleksander Morgado <aleksander@aleksander.es> | 2021-04-04 10:36:04 +0200 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2021-04-29 10:13:22 +0000 |
commit | 62cbf7fa86a3f4773184186b7f937a7bee8b0e5a (patch) | |
tree | ea212942aa9c24578bc7662752b7ab73777db9ab /src/mm-modem-helpers.c | |
parent | dd7938b3e48bf9726146ab5c4b941c056b9cbce9 (diff) |
modem-helpers: new profile list helpers
The new helpers allow converting a PDP context list returned from the
+CGDCONT? parser to a list of 3GPP profile objects.
The new mm_3gpp_profile_list_find_best() method is equivalent to the
mm_3gpp_select_best_cid() one, but using profile objects as
input/output instead of specific settings.
The unit tests that were testing mm_3gpp_select_best_cid() are also
converted to use mm_3gpp_profile_list_find_best().
Diffstat (limited to 'src/mm-modem-helpers.c')
-rw-r--r-- | src/mm-modem-helpers.c | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c index 41573ede..9a389ec3 100644 --- a/src/mm-modem-helpers.c +++ b/src/mm-modem-helpers.c @@ -1613,6 +1613,203 @@ mm_3gpp_select_best_cid (const gchar *apn, /*************************************************************************/ +MM3gppProfile * +mm_3gpp_profile_new_from_pdp_context (MM3gppPdpContext *pdp_context) +{ + MM3gppProfile *profile; + + profile = mm_3gpp_profile_new (); + mm_3gpp_profile_set_profile_id (profile, pdp_context->cid); + mm_3gpp_profile_set_apn (profile, pdp_context->apn); + mm_3gpp_profile_set_ip_type (profile, pdp_context->pdp_type); + return profile; +} + +GList * +mm_3gpp_profile_list_new_from_pdp_context_list (GList *pdp_context_list) +{ + GList *profile_list = NULL; + GList *l; + + for (l = pdp_context_list; l; l = g_list_next (l)) { + MM3gppPdpContext *pdp_context; + MM3gppProfile *profile; + + pdp_context = (MM3gppPdpContext *)l->data; + profile = mm_3gpp_profile_new_from_pdp_context (pdp_context); + profile_list = g_list_append (profile_list, profile); + } + return profile_list; +} + +void +mm_3gpp_profile_list_free (GList *profile_list) +{ + g_list_free_full (profile_list, g_object_unref); +} + +MM3gppProfile * +mm_3gpp_profile_list_find_by_profile_id (GList *profile_list, + gint profile_id, + GError **error) +{ + GList *l; + + for (l = profile_list; l; l = g_list_next (l)) { + MM3gppProfile *iter_profile = l->data; + + if (mm_3gpp_profile_get_profile_id (iter_profile) == profile_id) + return g_object_ref (iter_profile); + } + + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, + "Profile '%d' not found", profile_id); + return NULL; +} + +gint +mm_3gpp_profile_list_find_empty (GList *profile_list, + gint min_profile_id, + gint max_profile_id, + GError **error) +{ + GList *l; + gint profile_id; + + profile_id = min_profile_id; + for (l = profile_list; l; l = g_list_next (l)) { + MM3gppProfile *iter_profile = l->data; + gint iter_profile_id; + + iter_profile_id = mm_3gpp_profile_get_profile_id (iter_profile); + if (iter_profile_id > profile_id) + break; + if (iter_profile_id == profile_id) + profile_id++; + } + + if (profile_id > max_profile_id) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, + "No empty profile available"); + return MM_3GPP_PROFILE_ID_UNKNOWN; + } + + return profile_id; +} + +gint +mm_3gpp_profile_list_find_best (GList *profile_list, + MM3gppProfile *requested, + GEqualFunc cmp_apn, + MM3gppProfileCmpFlags cmp_flags, + gint min_profile_id, + gint max_profile_id, + gpointer log_object, + MM3gppProfile **out_reused, + gboolean *out_overwritten) +{ + GList *l; + MMBearerIpFamily requested_ip_type; + gint prev_profile_id = 0; + gint unused_profile_id = 0; + gint max_found_profile_id = 0; + gint max_allowed_profile_id = 0; + gint blank_profile_id = 0; + + g_assert (out_reused); + g_assert (out_overwritten); + + requested_ip_type = mm_3gpp_profile_get_ip_type (requested); + + /* When looking for exact profile matches we should not compare + * the profile id, as the requested profile won't have one set */ + cmp_flags |= MM_3GPP_PROFILE_CMP_FLAGS_NO_PROFILE_ID; + + /* Look for the exact PDP context we want */ + for (l = profile_list; l; l = g_list_next (l)) { + MM3gppProfile *iter_profile = l->data; + MMBearerIpFamily iter_ip_type; + const gchar *iter_apn; + gint iter_profile_id; + + iter_profile_id = mm_3gpp_profile_get_profile_id (iter_profile); + + /* Always prefer an exact match; compare all supported fields except for profile id */ + if (mm_3gpp_profile_cmp (iter_profile, requested, cmp_apn, cmp_flags)) { + mm_obj_dbg (log_object, "found exact context at profile %d", iter_profile_id); + *out_reused = g_object_ref (iter_profile); + *out_overwritten = FALSE; + return iter_profile_id; + } + + /* Same PDP type but with no APN set? we may use that one if no exact match found */ + iter_ip_type = mm_3gpp_profile_get_ip_type (iter_profile); + iter_apn = mm_3gpp_profile_get_apn (iter_profile); + if ((iter_ip_type == requested_ip_type) && (!iter_apn || !iter_apn[0]) && !blank_profile_id) + blank_profile_id = iter_profile_id; + + /* If an unused CID was not found yet and the previous CID is not (CID - 1), + * this means that (previous CID + 1) is an unused CID that can be used. + * This logic will allow us using unused CIDs that are available in the gaps + * between already defined contexts. + */ + if (!unused_profile_id && prev_profile_id && ((prev_profile_id + 1) < iter_profile_id)) + unused_profile_id = prev_profile_id + 1; + + /* Update previous CID value to the current CID for use in the next loop, + * unless an unused CID was already found. + */ + if (!unused_profile_id) + prev_profile_id = iter_profile_id; + + /* Update max CID if we found a bigger one */ + if (max_found_profile_id < iter_profile_id) + max_found_profile_id = iter_profile_id; + } + + /* Try to use an unused CID detected in between the already defined contexts */ + if (unused_profile_id) { + mm_obj_dbg (log_object, "found unused profile %d", unused_profile_id); + *out_reused = NULL; + *out_overwritten = FALSE; + return unused_profile_id; + } + + /* If the max existing CID found during CGDCONT? is below the max allowed + * CID, then we can use the next available CID because it's an unused one. */ + max_allowed_profile_id = max_profile_id; + if (max_found_profile_id && (max_found_profile_id < max_allowed_profile_id)) { + mm_obj_dbg (log_object, "found unused profile %d (<%d)", max_found_profile_id + 1, max_allowed_profile_id); + *out_reused = NULL; + *out_overwritten = FALSE; + return (max_found_profile_id + 1); + } + + /* Rewrite a context defined with no APN, if any */ + if (blank_profile_id) { + mm_obj_dbg (log_object, "rewriting profile %d with empty APN", blank_profile_id); + *out_reused = NULL; + *out_overwritten = TRUE; + return blank_profile_id; + } + + /* Rewrite the last existing one found */ + if (max_found_profile_id) { + mm_obj_dbg (log_object, "rewriting last profile %d detected", max_found_profile_id); + *out_reused = NULL; + *out_overwritten = TRUE; + return max_found_profile_id; + } + + /* Otherwise, just fallback to min CID */ + mm_obj_dbg (log_object, "falling back to profile %d", min_profile_id); + *out_reused = NULL; + *out_overwritten = TRUE; + return min_profile_id; +} + +/*************************************************************************/ + static void mm_3gpp_pdp_context_format_free (MM3gppPdpContextFormat *format) { |