[PATCH] Huawei GPS support (udev activated)

Aleksander Morgado aleksander at aleksander.es
Tue Jul 1 02:14:20 PDT 2014


On Mon, Jun 30, 2014 at 8:50 AM, David McCullough
<david.mccullough at accelecon.com> wrote:
>
> Hi all,
>
> Here is a patch to provide GPS support for Huawei modems.  This change
> tested and working on both the MU609 (3G) and MU909 (LTE) modems.
>
> There are a lot of options for configure the GPS that I have hardcoded to
> the most likely to work.  This should get you a working GPS lock.
>
> Cheers,
> Davidm
>
> Signed-off-by: David McCullough <david.mccullough at accelecon.com>
>

Patch doesn't apply in git master; can you rebase it?

Thanks!

>
>
> diff --git a/plugins/huawei/77-mm-huawei-net-port-types.rules b/plugins/huawei/77-mm-huawei-net-port-types.rules
> index 051b5d0..f906047 100644
> --- a/plugins/huawei/77-mm-huawei-net-port-types.rules
> +++ b/plugins/huawei/77-mm-huawei-net-port-types.rules
> @@ -9,6 +9,11 @@ SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="01"
>  SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="02", ATTRS{bInterfaceProtocol}=="01", ENV{ID_MM_HUAWEI_MODEM_PORT}="1"
>  SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="02", ATTRS{bInterfaceProtocol}=="02", ENV{ID_MM_HUAWEI_AT_PORT}="1"
>
> +# GPS NMEA port on MU609
> +SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="01", ATTRS{bInterfaceProtocol}=="05", ENV{ID_MM_HUAWEI_GPS_PORT}="1"
> +# GPS NMEA port on MU909
> +SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="01", ATTRS{bInterfaceProtocol}=="14", ENV{ID_MM_HUAWEI_GPS_PORT}="1"
> +
>  # Only the standard ECM or NCM port can support dial-up with AT NDISDUP through AT port
>  SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="02", ATTRS{bInterfaceSubClass}=="06",ATTRS{bInterfaceProtocol}=="00", ENV{ID_MM_HUAWEI_NDISDUP_SUPPORTED}="1"
>  SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="02", ATTRS{bInterfaceSubClass}=="0d",ATTRS{bInterfaceProtocol}=="00", ENV{ID_MM_HUAWEI_NDISDUP_SUPPORTED}="1"
> diff --git a/plugins/huawei/mm-broadband-modem-huawei.c b/plugins/huawei/mm-broadband-modem-huawei.c
> index b7b98a8..dcb6243 100644
> --- a/plugins/huawei/mm-broadband-modem-huawei.c
> +++ b/plugins/huawei/mm-broadband-modem-huawei.c
> @@ -39,6 +39,7 @@
>  #include "mm-iface-modem.h"
>  #include "mm-iface-modem-3gpp.h"
>  #include "mm-iface-modem-3gpp-ussd.h"
> +#include "mm-iface-modem-location.h"
>  #include "mm-iface-modem-time.h"
>  #include "mm-iface-modem-cdma.h"
>  #include "mm-broadband-modem-huawei.h"
> @@ -50,11 +51,13 @@
>  static void iface_modem_init (MMIfaceModem *iface);
>  static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
>  static void iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface);
> +static void iface_modem_location_init (MMIfaceModemLocation *iface);
>  static void iface_modem_cdma_init (MMIfaceModemCdma *iface);
>  static void iface_modem_time_init (MMIfaceModemTime *iface);
>
>  static MMIfaceModem *iface_modem_parent;
>  static MMIfaceModem3gpp *iface_modem_3gpp_parent;
> +static MMIfaceModemLocation *iface_modem_location_parent;
>  static MMIfaceModemCdma *iface_modem_cdma_parent;
>
>  G_DEFINE_TYPE_EXTENDED (MMBroadbandModemHuawei, mm_broadband_modem_huawei, MM_TYPE_BROADBAND_MODEM, 0,
> @@ -62,6 +65,7 @@ G_DEFINE_TYPE_EXTENDED (MMBroadbandModemHuawei, mm_broadband_modem_huawei, MM_TY
>                          G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)
>                          G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_USSD, iface_modem_3gpp_ussd_init)
>                          G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_CDMA, iface_modem_cdma_init)
> +                        G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init)
>                          G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init));
>
>  typedef enum {
> @@ -105,6 +109,8 @@ struct _MMBroadbandModemHuaweiPrivate {
>      FeatureSupport syscfgex_support;
>      FeatureSupport prefmode_support;
>
> +    MMModemLocationSource enabled_sources;
> +
>      GArray *syscfg_supported_modes;
>      GArray *syscfgex_supported_modes;
>      GArray *prefmode_supported_modes;
> @@ -3069,6 +3075,285 @@ huawei_modem_create_sim (MMIfaceModem *self,
>
>
>  /*****************************************************************************/
> +/* Location capabilities loading (Location interface) */
> +
> +static MMModemLocationSource
> +location_load_capabilities_finish (MMIfaceModemLocation *self,
> +                                   GAsyncResult *res,
> +                                   GError **error)
> +{
> +    if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
> +        return MM_MODEM_LOCATION_SOURCE_NONE;
> +
> +    return (MMModemLocationSource) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
> +}
> +
> +static void
> +parent_load_capabilities_ready (MMIfaceModemLocation *self,
> +                                GAsyncResult *res,
> +                                GSimpleAsyncResult *simple)
> +{
> +    MMModemLocationSource sources;
> +    GError *error = NULL;
> +
> +    sources = iface_modem_location_parent->load_capabilities_finish (self, res, &error);
> +    if (error) {
> +        g_simple_async_result_take_error (simple, error);
> +        g_simple_async_result_complete (simple);
> +        g_object_unref (simple);
> +        return;
> +    }
> +
> +    /* not sure how to check if GPS is supported, just allow it */
> +    if (mm_base_modem_peek_port_gps (MM_BASE_MODEM (self)))
> +        sources |= (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW);
> +
> +    /* So we're done, complete */
> +    g_simple_async_result_set_op_res_gpointer (simple,
> +                                               GUINT_TO_POINTER (sources),
> +                                               NULL);
> +    g_simple_async_result_complete (simple);
> +    g_object_unref (simple);
> +}
> +
> +static void
> +location_load_capabilities (MMIfaceModemLocation *self,
> +                            GAsyncReadyCallback callback,
> +                            gpointer user_data)
> +{
> +    GSimpleAsyncResult *result;
> +
> +    result = g_simple_async_result_new (G_OBJECT (self),
> +                                        callback,
> +                                        user_data,
> +                                        location_load_capabilities);
> +
> +    /* Chain up parent's setup */
> +    iface_modem_location_parent->load_capabilities (self,
> +                                                    (GAsyncReadyCallback)parent_load_capabilities_ready,
> +                                                    result);
> +}
> +
> +/*****************************************************************************/
> +/* Disable location gathering (Location interface) */
> +
> +static gboolean
> +disable_location_gathering_finish (MMIfaceModemLocation *self,
> +                                   GAsyncResult *res,
> +                                   GError **error)
> +{
> +    return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
> +}
> +
> +static void
> +gps_disabled_ready (MMBaseModem *self,
> +                    GAsyncResult *res,
> +                    GSimpleAsyncResult *simple)
> +{
> +    GError *error = NULL;
> +
> +    if (!mm_base_modem_at_command_full_finish (self, res, &error))
> +        g_simple_async_result_take_error (simple, error);
> +    else
> +        g_simple_async_result_set_op_res_gboolean (simple, TRUE);
> +
> +    g_simple_async_result_complete (simple);
> +    g_object_unref (simple);
> +}
> +
> +static void
> +disable_location_gathering (MMIfaceModemLocation *_self,
> +                            MMModemLocationSource source,
> +                            GAsyncReadyCallback callback,
> +                            gpointer user_data)
> +{
> +    MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self);
> +    GSimpleAsyncResult *result;
> +    gboolean stop_gps = FALSE;
> +
> +    result = g_simple_async_result_new (G_OBJECT (_self),
> +                                        callback,
> +                                        user_data,
> +                                        disable_location_gathering);
> +
> +    /* Only stop GPS engine if no GPS-related sources enabled */
> +    if (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
> +                  MM_MODEM_LOCATION_SOURCE_GPS_RAW)) {
> +        self->priv->enabled_sources &= ~source;
> +
> +        if (!(self->priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
> +                                            MM_MODEM_LOCATION_SOURCE_GPS_RAW)))
> +            stop_gps = TRUE;
> +    }
> +
> +    if (stop_gps) {
> +        mm_base_modem_at_command_full (MM_BASE_MODEM (_self),
> +                                       mm_base_modem_peek_port_primary (MM_BASE_MODEM (_self)),
> +                                       "^WPEND",
> +                                       3,
> +                                       FALSE,
> +                                       FALSE, /* raw */
> +                                       NULL, /* cancellable */
> +                                       (GAsyncReadyCallback)gps_disabled_ready,
> +                                       result);
> +        return;
> +    }
> +
> +    /* For any other location (e.g. 3GPP), or if still some GPS needed, just return */
> +    g_simple_async_result_set_op_res_gboolean (result, TRUE);
> +    g_simple_async_result_complete_in_idle (result);
> +    g_object_unref (result);
> +}
> +
> +/*****************************************************************************/
> +/* Enable location gathering (Location interface) */
> +
> +typedef struct {
> +    MMBroadbandModemHuawei *self;
> +    GSimpleAsyncResult *result;
> +    MMModemLocationSource source;
> +    int idx;
> +} EnableLocationGatheringContext;
> +
> +static void
> +enable_location_gathering_context_complete_and_free (EnableLocationGatheringContext *ctx)
> +{
> +    g_simple_async_result_complete (ctx->result);
> +    g_object_unref (ctx->result);
> +    g_object_unref (ctx->self);
> +    g_free (ctx);
> +}
> +
> +static gboolean
> +enable_location_gathering_finish (MMIfaceModemLocation *self,
> +                                  GAsyncResult *res,
> +                                  GError **error)
> +{
> +    return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
> +}
> +
> +static char *gps_startup[] = {
> +       "^WPDOM=0",
> +       "^WPDST=1",
> +       "^WPDFR=65535,30",
> +       "^WPDGP",
> +       NULL
> +};
> +
> +static void
> +gps_enabled_ready (MMBaseModem *self,
> +                   GAsyncResult *res,
> +                   EnableLocationGatheringContext *ctx)
> +{
> +    GError *error = NULL;
> +    MMPortSerialGps *gps_port;
> +
> +    if (!mm_base_modem_at_command_full_finish (self, res, &error)) {
> +        ctx->idx = 0;
> +        g_simple_async_result_take_error (ctx->result, error);
> +        enable_location_gathering_context_complete_and_free (ctx);
> +        return;
> +    }
> +
> +    /* ctx->idx++; */
> +    if (gps_startup[ctx->idx++] && gps_startup[ctx->idx]) {
> +       mm_base_modem_at_command_full (MM_BASE_MODEM (self),
> +                                      mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)),
> +                                      gps_startup[ctx->idx],
> +                                      3,
> +                                      FALSE,
> +                                      FALSE, /* raw */
> +                                      NULL, /* cancellable */
> +                                      (GAsyncReadyCallback)gps_enabled_ready,
> +                                      ctx);
> +       return;
> +    }
> +
> +    gps_port = mm_base_modem_peek_port_gps (self);
> +    if (!gps_port ||
> +        !mm_port_serial_open (MM_PORT_SERIAL (gps_port), &error)) {
> +        if (error)
> +            g_simple_async_result_take_error (ctx->result, error);
> +        else
> +            g_simple_async_result_set_error (ctx->result,
> +                                             MM_CORE_ERROR,
> +                                             MM_CORE_ERROR_FAILED,
> +                                             "Couldn't open raw GPS serial port");
> +    } else
> +        g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
> +
> +    enable_location_gathering_context_complete_and_free (ctx);
> +}
> +
> +static void
> +parent_enable_location_gathering_ready (MMIfaceModemLocation *self,
> +                                        GAsyncResult *res,
> +                                        EnableLocationGatheringContext *ctx)
> +{
> +    gboolean start_gps = FALSE;
> +    GError *error = NULL;
> +
> +    if (!iface_modem_location_parent->enable_location_gathering_finish (self, res, &error)) {
> +        g_simple_async_result_take_error (ctx->result, error);
> +        enable_location_gathering_context_complete_and_free (ctx);
> +        return;
> +    }
> +
> +    /* Now our own enabling */
> +
> +    /* NMEA and RAW are both enabled in the same way */
> +    if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
> +                       MM_MODEM_LOCATION_SOURCE_GPS_RAW)) {
> +        /* Only start GPS engine if not done already */
> +        if (!(ctx->self->priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
> +                                                  MM_MODEM_LOCATION_SOURCE_GPS_RAW)))
> +            start_gps = TRUE;
> +        ctx->self->priv->enabled_sources |= ctx->source;
> +    }
> +
> +    if (start_gps) {
> +        mm_base_modem_at_command_full (MM_BASE_MODEM (self),
> +                                       mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)),
> +                                                                          gps_startup[ctx->idx],
> +                                       3,
> +                                       FALSE,
> +                                       FALSE, /* raw */
> +                                       NULL, /* cancellable */
> +                                       (GAsyncReadyCallback)gps_enabled_ready,
> +                                       ctx);
> +        return;
> +    }
> +
> +    /* For any other location (e.g. 3GPP), or if GPS already running just return */
> +    g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
> +    enable_location_gathering_context_complete_and_free (ctx);
> +}
> +
> +static void
> +enable_location_gathering (MMIfaceModemLocation *self,
> +                           MMModemLocationSource source,
> +                           GAsyncReadyCallback callback,
> +                           gpointer user_data)
> +{
> +    EnableLocationGatheringContext *ctx;
> +
> +    ctx = g_new (EnableLocationGatheringContext, 1);
> +    ctx->self = g_object_ref (self);
> +    ctx->result = g_simple_async_result_new (G_OBJECT (self),
> +                                             callback,
> +                                             user_data,
> +                                             enable_location_gathering);
> +    ctx->source = source;
> +       ctx->idx = 0;
> +
> +    /* Chain up parent's gathering enable */
> +    iface_modem_location_parent->enable_location_gathering (self,
> +                                                            source,
> +                                                            (GAsyncReadyCallback)parent_enable_location_gathering_ready,
> +                                                            ctx);
> +}
> +
> +/*****************************************************************************/
>  /* Check support (Time interface) */
>
>  static gboolean
> @@ -3188,8 +3473,18 @@ set_ignored_unsolicited_events_handlers (MMBroadbandModemHuawei *self)
>  }
>
>  static void
> +gps_trace_received (MMPortSerialGps *port,
> +                    const gchar *trace,
> +                    MMIfaceModemLocation *self)
> +{
> +    mm_iface_modem_location_gps_update (self, trace);
> +}
> +
> +static void
>  setup_ports (MMBroadbandModem *self)
>  {
> +    MMPortSerialGps *gps_data_port;
> +
>      /* Call parent's setup ports first always */
>      MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_huawei_parent_class)->setup_ports (self);
>
> @@ -3199,6 +3494,20 @@ setup_ports (MMBroadbandModem *self)
>      /* Now reset the unsolicited messages we'll handle when enabled */
>      set_3gpp_unsolicited_events_handlers (MM_BROADBAND_MODEM_HUAWEI (self), FALSE);
>      set_cdma_unsolicited_events_handlers (MM_BROADBAND_MODEM_HUAWEI (self), FALSE);
> +
> +    /* NMEA GPS monitoring */
> +    gps_data_port = mm_base_modem_peek_port_gps (MM_BASE_MODEM (self));
> +    if (gps_data_port) {
> +       /* make sure GPS is stopped incase it was left enabled */
> +        mm_base_modem_at_command_full (MM_BASE_MODEM (self),
> +                                       mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)),
> +                                       "^WPEND",
> +                                       3, FALSE, FALSE, NULL, NULL, NULL);
> +        /* Add handler for the NMEA traces */
> +        mm_port_serial_gps_add_trace_handler (gps_data_port,
> +                                              (MMPortSerialGpsTraceFn)gps_trace_received,
> +                                              self, NULL);
> +    }
>  }
>
>  /*****************************************************************************/
> @@ -3390,6 +3699,19 @@ iface_modem_cdma_init (MMIfaceModemCdma *iface)
>  }
>
>  static void
> +iface_modem_location_init (MMIfaceModemLocation *iface)
> +{
> +    iface_modem_location_parent = g_type_interface_peek_parent (iface);
> +
> +    iface->load_capabilities = location_load_capabilities;
> +    iface->load_capabilities_finish = location_load_capabilities_finish;
> +    iface->enable_location_gathering = enable_location_gathering;
> +    iface->enable_location_gathering_finish = enable_location_gathering_finish;
> +    iface->disable_location_gathering = disable_location_gathering;
> +    iface->disable_location_gathering_finish = disable_location_gathering_finish;
> +}
> +
> +static void
>  iface_modem_time_init (MMIfaceModemTime *iface)
>  {
>      iface->check_support = modem_time_check_support;
> diff --git a/plugins/huawei/mm-plugin-huawei.c b/plugins/huawei/mm-plugin-huawei.c
> index 2078774..e07a3f9 100644
> --- a/plugins/huawei/mm-plugin-huawei.c
> +++ b/plugins/huawei/mm-plugin-huawei.c
> @@ -503,8 +503,11 @@ grab_port (MMPlugin *self,
>  {
>      MMPortSerialAtFlag pflags;
>      GUdevDevice *port;
> +    MMPortType port_type;
>
> +    port_type = mm_port_probe_get_port_type (probe);
>      port = mm_port_probe_peek_port (probe);
> +
>      if (g_udev_device_get_property_as_boolean (port, "ID_MM_HUAWEI_AT_PORT")) {
>          mm_dbg ("(%s/%s)' Port flagged as primary",
>                  mm_port_probe_get_port_subsys (probe),
> @@ -515,6 +518,11 @@ grab_port (MMPlugin *self,
>                  mm_port_probe_get_port_subsys (probe),
>                  mm_port_probe_get_port_name (probe));
>          pflags = MM_PORT_SERIAL_AT_FLAG_PPP;
> +    } else if (g_udev_device_get_property_as_boolean (port, "ID_MM_HUAWEI_GPS_PORT")) {
> +        mm_dbg ("(%s/%s) Port flagged as GPS",
> +                mm_port_probe_get_port_subsys (probe),
> +                mm_port_probe_get_port_name (probe));
> +        port_type = MM_PORT_TYPE_GPS;
>      } else {
>          gchar *str;
>
> @@ -530,7 +538,7 @@ grab_port (MMPlugin *self,
>      return mm_base_modem_grab_port (modem,
>                                      mm_port_probe_get_port_subsys (probe),
>                                      mm_port_probe_get_port_name (probe),
> -                                    mm_port_probe_get_port_type (probe),
> +                                    port_type,
>                                      pflags,
>                                      error);
>  }
>
> --
> David McCullough,  david.mccullough at accelecon.com,   Ph: 0410 560 763



-- 
Aleksander
https://aleksander.es


More information about the ModemManager-devel mailing list