[PATCH] broadband-modem/libqcdm: use signal strength from QCDM EVDO Pilot Sets log messages

Dan Williams dcbw at redhat.com
Wed Jul 27 15:45:43 UTC 2016


On Tue, 2016-07-26 at 17:44 +0200, Aleksander Morgado wrote:
> On Tue, Jul 26, 2016 at 5:24 PM, Dan Williams <dcbw at redhat.com>
> wrote:
> > 
> > When a CDMA-only modem is registered with the EVDO network, its not
> > possible to
> > read signal strength in the following cases:
> > 
> > 1) while a data connection is active on single-AT-port modems,
> > because the AT
> > port is used for PPP and not available for AT+CSQ, AT+CIND or
> > vendor-specific
> > signal strength commands
> > 
> > 2) when the modem reports only CDMA 1x signal strength with AT+CSQ
> > 
> > Now that we have a reasonable interpretation of RSSI from the QCDM
> > EVDO Pilot Sets V2 log messgae, use that when other means of
> > getting
> > signal strength aren't available.
> Nice thing! Do you have logs of a successful run for reference? +1
> from me anyway.

Pushed to git master...

Dan

> 
> > 
> > ---
> >  libqcdm/src/Makefile.am   |   2 +
> >  libqcdm/src/log-items.h   |  34 +++----
> >  libqcdm/src/logs.c        | 184
> > +++++++++++++++++++++++++++++++++++
> >  libqcdm/src/logs.h        |  50 ++++++++++
> >  src/mm-broadband-modem.c  | 240
> > +++++++++++++++++++++++++++++++++++++++++++++-
> >  src/mm-port-serial-qcdm.c | 160 ++++++++++++++++++++++++++++++-
> >  src/mm-port-serial-qcdm.h |  16 ++++
> >  7 files changed, 663 insertions(+), 23 deletions(-)
> >  create mode 100644 libqcdm/src/logs.c
> >  create mode 100644 libqcdm/src/logs.h
> > 
> > diff --git a/libqcdm/src/Makefile.am b/libqcdm/src/Makefile.am
> > index 2caaf00..f13098f 100644
> > --- a/libqcdm/src/Makefile.am
> > +++ b/libqcdm/src/Makefile.am
> > @@ -16,6 +16,8 @@ libqcdm_la_SOURCES = \
> >         commands.h \
> >         errors.c \
> >         errors.h \
> > +       logs.c \
> > +       logs.h \
> >         result.c \
> >         result.h \
> >         result-private.h \
> > diff --git a/libqcdm/src/log-items.h b/libqcdm/src/log-items.h
> > index 71f0f1e..8802dfd 100644
> > --- a/libqcdm/src/log-items.h
> > +++ b/libqcdm/src/log-items.h
> > @@ -37,7 +37,7 @@ enum {
> >      DM_LOG_ITEM_EVDO_REV_POWER_CONTROL          = 0x1063,
> >      DM_LOG_ITEM_EVDO_ARQ_EFFECTIVE_RECEIVE_RATE = 0x1066,
> >      DM_LOG_ITEM_EVDO_AIR_LINK_SUMMARY           = 0x1068,
> > -    DM_LOG_ITEM_EVDO_POWER                      = 0x1069
> > +    DM_LOG_ITEM_EVDO_POWER                      = 0x1069,
> >      DM_LOG_ITEM_EVDO_FWD_LINK_PACKET_SNAPSHOT   = 0x106A,
> >      DM_LOG_ITEM_EVDO_ACCESS_ATTEMPT             = 0x106C,
> >      DM_LOG_ITEM_EVDO_REV_ACTIVITY_BITS_BUFFER   = 0x106D,
> > @@ -91,14 +91,14 @@ struct DMLogItemCdmaReversePowerControl {
> >  typedef struct DMLogItemCdmaReversePowerControl
> > DMLogItemCdmaReversePowerControl;
> > 
> >  /* DM_LOG_ITEM_EVDO_PILOT_SETS_V2 */
> > -struct EvdoPilotSetsV2PilotRecord {
> > +struct DMLogItemEvdoPilotSetsV2Pilot {
> >      u_int16_t pilot_pn;
> >      /* HDR pilot energy doesn't appear to be in the same units as
> > 1x pilot
> > -     * energy (eg, -0.5 dBm increments).  Instead, you can
> > approximate EC/IO
> > -     * by using this formula empirically derived from simultaneous
> > AT!ECIO
> > -     * and HDR Pilot Sets V2 results from a Sierra modem:
> > +     * energy (eg, -0.5 dBm increments).  Instead it appears
> > roughly correlated
> > +     * to RSSI dBm by using this formula empirically derived from
> > simultaneous
> > +     * AT!RSSI and HDR Pilot Sets V2 results from a Sierra modem:
> >       *
> > -     * EC/IO = (pilot_energy / -50) + 1
> > +     * RSSI dBm = -110 + (MAX(pilot_energy - 50, 0) / 14)
> >       */
> >      u_int16_t pilot_energy;
> >      union {
> > @@ -122,22 +122,22 @@ struct EvdoPilotSetsV2PilotRecord {
> >          } Remaining;
> >      };
> >  } __attribute__ ((packed));
> > -typedef struct EvdoPilotSetsV2PilotRecord
> > EvdoPilotSetsV2PilotRecord;
> > +typedef struct DMLogItemEvdoPilotSetsV2Pilot
> > DMLogItemEvdoPilotSetsV2Pilot;
> > 
> >  /* DM_LOG_ITEM_EVDO_PILOT_SETS_V2 */
> >  struct DMLogItemEvdoPilotSetsV2 {
> >      u_int8_t pn_offset;
> > -    u_int8_t active_set_count;
> > -    u_int8_t active_set_window;
> > -    u_int16_t active_set_channel;
> > +    u_int8_t active_count;
> > +    u_int8_t active_window;
> > +    u_int16_t active_channel;
> >      u_int8_t unknown1;
> > -    u_int8_t candidate_set_count;
> > -    u_int8_t candidate_set_window;
> > -    u_int8_t remaining_set_count;
> > -    u_int8_t remaining_set_window;
> > +    u_int8_t candidate_count;
> > +    u_int8_t candidate_window;
> > +    u_int8_t remaining_count;
> > +    u_int8_t remaining_window;
> >      u_int8_t unknown2;
> > 
> > -    EvdoPilotSetsV2PilotRecord records[];
> > +    DMLogItemEvdoPilotSetsV2Pilot sets[];
> >  } __attribute__ ((packed));
> >  typedef struct DMLogItemEvdoPilotSetsV2 DMLogItemEvdoPilotSetsV2;
> > 
> > @@ -148,7 +148,7 @@ struct DMLogItemWcdmaTaFingerInfo {
> >      u_int8_t non_coherent_interval_len;
> >      u_int8_t num_paths;
> >      u_int32_t path_enr;
> > -    int32_t pn_pos_path
> > +    int32_t pn_pos_path;
> >      int16_t pri_cpich_psc;
> >      u_int8_t unknown1;
> >      u_int8_t sec_cpich_ssc;
> > @@ -214,7 +214,7 @@ typedef struct DMLogItemGsmBurstMetric
> > DMLogItemGsmBurstMetric;
> > 
> >  struct DMLogItemGsmBurstMetrics {
> >      u_int8_t channel;
> > -    DMLogItemBurstMetric metrics[4];
> > +    DMLogItemGsmBurstMetric metrics[4];
> >  } __attribute__ ((packed));
> >  typedef struct DMLogItemGsmBurstMetrics DMLogItemGsmBurstMetrics;
> > 
> > diff --git a/libqcdm/src/logs.c b/libqcdm/src/logs.c
> > new file mode 100644
> > index 0000000..ef604f6
> > --- /dev/null
> > +++ b/libqcdm/src/logs.c
> > @@ -0,0 +1,184 @@
> > +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-
> > offset: 4 -*- */
> > +/*
> > + * Copyright (C) 2010 Red Hat, Inc.
> > + *
> > + * This program is free software: you can redistribute it and/or
> > + * modify it under the terms of version 2 of the GNU General
> > Public
> > + * License as published by the Free Software Foundation
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > + * GNU General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU General Public
> > License
> > + * along with this program.  If not, see <http://www.gnu.org/licen
> > ses/>.
> > + */
> > +
> > +#include <string.h>
> > +#include <stdlib.h>
> > +#include <endian.h>
> > +
> > +#include "log-items.h"
> > +#include "logs.h"
> > +#include "errors.h"
> > +#include "dm-commands.h"
> > +#include "result-private.h"
> > +#include "utils.h"
> > +
> > +
> > +/*****************************************************************
> > *****/
> > +
> > +static qcdmbool
> > +check_log_item (const char *buf, size_t len, u_int16_t log_code,
> > size_t min_len, int *out_error)
> > +{
> > +    DMCmdLog *log_cmd = (DMCmdLog *) buf;
> > +
> > +    if (len < sizeof (DMCmdLog)) {
> > +        qcdm_err (0, "DM log item malformed (must be at least %zu
> > bytes in length)", sizeof (DMCmdLog));
> > +        if (out_error)
> > +            *out_error = -QCDM_ERROR_RESPONSE_MALFORMED;
> > +        return FALSE;
> > +    }
> > +
> > +    if (buf[0] != DIAG_CMD_LOG) {
> > +        if (out_error)
> > +            *out_error = -QCDM_ERROR_RESPONSE_UNEXPECTED;
> > +        return FALSE;
> > +    }
> > +
> > +    if (le16toh (log_cmd->log_code) != log_code) {
> > +        if (out_error)
> > +            *out_error = -QCDM_ERROR_RESPONSE_UNEXPECTED;
> > +        return FALSE;
> > +    }
> > +
> > +    if (len < sizeof (DMCmdLog) + min_len) {
> > +        qcdm_err (0, "DM log item response not long enough (got
> > %zu, expected "
> > +                  "at least %zu).", len, sizeof (DMCmdLog) +
> > min_len);
> > +        if (out_error)
> > +            *out_error = -QCDM_ERROR_RESPONSE_BAD_LENGTH;
> > +        return FALSE;
> > +    }
> > +
> > +    return TRUE;
> > +}
> > +
> > +/*****************************************************************
> > *****/
> > +
> > +#define PILOT_SETS_LOG_ACTIVE_SET    "active-set"
> > +#define PILOT_SETS_LOG_CANDIDATE_SET "candidate-set"
> > +#define PILOT_SETS_LOG_REMAINING_SET  "remaining-set"
> > +
> > +static const char *
> > +set_num_to_str (u_int32_t num)
> > +{
> > +    if (num == QCDM_LOG_ITEM_EVDO_PILOT_SETS_V2_TYPE_ACTIVE)
> > +        return PILOT_SETS_LOG_ACTIVE_SET;
> > +    if (num == QCDM_LOG_ITEM_EVDO_PILOT_SETS_V2_TYPE_CANDIDATE)
> > +        return PILOT_SETS_LOG_CANDIDATE_SET;
> > +    if (num == QCDM_LOG_ITEM_EVDO_PILOT_SETS_V2_TYPE_REMAINING)
> > +        return PILOT_SETS_LOG_REMAINING_SET;
> > +    return NULL;
> > +}
> > +
> > +QcdmResult *
> > +qcdm_log_item_evdo_pilot_sets_v2_new (const char *buf, size_t len,
> > int *out_error)
> > +{
> > +    QcdmResult *result = NULL;
> > +    DMLogItemEvdoPilotSetsV2 *pilot_sets;
> > +    DMCmdLog *log_cmd = (DMCmdLog *) buf;
> > +    size_t sets_len;
> > +
> > +    qcdm_return_val_if_fail (buf != NULL, NULL);
> > +
> > +    if (!check_log_item (buf, len, DM_LOG_ITEM_EVDO_PILOT_SETS_V2,
> > sizeof (DMLogItemEvdoPilotSetsV2), out_error))
> > +        return NULL;
> > +
> > +    pilot_sets = (DMLogItemEvdoPilotSetsV2 *) log_cmd->data;
> > +
> > +    result = qcdm_result_new ();
> > +
> > +    sets_len = pilot_sets->active_count * sizeof
> > (DMLogItemEvdoPilotSetsV2Pilot);
> > +    if (sets_len > 0) {
> > +        qcdm_result_add_u8_array (result,
> > +                                  PILOT_SETS_LOG_ACTIVE_SET,
> > +                                  (const u_int8_t *) &pilot_sets-
> > >sets[0],
> > +                                  sets_len);
> > +    }
> > +
> > +    sets_len = pilot_sets->candidate_count * sizeof
> > (DMLogItemEvdoPilotSetsV2Pilot);
> > +    if (sets_len > 0) {
> > +        qcdm_result_add_u8_array (result,
> > +                                  PILOT_SETS_LOG_CANDIDATE_SET,
> > +                                  (const u_int8_t *) &pilot_sets-
> > >sets[pilot_sets->active_count],
> > +                                  sets_len);
> > +    }
> > +
> > +    sets_len = pilot_sets->remaining_count * sizeof
> > (DMLogItemEvdoPilotSetsV2Pilot);
> > +    if (sets_len > 0) {
> > +        qcdm_result_add_u8_array (result,
> > +                                  PILOT_SETS_LOG_REMAINING_SET,
> > +                                  (const u_int8_t *) &pilot_sets-
> > >sets[pilot_sets->active_count + pilot_sets->candidate_count],
> > +                                  sets_len);
> > +    }
> > +
> > +    return result;
> > +
> > +}
> > +
> > +qcdmbool
> > +qcdm_log_item_evdo_pilot_sets_v2_get_num (QcdmResult *result,
> > +                                          u_int32_t set_type,
> > +                                          u_int32_t *out_num)
> > +{
> > +    const char *set_name;
> > +    const u_int8_t *array = NULL;
> > +    size_t array_len = 0;
> > +
> > +    qcdm_return_val_if_fail (result != NULL, FALSE);
> > +
> > +    set_name = set_num_to_str (set_type);
> > +    qcdm_return_val_if_fail (set_name != NULL, FALSE);
> > +
> > +    if (qcdm_result_get_u8_array (result, set_name, &array,
> > &array_len))
> > +        return FALSE;
> > +
> > +    *out_num = array_len / sizeof (DMLogItemEvdoPilotSetsV2Pilot);
> > +    return TRUE;
> > +}
> > +
> > +#define MAX(a, b)  (((a) > (b)) ? (a) : (b))
> > +
> > +qcdmbool
> > +qcdm_log_item_evdo_pilot_sets_v2_get_pilot (QcdmResult *result,
> > +                                            u_int32_t set_type,
> > +                                            u_int32_t num,
> > +                                            u_int32_t
> > *out_pilot_pn,
> > +                                            u_int32_t
> > *out_pilot_energy,
> > +                                            int32_t *out_rssi_dbm)
> > +{
> > +    const char *set_name;
> > +    DMLogItemEvdoPilotSetsV2Pilot *pilot;
> > +    const u_int8_t *array = NULL;
> > +    size_t array_len = 0;
> > +
> > +    qcdm_return_val_if_fail (result != NULL, FALSE);
> > +
> > +    set_name = set_num_to_str (set_type);
> > +    qcdm_return_val_if_fail (set_name != NULL, FALSE);
> > +
> > +    if (qcdm_result_get_u8_array (result, set_name, &array,
> > &array_len))
> > +        return FALSE;
> > +
> > +    qcdm_return_val_if_fail (num < array_len / sizeof
> > (DMLogItemEvdoPilotSetsV2Pilot), FALSE);
> > +
> > +    pilot = (DMLogItemEvdoPilotSetsV2Pilot *) &array[num * sizeof
> > (DMLogItemEvdoPilotSetsV2Pilot)];
> > +    *out_pilot_pn = le16toh (pilot->pilot_pn);
> > +    *out_pilot_energy = le16toh (pilot->pilot_energy);
> > +    *out_rssi_dbm = (int32_t) (-110.0 + ((float) MAX (le16toh
> > (pilot->pilot_energy) - 50, 0) / 14.0));
> > +    return TRUE;
> > +}
> > +
> > +/*****************************************************************
> > *****/
> > +
> > diff --git a/libqcdm/src/logs.h b/libqcdm/src/logs.h
> > new file mode 100644
> > index 0000000..bb17d1f
> > --- /dev/null
> > +++ b/libqcdm/src/logs.h
> > @@ -0,0 +1,50 @@
> > +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-
> > offset: 4 -*- */
> > +/*
> > + * Copyright (C) 2010 Red Hat, Inc.
> > + *
> > + * This program is free software: you can redistribute it and/or
> > + * modify it under the terms of version 2 of the GNU General
> > Public
> > + * License as published by the Free Software Foundation
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > + * GNU General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU General Public
> > License
> > + * along with this program.  If not, see <http://www.gnu.org/licen
> > ses/>.
> > + */
> > +
> > +#ifndef LIBQCDM_LOGS_H
> > +#define LIBQCDM_LOGS_H
> > +
> > +#include "utils.h"
> > +#include "result.h"
> > +
> > +/*****************************************************************
> > *****/
> > +
> > +enum {
> > +    QCDM_LOG_ITEM_EVDO_PILOT_SETS_V2_TYPE_UNKNOWN = 0,
> > +    QCDM_LOG_ITEM_EVDO_PILOT_SETS_V2_TYPE_ACTIVE = 1,
> > +    QCDM_LOG_ITEM_EVDO_PILOT_SETS_V2_TYPE_CANDIDATE = 2,
> > +    QCDM_LOG_ITEM_EVDO_PILOT_SETS_V2_TYPE_REMAINING = 3,
> > +};
> > +
> > +QcdmResult *qcdm_log_item_evdo_pilot_sets_v2_new       (const char
> > *buf,
> > +                                                        size_t
> > len,
> > +                                                        int
> > *out_error);
> > +
> > +qcdmbool    qcdm_log_item_evdo_pilot_sets_v2_get_num   (QcdmResult
> > *result,
> > +                                                        u_int32_t
> > set_type,
> > +                                                        u_int32_t
> > *out_num);
> > +
> > +qcdmbool    qcdm_log_item_evdo_pilot_sets_v2_get_pilot (QcdmResult
> > *result,
> > +                                                        u_int32_t
> > set_type,
> > +                                                        u_int32_t
> > num,
> > +                                                        u_int32_t
> > *out_pilot_pn,
> > +                                                        u_int32_t
> > *out_pilot_energy,
> > +                                                        int32_t
> > *out_rssi_dbm);
> > +
> > +/*****************************************************************
> > *****/
> > +
> > +#endif  /* LIBQCDM_LOGS_H */
> > diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c
> > index e1fd7ca..04c716d 100644
> > --- a/src/mm-broadband-modem.c
> > +++ b/src/mm-broadband-modem.c
> > @@ -53,6 +53,8 @@
> >  #include "mm-port-serial-qcdm.h"
> >  #include "libqcdm/src/errors.h"
> >  #include "libqcdm/src/commands.h"
> > +#include "libqcdm/src/logs.h"
> > +#include "libqcdm/src/log-items.h"
> > 
> >  static void iface_modem_init (MMIfaceModem *iface);
> >  static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
> > @@ -172,6 +174,7 @@ struct _MMBroadbandModemPrivate {
> >      gboolean checked_sprint_support;
> >      gboolean has_spservice;
> >      gboolean has_speri;
> > +    gint evdo_pilot_rssi;
> > 
> >      /*<--- Modem Simple interface --->*/
> >      /* Properties */
> > @@ -1713,6 +1716,45 @@ modem_load_supported_ip_families
> > (MMIfaceModem *self,
> >  /*****************************************************************
> > ************/
> >  /* Signal quality loading (Modem interface) */
> > 
> > +static void
> > +qcdm_evdo_pilot_sets_log_handle (MMPortSerialQcdm *port,
> > +                                 GByteArray *log_buffer,
> > +                                 gpointer user_data)
> > +{
> > +    MMBroadbandModem *self = MM_BROADBAND_MODEM (user_data);
> > +    QcdmResult *result;
> > +    u_int32_t num_active = 0;
> > +    u_int32_t pilot_pn = 0;
> > +    u_int32_t pilot_energy = 0;
> > +    int32_t rssi_dbm = 0;
> > +
> > +    result = qcdm_log_item_evdo_pilot_sets_v2_new ((const char *)
> > log_buffer->data,
> > +                                                   log_buffer-
> > >len,
> > +                                                   NULL);
> > +    if (!result)
> > +        return;
> > +
> > +    if (!qcdm_log_item_evdo_pilot_sets_v2_get_num (result,
> > +                                                   QCDM_LOG_ITEM_E
> > VDO_PILOT_SETS_V2_TYPE_ACTIVE,
> > +                                                   &num_active)) {
> > +        qcdm_result_unref (result);
> > +        return;
> > +    }
> > +
> > +    if (num_active > 0 &&
> > +        qcdm_log_item_evdo_pilot_sets_v2_get_pilot (result,
> > +                                                    QCDM_LOG_ITEM_
> > EVDO_PILOT_SETS_V2_TYPE_ACTIVE,
> > +                                                    0,
> > +                                                    &pilot_pn,
> > +                                                    &pilot_energy,
> > +                                                    &rssi_dbm)) {
> > +        mm_dbg ("EVDO active pilot RSSI: %ddBm", rssi_dbm);
> > +        self->priv->evdo_pilot_rssi = rssi_dbm;
> > +    }
> > +
> > +    qcdm_result_unref (result);
> > +}
> > +
> >  typedef struct {
> >      MMBroadbandModem *self;
> >      GSimpleAsyncResult *result;
> > @@ -1742,6 +1784,21 @@ modem_load_signal_quality_finish
> > (MMIfaceModem *self,
> >                                   G_SIMPLE_ASYNC_RESULT (res)));
> >  }
> > 
> > +static guint
> > +signal_quality_evdo_pilot_sets (MMBroadbandModem *self)
> > +{
> > +    gint dbm;
> > +
> > +    if (self->priv->modem_cdma_evdo_registration_state ==
> > MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN)
> > +        return 0;
> > +
> > +    if (self->priv->evdo_pilot_rssi >= 0)
> > +        return 0;
> > +
> > +    dbm = CLAMP (self->priv->evdo_pilot_rssi, -113, -51);
> > +    return 100 - ((dbm + 51) * 100 / (-113 + 51));
> > +}
> > +
> >  static void
> >  signal_quality_csq_ready (MMBroadbandModem *self,
> >                            GAsyncResult *res,
> > @@ -1767,8 +1824,11 @@ signal_quality_csq_ready (MMBroadbandModem
> > *self,
> >          result_str = mm_strip_tag (result_str, "+CSQ:");
> >          if (sscanf (result_str, "%d, %d", &quality, &ber)) {
> >              if (quality == 99) {
> > -                /* 99 means unknown, no service, etc */
> > -                quality = 0;
> > +                /* 99 can mean unknown, no service, etc.  But the
> > modem may
> > +                 * also only report CDMA 1x quality in CSQ, so try
> > EVDO via
> > +                 * QCDM log messages too.
> > +                 */
> > +                quality = signal_quality_evdo_pilot_sets (self);
> >              } else {
> >                  /* Normalize the quality */
> >                  quality = CLAMP (quality, 0, 31) * 100 / 31;
> > @@ -1977,6 +2037,17 @@ static void
> >  signal_quality_qcdm (SignalQualityContext *ctx)
> >  {
> >      GByteArray *pilot_sets;
> > +    guint quality;
> > +
> > +    /* If EVDO is active try that signal strength first */
> > +    quality = signal_quality_evdo_pilot_sets (ctx->self);
> > +    if (quality > 0) {
> > +        g_simple_async_result_set_op_res_gpointer (ctx->result,
> > +                                                   GUINT_TO_POINTE
> > R (quality),
> > +                                                   NULL);
> > +        signal_quality_context_complete_and_free (ctx);
> > +        return;
> > +    }
> > 
> >      /* Use CDMA1x pilot EC/IO if we can */
> >      pilot_sets = g_byte_array_sized_new (25);
> > @@ -6713,6 +6784,167 @@ modem_cdma_load_meid (MMIfaceModemCdma
> > *self,
> >  }
> > 
> >  /*****************************************************************
> > ************/
> > +/* Setup/Cleanup unsolicited events (CDMA interface) */
> > +
> > +typedef struct {
> > +    MMBroadbandModem *self;
> > +    gboolean setup;
> > +    GSimpleAsyncResult *result;
> > +    MMPortSerialQcdm *qcdm;
> > +} CdmaUnsolicitedEventsContext;
> > +
> > +static void
> > +cdma_unsolicited_events_context_complete_and_free
> > (CdmaUnsolicitedEventsContext *ctx,
> > +                                                   gboolean
> > close_port,
> > +                                                   GError *error)
> > +{
> > +    if (error)
> > +        g_simple_async_result_take_error (ctx->result, error);
> > +    else
> > +        g_simple_async_result_set_op_res_gboolean (ctx->result,
> > TRUE);
> > +    g_simple_async_result_complete_in_idle (ctx->result);
> > +
> > +    g_clear_object (&ctx->result);
> > +    g_clear_object (&ctx->self);
> > +
> > +    if (ctx->qcdm && close_port)
> > +        mm_port_serial_close (MM_PORT_SERIAL (ctx->qcdm));
> > +    g_clear_object (&ctx->qcdm);
> > +
> > +    g_free (ctx);
> > +}
> > +
> > +static void
> > +logcmd_qcdm_ready (MMPortSerialQcdm *port,
> > +                   GAsyncResult *res,
> > +                   CdmaUnsolicitedEventsContext *ctx)
> > +{
> > +    QcdmResult *result;
> > +    gint err = QCDM_SUCCESS;
> > +    GByteArray *response;
> > +    GError *error = NULL;
> > +
> > +    response = mm_port_serial_qcdm_command_finish (port, res,
> > &error);
> > +    if (error) {
> > +        cdma_unsolicited_events_context_complete_and_free (ctx,
> > TRUE, error);
> > +        return;
> > +    }
> > +
> > +    /* Parse the response */
> > +    result = qcdm_cmd_log_config_set_mask_result ((const gchar *)
> > response->data,
> > +                                                  response->len,
> > +                                                  &err);
> > +    g_byte_array_unref (response);
> > +    if (!result) {
> > +        error = g_error_new (MM_CORE_ERROR,
> > +                             MM_CORE_ERROR_FAILED,
> > +                             "Failed to parse Log Config Set Mask
> > command result: %d",
> > +                             err);
> > +        cdma_unsolicited_events_context_complete_and_free (ctx,
> > TRUE, error);
> > +        return;
> > +    }
> > +
> > +    mm_port_serial_qcdm_add_unsolicited_msg_handler (port,
> > +                                                     DM_LOG_ITEM_E
> > VDO_PILOT_SETS_V2,
> > +                                                     ctx->setup ?
> > qcdm_evdo_pilot_sets_log_handle : NULL,
> > +                                                     ctx->self,
> > +                                                     NULL);
> > +
> > +    qcdm_result_unref (result);
> > +
> > +    /* Balance the mm_port_seral_open() from
> > modem_cdma_setup_cleanup_unsolicited_events().
> > +     * We want to close it in either case:
> > +     *  (a) we're cleaning up and setup opened the port
> > +     *  (b) if it was unexpectedly closed before cleanup and thus
> > cleanup opened it
> > +     *
> > +     * Setup should leave the port open to allow log messages to
> > be received
> > +     * and sent to handlers.
> > +     */
> > +    cdma_unsolicited_events_context_complete_and_free (ctx, ctx-
> > >setup ? FALSE : TRUE, NULL);
> > +}
> > +
> > +static void
> > +modem_cdma_setup_cleanup_unsolicited_events (MMBroadbandModem
> > *self,
> > +                                             gboolean setup,
> > +                                             GAsyncReadyCallback
> > callback,
> > +                                             gpointer user_data)
> > +{
> > +    CdmaUnsolicitedEventsContext *ctx;
> > +    GByteArray *logcmd;
> > +    u_int16_t log_items[] = { DM_LOG_ITEM_EVDO_PILOT_SETS_V2, 0 };
> > +    GError *error = NULL;
> > +
> > +    ctx = g_new0 (CdmaUnsolicitedEventsContext, 1);
> > +    ctx->self = g_object_ref (self);
> > +    ctx->setup = TRUE;
> > +    ctx->result = g_simple_async_result_new (G_OBJECT (self),
> > +                                             callback,
> > +                                             user_data,
> > +                                             modem_cdma_setup_clea
> > nup_unsolicited_events);
> > +    ctx->qcdm = mm_base_modem_get_port_qcdm (MM_BASE_MODEM
> > (self));
> > +    if (!ctx->qcdm) {
> > +        cdma_unsolicited_events_context_complete_and_free (ctx,
> > FALSE, NULL);
> > +        return;
> > +    }
> > +
> > +    /* Setup must open the QCDM port and keep it open to receive
> > unsolicited
> > +     * events.  Cleanup expects the port to already be opened from
> > setup, but
> > +     * if not we still want to open it and try to disable log
> > messages.
> > +     */
> > +    if (setup || !mm_port_serial_is_open (MM_PORT_SERIAL (ctx-
> > >qcdm))) {
> > +        if (!mm_port_serial_open (MM_PORT_SERIAL (ctx->qcdm),
> > &error)) {
> > +            cdma_unsolicited_events_context_complete_and_free
> > (ctx, FALSE, error);
> > +            return;
> > +        }
> > +    }
> > +
> > +    logcmd = g_byte_array_sized_new (512);
> > +    logcmd->len = qcdm_cmd_log_config_set_mask_new ((char *)
> > logcmd->data,
> > +                                                    512,
> > +                                                    0x01,  /*
> > Equipment ID */
> > +                                                    setup ?
> > log_items : NULL);
> > +    assert (logcmd->len);
> > +
> > +    mm_port_serial_qcdm_command (ctx->qcdm,
> > +                                 logcmd,
> > +                                 5,
> > +                                 NULL,
> > +                                 (GAsyncReadyCallback)logcmd_qcdm_
> > ready,
> > +                                 ctx);
> > +    g_byte_array_unref (logcmd);
> > +}
> > +
> > +static gboolean
> > +modem_cdma_setup_cleanup_unsolicited_events_finish
> > (MMIfaceModemCdma *self,
> > +                                                    GAsyncResult
> > *res,
> > +                                                    GError
> > **error)
> > +{
> > +    return !g_simple_async_result_propagate_error
> > (G_SIMPLE_ASYNC_RESULT (res), error);
> > +}
> > +
> > +static void
> > +modem_cdma_setup_unsolicited_events (MMIfaceModemCdma *self,
> > +                                     GAsyncReadyCallback callback,
> > +                                     gpointer user_data)
> > +{
> > +    modem_cdma_setup_cleanup_unsolicited_events
> > (MM_BROADBAND_MODEM (self),
> > +                                                 TRUE,
> > +                                                 callback,
> > +                                                 user_data);
> > +}
> > +
> > +static void
> > +modem_cdma_cleanup_unsolicited_events (MMIfaceModemCdma *self,
> > +                                       GAsyncReadyCallback
> > callback,
> > +                                       gpointer user_data)
> > +{
> > +    modem_cdma_setup_cleanup_unsolicited_events
> > (MM_BROADBAND_MODEM (self),
> > +                                                 FALSE,
> > +                                                 callback,
> > +                                                 user_data);
> > +}
> > +
> > +/*****************************************************************
> > ************/
> >  /* HDR state check (CDMA interface) */
> > 
> >  typedef struct {
> > @@ -10429,6 +10661,10 @@ iface_modem_cdma_init (MMIfaceModemCdma
> > *iface)
> >      iface->load_meid_finish = modem_cdma_load_meid_finish;
> > 
> >      /* Registration check steps */
> > +    iface->setup_unsolicited_events =
> > modem_cdma_setup_unsolicited_events;
> > +    iface->setup_unsolicited_events_finish =
> > modem_cdma_setup_cleanup_unsolicited_events_finish;
> > +    iface->cleanup_unsolicited_events =
> > modem_cdma_cleanup_unsolicited_events;
> > +    iface->cleanup_unsolicited_events_finish =
> > modem_cdma_setup_cleanup_unsolicited_events_finish;
> >      iface->setup_registration_checks =
> > modem_cdma_setup_registration_checks;
> >      iface->setup_registration_checks_finish =
> > modem_cdma_setup_registration_checks_finish;
> >      iface->get_call_manager_state =
> > modem_cdma_get_call_manager_state;
> > diff --git a/src/mm-port-serial-qcdm.c b/src/mm-port-serial-qcdm.c
> > index 7732851..e997bb0 100644
> > --- a/src/mm-port-serial-qcdm.c
> > +++ b/src/mm-port-serial-qcdm.c
> > @@ -26,10 +26,15 @@
> >  #include "libqcdm/src/com.h"
> >  #include "libqcdm/src/utils.h"
> >  #include "libqcdm/src/errors.h"
> > +#include "libqcdm/src/dm-commands.h"
> >  #include "mm-log.h"
> > 
> >  G_DEFINE_TYPE (MMPortSerialQcdm, mm_port_serial_qcdm,
> > MM_TYPE_PORT_SERIAL)
> > 
> > +struct _MMPortSerialQcdmPrivate {
> > +    GSList *unsolicited_msg_handlers;
> > +};
> > +
> >  /*****************************************************************
> > ************/
> > 
> >  static gboolean
> > @@ -60,10 +65,10 @@ find_qcdm_start (GByteArray *response, gsize
> > *start)
> >  }
> > 
> >  static MMPortSerialResponseType
> > -parse_response (MMPortSerial *port,
> > -                GByteArray *response,
> > -                GByteArray **parsed_response,
> > -                GError **error)
> > +parse_qcdm (GByteArray *response,
> > +            gboolean want_log,
> > +            GByteArray **parsed_response,
> > +            GError **error)
> >  {
> >      gsize start = 0;
> >      gsize used = 0;
> > @@ -111,6 +116,14 @@ parse_response (MMPortSerial *port,
> >          return MM_PORT_SERIAL_RESPONSE_NONE;
> >      }
> > 
> > +    if (want_log && unescaped_buffer[0] != DIAG_CMD_LOG) {
> > +        /* If we only want log items and this isn't one, don't
> > remove this
> > +         * DM packet from the buffer.
> > +         */
> > +        g_free (unescaped_buffer);
> > +        return MM_PORT_SERIAL_RESPONSE_NONE;
> > +    }
> > +
> >      /* Successfully decapsulated the DM command. We'll build a new
> > byte array
> >       * with the response, and leave the input buffer cleaned up.
> > */
> >      g_assert (unescaped_len <= 1024);
> > @@ -124,6 +137,15 @@ parse_response (MMPortSerial *port,
> >      return MM_PORT_SERIAL_RESPONSE_BUFFER;
> >  }
> > 
> > +static MMPortSerialResponseType
> > +parse_response (MMPortSerial *port,
> > +                GByteArray *response,
> > +                GByteArray **parsed_response,
> > +                GError **error)
> > +{
> > +    return parse_qcdm (response, FALSE, parsed_response, error);
> > +}
> > +
> >  /*****************************************************************
> > ************/
> > 
> >  GByteArray *
> > @@ -202,6 +224,111 @@ debug_log (MMPortSerial *port, const char
> > *prefix, const char *buf, gsize len)
> > 
> >  /*****************************************************************
> > ************/
> > 
> > +typedef struct {
> > +    guint log_code;
> > +    MMPortSerialQcdmUnsolicitedMsgFn callback;
> > +    gboolean enable;
> > +    gpointer user_data;
> > +    GDestroyNotify notify;
> > +} MMQcdmUnsolicitedMsgHandler;
> > +
> > +static gint
> > +unsolicited_msg_handler_cmp (MMQcdmUnsolicitedMsgHandler *handler,
> > +                             gpointer log_code)
> > +{
> > +    return handler->log_code - GPOINTER_TO_UINT (log_code);
> > +}
> > +
> > +void
> > +mm_port_serial_qcdm_add_unsolicited_msg_handler (MMPortSerialQcdm
> > *self,
> > +                                                 guint log_code,
> > +                                                 MMPortSerialQcdmU
> > nsolicitedMsgFn callback,
> > +                                                 gpointer
> > user_data,
> > +                                                 GDestroyNotify
> > notify)
> > +{
> > +    GSList *existing;
> > +    MMQcdmUnsolicitedMsgHandler *handler;
> > +
> > +    g_return_if_fail (MM_IS_PORT_SERIAL_QCDM (self));
> > +    g_return_if_fail (log_code > 0 && log_code <= G_MAXUINT16);
> > +
> > +    existing = g_slist_find_custom (self->priv-
> > >unsolicited_msg_handlers,
> > +                                    GUINT_TO_POINTER (log_code),
> > +                                    (GCompareFunc)unsolicited_msg_
> > handler_cmp);
> > +    if (existing) {
> > +        handler = existing->data;
> > +        /* We OVERWRITE any existing one, so if any context data
> > existing, free it */
> > +        if (handler->notify)
> > +            handler->notify (handler->user_data);
> > +    } else {
> > +        handler = g_slice_new (MMQcdmUnsolicitedMsgHandler);
> > +        self->priv->unsolicited_msg_handlers = g_slist_append
> > (self->priv->unsolicited_msg_handlers, handler);
> > +        handler->log_code = log_code;
> > +    }
> > +
> > +    handler->callback = callback;
> > +    handler->enable = TRUE;
> > +    handler->user_data = user_data;
> > +    handler->notify = notify;
> > +}
> > +
> > +void
> > +mm_port_serial_qcdm_enable_unsolicited_msg_handler
> > (MMPortSerialQcdm *self,
> > +                                                    guint
> > log_code,
> > +                                                    gboolean
> > enable)
> > +{
> > +    GSList *existing;
> > +    MMQcdmUnsolicitedMsgHandler *handler;
> > +
> > +    g_return_if_fail (MM_IS_PORT_SERIAL_QCDM (self));
> > +    g_return_if_fail (log_code > 0 && log_code <= G_MAXUINT16);
> > +
> > +    existing = g_slist_find_custom (self->priv-
> > >unsolicited_msg_handlers,
> > +                                    GUINT_TO_POINTER (log_code),
> > +                                    (GCompareFunc)unsolicited_msg_
> > handler_cmp);
> > +    if (existing) {
> > +        handler = existing->data;
> > +        handler->enable = enable;
> > +    }
> > +}
> > +
> > +static void
> > +parse_unsolicited (MMPortSerial *port, GByteArray *response)
> > +{
> > +    MMPortSerialQcdm *self = MM_PORT_SERIAL_QCDM (port);
> > +    GByteArray *log_buffer = NULL;
> > +    GSList *iter;
> > +
> > +    if (parse_qcdm (response,
> > +                    TRUE,
> > +                    &log_buffer,
> > +                    NULL) != MM_PORT_SERIAL_RESPONSE_BUFFER) {
> > +        return;
> > +    }
> > +
> > +    /* These should be guaranteed by parse_qcdm() */
> > +    g_return_if_fail (log_buffer);
> > +    g_return_if_fail (log_buffer->len > 0);
> > +    g_return_if_fail (log_buffer->data[0] == DIAG_CMD_LOG);
> > +
> > +    if (log_buffer->len < sizeof (DMCmdLog))
> > +        return;
> > +
> > +    for (iter = self->priv->unsolicited_msg_handlers; iter; iter =
> > iter->next) {
> > +        MMQcdmUnsolicitedMsgHandler *handler =
> > (MMQcdmUnsolicitedMsgHandler *) iter->data;
> > +        DMCmdLog *log_cmd = (DMCmdLog *) log_buffer->data;
> > +
> > +        if (!handler->enable)
> > +            continue;
> > +        if (handler->log_code != le16toh (log_cmd->log_code))
> > +            continue;
> > +        if (handler->callback)
> > +            handler->callback (self, log_buffer, handler-
> > >user_data);
> > +    }
> > +}
> > +
> > +/*****************************************************************
> > ************/
> > +
> >  static gboolean
> >  config_fd (MMPortSerial *port, int fd, GError **error)
> >  {
> > @@ -250,14 +377,39 @@ mm_port_serial_qcdm_new_fd (int fd)
> >  static void
> >  mm_port_serial_qcdm_init (MMPortSerialQcdm *self)
> >  {
> > +    self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
> > MM_TYPE_PORT_SERIAL_QCDM, MMPortSerialQcdmPrivate);
> > +}
> > +
> > +static void
> > +finalize (GObject *object)
> > +{
> > +    MMPortSerialQcdm *self = MM_PORT_SERIAL_QCDM (object);
> > +
> > +    while (self->priv->unsolicited_msg_handlers) {
> > +        MMQcdmUnsolicitedMsgHandler *handler =
> > (MMQcdmUnsolicitedMsgHandler *) self->priv-
> > >unsolicited_msg_handlers->data;
> > +
> > +        if (handler->notify)
> > +            handler->notify (handler->user_data);
> > +
> > +        g_slice_free (MMQcdmUnsolicitedMsgHandler, handler);
> > +        self->priv->unsolicited_msg_handlers = g_slist_delete_link
> > (self->priv->unsolicited_msg_handlers,
> > +                                                                  
> >   self->priv->unsolicited_msg_handlers);
> > +    }
> > +
> > +    G_OBJECT_CLASS (mm_port_serial_qcdm_parent_class)->finalize
> > (object);
> >  }
> > 
> >  static void
> >  mm_port_serial_qcdm_class_init (MMPortSerialQcdmClass *klass)
> >  {
> > +    GObjectClass *object_class = G_OBJECT_CLASS (klass);
> >      MMPortSerialClass *port_class = MM_PORT_SERIAL_CLASS (klass);
> > 
> > +    g_type_class_add_private (object_class, sizeof
> > (MMPortSerialQcdmPrivate));
> > +
> >      /* Virtual methods */
> > +    object_class->finalize = finalize;
> > +    port_class->parse_unsolicited = parse_unsolicited;
> >      port_class->parse_response = parse_response;
> >      port_class->config_fd = config_fd;
> >      port_class->debug_log = debug_log;
> > diff --git a/src/mm-port-serial-qcdm.h b/src/mm-port-serial-qcdm.h
> > index 5e3e38f..e7ba01f 100644
> > --- a/src/mm-port-serial-qcdm.h
> > +++ b/src/mm-port-serial-qcdm.h
> > @@ -31,9 +31,11 @@
> > 
> >  typedef struct _MMPortSerialQcdm MMPortSerialQcdm;
> >  typedef struct _MMPortSerialQcdmClass MMPortSerialQcdmClass;
> > +typedef struct _MMPortSerialQcdmPrivate MMPortSerialQcdmPrivate;
> > 
> >  struct _MMPortSerialQcdm {
> >      MMPortSerial parent;
> > +    MMPortSerialQcdmPrivate *priv;
> >  };
> > 
> >  struct _MMPortSerialQcdmClass {
> > @@ -55,4 +57,18 @@ GByteArray *mm_port_serial_qcdm_command_finish
> > (MMPortSerialQcdm *self,
> >                                                  GAsyncResult *res,
> >                                                  GError **error);
> > 
> > +typedef void (*MMPortSerialQcdmUnsolicitedMsgFn) (MMPortSerialQcdm
> > *port,
> > +                                                  GByteArray
> > *log_buffer,
> > +                                                  gpointer
> > user_data);
> > +
> > +void     mm_port_serial_qcdm_add_unsolicited_msg_handler
> > (MMPortSerialQcdm *self,
> > +                                                          guint
> > log_code,
> > +                                                          MMPortSe
> > rialQcdmUnsolicitedMsgFn callback,
> > +                                                          gpointer
> > user_data,
> > +                                                          GDestroy
> > Notify notify);
> > +
> > +void     mm_port_serial_qcdm_enable_unsolicited_msg_handler
> > (MMPortSerialQcdm *self,
> > +                                                             guint
> > log_code,
> > +                                                             gbool
> > ean enable);
> > +
> >  #endif /* MM_PORT_SERIAL_QCDM_H */
> > --
> > 2.5.5
> > _______________________________________________
> > ModemManager-devel mailing list
> > ModemManager-devel at lists.freedesktop.org
> > https://lists.freedesktop.org/mailman/listinfo/modemmanager-devel
> 
> 


More information about the ModemManager-devel mailing list