xmm: implement XLCSLSR based GPS management and report

This commit is contained in:
Aleksander Morgado
2018-10-17 11:17:11 +02:00
parent 04ca18c01f
commit 2ffd0adf05
7 changed files with 932 additions and 2 deletions
+37 -2
View File
@@ -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;
}
+37
View File
@@ -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;
}
+122
View File
@@ -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;
}
+8
View File
@@ -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 */
+638
View File
@@ -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;
+33
View File
@@ -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 ();
}