broadband-modem: default implementation of the network time interface

Add a default implementation that queries the real-time clock using the
AT+CCLK? command.  Also set AT+CTZU=1 in case a modem requires it.
This commit is contained in:
Jason Simmons
2015-03-13 15:30:58 -07:00
committed by Aleksander Morgado
parent a92566ec0e
commit 3ad64c8f5a
4 changed files with 253 additions and 0 deletions

View File

@@ -7510,6 +7510,96 @@ enable_location_gathering (MMIfaceModemLocation *self,
g_object_unref (result); g_object_unref (result);
} }
/*****************************************************************************/
/* Load network time (Time interface) */
static gchar *
modem_time_load_network_time_finish (MMIfaceModemTime *self,
GAsyncResult *res,
GError **error)
{
const gchar *response;
gchar *result = NULL;
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
if (response)
mm_parse_cclk_response (response, &result, NULL, error);
return result;
}
static void
modem_time_load_network_time (MMIfaceModemTime *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CCLK?",
3,
FALSE,
callback,
user_data);
}
/*****************************************************************************/
/* Load network timezone (Time interface) */
static MMNetworkTimezone *
modem_time_load_network_timezone_finish (MMIfaceModemTime *self,
GAsyncResult *res,
GError **error)
{
const gchar *response;
MMNetworkTimezone *tz = NULL;
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, NULL);
if (response)
mm_parse_cclk_response (response, NULL, &tz, error);
return tz;
}
static void
modem_time_load_network_timezone (MMIfaceModemTime *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CCLK?",
3,
FALSE,
callback,
user_data);
}
/*****************************************************************************/
/* Check support (Time interface) */
static const MMBaseModemAtCommand time_check_sequence[] = {
{ "+CTZU=1", 3, TRUE, mm_base_modem_response_processor_no_result_continue },
{ "+CCLK?", 3, TRUE, mm_base_modem_response_processor_string },
{ NULL }
};
static gboolean
modem_time_check_support_finish (MMIfaceModemTime *self,
GAsyncResult *res,
GError **error)
{
return !!mm_base_modem_at_sequence_finish (MM_BASE_MODEM (self), res, NULL, error);
}
static void
modem_time_check_support (MMIfaceModemTime *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
mm_base_modem_at_sequence (MM_BASE_MODEM (self),
time_check_sequence,
NULL, /* response_processor_context */
NULL, /* response_processor_context_free */
callback,
user_data);
}
/*****************************************************************************/ /*****************************************************************************/
static const gchar *primary_init_sequence[] = { static const gchar *primary_init_sequence[] = {
@@ -9824,6 +9914,12 @@ iface_modem_messaging_init (MMIfaceModemMessaging *iface)
static void static void
iface_modem_time_init (MMIfaceModemTime *iface) 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_finish = modem_time_load_network_time_finish;
iface->load_network_timezone = modem_time_load_network_timezone;
iface->load_network_timezone_finish = modem_time_load_network_timezone_finish;
} }
static void static void

View File

@@ -2621,3 +2621,78 @@ mm_parse_gsn (const char *gsn,
return success; return success;
} }
/*****************************************************************************/
/* +CCLK response parser */
gboolean
mm_parse_cclk_response (const char *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;
gint tz = 0;
gboolean ret = FALSE;
g_assert (iso8601p || tzp); /* at least one */
/* Sample reply: +CCLK: "15/03/05,14:14:26-32" */
r = g_regex_new ("[+]CCLK: \"(\\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 +CCLK results: ");
} else {
g_set_error_literal (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Couldn't match +CCLK reply");
}
} else {
/* Remember that g_match_info_get_match_count() includes match #0 */
g_assert (g_match_info_get_match_count (match_info) >= 8);
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)) {
/* adjust year */
year += 2000;
/*
* tz = timezone offset in 15 minute intervals
*/
if (iso8601p) {
/* Return ISO-8601 format date/time string */
*iso8601p = mm_new_iso8601_time (year, month, day, hour,
minute, second,
TRUE, (tz * 15));
}
if (tzp) {
*tzp = mm_network_timezone_new ();
mm_network_timezone_set_offset (*tzp, tz * 15);
}
ret = TRUE;
} else {
g_set_error_literal (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Failed to parse +CCLK reply");
}
}
if (match_info)
g_match_info_free (match_info);
g_regex_unref (r);
return ret;
}

View File

@@ -20,6 +20,9 @@
#include <ModemManager.h> #include <ModemManager.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "glib-object.h" #include "glib-object.h"
#include "mm-charsets.h" #include "mm-charsets.h"
@@ -246,4 +249,10 @@ gboolean mm_parse_gsn (const char *gsn,
gchar **out_meid, gchar **out_meid,
gchar **out_esn); gchar **out_esn);
/* +CCLK response parser */
gboolean mm_parse_cclk_response (const gchar *response,
gchar **iso8601p,
MMNetworkTimezone **tzp,
GError **error);
#endif /* MM_MODEM_HELPERS_H */ #endif /* MM_MODEM_HELPERS_H */

View File

@@ -2340,6 +2340,77 @@ test_supported_capability_filter (void *f, gpointer d)
g_array_unref (combinations); g_array_unref (combinations);
} }
/*****************************************************************************/
/* Test +CCLK responses */
typedef struct {
const gchar *str;
gboolean ret;
gboolean test_iso8601;
gboolean test_tz;
gchar *iso8601;
gint32 offset;
} CclkTest;
static const CclkTest cclk_tests[] = {
{ "+CCLK: \"14/08/05,04:00:21+40\"", TRUE, TRUE, FALSE,
"2014-08-05T04:00:21+10:00", 600 },
{ "+CCLK: \"14/08/05,04:00:21+40\"", TRUE, FALSE, TRUE,
"2014-08-05T04:00:21+10:00", 600 },
{ "+CCLK: \"14/08/05,04:00:21+40\"", TRUE, TRUE, TRUE,
"2014-08-05T04:00:21+10:00", 600 },
{ "+CCLK: \"15/02/28,20:30:40-32\"", TRUE, TRUE, FALSE,
"2015-02-28T20:30:40-08:00", -480 },
{ "+CCLK: \"15/02/28,20:30:40-32\"", TRUE, FALSE, TRUE,
"2015-02-28T20:30:40-08:00", -480 },
{ "+CCLK: \"15/02/28,20:30:40-32\"", TRUE, TRUE, TRUE,
"2015-02-28T20:30:40-08:00", -480 },
{ "+CCLK: \"XX/XX/XX,XX:XX:XX+XX\"", FALSE, TRUE, FALSE,
NULL, MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN },
{ NULL, FALSE, FALSE, FALSE, NULL, MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN }
};
static void
test_cclk_response (void)
{
guint i;
for (i = 0; cclk_tests[i].str; i++) {
GError *error = NULL;
gchar *iso8601 = NULL;
MMNetworkTimezone *tz = NULL;
gboolean ret;
ret = mm_parse_cclk_response (cclk_tests[i].str,
cclk_tests[i].test_iso8601 ? &iso8601 : NULL,
cclk_tests[i].test_tz ? &tz : NULL,
&error);
g_assert (ret == cclk_tests[i].ret);
g_assert (ret == (error ? FALSE : TRUE));
g_clear_error (&error);
if (cclk_tests[i].test_iso8601)
g_assert_cmpstr (cclk_tests[i].iso8601, ==, iso8601);
if (cclk_tests[i].test_tz) {
g_assert (mm_network_timezone_get_offset (tz) == cclk_tests[i].offset);
g_assert (mm_network_timezone_get_dst_offset (tz) == MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN);
g_assert (mm_network_timezone_get_leap_seconds (tz) == MM_NETWORK_TIMEZONE_LEAP_SECONDS_UNKNOWN);
}
if (iso8601)
g_free (iso8601);
if (tz)
g_object_unref (tz);
}
}
/*****************************************************************************/ /*****************************************************************************/
void void
@@ -2501,6 +2572,8 @@ int main (int argc, char **argv)
g_test_suite_add (suite, TESTCASE (test_supported_capability_filter, NULL)); g_test_suite_add (suite, TESTCASE (test_supported_capability_filter, NULL));
g_test_suite_add (suite, TESTCASE (test_cclk_response, NULL));
result = g_test_run (); result = g_test_run ();
reg_test_data_free (reg_data); reg_test_data_free (reg_data);