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);
|
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
|
||||||
|
@@ -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;
|
||||||
|
}
|
||||||
|
@@ -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 */
|
||||||
|
@@ -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);
|
||||||
|
Reference in New Issue
Block a user