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:

committed by
Aleksander Morgado

parent
a92566ec0e
commit
3ad64c8f5a
@@ -7510,6 +7510,96 @@ enable_location_gathering (MMIfaceModemLocation *self,
|
||||
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[] = {
|
||||
@@ -9824,6 +9914,12 @@ iface_modem_messaging_init (MMIfaceModemMessaging *iface)
|
||||
static void
|
||||
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
|
||||
|
@@ -2621,3 +2621,78 @@ mm_parse_gsn (const char *gsn,
|
||||
|
||||
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;
|
||||
}
|
||||
|
@@ -20,6 +20,9 @@
|
||||
|
||||
#include <ModemManager.h>
|
||||
|
||||
#define _LIBMM_INSIDE_MM
|
||||
#include <libmm-glib.h>
|
||||
|
||||
#include "glib-object.h"
|
||||
#include "mm-charsets.h"
|
||||
|
||||
@@ -246,4 +249,10 @@ gboolean mm_parse_gsn (const char *gsn,
|
||||
gchar **out_meid,
|
||||
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 */
|
||||
|
@@ -2340,6 +2340,77 @@ test_supported_capability_filter (void *f, gpointer d)
|
||||
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
|
||||
@@ -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_cclk_response, NULL));
|
||||
|
||||
result = g_test_run ();
|
||||
|
||||
reg_test_data_free (reg_data);
|
||||
|
Reference in New Issue
Block a user