diff options
author | David McCullough <david.mccullough@accelecon.com> | 2014-08-06 17:30:22 +1000 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2014-08-11 18:31:46 +0200 |
commit | bd0ffd24f1c95a1974176a1cc0eb6f3444f45e8e (patch) | |
tree | 2700751f6f1d65452e27edba1156e25fa97cd257 | |
parent | 8c4318d87a5cc604d04913c66ac3e4ebda10bc18 (diff) |
huawei: improve support for network time on Huawei modules
Third revision of Huawei nwtime support. Takes on feedback from the
mailing list including helpers, some basic tests and use of the ^NTCT
command to determine network time support (^NWTIME). Expanded test cases,
more use of g_assert and more logical helper return values/errors.
Signed-off-by: David McCullough <david.mccullough@accelecon.com>
-rw-r--r-- | plugins/huawei/mm-broadband-modem-huawei.c | 168 | ||||
-rw-r--r-- | plugins/huawei/mm-modem-helpers-huawei.c | 151 | ||||
-rw-r--r-- | plugins/huawei/mm-modem-helpers-huawei.h | 16 | ||||
-rw-r--r-- | plugins/huawei/tests/test-modem-helpers-huawei.c | 134 |
4 files changed, 404 insertions, 65 deletions
diff --git a/plugins/huawei/mm-broadband-modem-huawei.c b/plugins/huawei/mm-broadband-modem-huawei.c index 9cbbfb85..6630043f 100644 --- a/plugins/huawei/mm-broadband-modem-huawei.c +++ b/plugins/huawei/mm-broadband-modem-huawei.c @@ -108,6 +108,8 @@ struct _MMBroadbandModemHuaweiPrivate { FeatureSupport syscfg_support; FeatureSupport syscfgex_support; FeatureSupport prefmode_support; + FeatureSupport time_support; + FeatureSupport nwtime_support; MMModemLocationSource enabled_sources; @@ -2840,70 +2842,70 @@ get_detailed_registration_state (MMIfaceModemCdma *self, /*****************************************************************************/ /* Load network time (Time interface) */ +static MMNetworkTimezone * +modem_time_load_network_timezone_finish (MMIfaceModemTime *_self, + GAsyncResult *res, + GError **error) +{ + MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self); + MMNetworkTimezone *tz = NULL; + const gchar *response; + + g_assert (self->priv->nwtime_support == FEATURE_SUPPORTED || + self->priv->time_support == FEATURE_SUPPORTED); + + response = mm_base_modem_at_command_finish (MM_BASE_MODEM (_self), res, error); + if (!response) + return NULL; + + if (self->priv->nwtime_support == FEATURE_SUPPORTED) + mm_huawei_parse_nwtime_response (response, NULL, &tz, error); + else if (self->priv->time_support == FEATURE_SUPPORTED) + mm_huawei_parse_time_response (response, NULL, &tz, error); + return tz; +} + + static gchar * -modem_time_load_network_time_finish (MMIfaceModemTime *self, +modem_time_load_network_time_finish (MMIfaceModemTime *_self, GAsyncResult *res, GError **error) { + MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self); const gchar *response; - GRegex *r; - GMatchInfo *match_info = NULL; - GError *match_error = NULL; - guint year, month, day, hour, minute, second; - gchar *result = NULL; + gchar *iso8601 = NULL; - response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); + g_assert (self->priv->nwtime_support == FEATURE_SUPPORTED || + self->priv->time_support == FEATURE_SUPPORTED); + + response = mm_base_modem_at_command_finish (MM_BASE_MODEM (_self), res, error); if (!response) return NULL; - /* Already in ISO-8601 format, but verify just to be sure */ - r = g_regex_new ("\\^TIME:\\s*(\\d+)/(\\d+)/(\\d+)\\s*(\\d+):(\\d+):(\\d*)$", 0, 0, NULL); - g_assert (r != NULL); - - if (!g_regex_match_full (r, response, -1, 0, 0, &match_info, &match_error)) { - if (match_error) { - g_propagate_error (error, match_error); - g_prefix_error (error, "Could not parse ^TIME results: "); - } else { - g_set_error_literal (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Couldn't match ^TIME reply"); - } - } else { - /* Remember that g_match_info_get_match_count() includes match #0 */ - g_assert (g_match_info_get_match_count (match_info) >= 7); - - if (mm_get_uint_from_match_info (match_info, 1, &year) && - mm_get_uint_from_match_info (match_info, 2, &month) && - mm_get_uint_from_match_info (match_info, 3, &day) && - mm_get_uint_from_match_info (match_info, 4, &hour) && - mm_get_uint_from_match_info (match_info, 5, &minute) && - mm_get_uint_from_match_info (match_info, 6, &second)) { - /* Return ISO-8601 format date/time string */ - result = g_strdup_printf ("%04d/%02d/%02d %02d:%02d:%02d", - year, month, day, hour, minute, second); - } else { - g_set_error_literal (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Failed to parse ^TIME reply"); - } - } - - if (match_info) - g_match_info_free (match_info); - g_regex_unref (r); - return result; + if (self->priv->nwtime_support == FEATURE_SUPPORTED) + mm_huawei_parse_nwtime_response (response, &iso8601, NULL, error); + else if (self->priv->time_support == FEATURE_SUPPORTED) + mm_huawei_parse_time_response (response, &iso8601, NULL, error); + return iso8601; } static void -modem_time_load_network_time (MMIfaceModemTime *self, +modem_time_load_network_time_or_zone (MMIfaceModemTime *_self, GAsyncReadyCallback callback, gpointer user_data) { + const char *command = NULL; + MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self); + + if (self->priv->nwtime_support == FEATURE_SUPPORTED) + command = "^NWTIME?"; + else if (self->priv->time_support == FEATURE_SUPPORTED) + command = "^TIME"; + + g_assert (command != NULL); + mm_base_modem_at_command (MM_BASE_MODEM (self), - "^TIME", + command, 3, FALSE, callback, @@ -3487,28 +3489,61 @@ enable_location_gathering (MMIfaceModemLocation *self, /* Check support (Time interface) */ static gboolean -modem_time_check_support_finish (MMIfaceModemTime *self, +modem_time_check_support_finish (MMIfaceModemTime *_self, GAsyncResult *res, GError **error) { - return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); + MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self); + if (self->priv->nwtime_support == FEATURE_SUPPORTED) + return TRUE; + if (self->priv->time_support == FEATURE_SUPPORTED) + return TRUE; + return FALSE; } static void -modem_time_check_ready (MMBroadbandModem *self, +modem_time_check_ready (MMBaseModem *self, GAsyncResult *res, GSimpleAsyncResult *simple) { - GError *error = NULL; - - mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); - if (error) - g_simple_async_result_take_error (simple, error); - + (void)mm_base_modem_at_sequence_finish (self, res, NULL, NULL); g_simple_async_result_complete (simple); g_object_unref (simple); } +static gboolean +modem_check_time_reply (MMBaseModem *_self, + gpointer none, + const gchar *command, + const gchar *response, + gboolean last_command, + const GError *error, + GVariant **result, + GError **result_error) +{ + MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self); + + if (!error) { + if (strstr (response, "^NTCT")) + self->priv->nwtime_support = FEATURE_SUPPORTED; + else if (strstr (response, "^TIME")) + self->priv->time_support = FEATURE_SUPPORTED; + } else { + if (strstr (command, "^NTCT")) + self->priv->nwtime_support = FEATURE_NOT_SUPPORTED; + else if (strstr (command, "^TIME")) + self->priv->time_support = FEATURE_NOT_SUPPORTED; + } + + return FALSE; +} + +static const MMBaseModemAtCommand time_cmd_sequence[] = { + { "^NTCT?", 3, FALSE, modem_check_time_reply }, /* 3GPP/LTE */ + { "^TIME", 3, FALSE, modem_check_time_reply }, /* CDMA */ + { NULL } +}; + static void modem_time_check_support (MMIfaceModemTime *self, GAsyncReadyCallback callback, @@ -3521,13 +3556,12 @@ modem_time_check_support (MMIfaceModemTime *self, user_data, modem_time_check_support); - /* Only CDMA devices support this at the moment */ - mm_base_modem_at_command (MM_BASE_MODEM (self), - "^TIME", - 3, - TRUE, - (GAsyncReadyCallback)modem_time_check_ready, - result); + mm_base_modem_at_sequence (MM_BASE_MODEM (self), + time_cmd_sequence, + NULL, /* response_processor_context */ + NULL, /* response_processor_context_free */ + (GAsyncReadyCallback)modem_time_check_ready, + result); } /*****************************************************************************/ @@ -3714,6 +3748,8 @@ mm_broadband_modem_huawei_init (MMBroadbandModemHuawei *self) self->priv->syscfg_support = FEATURE_SUPPORT_UNKNOWN; self->priv->syscfgex_support = FEATURE_SUPPORT_UNKNOWN; self->priv->prefmode_support = FEATURE_SUPPORT_UNKNOWN; + self->priv->nwtime_support = FEATURE_SUPPORT_UNKNOWN; + self->priv->time_support = FEATURE_SUPPORT_UNKNOWN; } static void @@ -3845,8 +3881,10 @@ iface_modem_time_init (MMIfaceModemTime *iface) { iface->check_support = modem_time_check_support; iface->check_support_finish = modem_time_check_support_finish; - iface->load_network_time = modem_time_load_network_time; + iface->load_network_time = modem_time_load_network_time_or_zone; iface->load_network_time_finish = modem_time_load_network_time_finish; + iface->load_network_timezone = modem_time_load_network_time_or_zone; + iface->load_network_timezone_finish = modem_time_load_network_timezone_finish; } static void diff --git a/plugins/huawei/mm-modem-helpers-huawei.c b/plugins/huawei/mm-modem-helpers-huawei.c index 656c6de0..7612e64d 100644 --- a/plugins/huawei/mm-modem-helpers-huawei.c +++ b/plugins/huawei/mm-modem-helpers-huawei.c @@ -1033,3 +1033,154 @@ mm_huawei_parse_syscfgex_response (const gchar *response, g_strfreev (split); return NULL; } + +/*****************************************************************************/ +/* ^NWTIME response parser */ + +gboolean mm_huawei_parse_nwtime_response (const gchar *response, + gchar **iso8601p, + MMNetworkTimezone **tzp, + GError **error) +{ + GRegex *r; + GMatchInfo *match_info = NULL; + GError *match_error = NULL; + guint year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0, dt = 0; + gint tz = 0; + gboolean ret = FALSE; + + g_assert (iso8601p || tzp); /* at least one */ + + r = g_regex_new ("\\^NWTIME:\\s*(\\d+)/(\\d+)/(\\d+),(\\d+):(\\d+):(\\d*)([\\-\\+\\d]+),(\\d+)$", 0, 0, NULL); + g_assert (r != NULL); + + if (!g_regex_match_full (r, response, -1, 0, 0, &match_info, &match_error)) { + if (match_error) { + g_propagate_error (error, match_error); + g_prefix_error (error, "Could not parse ^NWTIME results: "); + } else { + g_set_error_literal (error, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't match ^NWTIME reply"); + } + } else { + /* Remember that g_match_info_get_match_count() includes match #0 */ + g_assert (g_match_info_get_match_count (match_info) >= 9); + + if (mm_get_uint_from_match_info (match_info, 1, &year) && + mm_get_uint_from_match_info (match_info, 2, &month) && + mm_get_uint_from_match_info (match_info, 3, &day) && + mm_get_uint_from_match_info (match_info, 4, &hour) && + mm_get_uint_from_match_info (match_info, 5, &minute) && + mm_get_uint_from_match_info (match_info, 6, &second) && + mm_get_int_from_match_info (match_info, 7, &tz) && + mm_get_uint_from_match_info (match_info, 8, &dt)) { + /* adjust year */ + if (year < 100) + year += 2000; + /* + * tz = timezone offset in 15 minute intervals + * dt = daylight adjustment, 0 = none, 1 = 1 hour, 2 = 2 hours + * other values are marked reserved. + */ + if (iso8601p) { + /* Return ISO-8601 format date/time string */ + *iso8601p = mm_new_iso8601_time (year, month, day, hour, + minute, second, + TRUE, (tz * 15) + (dt * 60)); + } + if (tzp) { + *tzp = mm_network_timezone_new (); + mm_network_timezone_set_offset (*tzp, tz * 15); + mm_network_timezone_set_dst_offset (*tzp, dt * 60); + } + + ret = TRUE; + } else { + g_set_error_literal (error, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Failed to parse ^NWTIME reply"); + } + } + + if (match_info) + g_match_info_free (match_info); + g_regex_unref (r); + + return ret; +} + +/*****************************************************************************/ +/* ^TIME response parser */ + +gboolean mm_huawei_parse_time_response (const gchar *response, + gchar **iso8601p, + MMNetworkTimezone **tzp, + GError **error) +{ + GRegex *r; + GMatchInfo *match_info = NULL; + GError *match_error = NULL; + guint year, month, day, hour, minute, second; + gboolean ret = FALSE; + + g_assert (iso8601p || tzp); /* at least one */ + + /* TIME response cannot ever provide TZ info */ + if (tzp) { + g_set_error_literal (error, + MM_CORE_ERROR, + MM_CORE_ERROR_UNSUPPORTED, + "^TIME does not provide timezone information"); + return FALSE; + } + + /* Already in ISO-8601 format, but verify just to be sure */ + r = g_regex_new ("\\^TIME:\\s*(\\d+)/(\\d+)/(\\d+)\\s*(\\d+):(\\d+):(\\d*)$", 0, 0, NULL); + g_assert (r != NULL); + + if (!g_regex_match_full (r, response, -1, 0, 0, &match_info, &match_error)) { + if (match_error) { + g_propagate_error (error, match_error); + g_prefix_error (error, "Could not parse ^TIME results: "); + } else { + g_set_error_literal (error, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't match ^TIME reply"); + } + } else { + /* Remember that g_match_info_get_match_count() includes match #0 */ + g_assert (g_match_info_get_match_count (match_info) >= 7); + + if (mm_get_uint_from_match_info (match_info, 1, &year) && + mm_get_uint_from_match_info (match_info, 2, &month) && + mm_get_uint_from_match_info (match_info, 3, &day) && + mm_get_uint_from_match_info (match_info, 4, &hour) && + mm_get_uint_from_match_info (match_info, 5, &minute) && + mm_get_uint_from_match_info (match_info, 6, &second)) { + /* adjust year */ + if (year < 100) + year += 2000; + /* Return ISO-8601 format date/time string */ + if (iso8601p) + *iso8601p = mm_new_iso8601_time (year, month, day, hour, + minute, second, FALSE, 0); + ret = TRUE; + } else { + g_set_error_literal (error, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Failed to parse ^TIME reply"); + } + } + + if (match_info) + g_match_info_free (match_info); + g_regex_unref (r); + + return ret; +} + diff --git a/plugins/huawei/mm-modem-helpers-huawei.h b/plugins/huawei/mm-modem-helpers-huawei.h index 05894f34..e225e908 100644 --- a/plugins/huawei/mm-modem-helpers-huawei.h +++ b/plugins/huawei/mm-modem-helpers-huawei.h @@ -113,4 +113,20 @@ const MMHuaweiSyscfgexCombination *mm_huawei_parse_syscfgex_response (const gcha const GArray *supported_mode_combinations, GError **error); +/*****************************************************************************/ +/* ^NWTIME response parser */ + +gboolean mm_huawei_parse_nwtime_response (const gchar *response, + gchar **iso8601p, + MMNetworkTimezone **tzp, + GError **error); + +/*****************************************************************************/ +/* ^TIME response parser */ + +gboolean mm_huawei_parse_time_response (const gchar *response, + gchar **iso8601p, + MMNetworkTimezone **tzp, + GError **error); + #endif /* MM_MODEM_HELPERS_HUAWEI_H */ diff --git a/plugins/huawei/tests/test-modem-helpers-huawei.c b/plugins/huawei/tests/test-modem-helpers-huawei.c index b74821fb..9e92eee2 100644 --- a/plugins/huawei/tests/test-modem-helpers-huawei.c +++ b/plugins/huawei/tests/test-modem-helpers-huawei.c @@ -1002,6 +1002,138 @@ test_syscfgex_response (void) } /*****************************************************************************/ +/* Test ^NWTIME responses */ + +typedef struct { + const gchar *str; + gboolean ret; + gboolean test_iso8601; + gboolean test_tz; + gchar *iso8601; + gint32 offset; + gint32 dst_offset; + gint32 leap_seconds; +} NwtimeTest; + +#define NWT_UNKNOWN MM_NETWORK_TIMEZONE_LEAP_SECONDS_UNKNOWN + +static const NwtimeTest nwtime_tests[] = { + { "^NWTIME: 14/08/05,04:00:21+40,00", TRUE, TRUE, FALSE, + "2014-08-05T04:00:21+10:00", 600, 0, NWT_UNKNOWN }, + { "^NWTIME: 14/08/05,04:00:21+40,00", TRUE, FALSE, TRUE, + "2014-08-05T04:00:21+10:00", 600, 0, NWT_UNKNOWN }, + { "^NWTIME: 14/08/05,04:00:21+40,00", TRUE, TRUE, TRUE, + "2014-08-05T04:00:21+10:00", 600, 0, NWT_UNKNOWN }, + + { "^NWTIME: 14/08/05,04:00:21+20,00", TRUE, TRUE, FALSE, + "2014-08-05T04:00:21+05:00", 300, 0, NWT_UNKNOWN }, + { "^NWTIME: 14/08/05,04:00:21+20,00", TRUE, FALSE, TRUE, + "2014-08-05T04:00:21+05:00", 300, 0, NWT_UNKNOWN }, + { "^NWTIME: 14/08/05,04:00:21+20,00", TRUE, TRUE, TRUE, + "2014-08-05T04:00:21+05:00", 300, 0, NWT_UNKNOWN }, + + { "^NWTIME: 14/08/05,04:00:21+40,01", TRUE, TRUE, FALSE, + "2014-08-05T04:00:21+11:00", 600, 60, NWT_UNKNOWN }, + { "^NWTIME: 14/08/05,04:00:21+40,01", TRUE, FALSE, TRUE, + "2014-08-05T04:00:21+11:00", 600, 60, NWT_UNKNOWN }, + { "^NWTIME: 14/08/05,04:00:21+40,01", TRUE, TRUE, TRUE, + "2014-08-05T04:00:21+11:00", 600, 60, NWT_UNKNOWN }, + + { "^NWTIME: 14/08/05,04:00:21+40,02", TRUE, TRUE, FALSE, + "2014-08-05T04:00:21+12:00", 600, 120, NWT_UNKNOWN }, + { "^NWTIME: 14/08/05,04:00:21+40,02", TRUE, FALSE, TRUE, + "2014-08-05T04:00:21+12:00", 600, 120, NWT_UNKNOWN }, + { "^NWTIME: 14/08/05,04:00:21+40,02", TRUE, TRUE, TRUE, + "2014-08-05T04:00:21+12:00", 600, 120, NWT_UNKNOWN }, + + { "^TIME: XX/XX/XX,XX:XX:XX+XX,XX", FALSE, TRUE, FALSE, + NULL, NWT_UNKNOWN, NWT_UNKNOWN, NWT_UNKNOWN }, + + { "^TIME: 14/08/05,04:00:21+40,00", FALSE, TRUE, FALSE, + NULL, NWT_UNKNOWN, NWT_UNKNOWN, NWT_UNKNOWN }, + + { NULL, FALSE, FALSE, FALSE, NULL, NWT_UNKNOWN, NWT_UNKNOWN, NWT_UNKNOWN } +}; + +static void +test_nwtime (void) +{ + guint i; + + for (i = 0; nwtime_tests[i].str; i++) { + GError *error = NULL; + gchar *iso8601 = NULL; + MMNetworkTimezone *tz = NULL; + gboolean ret; + + ret = mm_huawei_parse_nwtime_response (nwtime_tests[i].str, + nwtime_tests[i].test_iso8601 ? &iso8601 : NULL, + nwtime_tests[i].test_tz ? &tz : NULL, + &error); + + g_assert (ret == nwtime_tests[i].ret); + g_assert (ret == (error ? FALSE : TRUE)); + + g_clear_error (&error); + + if (nwtime_tests[i].test_iso8601) + g_assert_cmpstr (nwtime_tests[i].iso8601, ==, iso8601); + + if (nwtime_tests[i].test_tz) { + g_assert (nwtime_tests[i].offset == mm_network_timezone_get_offset (tz)); + g_assert (nwtime_tests[i].dst_offset == mm_network_timezone_get_dst_offset (tz)); + g_assert (nwtime_tests[i].leap_seconds == mm_network_timezone_get_leap_seconds (tz)); + } + + if (iso8601) + g_free (iso8601); + } +} + +/*****************************************************************************/ +/* Test ^TIME responses */ + +typedef struct { + const gchar *str; + gboolean ret; + gchar *iso8601; +} TimeTest; + +static const TimeTest time_tests[] = { + { "^TIME: 14/08/05 04:00:21", TRUE, "2014-08-05T04:00:21" }, + { "^TIME: 2014/08/05 04:00:21", TRUE, "2014-08-05T04:00:21" }, + { "^TIME: 14-08-05 04:00:21", FALSE, NULL }, + { "^TIME: 14-08-05,04:00:21", FALSE, NULL }, + { "^TIME: 14/08/05 04:00:21 AEST", FALSE, NULL }, + { NULL, FALSE, NULL } +}; + +static void +test_time (void) +{ + guint i; + + for (i = 0; time_tests[i].str; i++) { + GError *error = NULL; + gchar *iso8601 = NULL; + gboolean ret; + + ret = mm_huawei_parse_time_response (time_tests[i].str, + &iso8601, + NULL, + &error); + + g_assert (ret == time_tests[i].ret); + g_assert (ret == (error ? FALSE : TRUE)); + + g_assert_cmpstr (time_tests[i].iso8601, ==, iso8601); + + if (iso8601) + g_free (iso8601); + } +} + +/*****************************************************************************/ void _mm_log (const char *loc, @@ -1039,6 +1171,8 @@ int main (int argc, char **argv) g_test_add_func ("/MM/huawei/syscfg/response", test_syscfg_response); g_test_add_func ("/MM/huawei/syscfgex", test_syscfgex); g_test_add_func ("/MM/huawei/syscfgex/response", test_syscfgex_response); + g_test_add_func ("/MM/huawei/nwtime", test_nwtime); + g_test_add_func ("/MM/huawei/time", test_time); return g_test_run (); } |