summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
authorDavid McCullough <david.mccullough@accelecon.com>2014-07-02 21:26:16 +1000
committerAleksander Morgado <aleksander@aleksander.es>2014-07-03 16:01:02 +0200
commitd73b633639d1549b8543282a4af5a8906e645233 (patch)
tree0d09789cdc859ec94a42e218ab631ba71101ef2c /plugins
parent09eb87f8994551e11ab3844101a4302fa0a3cb99 (diff)
huawei: GPS support for MU609/MU909
Implement GPS support on the MU609 and MU090 Huawei modules. Its highly likely the commands are the same for other Huawei modems and it just needs to be activated via udev rules that flag the GPS port with ID_MM_HUAWEI_GPS_PORT=1. There are a lot of options that can be tweaked on the Huawei GPS setup, this code just chooses a simple default for unassisted, standalone GPS operation. Signed-off-by: David McCullough <david.mccullough@accelecon.com>
Diffstat (limited to 'plugins')
-rw-r--r--plugins/huawei/77-mm-huawei-net-port-types.rules5
-rw-r--r--plugins/huawei/mm-broadband-modem-huawei.c322
-rw-r--r--plugins/huawei/mm-plugin-huawei.c12
3 files changed, 337 insertions, 2 deletions
diff --git a/plugins/huawei/77-mm-huawei-net-port-types.rules b/plugins/huawei/77-mm-huawei-net-port-types.rules
index 7309be8a..f60f1f8a 100644
--- a/plugins/huawei/77-mm-huawei-net-port-types.rules
+++ b/plugins/huawei/77-mm-huawei-net-port-types.rules
@@ -12,6 +12,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 46d15a5f..614b3542 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;
@@ -3174,6 +3180,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
@@ -3292,8 +3577,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);
@@ -3303,6 +3598,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);
+ }
}
/*****************************************************************************/
@@ -3494,6 +3803,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 c9b923e4..30b18474 100644
--- a/plugins/huawei/mm-plugin-huawei.c
+++ b/plugins/huawei/mm-plugin-huawei.c
@@ -521,10 +521,13 @@ grab_port (MMPlugin *self,
MMPortProbe *probe,
GError **error)
{
- MMPortSerialAtFlag pflags;
+ MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
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),
@@ -535,6 +538,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;
@@ -551,7 +559,7 @@ grab_port (MMPlugin *self,
mm_port_probe_get_port_subsys (probe),
mm_port_probe_get_port_name (probe),
mm_port_probe_get_parent_path (probe),
- mm_port_probe_get_port_type (probe),
+ port_type,
pflags,
error);
}