xmm: implement XLCSLSR based GPS management and report
This commit is contained in:
@@ -24,14 +24,19 @@
|
||||
#include "ModemManager.h"
|
||||
#include "mm-log.h"
|
||||
#include "mm-iface-modem.h"
|
||||
#include "mm-iface-modem-location.h"
|
||||
#include "mm-broadband-modem-mbim-xmm.h"
|
||||
#include "mm-shared-xmm.h"
|
||||
|
||||
static void iface_modem_init (MMIfaceModem *iface);
|
||||
static void shared_xmm_init (MMSharedXmm *iface);
|
||||
static void iface_modem_init (MMIfaceModem *iface);
|
||||
static void iface_modem_location_init (MMIfaceModemLocation *iface);
|
||||
static void shared_xmm_init (MMSharedXmm *iface);
|
||||
|
||||
static MMIfaceModemLocation *iface_modem_location_parent;
|
||||
|
||||
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbimXmm, mm_broadband_modem_mbim_xmm, MM_TYPE_BROADBAND_MODEM_MBIM, 0,
|
||||
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
|
||||
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init)
|
||||
G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_XMM, shared_xmm_init))
|
||||
|
||||
/*****************************************************************************/
|
||||
@@ -81,12 +86,42 @@ iface_modem_init (MMIfaceModem *iface)
|
||||
iface->reset_finish = mm_shared_xmm_reset_finish;
|
||||
}
|
||||
|
||||
static void
|
||||
iface_modem_location_init (MMIfaceModemLocation *iface)
|
||||
{
|
||||
iface_modem_location_parent = g_type_interface_peek_parent (iface);
|
||||
|
||||
iface->load_capabilities = mm_shared_xmm_location_load_capabilities;
|
||||
iface->load_capabilities_finish = mm_shared_xmm_location_load_capabilities_finish;
|
||||
iface->enable_location_gathering = mm_shared_xmm_enable_location_gathering;
|
||||
iface->enable_location_gathering_finish = mm_shared_xmm_enable_location_gathering_finish;
|
||||
iface->disable_location_gathering = mm_shared_xmm_disable_location_gathering;
|
||||
iface->disable_location_gathering_finish = mm_shared_xmm_disable_location_gathering_finish;
|
||||
}
|
||||
|
||||
static MMBroadbandModemClass *
|
||||
peek_parent_broadband_modem_class (MMSharedXmm *self)
|
||||
{
|
||||
return MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_mbim_xmm_parent_class);
|
||||
}
|
||||
|
||||
static MMIfaceModemLocation *
|
||||
peek_parent_location_interface (MMSharedXmm *self)
|
||||
{
|
||||
return iface_modem_location_parent;
|
||||
}
|
||||
|
||||
static void
|
||||
shared_xmm_init (MMSharedXmm *iface)
|
||||
{
|
||||
iface->peek_parent_broadband_modem_class = peek_parent_broadband_modem_class;
|
||||
iface->peek_parent_location_interface = peek_parent_location_interface;
|
||||
}
|
||||
|
||||
static void
|
||||
mm_broadband_modem_mbim_xmm_class_init (MMBroadbandModemMbimXmmClass *klass)
|
||||
{
|
||||
MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass);
|
||||
|
||||
broadband_modem_class->setup_ports = mm_shared_xmm_setup_ports;
|
||||
}
|
||||
|
||||
@@ -24,16 +24,22 @@
|
||||
#include "ModemManager.h"
|
||||
#include "mm-log.h"
|
||||
#include "mm-iface-modem.h"
|
||||
#include "mm-iface-modem-location.h"
|
||||
#include "mm-broadband-modem-xmm.h"
|
||||
#include "mm-shared-xmm.h"
|
||||
|
||||
|
||||
static void iface_modem_init (MMIfaceModem *iface);
|
||||
static void shared_xmm_init (MMSharedXmm *iface);
|
||||
static void iface_modem_signal_init (MMIfaceModemSignal *iface);
|
||||
static void iface_modem_location_init (MMIfaceModemLocation *iface);
|
||||
|
||||
static MMIfaceModemLocation *iface_modem_location_parent;
|
||||
|
||||
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemXmm, mm_broadband_modem_xmm, MM_TYPE_BROADBAND_MODEM, 0,
|
||||
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
|
||||
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_SIGNAL, iface_modem_signal_init)
|
||||
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init)
|
||||
G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_XMM, shared_xmm_init))
|
||||
|
||||
/*****************************************************************************/
|
||||
@@ -88,6 +94,32 @@ iface_modem_init (MMIfaceModem *iface)
|
||||
iface->reset_finish = mm_shared_xmm_reset_finish;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
iface_modem_location_init (MMIfaceModemLocation *iface)
|
||||
{
|
||||
iface_modem_location_parent = g_type_interface_peek_parent (iface);
|
||||
|
||||
iface->load_capabilities = mm_shared_xmm_location_load_capabilities;
|
||||
iface->load_capabilities_finish = mm_shared_xmm_location_load_capabilities_finish;
|
||||
iface->enable_location_gathering = mm_shared_xmm_enable_location_gathering;
|
||||
iface->enable_location_gathering_finish = mm_shared_xmm_enable_location_gathering_finish;
|
||||
iface->disable_location_gathering = mm_shared_xmm_disable_location_gathering;
|
||||
iface->disable_location_gathering_finish = mm_shared_xmm_disable_location_gathering_finish;
|
||||
}
|
||||
|
||||
static MMBroadbandModemClass *
|
||||
peek_parent_broadband_modem_class (MMSharedXmm *self)
|
||||
{
|
||||
return MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_xmm_parent_class);
|
||||
}
|
||||
|
||||
static MMIfaceModemLocation *
|
||||
peek_parent_location_interface (MMSharedXmm *self)
|
||||
{
|
||||
return iface_modem_location_parent;
|
||||
}
|
||||
|
||||
static void
|
||||
iface_modem_signal_init (MMIfaceModemSignal *iface)
|
||||
{
|
||||
@@ -100,9 +132,14 @@ iface_modem_signal_init (MMIfaceModemSignal *iface)
|
||||
static void
|
||||
shared_xmm_init (MMSharedXmm *iface)
|
||||
{
|
||||
iface->peek_parent_broadband_modem_class = peek_parent_broadband_modem_class;
|
||||
iface->peek_parent_location_interface = peek_parent_location_interface;
|
||||
}
|
||||
|
||||
static void
|
||||
mm_broadband_modem_xmm_class_init (MMBroadbandModemXmmClass *klass)
|
||||
{
|
||||
MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass);
|
||||
|
||||
broadband_modem_class->setup_ports = mm_shared_xmm_setup_ports;
|
||||
}
|
||||
|
||||
@@ -808,3 +808,125 @@ mm_xmm_xcesq_response_to_signal_info (const gchar *response,
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* AT+XLCSLSR=? response parser */
|
||||
|
||||
static gboolean
|
||||
number_group_contains_value (const gchar *group,
|
||||
const gchar *group_name,
|
||||
guint value,
|
||||
GError **error)
|
||||
{
|
||||
GArray *aux;
|
||||
guint i;
|
||||
gboolean found;
|
||||
|
||||
aux = mm_parse_uint_list (group, NULL);
|
||||
if (!aux) {
|
||||
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
|
||||
"Unsupported +XLCSLSR format: invalid %s field format", group_name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
found = FALSE;
|
||||
for (i = 0; i < aux->len; i++) {
|
||||
guint value_i;
|
||||
|
||||
value_i = g_array_index (aux, guint, i);
|
||||
if (value == value_i) {
|
||||
found = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_array_unref (aux);
|
||||
return found;
|
||||
}
|
||||
|
||||
gboolean
|
||||
mm_xmm_parse_xlcslsr_test_response (const gchar *response,
|
||||
gboolean *transport_protocol_invalid_supported,
|
||||
gboolean *standalone_position_mode_supported,
|
||||
gboolean *loc_response_type_nmea_supported,
|
||||
gboolean *gnss_type_gps_glonass_supported,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
gchar **groups = NULL;
|
||||
GError *inner_error = NULL;
|
||||
|
||||
/*
|
||||
* AT+XLCSLSR=?
|
||||
* +XLCSLSR:(0-2),(0-3), ,(0,1), ,(0,1),(0 -7200),(0-255),(0-1),(0-2),(1-256),(0,1)
|
||||
* transport_protocol: 2 (invalid)
|
||||
* pos_mode: 3 (standalone)
|
||||
* client_id: <empty>
|
||||
* client_id_type: <empty>
|
||||
* mlc_number: <empty>
|
||||
* mlc_number_type: <empty>
|
||||
* interval: 1 (seconds)
|
||||
* service_type_id: <empty>
|
||||
* pseudonym_indicator: <empty>
|
||||
* loc_response_type: 1 (NMEA strings)
|
||||
* nmea_mask: 118 (01110110: GGA,GSA,GSV,RMC,VTG)
|
||||
* gnss_type: 0 (GPS or GLONASS)
|
||||
*/
|
||||
response = mm_strip_tag (response, "+XLCSLSR:");
|
||||
groups = mm_split_string_groups (response);
|
||||
|
||||
/* We expect 12 groups */
|
||||
if (g_strv_length (groups) < 12) {
|
||||
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
|
||||
"Unsupported +XLCSLSR format: expected 12 fields");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (transport_protocol_invalid_supported) {
|
||||
*transport_protocol_invalid_supported = number_group_contains_value (groups[0],
|
||||
"transport protocol",
|
||||
2, /* invalid */
|
||||
&inner_error);
|
||||
if (inner_error)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (standalone_position_mode_supported) {
|
||||
*standalone_position_mode_supported = number_group_contains_value (groups[1],
|
||||
"position mode",
|
||||
3, /* standalone */
|
||||
&inner_error);
|
||||
if (inner_error)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (loc_response_type_nmea_supported) {
|
||||
*loc_response_type_nmea_supported = number_group_contains_value (groups[9],
|
||||
"location response type",
|
||||
1, /* NMEA */
|
||||
&inner_error);
|
||||
if (inner_error)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (gnss_type_gps_glonass_supported) {
|
||||
*gnss_type_gps_glonass_supported = number_group_contains_value (groups[11],
|
||||
"gnss type",
|
||||
0, /* GPS/GLONASS */
|
||||
&inner_error);
|
||||
if (inner_error)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
|
||||
out:
|
||||
g_strfreev (groups);
|
||||
|
||||
if (inner_error) {
|
||||
g_propagate_error (error, inner_error);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -55,4 +55,12 @@ gboolean mm_xmm_xcesq_response_to_signal_info (const gchar *response,
|
||||
MMSignal **out_lte,
|
||||
GError **error);
|
||||
|
||||
/* AT+XLCSLSR=? response parser */
|
||||
gboolean mm_xmm_parse_xlcslsr_test_response (const gchar *response,
|
||||
gboolean *transport_protocol_invalid_supported,
|
||||
gboolean *standalone_position_mode_supported,
|
||||
gboolean *loc_response_type_nmea_supported,
|
||||
gboolean *gnss_type_gps_glonass_supported,
|
||||
GError **error);
|
||||
|
||||
#endif /* MM_MODEM_HELPERS_XMM_H */
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "mm-log.h"
|
||||
#include "mm-iface-modem.h"
|
||||
#include "mm-iface-modem-signal.h"
|
||||
#include "mm-iface-modem-location.h"
|
||||
#include "mm-base-modem.h"
|
||||
#include "mm-base-modem-at.h"
|
||||
#include "mm-shared-xmm.h"
|
||||
@@ -35,19 +36,40 @@
|
||||
#define PRIVATE_TAG "shared-xmm-private-tag"
|
||||
static GQuark private_quark;
|
||||
|
||||
typedef enum {
|
||||
GPS_ENGINE_STATE_OFF,
|
||||
GPS_ENGINE_STATE_STANDALONE,
|
||||
} GpsEngineState;
|
||||
|
||||
typedef struct {
|
||||
/* Broadband modem class support */
|
||||
MMBroadbandModemClass *broadband_modem_class_parent;
|
||||
|
||||
/* Modem interface support */
|
||||
GArray *supported_modes;
|
||||
GArray *supported_bands;
|
||||
MMModemMode allowed_modes;
|
||||
|
||||
/* Location interface support */
|
||||
MMIfaceModemLocation *iface_modem_location_parent;
|
||||
MMModemLocationSource supported_sources;
|
||||
MMModemLocationSource enabled_sources;
|
||||
GpsEngineState gps_engine_state;
|
||||
MMPortSerialAt *gps_port;
|
||||
GRegex *xlsrstop_regex;
|
||||
GRegex *nmea_regex;
|
||||
} Private;
|
||||
|
||||
static void
|
||||
private_free (Private *priv)
|
||||
{
|
||||
g_clear_object (&priv->gps_port);
|
||||
if (priv->supported_modes)
|
||||
g_array_unref (priv->supported_modes);
|
||||
if (priv->supported_bands)
|
||||
g_array_unref (priv->supported_bands);
|
||||
g_regex_unref (priv->xlsrstop_regex);
|
||||
g_regex_unref (priv->nmea_regex);
|
||||
g_slice_free (Private, priv);
|
||||
}
|
||||
|
||||
@@ -62,6 +84,20 @@ get_private (MMSharedXmm *self)
|
||||
priv = g_object_get_qdata (G_OBJECT (self), private_quark);
|
||||
if (!priv) {
|
||||
priv = g_slice_new0 (Private);
|
||||
priv->gps_engine_state = GPS_ENGINE_STATE_OFF;
|
||||
|
||||
/* Setup regex for URCs */
|
||||
priv->xlsrstop_regex = g_regex_new ("\\r\\n\\+XLSRSTOP:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
|
||||
priv->nmea_regex = g_regex_new ("(?:\\r\\n)?(?:\\r\\n)?(\\$G.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
|
||||
|
||||
/* Setup parent class' MMBroadbandModemClass */
|
||||
g_assert (MM_SHARED_XMM_GET_INTERFACE (self)->peek_parent_broadband_modem_class);
|
||||
priv->broadband_modem_class_parent = MM_SHARED_XMM_GET_INTERFACE (self)->peek_parent_broadband_modem_class (self);
|
||||
|
||||
/* Setup parent class' MMIfaceModemLocation */
|
||||
g_assert (MM_SHARED_XMM_GET_INTERFACE (self)->peek_parent_location_interface);
|
||||
priv->iface_modem_location_parent = MM_SHARED_XMM_GET_INTERFACE (self)->peek_parent_location_interface (self);
|
||||
|
||||
g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free);
|
||||
}
|
||||
|
||||
@@ -753,6 +789,607 @@ mm_shared_xmm_signal_load_values (MMIfaceModemSignal *self,
|
||||
user_data);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Load capabilities (Location interface) */
|
||||
|
||||
MMModemLocationSource
|
||||
mm_shared_xmm_location_load_capabilities_finish (MMIfaceModemLocation *self,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
GError *inner_error = NULL;
|
||||
gssize value;
|
||||
|
||||
value = g_task_propagate_int (G_TASK (res), &inner_error);
|
||||
if (inner_error) {
|
||||
g_propagate_error (error, inner_error);
|
||||
return MM_MODEM_LOCATION_SOURCE_NONE;
|
||||
}
|
||||
return (MMModemLocationSource)value;
|
||||
}
|
||||
|
||||
static void
|
||||
xlcslsr_test_ready (MMBaseModem *self,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
MMModemLocationSource sources;
|
||||
const gchar *response;
|
||||
GError *error = NULL;
|
||||
Private *priv;
|
||||
gboolean transport_protocol_invalid_supported;
|
||||
gboolean standalone_position_mode_supported;
|
||||
gboolean loc_response_type_nmea_supported;
|
||||
gboolean gnss_type_gps_glonass_supported;
|
||||
|
||||
priv = get_private (MM_SHARED_XMM (self));
|
||||
|
||||
/* Recover parent sources */
|
||||
sources = GPOINTER_TO_UINT (g_task_get_task_data (task));
|
||||
|
||||
response = mm_base_modem_at_command_finish (self, res, &error);
|
||||
if (!response ||
|
||||
!mm_xmm_parse_xlcslsr_test_response (response,
|
||||
&transport_protocol_invalid_supported,
|
||||
&standalone_position_mode_supported,
|
||||
&loc_response_type_nmea_supported,
|
||||
&gnss_type_gps_glonass_supported,
|
||||
&error)) {
|
||||
mm_dbg ("XLCSLSR based GPS control unsupported: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
} else if (!transport_protocol_invalid_supported ||
|
||||
!standalone_position_mode_supported ||
|
||||
!loc_response_type_nmea_supported ||
|
||||
!gnss_type_gps_glonass_supported) {
|
||||
mm_dbg ("XLCSLSR based GPS control unsupported: protocol %s, standalone %s, nmea %s, gps/glonass %s",
|
||||
transport_protocol_invalid_supported ? "supported" : "unsupported",
|
||||
standalone_position_mode_supported ? "supported" : "unsupported",
|
||||
loc_response_type_nmea_supported ? "supported" : "unsupported",
|
||||
gnss_type_gps_glonass_supported ? "supported" : "unsupported");
|
||||
} else {
|
||||
mm_dbg ("XLCSLSR based GPS control supported");
|
||||
priv->supported_sources |= (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW);
|
||||
sources |= priv->supported_sources;
|
||||
}
|
||||
|
||||
g_task_return_int (task, sources);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
static void
|
||||
parent_load_capabilities_ready (MMIfaceModemLocation *self,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
MMModemLocationSource sources;
|
||||
GError *error = NULL;
|
||||
Private *priv;
|
||||
|
||||
priv = get_private (MM_SHARED_XMM (self));
|
||||
|
||||
sources = priv->iface_modem_location_parent->load_capabilities_finish (self, res, &error);
|
||||
if (error) {
|
||||
g_task_return_error (task, error);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
/* If parent already supports GPS sources, we won't do anything else */
|
||||
if (sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)) {
|
||||
mm_dbg ("No need to run XLCSLSR based location gathering");
|
||||
g_task_return_int (task, sources);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Cache sources supported by the parent */
|
||||
g_task_set_task_data (task, GUINT_TO_POINTER (sources), NULL);
|
||||
|
||||
mm_base_modem_at_command (
|
||||
MM_BASE_MODEM (g_task_get_source_object (task)),
|
||||
"+XLCSLSR=?",
|
||||
3,
|
||||
TRUE, /* allow caching */
|
||||
(GAsyncReadyCallback)xlcslsr_test_ready,
|
||||
task);
|
||||
}
|
||||
|
||||
void
|
||||
mm_shared_xmm_location_load_capabilities (MMIfaceModemLocation *self,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
Private *priv;
|
||||
|
||||
priv = get_private (MM_SHARED_XMM (self));
|
||||
task = g_task_new (self, NULL, callback, user_data);
|
||||
|
||||
g_assert (priv->iface_modem_location_parent);
|
||||
g_assert (priv->iface_modem_location_parent->load_capabilities);
|
||||
g_assert (priv->iface_modem_location_parent->load_capabilities_finish);
|
||||
|
||||
priv->iface_modem_location_parent->load_capabilities (self,
|
||||
(GAsyncReadyCallback)parent_load_capabilities_ready,
|
||||
task);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* GPS engine state selection */
|
||||
|
||||
static void
|
||||
nmea_received (MMPortSerialAt *port,
|
||||
GMatchInfo *info,
|
||||
MMSharedXmm *self)
|
||||
{
|
||||
gchar *trace;
|
||||
|
||||
trace = g_match_info_fetch (info, 1);
|
||||
|
||||
/* Helper to debug GPS location related issues. Don't depend on a real GPS
|
||||
* fix for debugging, just use some random values to update */
|
||||
#if 0
|
||||
{
|
||||
const gchar *prefix = NULL;
|
||||
const gchar *lat = NULL;
|
||||
|
||||
/* lat N/S just to test which one is used */
|
||||
if (g_str_has_prefix (trace, "$GPGGA")) {
|
||||
prefix = "GPGGA";
|
||||
lat = "S";
|
||||
} else if (g_str_has_prefix (trace, "$GNGGA")) {
|
||||
prefix = "GNGGA";
|
||||
lat = "N";
|
||||
}
|
||||
|
||||
if (prefix && lat) {
|
||||
GString *str;
|
||||
GDateTime *now;
|
||||
|
||||
mm_dbg ("GGA trace detected: '%s'", trace);
|
||||
g_free (trace);
|
||||
|
||||
now = g_date_time_new_now_utc ();
|
||||
str = g_string_new ("");
|
||||
g_string_append_printf (str,
|
||||
"$%s,%02u%02u%02u,4807.038,%s,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47",
|
||||
prefix,
|
||||
g_date_time_get_hour (now),
|
||||
g_date_time_get_minute (now),
|
||||
g_date_time_get_second (now),
|
||||
lat);
|
||||
mm_iface_modem_location_gps_update (MM_IFACE_MODEM_LOCATION (self), str->str);
|
||||
g_string_free (str, TRUE);
|
||||
g_date_time_unref (now);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
mm_iface_modem_location_gps_update (MM_IFACE_MODEM_LOCATION (self), trace);
|
||||
g_free (trace);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gps_engine_state_select_finish (MMSharedXmm *self,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_boolean (G_TASK (res), error);
|
||||
}
|
||||
|
||||
static void
|
||||
xlcslsr_ready (MMBaseModem *self,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
GpsEngineState state;
|
||||
const gchar *response;
|
||||
GError *error = NULL;
|
||||
Private *priv;
|
||||
|
||||
priv = get_private (MM_SHARED_XMM (self));
|
||||
|
||||
response = mm_base_modem_at_command_full_finish (self, res, &error);
|
||||
if (!response) {
|
||||
g_clear_object (&priv->gps_port);
|
||||
g_task_return_error (task, error);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
state = GPOINTER_TO_UINT (g_task_get_task_data (task));
|
||||
|
||||
g_assert (priv->gps_port);
|
||||
mm_port_serial_at_add_unsolicited_msg_handler (priv->gps_port,
|
||||
priv->nmea_regex,
|
||||
(MMPortSerialAtUnsolicitedMsgFn)nmea_received,
|
||||
self,
|
||||
NULL);
|
||||
priv->gps_engine_state = state;
|
||||
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
static void
|
||||
gps_engine_start (GTask *task)
|
||||
{
|
||||
MMSharedXmm *self;
|
||||
Private *priv;
|
||||
|
||||
self = g_task_get_source_object (task);
|
||||
priv = get_private (self);
|
||||
|
||||
/* Look for an AT port to use for GPS. Prefer secondary port if there is one,
|
||||
* otherwise use primary */
|
||||
g_assert (!priv->gps_port);
|
||||
priv->gps_port = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self));
|
||||
if (!priv->gps_port) {
|
||||
priv->gps_port = mm_base_modem_get_port_primary (MM_BASE_MODEM (self));
|
||||
if (!priv->gps_port) {
|
||||
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||
"No valid port found to control GPS");
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* AT+XLCSLSR
|
||||
* transport_protocol: 2 (invalid)
|
||||
* pos_mode: 3 (standalone)
|
||||
* client_id: <empty>
|
||||
* client_id_type: <empty>
|
||||
* mlc_number: <empty>
|
||||
* mlc_number_type: <empty>
|
||||
* interval: 1 (seconds)
|
||||
* service_type_id: <empty>
|
||||
* pseudonym_indicator: <empty>
|
||||
* loc_response_type: 1 (NMEA strings)
|
||||
* nmea_mask: 118 (01110110: GGA,GSA,GSV,RMC,VTG)
|
||||
* gnss_type: 0 (GPS or GLONASS)
|
||||
*/
|
||||
g_assert (priv->gps_port);
|
||||
mm_base_modem_at_command_full (MM_BASE_MODEM (self),
|
||||
priv->gps_port,
|
||||
"AT+XLCSLSR=2,3,,,,,1,,,1,118,0",
|
||||
3,
|
||||
FALSE,
|
||||
FALSE, /* raw */
|
||||
NULL, /* cancellable */
|
||||
(GAsyncReadyCallback)xlcslsr_ready,
|
||||
task);
|
||||
}
|
||||
|
||||
static void
|
||||
xlsrstop_ready (MMBaseModem *self,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
GpsEngineState state;
|
||||
GError *error = NULL;
|
||||
Private *priv;
|
||||
|
||||
mm_base_modem_at_command_full_finish (self, res, &error);
|
||||
|
||||
priv = get_private (MM_SHARED_XMM (self));
|
||||
state = GPOINTER_TO_UINT (g_task_get_task_data (task));
|
||||
|
||||
g_assert (priv->gps_port);
|
||||
mm_port_serial_at_add_unsolicited_msg_handler (priv->gps_port, priv->nmea_regex, NULL, NULL, NULL);
|
||||
g_clear_object (&priv->gps_port);
|
||||
priv->gps_engine_state = GPS_ENGINE_STATE_OFF;
|
||||
|
||||
/* If already reached requested state, we're done */
|
||||
if (state == priv->gps_engine_state) {
|
||||
/* If we had an error when requesting this specific state, report it */
|
||||
if (error)
|
||||
g_task_return_error (task, error);
|
||||
else
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ignore errors if the stop operation was an intermediate one */
|
||||
g_clear_error (&error);
|
||||
|
||||
/* Otherwise, start with new state */
|
||||
gps_engine_start (task);
|
||||
}
|
||||
|
||||
static void
|
||||
gps_engine_stop (GTask *task)
|
||||
{
|
||||
MMSharedXmm *self;
|
||||
Private *priv;
|
||||
|
||||
self = g_task_get_source_object (task);
|
||||
priv = get_private (self);
|
||||
|
||||
g_assert (priv->gps_port);
|
||||
mm_base_modem_at_command_full (MM_BASE_MODEM (self),
|
||||
priv->gps_port,
|
||||
"+XLSRSTOP",
|
||||
3,
|
||||
FALSE,
|
||||
FALSE, /* raw */
|
||||
NULL, /* cancellable */
|
||||
(GAsyncReadyCallback)xlsrstop_ready,
|
||||
task);
|
||||
}
|
||||
|
||||
static void
|
||||
gps_engine_state_select (MMSharedXmm *self,
|
||||
GpsEngineState state,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
Private *priv;
|
||||
|
||||
task = g_task_new (self, NULL, callback, user_data);
|
||||
g_task_set_task_data (task, GUINT_TO_POINTER (state), NULL);
|
||||
|
||||
priv = get_private (self);
|
||||
|
||||
/* If already in the requested state, we're done */
|
||||
if (state == priv->gps_engine_state) {
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
/* If states are different we always STOP first */
|
||||
if (priv->gps_engine_state != GPS_ENGINE_STATE_OFF) {
|
||||
gps_engine_stop (task);
|
||||
return;
|
||||
}
|
||||
|
||||
/* If GPS already stopped, go on to START right away */
|
||||
g_assert (state != GPS_ENGINE_STATE_OFF);
|
||||
gps_engine_start (task);
|
||||
}
|
||||
|
||||
static GpsEngineState
|
||||
gps_engine_state_get_expected (MMModemLocationSource sources)
|
||||
{
|
||||
/* If at lease one of GPS nmea/raw sources enabled, engine started */
|
||||
if (sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW))
|
||||
return GPS_ENGINE_STATE_STANDALONE;
|
||||
|
||||
/* If no GPS nmea/raw sources enabled, engine stopped */
|
||||
return GPS_ENGINE_STATE_OFF;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Disable location gathering (Location interface) */
|
||||
|
||||
gboolean
|
||||
mm_shared_xmm_disable_location_gathering_finish (MMIfaceModemLocation *self,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_boolean (G_TASK (res), error);
|
||||
}
|
||||
|
||||
static void
|
||||
disable_gps_engine_state_select_ready (MMSharedXmm *self,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
MMModemLocationSource source;
|
||||
GError *error = NULL;
|
||||
Private *priv;
|
||||
|
||||
priv = get_private (MM_SHARED_XMM (self));
|
||||
|
||||
if (!gps_engine_state_select_finish (self, res, &error)) {
|
||||
g_task_return_error (task, error);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
source = GPOINTER_TO_UINT (g_task_get_task_data (task));
|
||||
priv->enabled_sources &= ~source;
|
||||
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
static void
|
||||
parent_disable_location_gathering_ready (MMIfaceModemLocation *self,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
GError *error;
|
||||
Private *priv;
|
||||
|
||||
priv = get_private (MM_SHARED_XMM (self));
|
||||
|
||||
g_assert (priv->iface_modem_location_parent);
|
||||
if (!priv->iface_modem_location_parent->disable_location_gathering_finish (self, res, &error))
|
||||
g_task_return_error (task, error);
|
||||
else
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
void
|
||||
mm_shared_xmm_disable_location_gathering (MMIfaceModemLocation *self,
|
||||
MMModemLocationSource source,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
Private *priv;
|
||||
GTask *task;
|
||||
|
||||
task = g_task_new (self, NULL, callback, user_data);
|
||||
g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL);
|
||||
|
||||
priv = get_private (MM_SHARED_XMM (self));
|
||||
g_assert (priv->iface_modem_location_parent);
|
||||
|
||||
/* Only consider request if it applies to one of the sources we are
|
||||
* supporting, otherwise run parent disable */
|
||||
if (!(priv->supported_sources & source)) {
|
||||
/* If disabling implemented by the parent, run it. */
|
||||
if (priv->iface_modem_location_parent->disable_location_gathering &&
|
||||
priv->iface_modem_location_parent->disable_location_gathering_finish) {
|
||||
priv->iface_modem_location_parent->disable_location_gathering (self,
|
||||
source,
|
||||
(GAsyncReadyCallback)parent_disable_location_gathering_ready,
|
||||
task);
|
||||
return;
|
||||
}
|
||||
/* Otherwise, we're done */
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
/* We only expect GPS sources here */
|
||||
g_assert (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW));
|
||||
|
||||
/* Update engine based on the expected sources */
|
||||
gps_engine_state_select (MM_SHARED_XMM (self),
|
||||
gps_engine_state_get_expected (priv->enabled_sources & ~source),
|
||||
(GAsyncReadyCallback) disable_gps_engine_state_select_ready,
|
||||
task);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Enable location gathering (Location interface) */
|
||||
|
||||
gboolean
|
||||
mm_shared_xmm_enable_location_gathering_finish (MMIfaceModemLocation *self,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_boolean (G_TASK (res), error);
|
||||
}
|
||||
|
||||
static void
|
||||
enable_gps_engine_state_select_ready (MMSharedXmm *self,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
MMModemLocationSource source;
|
||||
GError *error = NULL;
|
||||
Private *priv;
|
||||
|
||||
priv = get_private (MM_SHARED_XMM (self));
|
||||
|
||||
if (!gps_engine_state_select_finish (self, res, &error)) {
|
||||
g_task_return_error (task, error);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
source = GPOINTER_TO_UINT (g_task_get_task_data (task));
|
||||
priv->enabled_sources |= source;
|
||||
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
static void
|
||||
parent_enable_location_gathering_ready (MMIfaceModemLocation *self,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
GError *error;
|
||||
Private *priv;
|
||||
|
||||
priv = get_private (MM_SHARED_XMM (self));
|
||||
|
||||
g_assert (priv->iface_modem_location_parent);
|
||||
if (!priv->iface_modem_location_parent->enable_location_gathering_finish (self, res, &error))
|
||||
g_task_return_error (task, error);
|
||||
else
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
void
|
||||
mm_shared_xmm_enable_location_gathering (MMIfaceModemLocation *self,
|
||||
MMModemLocationSource source,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
Private *priv;
|
||||
GTask *task;
|
||||
|
||||
task = g_task_new (self, NULL, callback, user_data);
|
||||
g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL);
|
||||
|
||||
priv = get_private (MM_SHARED_XMM (self));
|
||||
g_assert (priv->iface_modem_location_parent);
|
||||
g_assert (priv->iface_modem_location_parent->enable_location_gathering);
|
||||
g_assert (priv->iface_modem_location_parent->enable_location_gathering_finish);
|
||||
|
||||
/* Only consider request if it applies to one of the sources we are
|
||||
* supporting, otherwise run parent enable */
|
||||
if (!(priv->supported_sources & source)) {
|
||||
priv->iface_modem_location_parent->enable_location_gathering (self,
|
||||
source,
|
||||
(GAsyncReadyCallback)parent_enable_location_gathering_ready,
|
||||
task);
|
||||
return;
|
||||
}
|
||||
|
||||
/* We only expect GPS sources here */
|
||||
g_assert (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW));
|
||||
|
||||
/* Update engine based on the expected sources */
|
||||
gps_engine_state_select (MM_SHARED_XMM (self),
|
||||
gps_engine_state_get_expected (priv->enabled_sources | source),
|
||||
(GAsyncReadyCallback) enable_gps_engine_state_select_ready,
|
||||
task);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
void
|
||||
mm_shared_xmm_setup_ports (MMBroadbandModem *self)
|
||||
{
|
||||
Private *priv;
|
||||
MMPortSerialAt *ports[2];
|
||||
guint i;
|
||||
|
||||
priv = get_private (MM_SHARED_XMM (self));
|
||||
g_assert (priv->broadband_modem_class_parent);
|
||||
g_assert (priv->broadband_modem_class_parent->setup_ports);
|
||||
|
||||
/* Parent setup first always */
|
||||
priv->broadband_modem_class_parent->setup_ports (self);
|
||||
|
||||
ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
|
||||
ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
|
||||
|
||||
/* Setup primary and secondary ports */
|
||||
for (i = 0; i < G_N_ELEMENTS (ports); i++) {
|
||||
if (!ports[i])
|
||||
continue;
|
||||
|
||||
/* After running AT+XLSRSTOP we may get an unsolicited response
|
||||
* reporting its status, we just ignore it. */
|
||||
mm_port_serial_at_add_unsolicited_msg_handler (
|
||||
ports[i],
|
||||
priv->xlsrstop_regex,
|
||||
NULL, NULL, NULL);
|
||||
|
||||
|
||||
|
||||
/* make sure GPS is stopped in case it was left enabled */
|
||||
mm_base_modem_at_command_full (MM_BASE_MODEM (self),
|
||||
ports[i],
|
||||
"+XLSRSTOP",
|
||||
3, FALSE, FALSE, NULL, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static void
|
||||
@@ -774,6 +1411,7 @@ mm_shared_xmm_get_type (void)
|
||||
|
||||
shared_xmm_type = g_type_register_static (G_TYPE_INTERFACE, "MMSharedXmm", &info, 0);
|
||||
g_type_interface_add_prerequisite (shared_xmm_type, MM_TYPE_IFACE_MODEM);
|
||||
g_type_interface_add_prerequisite (shared_xmm_type, MM_TYPE_IFACE_MODEM_LOCATION);
|
||||
}
|
||||
|
||||
return shared_xmm_type;
|
||||
|
||||
@@ -22,8 +22,10 @@
|
||||
#define _LIBMM_INSIDE_MM
|
||||
#include <libmm-glib.h>
|
||||
|
||||
#include "mm-broadband-modem.h"
|
||||
#include "mm-iface-modem.h"
|
||||
#include "mm-iface-modem-signal.h"
|
||||
#include "mm-iface-modem-location.h"
|
||||
|
||||
#define MM_TYPE_SHARED_XMM (mm_shared_xmm_get_type ())
|
||||
#define MM_SHARED_XMM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SHARED_XMM, MMSharedXmm))
|
||||
@@ -34,10 +36,20 @@ typedef struct _MMSharedXmm MMSharedXmm;
|
||||
|
||||
struct _MMSharedXmm {
|
||||
GTypeInterface g_iface;
|
||||
|
||||
/* Peek broadband modem class of the parent class of the object */
|
||||
MMBroadbandModemClass * (* peek_parent_broadband_modem_class) (MMSharedXmm *self);
|
||||
|
||||
/* Peek location interface of the parent class of the object */
|
||||
MMIfaceModemLocation * (* peek_parent_location_interface) (MMSharedXmm *self);
|
||||
};
|
||||
|
||||
GType mm_shared_xmm_get_type (void);
|
||||
|
||||
/* Shared XMM device setup */
|
||||
|
||||
void mm_shared_xmm_setup_ports (MMBroadbandModem *self);
|
||||
|
||||
/* Shared XMM device management support */
|
||||
|
||||
void mm_shared_xmm_load_supported_modes (MMIfaceModem *self,
|
||||
@@ -134,4 +146,25 @@ void mm_shared_xmm_signal_load_values (MMIfaceModemSign
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
void mm_shared_xmm_location_load_capabilities (MMIfaceModemLocation *self,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
MMModemLocationSource mm_shared_xmm_location_load_capabilities_finish (MMIfaceModemLocation *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
void mm_shared_xmm_enable_location_gathering (MMIfaceModemLocation *self,
|
||||
MMModemLocationSource source,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean mm_shared_xmm_enable_location_gathering_finish (MMIfaceModemLocation *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
void mm_shared_xmm_disable_location_gathering (MMIfaceModemLocation *self,
|
||||
MMModemLocationSource source,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean mm_shared_xmm_disable_location_gathering_finish (MMIfaceModemLocation *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
#endif /* MM_SHARED_XMM_H */
|
||||
|
||||
@@ -648,6 +648,61 @@ test_xcesq_response_to_signal (void)
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* AT+XLCSLSR=? response parser */
|
||||
|
||||
typedef struct {
|
||||
const gchar *response;
|
||||
gboolean expected_transport_protocol_invalid_supported;
|
||||
gboolean expected_standalone_position_mode_supported;
|
||||
gboolean expected_loc_response_type_nmea_supported;
|
||||
gboolean expected_gnss_type_gps_glonass_supported;
|
||||
} XlcslsrTest;
|
||||
|
||||
static XlcslsrTest xlcslsr_tests[] = {
|
||||
{
|
||||
"+XLCSLSR:(0-2),(0-3), ,(0-1), ,(0-1),(0-7200),(0-255),(0-1),(0-2),(1-256),(0-1)",
|
||||
TRUE, TRUE, TRUE, TRUE
|
||||
},
|
||||
{
|
||||
"+XLCSLSR:(0,1,2),(0,1,2,3), ,(0,1), ,(0,1),(0-7200),(0-255),(0,1),(0,1,2),(1-256),(0,1)",
|
||||
TRUE, TRUE, TRUE, TRUE
|
||||
},
|
||||
{
|
||||
"+XLCSLSR:(0-1),(0-2), ,(0,1), ,(0,1),(0 -7200),(0-255),(0-1),(0),(1-256),(1)",
|
||||
FALSE, FALSE, FALSE, FALSE
|
||||
},
|
||||
};
|
||||
|
||||
static void
|
||||
test_xlcslsr_test (void)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (xlcslsr_tests); i++) {
|
||||
GError *error = NULL;
|
||||
gboolean ret;
|
||||
gboolean transport_protocol_invalid_supported;
|
||||
gboolean standalone_position_mode_supported;
|
||||
gboolean loc_response_type_nmea_supported;
|
||||
gboolean gnss_type_gps_glonass_supported;
|
||||
|
||||
ret = mm_xmm_parse_xlcslsr_test_response (xlcslsr_tests[i].response,
|
||||
&transport_protocol_invalid_supported,
|
||||
&standalone_position_mode_supported,
|
||||
&loc_response_type_nmea_supported,
|
||||
&gnss_type_gps_glonass_supported,
|
||||
&error);
|
||||
g_assert_no_error (error);
|
||||
g_assert (ret);
|
||||
|
||||
g_assert (transport_protocol_invalid_supported == xlcslsr_tests[i].expected_transport_protocol_invalid_supported);
|
||||
g_assert (standalone_position_mode_supported == xlcslsr_tests[i].expected_standalone_position_mode_supported);
|
||||
g_assert (loc_response_type_nmea_supported == xlcslsr_tests[i].expected_loc_response_type_nmea_supported);
|
||||
g_assert (gnss_type_gps_glonass_supported == xlcslsr_tests[i].expected_gnss_type_gps_glonass_supported);
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
void
|
||||
@@ -688,5 +743,7 @@ int main (int argc, char **argv)
|
||||
g_test_add_func ("/MM/xmm/xcesq/query_response", test_xcesq_response);
|
||||
g_test_add_func ("/MM/xmm/xcesq/query_response_to_signal", test_xcesq_response_to_signal);
|
||||
|
||||
g_test_add_func ("/MM/xmm/xlcslsr/test", test_xlcslsr_test);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user