<div dir="ltr"><div class="gmail_default" style="font-family:arial,helvetica,sans-serif">Of course it looks great, I just want to have a try tomorrow on a modem to double check it.<br></div></div><div class="gmail_extra"><br><div class="gmail_quote">On 4 September 2017 at 18:29, Carlo Lobrano <span dir="ltr"><<a href="mailto:c.lobrano@gmail.com" target="_blank">c.lobrano@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><span class=""><div class="gmail_default" style="font-family:arial,helvetica,sans-serif">> Using 
its own GTask ends up requiring the extra _ready() method, but that<br>
> makes the flow much more clear and consistent I think.</div><div class="gmail_default" style="font-family:arial,helvetica,sans-serif"><br></div></span><div class="gmail_default" style="font-family:arial,helvetica,sans-serif">yeah, I agree too</div><div class="gmail_default" style="font-family:arial,helvetica,sans-serif"><br></div><div class="gmail_default" style="font-family:arial,helvetica,sans-serif"><span class="">> I've pushed your patch to the "clobrano/qss-unsolicited-<wbr>power" branch<br>> in my github repo:<br>>   <a href="https://github.com/aleksander0m/ModemManager/commits/clobrano/qss-unsolicited-power" target="_blank">https://github.com/<wbr>aleksander0m/ModemManager/<wbr>commits/clobrano/qss-<wbr>unsolicited-power</a><br>> <br>> I've fixed up several whitespace issues in your patch, and then pushed<br>> several other commits on top. Could you give them a look to make sure<br>> they're ok?<br><br></span>sure, I'm going to check it, thanks for the explanation and the fixes!<br><br><br>
</div></div><div class="HOEnZb"><div class="h5"><div class="gmail_extra"><br><div class="gmail_quote">On 4 September 2017 at 18:09, Aleksander Morgado <span dir="ltr"><<a href="mailto:aleksander@aleksander.es" target="_blank">aleksander@aleksander.es</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Hey!<br>
<span><br>
On Mon, Sep 4, 2017 at 8:13 AM, Carlo Lobrano <<a href="mailto:c.lobrano@gmail.com" target="_blank">c.lobrano@gmail.com</a>> wrote:<br>
> When transitioning between power-low and power-on modes, Telit modems<br>
> switch the SIM off/on, which leads to the emission of #QSS unsolicited not<br>
> related to actual SIM swaps.<br>
><br>
> To handle this #QSS unsolicited, this patch:<br>
><br>
> * disables reacting on #QSS unsolicited when modem_power_down is received<br>
> * implements modem_after_power_up that:<br>
>     - checks whether the SIM has been changed, matching cached SIM<br>
>       Identifier with the value in the current SIM. If SIM Identifier,<br>
>       is different, sim hot swap ports detected is called.<br>
>     - re-enables reacting on #QSS unsolicited<br>
><br>
> ---<br>
><br>
> Hi Aleksander,<br>
><br>
> I should have fixed all the problems, but I'm still not sure about<br>
> base-sim's implementation of mm_base_sim_load_sim_identifie<wbr>r and<br>
> mm_base_sim_load_sim_identifie<wbr>r_finish.<br>
><br>
> I saw that, generally, similar functions have their own ..._ready counterpart,<br>
> and maybe a GTask that takes care of caller's callback,<br>
> but I think I shouldn't need it and let the caller's callback do all the<br>
> job. However, as I said, I'm not really sure this is right.<br>
><br>
<br>
</span>What you did is technically right, although I always prefer to avoid<br>
putting much logic in the finish() method (as it's really duplicating<br>
logic), so I always have the async method manage its own GTask. In<br>
this way, the finish() method doesn't need to do extra checks like "do<br>
I need to do special things based on how the GTask was created?" Using<br>
its own GTask ends up requiring the extra _ready() method, but that<br>
makes the flow much more clear and consistent I think.<br>
<br>
I've pushed your patch to the "clobrano/qss-unsolicited-powe<wbr>r" branch<br>
in my github repo:<br>
  <a href="https://github.com/aleksander0m/ModemManager/commits/clobrano/qss-unsolicited-power" rel="noreferrer" target="_blank">https://github.com/aleksander0<wbr>m/ModemManager/commits/<wbr>clobrano/qss-unsolicited-power</a><br>
<br>
I've fixed up several whitespace issues in your patch, and then pushed<br>
several other commits on top. Could you give them a look to make sure<br>
they're ok? The last patch does the own-GTask think discussed above.<br>
<div class="m_4760795883497439400HOEnZb"><div class="m_4760795883497439400h5"><br>
<br>
> ---<br>
>  plugins/telit/mm-broadband-mod<wbr>em-telit.c | 181 ++++++++++++++++++++++++++++++<wbr>-<br>
>  src/mm-base-sim.c                        |  51 +++++++++<br>
>  src/mm-base-sim.h                        |  80 +++++++-------<br>
>  3 files changed, 271 insertions(+), 41 deletions(-)<br>
><br>
> diff --git a/plugins/telit/mm-broadband-m<wbr>odem-telit.c b/plugins/telit/mm-broadband-m<wbr>odem-telit.c<br>
> index a9fc5f0..b30e7c9 100644<br>
> --- a/plugins/telit/mm-broadband-m<wbr>odem-telit.c<br>
> +++ b/plugins/telit/mm-broadband-m<wbr>odem-telit.c<br>
> @@ -58,6 +58,7 @@ struct _MMBroadbandModemTelitPrivate {<br>
>      MMTelitCsimLockState csim_lock_state;<br>
>      GTask *csim_lock_task;<br>
>      guint csim_lock_timeout_id;<br>
> +    gboolean parse_qss;<br>
>  };<br>
><br>
>  /*****************************<wbr>******************************<wbr>******************/<br>
> @@ -151,10 +152,15 @@ telit_qss_unsolicited_handler (MMPortSerialAt *port,<br>
>      }<br>
><br>
>      if (cur_qss_status != prev_qss_status)<br>
> -        mm_dbg ("QSS handler: status changed '%s -> %s",<br>
> +        mm_dbg ("QSS handler: status changed '%s -> %s'",<br>
>                  mm_telit_qss_status_get_string (prev_qss_status),<br>
>                  mm_telit_qss_status_get_string (cur_qss_status));<br>
><br>
> +    if (self->priv->parse_qss == FALSE) {<br>
> +        mm_dbg ("QSS: message ignored");<br>
> +        return;<br>
> +    }<br>
> +<br>
>      if ((prev_qss_status == QSS_STATUS_SIM_REMOVED && cur_qss_status != QSS_STATUS_SIM_REMOVED) ||<br>
>          (prev_qss_status > QSS_STATUS_SIM_REMOVED && cur_qss_status == QSS_STATUS_SIM_REMOVED)) {<br>
>          mm_info ("QSS handler: SIM swap detected");<br>
> @@ -903,14 +909,175 @@ modem_load_unlock_retries (MMIfaceModem *self,<br>
>  }<br>
><br>
>  /*****************************<wbr>******************************<wbr>******************/<br>
> +/* Modem after power up (Modem interface) */<br>
> +typedef enum {<br>
> +    AFTER_POWER_UP_STEP_FIRST,<br>
> +    AFTER_POWER_UP_STEP_GET_SIM_ID<wbr>ENTIFIER,<br>
> +    AFTER_POWER_UP_STEP_ENABLE_QSS<wbr>_PARSING,<br>
> +    AFTER_POWER_UP_STEP_LAST<br>
> +} ModemAfterPowerUpStep;<br>
> +<br>
> +typedef struct {<br>
> +    gboolean has_sim_changed;<br>
> +    guint retries;<br>
> +    ModemAfterPowerUpStep step;<br>
> +} AfterPowerUpContext;<br>
> +<br>
> +static void after_power_up_step (GTask *task);<br>
> +<br>
> +static gboolean<br>
> +modem_after_power_up_finish (MMIfaceModem *self,<br>
> +                             GAsyncResult *res,<br>
> +                             GError **error)<br>
> +{<br>
> +    return g_task_propagate_boolean (G_TASK (res), error);<br>
> +}<br>
> +<br>
> +static void<br>
> +load_sim_identifier_ready (MMBaseSim *sim,<br>
> +                           GAsyncResult *res,<br>
> +                           GTask *task)<br>
> +{<br>
> +    AfterPowerUpContext *ctx;<br>
> +    GError *error = NULL;<br>
> +    gchar *current_simid;<br>
> +    gchar *cached_simid;<br>
> +<br>
> +    ctx = g_task_get_task_data (task);<br>
> +<br>
> +    cached_simid = (gchar *)mm_gdbus_sim_get_sim_identif<wbr>ier (MM_GDBUS_SIM (sim));<br>
> +    current_simid = mm_base_sim_load_sim_identifie<wbr>r_finish (sim, res, &error);<br>
> +<br>
> +    if (error) {<br>
> +        if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) {<br>
> +            mm_warn ("could not load SIM identifier: %s", error->message);<br>
> +            g_clear_error (&error);<br>
> +            goto out;<br>
> +        }<br>
> +<br>
> +        if (ctx->retries-- > 0) {<br>
> +            mm_warn ("could not load SIM identifier: %s (%d retries left)",<br>
> +                     error->message, ctx->retries);<br>
> +            g_clear_error (&error);<br>
> +            g_timeout_add_seconds (1, (GSourceFunc) after_power_up_step, task);<br>
> +            return;<br>
> +        }<br>
> +<br>
> +        mm_warn ("could not load SIM identifier: %s", error->message);<br>
> +        g_clear_error (&error);<br>
> +        goto out;<br>
> +    }<br>
> +<br>
> +    if (g_strcmp0 (current_simid, cached_simid) != 0) {<br>
> +        mm_warn ("sim identifier has changed: possible SIM swap during power down/low");<br>
> +        ctx->has_sim_changed = TRUE;<br>
> +    }<br>
> +<br>
> +out:<br>
> +    g_free (current_simid);<br>
> +    ctx->step++;<br>
> +    after_power_up_step (task);<br>
> +}<br>
> +<br>
> +static void<br>
> +after_power_up_step (GTask *task)<br>
> +{<br>
> +    AfterPowerUpContext *ctx;<br>
> +    MMBroadbandModemTelit *self;<br>
> +    MMBaseSim *sim = NULL;<br>
> +<br>
> +    ctx = g_task_get_task_data (task);<br>
> +    self = g_task_get_source_object (task);<br>
> +<br>
> +    g_object_get (MM_BROADBAND_MODEM_TELIT (self),<br>
> +                  MM_IFACE_MODEM_SIM, &sim,<br>
> +                  NULL);<br>
> +    if (!sim) {<br>
> +        g_task_return_new_error (task,<br>
> +                                 MM_CORE_ERROR,<br>
> +                                 MM_CORE_ERROR_FAILED,<br>
> +                                 "could not acquire sim object");<br>
> +        return;<br>
> +    }<br>
> +<br>
> +    switch (ctx->step) {<br>
> +    case AFTER_POWER_UP_STEP_FIRST:<br>
> +        /* Fall back on next step */<br>
> +        ctx->step++;<br>
> +    case AFTER_POWER_UP_STEP_GET_SIM_ID<wbr>ENTIFIER:<br>
> +        mm_base_sim_load_sim_identifie<wbr>r (sim,<br>
> +                                         (GAsyncReadyCallback)load_<wbr>sim_identifier_ready,<br>
> +                                         task);<br>
> +        g_object_unref (sim);<br>
> +        return;<br>
> +    case AFTER_POWER_UP_STEP_ENABLE_QSS<wbr>_PARSING:<br>
> +        mm_dbg ("Stop ignoring #QSS");<br>
> +        self->priv->parse_qss = TRUE;<br>
> +<br>
> +        /* Fall back on next step */<br>
> +        ctx->step++;<br>
> +    case AFTER_POWER_UP_STEP_LAST:<br>
> +        if (ctx->has_sim_changed)<br>
> +            mm_broadband_modem_update_sim_<wbr>hot_swap_detected (MM_BROADBAND_MODEM (self));<br>
> +<br>
> +        g_task_return_boolean (task, TRUE);<br>
> +        g_object_unref (task);<br>
> +        break;<br>
> +    default:<br>
> +        g_assert_not_reached ();<br>
> +    }<br>
> +}<br>
> +<br>
> +static void<br>
> +modem_after_power_up (MMIfaceModem *self,<br>
> +                      GAsyncReadyCallback callback,<br>
> +                      gpointer user_data)<br>
> +{<br>
> +    GTask *task;<br>
> +    AfterPowerUpContext *ctx;<br>
> +<br>
> +    ctx = g_new0 (AfterPowerUpContext, 1);<br>
> +    ctx->step = AFTER_POWER_UP_STEP_FIRST;<br>
> +    ctx->has_sim_changed = FALSE;<br>
> +    ctx->retries = 3;<br>
> +<br>
> +    task = g_task_new (self, NULL, callback, user_data);<br>
> +    g_task_set_task_data (task, ctx, (GDestroyNotify) g_free);<br>
> +<br>
> +    after_power_up_step (task);<br>
> +}<br>
> +<br>
> +<br>
> +/****************************<wbr>******************************<wbr>*******************/<br>
>  /* Modem power down (Modem interface) */<br>
><br>
> +static void<br>
> +telit_modem_power_down_ready (MMBaseModem *self,<br>
> +                              GAsyncResult *res,<br>
> +                              GTask *task)<br>
> +{<br>
> +    GError *error = NULL;<br>
> +<br>
> +    if (mm_base_modem_at_command_fini<wbr>sh (self, res, &error)) {<br>
> +        mm_dbg ("Ignore #QSS unsolicited during power down/low");<br>
> +        MM_BROADBAND_MODEM_TELIT (self)->priv->parse_qss = FALSE;<br>
> +    }<br>
> +<br>
> +    if (error) {<br>
> +        mm_err ("modem power down: %s", error->message);<br>
> +        g_clear_error (&error);<br>
> +    }<br>
> +<br>
> +    g_task_return_boolean (task, TRUE);<br>
> +    g_object_unref (task);<br>
> +}<br>
> +<br>
>  static gboolean<br>
>  modem_power_down_finish (MMIfaceModem *self,<br>
>                           GAsyncResult *res,<br>
>                           GError **error)<br>
>  {<br>
> -    return !!mm_base_modem_at_command_fin<wbr>ish (MM_BASE_MODEM (self), res, error);<br>
> +    return g_task_propagate_boolean (G_TASK (res), error);<br>
>  }<br>
><br>
>  static void<br>
> @@ -918,12 +1085,15 @@ modem_power_down (MMIfaceModem *self,<br>
>                    GAsyncReadyCallback callback,<br>
>                    gpointer user_data)<br>
>  {<br>
> +    GTask *task;<br>
> +<br>
> +    task = g_task_new (self, NULL, callback, user_data);<br>
>      mm_base_modem_at_command (MM_BASE_MODEM (self),<br>
>                                "+CFUN=4",<br>
>                                20,<br>
>                                FALSE,<br>
> -                              callback,<br>
> -                              user_data);<br>
> +                              (GAsyncReadyCallback) telit_modem_power_down_ready,<br>
> +                              task);<br>
>  }<br>
><br>
>  /*****************************<wbr>******************************<wbr>******************/<br>
> @@ -1457,6 +1627,7 @@ mm_broadband_modem_telit_init (MMBroadbandModemTelit *self)<br>
>      self->priv->csim_lock_support = FEATURE_SUPPORT_UNKNOWN;<br>
>      self->priv->csim_lock_state = CSIM_LOCK_STATE_UNKNOWN;<br>
>      self->priv->qss_status = QSS_STATUS_UNKNOWN;<br>
> +    self->priv->parse_qss = TRUE;<br>
>  }<br>
><br>
>  static void<br>
> @@ -1474,6 +1645,8 @@ iface_modem_init (MMIfaceModem *iface)<br>
>      iface->load_unlock_retries = modem_load_unlock_retries;<br>
>      iface->reset = modem_reset;<br>
>      iface->reset_finish = modem_reset_finish;<br>
> +    iface->modem_after_power_up = modem_after_power_up;<br>
> +    iface->modem_after_power_up_fi<wbr>nish = modem_after_power_up_finish;<br>
>      iface->modem_power_down = modem_power_down;<br>
>      iface->modem_power_down_finish = modem_power_down_finish;<br>
>      iface->load_access_technologie<wbr>s = load_access_technologies;<br>
> diff --git a/src/mm-base-sim.c b/src/mm-base-sim.c<br>
> index f3525e0..d03f342 100644<br>
> --- a/src/mm-base-sim.c<br>
> +++ b/src/mm-base-sim.c<br>
> @@ -701,6 +701,57 @@ mm_base_sim_send_puk (MMBaseSim *self,<br>
>  }<br>
><br>
>  /*****************************<wbr>******************************<wbr>******************/<br>
> +/* LOAD SIM IDENTIFIER */<br>
> +gchar *<br>
> +mm_base_sim_load_sim_identifi<wbr>er_finish (MMBaseSim *self,<br>
> +                                        GAsyncResult *res,<br>
> +                                        GError **error) {<br>
> +    GError *inner_error = NULL;<br>
> +    gchar *simid;<br>
> +<br>
> +    if (g_async_result_is_tagged (res, mm_base_sim_load_sim_identifie<wbr>r) ||<br>
> +        !MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier_fi<wbr>nish) {<br>
> +        inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,<br>
> +                                   "not implemented");<br>
> +        g_propagate_error (error, inner_error);<br>
> +        return NULL;<br>
> +    }<br>
> +<br>
> +    simid = MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier_fi<wbr>nish (self, res, &inner_error);<br>
> +    if (inner_error) {<br>
> +        g_propagate_error (error, inner_error);<br>
> +        return NULL;<br>
> +    }<br>
> +<br>
> +    return simid;<br>
> +}<br>
> +<br>
> +void<br>
> +mm_base_sim_load_sim_identifi<wbr>er (MMBaseSim *self,<br>
> +                                 GAsyncReadyCallback callback,<br>
> +                                 gpointer user_data)<br>
> +{<br>
> +    if (!MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier &&<br>
> +        !MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier_fi<wbr>nish) {<br>
> +        g_task_report_new_error (self,<br>
> +                                 callback,<br>
> +                                 user_data,<br>
> +                                 mm_base_sim_load_sim_identifi<wbr>er,<br>
> +                                 MM_CORE_ERROR,<br>
> +                                 MM_CORE_ERROR_UNSUPPORTED,<br>
> +                                 "not implemented");<br>
> +        return;<br>
> +    }<br>
> +<br>
> +    MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier (<br>
> +            self,<br>
> +            (GAsyncReadyCallback)callback,<br>
> +            user_data);<br>
> +<br>
> +    return;<br>
> +}<br>
> +<br>
> +/****************************<wbr>******************************<wbr>*******************/<br>
>  /* SEND PIN (DBus call handling) */<br>
><br>
>  typedef struct {<br>
> diff --git a/src/mm-base-sim.h b/src/mm-base-sim.h<br>
> index 1903994..22f02c8 100644<br>
> --- a/src/mm-base-sim.h<br>
> +++ b/src/mm-base-sim.h<br>
> @@ -50,8 +50,7 @@ struct _MMBaseSim {<br>
><br>
>  struct _MMBaseSimClass {<br>
>      MmGdbusSimSkeletonClass parent;<br>
> -<br>
> -    /* Load SIM identifier (async) */<br>
> +/* Load SIM identifier (async) */<br>
>      void    (* load_sim_identifier)        (MMBaseSim *self,<br>
>                                              GAsyncReadyCallback callback,<br>
>                                              gpointer user_data);<br>
> @@ -129,40 +128,47 @@ struct _MMBaseSimClass {<br>
><br>
>  GType mm_base_sim_get_type (void);<br>
><br>
> -void         mm_base_sim_new               (MMBaseModem *modem,<br>
> -                                            GCancellable *cancellable,<br>
> -                                            GAsyncReadyCallback callback,<br>
> -                                            gpointer user_data);<br>
> -MMBaseSim   *mm_base_sim_new_finish        (GAsyncResult  *res,<br>
> -                                            GError       **error);<br>
> -<br>
> -void         mm_base_sim_initialize        (MMBaseSim *self,<br>
> -                                            GCancellable *cancellable,<br>
> -                                            GAsyncReadyCallback callback,<br>
> -                                            gpointer user_data);<br>
> -gboolean     mm_base_sim_initialize_finish (MMBaseSim *self,<br>
> -                                            GAsyncResult *result,<br>
> -                                            GError **error);<br>
> -<br>
> -void         mm_base_sim_send_pin          (MMBaseSim *self,<br>
> -                                            const gchar *pin,<br>
> -                                            GAsyncReadyCallback callback,<br>
> -                                            gpointer user_data);<br>
> -gboolean     mm_base_sim_send_pin_finish   (MMBaseSim *self,<br>
> -                                            GAsyncResult *res,<br>
> -                                            GError **error);<br>
> -<br>
> -void         mm_base_sim_send_puk          (MMBaseSim *self,<br>
> -                                            const gchar *puk,<br>
> -                                            const gchar *new_pin,<br>
> -                                            GAsyncReadyCallback callback,<br>
> -                                            gpointer user_data);<br>
> -gboolean     mm_base_sim_send_puk_finish   (MMBaseSim *self,<br>
> -                                            GAsyncResult *res,<br>
> -                                            GError **error);<br>
> -<br>
> -void         mm_base_sim_export            (MMBaseSim *self);<br>
> -<br>
> -const gchar *mm_base_sim_get_path          (MMBaseSim *sim);<br>
> +void         mm_base_sim_new                        (MMBaseModem *modem,<br>
> +                                                     GCancellable *cancellable,<br>
> +                                                     GAsyncReadyCallback callback,<br>
> +                                                     gpointer user_data);<br>
> +MMBaseSim   *mm_base_sim_new_finish                 (GAsyncResult  *res,<br>
> +                                                     GError       **error);<br>
> +<br>
> +void         mm_base_sim_initialize                 (MMBaseSim *self,<br>
> +                                                     GCancellable *cancellable,<br>
> +                                                     GAsyncReadyCallback callback,<br>
> +                                                     gpointer user_data);<br>
> +gboolean     mm_base_sim_initialize_<wbr>finish          (MMBaseSim *self,<br>
> +                                                     GAsyncResult *result,<br>
> +                                                     GError **error);<br>
> +<br>
> +void         mm_base_sim_send_pin                   (MMBaseSim *self,<br>
> +                                                     const gchar *pin,<br>
> +                                                     GAsyncReadyCallback callback,<br>
> +                                                     gpointer user_data);<br>
> +gboolean     mm_base_sim_send_pin_finish            (MMBaseSim *self,<br>
> +                                                     GAsyncResult *res,<br>
> +                                                     GError **error);<br>
> +<br>
> +void         mm_base_sim_send_puk                   (MMBaseSim *self,<br>
> +                                                     const gchar *puk,<br>
> +                                                     const gchar *new_pin,<br>
> +                                                     GAsyncReadyCallback callback,<br>
> +                                                     gpointer user_data);<br>
> +gboolean     mm_base_sim_send_puk_finish            (MMBaseSim *self,<br>
> +                                                     GAsyncResult *res,<br>
> +                                                     GError **error);<br>
> +<br>
> +void         mm_base_sim_export                     (MMBaseSim *self);<br>
> +<br>
> +const gchar *mm_base_sim_get_path                   (MMBaseSim *sim);<br>
> +<br>
> +void          mm_base_sim_load_sim_identifie<wbr>r        (MMBaseSim *self,<br>
> +                                                     GAsyncReadyCallback callback,<br>
> +                                                     gpointer user_data);<br>
> +gchar       *mm_base_sim_load_sim_identif<wbr>ier_finish (MMBaseSim *self,<br>
> +                                                     GAsyncResult *res,<br>
> +                                                     GError **error);<br>
><br>
>  #endif /* MM_BASE_SIM_H */<br>
> --<br>
> 2.9.3<br>
><br>
</div></div><div class="m_4760795883497439400HOEnZb"><div class="m_4760795883497439400h5">> ______________________________<wbr>_________________<br>
> ModemManager-devel mailing list<br>
> <a href="mailto:ModemManager-devel@lists.freedesktop.org" target="_blank">ModemManager-devel@lists.freed<wbr>esktop.org</a><br>
> <a href="https://lists.freedesktop.org/mailman/listinfo/modemmanager-devel" rel="noreferrer" target="_blank">https://lists.freedesktop.org/<wbr>mailman/listinfo/modemmanager-<wbr>devel</a><br>
<br>
<br>
<br>
--<br>
Aleksander<br>
<a href="https://aleksander.es" rel="noreferrer" target="_blank">https://aleksander.es</a><br>
</div></div></blockquote></div><br></div>
</div></div></blockquote></div><br></div>