diff --git a/plugins/xmm/mm-broadband-modem-mbim-xmm.c b/plugins/xmm/mm-broadband-modem-mbim-xmm.c index 9001d401ea8d44af78d0ba8b39b133f700b23e25..85a91fc85e7678edc406cc7f265063e36ca6613f 100644 --- a/plugins/xmm/mm-broadband-modem-mbim-xmm.c +++ b/plugins/xmm/mm-broadband-modem-mbim-xmm.c @@ -97,6 +97,10 @@ iface_modem_location_init (MMIfaceModemLocation *iface) 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; + iface->load_supl_server = mm_shared_xmm_location_load_supl_server; + iface->load_supl_server_finish = mm_shared_xmm_location_load_supl_server_finish; + iface->set_supl_server = mm_shared_xmm_location_set_supl_server; + iface->set_supl_server_finish = mm_shared_xmm_location_set_supl_server_finish; } static MMBroadbandModemClass * diff --git a/plugins/xmm/mm-broadband-modem-xmm.c b/plugins/xmm/mm-broadband-modem-xmm.c index 6f5d3831ca853504381b81e7902220cafde67e7a..ab322162681e8ea9cbcf8869acbf708cd690b52a 100644 --- a/plugins/xmm/mm-broadband-modem-xmm.c +++ b/plugins/xmm/mm-broadband-modem-xmm.c @@ -106,6 +106,10 @@ iface_modem_location_init (MMIfaceModemLocation *iface) 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; + iface->load_supl_server = mm_shared_xmm_location_load_supl_server; + iface->load_supl_server_finish = mm_shared_xmm_location_load_supl_server_finish; + iface->set_supl_server = mm_shared_xmm_location_set_supl_server; + iface->set_supl_server_finish = mm_shared_xmm_location_set_supl_server_finish; } static MMBroadbandModemClass * diff --git a/plugins/xmm/mm-modem-helpers-xmm.c b/plugins/xmm/mm-modem-helpers-xmm.c index bcc24f6c26c9c7195b1c4ed9668c85764a61e421..a3eb5f9b689276605836d1a11412e47350470133 100644 --- a/plugins/xmm/mm-modem-helpers-xmm.c +++ b/plugins/xmm/mm-modem-helpers-xmm.c @@ -847,7 +847,9 @@ number_group_contains_value (const gchar *group, gboolean mm_xmm_parse_xlcslsr_test_response (const gchar *response, gboolean *transport_protocol_invalid_supported, + gboolean *transport_protocol_supl_supported, gboolean *standalone_position_mode_supported, + gboolean *ms_assisted_based_position_mode_supported, gboolean *loc_response_type_nmea_supported, gboolean *gnss_type_gps_glonass_supported, GError **error) @@ -859,8 +861,8 @@ mm_xmm_parse_xlcslsr_test_response (const gchar *response, /* * 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) + * transport_protocol: 2 (invalid) or 1 (supl) + * pos_mode: 3 (standalone) or 2 (ms assisted/based) * client_id: <empty> * client_id_type: <empty> * mlc_number: <empty> @@ -891,6 +893,15 @@ mm_xmm_parse_xlcslsr_test_response (const gchar *response, goto out; } + if (transport_protocol_supl_supported) { + *transport_protocol_supl_supported = number_group_contains_value (groups[0], + "transport protocol", + 1, /* supl */ + &inner_error); + if (inner_error) + goto out; + } + if (standalone_position_mode_supported) { *standalone_position_mode_supported = number_group_contains_value (groups[1], "position mode", @@ -900,6 +911,15 @@ mm_xmm_parse_xlcslsr_test_response (const gchar *response, goto out; } + if (ms_assisted_based_position_mode_supported) { + *ms_assisted_based_position_mode_supported = number_group_contains_value (groups[1], + "position mode", + 2, /* ms assisted/based */ + &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", @@ -930,3 +950,64 @@ mm_xmm_parse_xlcslsr_test_response (const gchar *response, return ret; } + +/*****************************************************************************/ +/* AT+XLCSSLP? response parser */ + +gboolean +mm_xmm_parse_xlcsslp_query_response (const gchar *response, + gchar **supl_address, + GError **error) +{ + GRegex *r; + GMatchInfo *match_info; + GError *inner_error = NULL; + gchar *address = NULL; + guint port = 0; + + /* + * E.g.: + * +XLCSSLP:1,"www.spirent-lcs.com",7275 + */ + + r = g_regex_new ("\\+XLCSSLP:\\s*(\\d+),([^,]*),(\\d+)(?:\\r\\n)?", + G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL); + g_assert (r != NULL); + + g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); + if (!inner_error && g_match_info_matches (match_info)) { + guint type; + + /* We only support types 0 (IPv4) and 1 (FQDN) */ + mm_get_uint_from_match_info (match_info, 1, &type); + if (type != 0 && type != 1) { + inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, + "Unsupported SUPL server address type (%u) in response: %s", type, response); + goto out; + } + + address = mm_get_string_unquoted_from_match_info (match_info, 2); + mm_get_uint_from_match_info (match_info, 3, &port); + if (!port) { + inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Invalid SUPL address port number in response: %s", response); + goto out; + } + } + +out: + if (match_info) + g_match_info_free (match_info); + g_regex_unref (r); + + if (inner_error) { + g_propagate_error (error, inner_error); + return FALSE; + } + + if (supl_address) + *supl_address = g_strdup_printf ("%s:%u", address, port); + g_free (address); + + return TRUE; +} diff --git a/plugins/xmm/mm-modem-helpers-xmm.h b/plugins/xmm/mm-modem-helpers-xmm.h index 9c77b14a631f2b4cf52899481b5b302e7525fa38..f70fc346bb0f1e8d04f45a53e35390c75c185328 100644 --- a/plugins/xmm/mm-modem-helpers-xmm.h +++ b/plugins/xmm/mm-modem-helpers-xmm.h @@ -58,9 +58,16 @@ gboolean mm_xmm_xcesq_response_to_signal_info (const gchar *response, /* AT+XLCSLSR=? response parser */ gboolean mm_xmm_parse_xlcslsr_test_response (const gchar *response, gboolean *transport_protocol_invalid_supported, + gboolean *transport_protocol_supl_supported, gboolean *standalone_position_mode_supported, + gboolean *ms_assisted_based_position_mode_supported, gboolean *loc_response_type_nmea_supported, gboolean *gnss_type_gps_glonass_supported, GError **error); +/* AT+XLCSSLP? response parser */ +gboolean mm_xmm_parse_xlcsslp_query_response (const gchar *response, + gchar **supl_address, + GError **error); + #endif /* MM_MODEM_HELPERS_XMM_H */ diff --git a/plugins/xmm/mm-shared-xmm.c b/plugins/xmm/mm-shared-xmm.c index a1d59a15514dafbc549b7181915ce5af8cbabc89..fcddee108234e6a8a58daf3f2675204fba86b3d0 100644 --- a/plugins/xmm/mm-shared-xmm.c +++ b/plugins/xmm/mm-shared-xmm.c @@ -14,6 +14,7 @@ */ #include <config.h> +#include <arpa/inet.h> #include <glib-object.h> #include <gio/gio.h> @@ -39,6 +40,7 @@ static GQuark private_quark; typedef enum { GPS_ENGINE_STATE_OFF, GPS_ENGINE_STATE_STANDALONE, + GPS_ENGINE_STATE_ASSISTED, } GpsEngineState; typedef struct { @@ -818,7 +820,9 @@ xlcslsr_test_ready (MMBaseModem *self, GError *error = NULL; Private *priv; gboolean transport_protocol_invalid_supported; + gboolean transport_protocol_supl_supported; gboolean standalone_position_mode_supported; + gboolean ms_assisted_based_position_mode_supported; gboolean loc_response_type_nmea_supported; gboolean gnss_type_gps_glonass_supported; @@ -831,7 +835,9 @@ xlcslsr_test_ready (MMBaseModem *self, if (!response || !mm_xmm_parse_xlcslsr_test_response (response, &transport_protocol_invalid_supported, + &transport_protocol_supl_supported, &standalone_position_mode_supported, + &ms_assisted_based_position_mode_supported, &loc_response_type_nmea_supported, &gnss_type_gps_glonass_supported, &error)) { @@ -841,7 +847,7 @@ xlcslsr_test_ready (MMBaseModem *self, !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", + mm_dbg ("XLCSLSR based GPS control unsupported: protocol invalid %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", @@ -849,6 +855,16 @@ xlcslsr_test_ready (MMBaseModem *self, } else { mm_dbg ("XLCSLSR based GPS control supported"); priv->supported_sources |= (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW); + + if (transport_protocol_supl_supported && ms_assisted_based_position_mode_supported) { + mm_dbg ("XLCSLSR based A-GPS control supported"); + priv->supported_sources |= MM_MODEM_LOCATION_SOURCE_AGPS; + } else { + mm_dbg ("XLCSLSR based A-GPS control unsupported: protocol supl %s, ms assisted/based %s", + transport_protocol_supl_supported ? "supported" : "unsupported", + ms_assisted_based_position_mode_supported ? "supported" : "unsupported"); + } + sources |= priv->supported_sources; } @@ -1015,11 +1031,16 @@ xlcslsr_ready (MMBaseModem *self, static void gps_engine_start (GTask *task) { - MMSharedXmm *self; - Private *priv; + GpsEngineState state; + MMSharedXmm *self; + Private *priv; + guint transport_protocol = 0; + guint pos_mode = 0; + gchar *cmd; - self = g_task_get_source_object (task); - priv = get_private (self); + self = g_task_get_source_object (task); + priv = get_private (self); + state = GPOINTER_TO_UINT (g_task_get_task_data (task)); /* Look for an AT port to use for GPS. Prefer secondary port if there is one, * otherwise use primary */ @@ -1035,10 +1056,24 @@ gps_engine_start (GTask *task) } } + switch (state) { + case GPS_ENGINE_STATE_STANDALONE: + transport_protocol = 2; + pos_mode = 3; + break; + case GPS_ENGINE_STATE_ASSISTED: + transport_protocol = 1; + pos_mode = 2; + break; + default: + g_assert_not_reached (); + break; + } + /* * AT+XLCSLSR - * transport_protocol: 2 (invalid) - * pos_mode: 3 (standalone) + * transport_protocol: 2 (invalid) or 1 (supl) + * pos_mode: 3 (standalone) or 2 (ms assisted+based) * client_id: <empty> * client_id_type: <empty> * mlc_number: <empty> @@ -1051,15 +1086,17 @@ gps_engine_start (GTask *task) * gnss_type: 0 (GPS or GLONASS) */ g_assert (priv->gps_port); + cmd = g_strdup_printf ("AT+XLCSLSR=%u,%u,,,,,1,,,1,118,0", transport_protocol, pos_mode); mm_base_modem_at_command_full (MM_BASE_MODEM (self), priv->gps_port, - "AT+XLCSLSR=2,3,,,,,1,,,1,118,0", + cmd, 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)xlcslsr_ready, task); + g_free (cmd); } static void @@ -1156,11 +1193,15 @@ 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)) + if (sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)) { + /* If A-GPS is enabled, ASSISTED mode */ + if (sources & MM_MODEM_LOCATION_SOURCE_AGPS) + return GPS_ENGINE_STATE_ASSISTED; + /* Otherwise, STANDALONE */ return GPS_ENGINE_STATE_STANDALONE; - + } /* If no GPS nmea/raw sources enabled, engine stopped */ - return GPS_ENGINE_STATE_OFF; + return GPS_ENGINE_STATE_OFF; } /*****************************************************************************/ @@ -1250,7 +1291,9 @@ mm_shared_xmm_disable_location_gathering (MMIfaceModemLocation *self, } /* We only expect GPS sources here */ - g_assert (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)); + g_assert (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | + MM_MODEM_LOCATION_SOURCE_GPS_RAW | + MM_MODEM_LOCATION_SOURCE_AGPS)); /* Update engine based on the expected sources */ gps_engine_state_select (MM_SHARED_XMM (self), @@ -1340,7 +1383,9 @@ mm_shared_xmm_enable_location_gathering (MMIfaceModemLocation *self, } /* We only expect GPS sources here */ - g_assert (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)); + g_assert (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | + MM_MODEM_LOCATION_SOURCE_GPS_RAW | + MM_MODEM_LOCATION_SOURCE_AGPS)); /* Update engine based on the expected sources */ gps_engine_state_select (MM_SHARED_XMM (self), @@ -1349,6 +1394,113 @@ mm_shared_xmm_enable_location_gathering (MMIfaceModemLocation *self, task); } +/*****************************************************************************/ +/* Location: Load SUPL server */ + +gchar * +mm_shared_xmm_location_load_supl_server_finish (MMIfaceModemLocation *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_pointer (G_TASK (res), error); +} + +static void +xlcsslp_query_ready (MMBaseModem *self, + GAsyncResult *res, + GTask *task) +{ + const gchar *response; + GError *error = NULL; + gchar *supl_address; + + response = mm_base_modem_at_command_finish (self, res, &error); + if (!response || !mm_xmm_parse_xlcsslp_query_response (response, &supl_address, &error)) + g_task_return_error (task, error); + else + g_task_return_pointer (task, supl_address, g_free); + g_object_unref (task); +} + +void +mm_shared_xmm_location_load_supl_server (MMIfaceModemLocation *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + + task = g_task_new (self, NULL, callback, user_data); + + mm_base_modem_at_command (MM_BASE_MODEM (self), + "+XLCSSLP?", + 3, + FALSE, + (GAsyncReadyCallback)xlcsslp_query_ready, + task); +} + +/*****************************************************************************/ + +gboolean +mm_shared_xmm_location_set_supl_server_finish (MMIfaceModemLocation *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +xlcsslp_set_ready (MMBaseModem *self, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + + if (!mm_base_modem_at_command_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_location_set_supl_server (MMIfaceModemLocation *self, + const gchar *supl, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + gchar *cmd = NULL; + gchar *fqdn = NULL; + guint32 ip; + guint16 port; + + task = g_task_new (self, NULL, callback, user_data); + + mm_parse_supl_address (supl, &fqdn, &ip, &port, NULL); + g_assert (port); + if (fqdn) + cmd = g_strdup_printf ("+XLCSSLP=1,%s,%u", fqdn, port); + else if (ip) { + struct in_addr a = { .s_addr = ip }; + gchar buf[INET_ADDRSTRLEN + 1] = { 0 }; + + /* we got 'ip' from inet_pton(), so this next step should always succeed */ + g_assert (inet_ntop (AF_INET, &a, buf, sizeof (buf) - 1)); + cmd = g_strdup_printf ("+XLCSSLP=0,%s,%u", buf, port); + } else + g_assert_not_reached (); + + mm_base_modem_at_command (MM_BASE_MODEM (self), + cmd, + 3, + FALSE, + (GAsyncReadyCallback)xlcsslp_set_ready, + task); + g_free (cmd); + g_free (fqdn); +} + /*****************************************************************************/ void diff --git a/plugins/xmm/mm-shared-xmm.h b/plugins/xmm/mm-shared-xmm.h index 1ff31ff1a130392f905cb6c8cd9e36fa30d964ab..1a4f47441865293c99c7bf51e47542af1de0001b 100644 --- a/plugins/xmm/mm-shared-xmm.h +++ b/plugins/xmm/mm-shared-xmm.h @@ -166,5 +166,18 @@ void mm_shared_xmm_disable_location_gathering (MMIfaceMo gboolean mm_shared_xmm_disable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); +void mm_shared_xmm_location_load_supl_server (MMIfaceModemLocation *self, + GAsyncReadyCallback callback, + gpointer user_data); +gchar *mm_shared_xmm_location_load_supl_server_finish (MMIfaceModemLocation *self, + GAsyncResult *res, + GError **error); +void mm_shared_xmm_location_set_supl_server (MMIfaceModemLocation *self, + const gchar *supl, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean mm_shared_xmm_location_set_supl_server_finish (MMIfaceModemLocation *self, + GAsyncResult *res, + GError **error); #endif /* MM_SHARED_XMM_H */ diff --git a/plugins/xmm/tests/test-modem-helpers-xmm.c b/plugins/xmm/tests/test-modem-helpers-xmm.c index df2306d42feec7c3dde0db282796fb5009864249..28aea936a77293942e6588eecba21c978e7081c8 100644 --- a/plugins/xmm/tests/test-modem-helpers-xmm.c +++ b/plugins/xmm/tests/test-modem-helpers-xmm.c @@ -654,7 +654,9 @@ test_xcesq_response_to_signal (void) typedef struct { const gchar *response; gboolean expected_transport_protocol_invalid_supported; + gboolean expected_transport_protocol_supl_supported; gboolean expected_standalone_position_mode_supported; + gboolean expected_ms_assisted_based_position_mode_supported; gboolean expected_loc_response_type_nmea_supported; gboolean expected_gnss_type_gps_glonass_supported; } XlcslsrTest; @@ -662,15 +664,15 @@ typedef struct { 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 + TRUE, TRUE, 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 + TRUE, TRUE, 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 + FALSE, TRUE, FALSE, TRUE, FALSE, FALSE }, }; @@ -683,24 +685,69 @@ test_xlcslsr_test (void) GError *error = NULL; gboolean ret; gboolean transport_protocol_invalid_supported; + gboolean transport_protocol_supl_supported; gboolean standalone_position_mode_supported; + gboolean ms_assisted_based_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, + &transport_protocol_supl_supported, &standalone_position_mode_supported, + &ms_assisted_based_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); ->>>>>>> 090c3dd6... xmm: implement XLCSLSR based GPS management and report + g_assert (transport_protocol_invalid_supported == xlcslsr_tests[i].expected_transport_protocol_invalid_supported); + g_assert (transport_protocol_supl_supported == xlcslsr_tests[i].expected_transport_protocol_supl_supported); + g_assert (standalone_position_mode_supported == xlcslsr_tests[i].expected_standalone_position_mode_supported); + g_assert (ms_assisted_based_position_mode_supported == xlcslsr_tests[i].expected_ms_assisted_based_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); + } +} + +/*****************************************************************************/ +/* AT+XLCSSLP? response parser */ + +typedef struct { + const gchar *response; + const gchar *expected; +} XlcsslpQuery; + +static XlcsslpQuery xlcsslp_queries[] = { + { + "+XLCSSLP:1,\"www.spirent-lcs.com\",7275", + "www.spirent-lcs.com:7275" + }, + { + "+XLCSSLP:0,\"123.123.123.123\",7275", + "123.123.123.123:7275" + }, +}; + +static void +test_xlcsslp_queries (void) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (xlcsslp_queries); i++) { + GError *error = NULL; + gchar *supl_server = NULL; + gboolean ret; + + ret = mm_xmm_parse_xlcsslp_query_response (xlcsslp_queries[i].response, + &supl_server, + &error); + g_assert_no_error (error); + g_assert (ret); + + g_assert_cmpstr (supl_server, ==, xlcsslp_queries[i].expected); + g_free (supl_server); } } @@ -746,5 +793,7 @@ int main (int argc, char **argv) g_test_add_func ("/MM/xmm/xlcslsr/test", test_xlcslsr_test); + g_test_add_func ("/MM/xmm/xlcsslp/query", test_xlcsslp_queries); + return g_test_run (); }