<div dir="ltr"><div>Moved this into the base broadband modem object.  Please take another look.</div><div><br></div><div><br></div><div>Add support for querying the real-time clock to the Telit plugin.</div><div>Tested with a Telit HE910 modem.</div><div>---</div><div> src/mm-broadband-modem.c       | 96 ++++++++++++++++++++++++++++++++++++++++++</div><div> src/mm-modem-helpers.c         | 75 +++++++++++++++++++++++++++++++++</div><div> src/mm-modem-helpers.h         |  9 ++++</div><div> src/tests/test-modem-helpers.c | 73 ++++++++++++++++++++++++++++++++</div><div> 4 files changed, 253 insertions(+)</div><div><br></div><div>diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c</div><div>index 17c128c..feebbc6 100644</div><div>--- a/src/mm-broadband-modem.c</div><div>+++ b/src/mm-broadband-modem.c</div><div>@@ -7511,6 +7511,96 @@ enable_location_gathering (MMIfaceModemLocation *self,</div><div> }</div><div> </div><div> /*****************************************************************************/</div><div>+/* Load network time (Time interface) */</div><div>+</div><div>+static gchar *</div><div>+modem_time_load_network_time_finish (MMIfaceModemTime *self,</div><div>+                                     GAsyncResult *res,</div><div>+                                     GError **error)</div><div>+{</div><div>+    const gchar *response;</div><div>+    gchar *result = NULL;</div><div>+</div><div>+    response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);</div><div>+    if (response)</div><div>+        mm_parse_cclk_response (response, &result, NULL, error);</div><div>+    return result;</div><div>+}</div><div>+</div><div>+static void</div><div>+modem_time_load_network_time (MMIfaceModemTime *self,</div><div>+                              GAsyncReadyCallback callback,</div><div>+                              gpointer user_data)</div><div>+{</div><div>+    mm_base_modem_at_command (MM_BASE_MODEM (self),</div><div>+                              "+CCLK?",</div><div>+                              3,</div><div>+                              FALSE,</div><div>+                              callback,</div><div>+                              user_data);</div><div>+}</div><div>+</div><div>+/*****************************************************************************/</div><div>+/* Load network timezone (Time interface) */</div><div>+</div><div>+static MMNetworkTimezone *</div><div>+modem_time_load_network_timezone_finish (MMIfaceModemTime *self,</div><div>+                                         GAsyncResult *res,</div><div>+                                         GError **error)</div><div>+{</div><div>+    const gchar *response;</div><div>+    MMNetworkTimezone *tz = NULL;</div><div>+</div><div>+    response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, NULL);</div><div>+    if (response)</div><div>+        mm_parse_cclk_response (response, NULL, &tz, error);</div><div>+    return tz;</div><div>+}</div><div>+</div><div>+static void</div><div>+modem_time_load_network_timezone (MMIfaceModemTime *self,</div><div>+                                  GAsyncReadyCallback callback,</div><div>+                                  gpointer user_data)</div><div>+{</div><div>+    mm_base_modem_at_command (MM_BASE_MODEM (self),</div><div>+                              "+CCLK?",</div><div>+                              3,</div><div>+                              FALSE,</div><div>+                              callback,</div><div>+                              user_data);</div><div>+}</div><div>+</div><div>+/*****************************************************************************/</div><div>+/* Check support (Time interface) */</div><div>+</div><div>+static const MMBaseModemAtCommand time_check_sequence[] = {</div><div>+    { "+CTZU=1",  3, TRUE, mm_base_modem_response_processor_no_result_continue },</div><div>+    { "+CCLK?",   3, TRUE, mm_base_modem_response_processor_string },</div><div>+    { NULL }</div><div>+};</div><div>+</div><div>+static gboolean</div><div>+modem_time_check_support_finish (MMIfaceModemTime *self,</div><div>+                                 GAsyncResult *res,</div><div>+                                 GError **error)</div><div>+{</div><div>+    return !!mm_base_modem_at_sequence_finish (MM_BASE_MODEM (self), res, NULL, error);</div><div>+}</div><div>+</div><div>+static void</div><div>+modem_time_check_support (MMIfaceModemTime *self,</div><div>+                          GAsyncReadyCallback callback,</div><div>+                          gpointer user_data)</div><div>+{</div><div>+    mm_base_modem_at_sequence (MM_BASE_MODEM (self),</div><div>+                               time_check_sequence,</div><div>+                               NULL, /* response_processor_context */</div><div>+                               NULL, /* response_processor_context_free */</div><div>+                               callback,</div><div>+                               user_data);</div><div>+}</div><div>+</div><div>+/*****************************************************************************/</div><div> </div><div> static const gchar *primary_init_sequence[] = {</div><div>     /* Ensure echo is off */</div><div>@@ -9824,6 +9914,12 @@ iface_modem_messaging_init (MMIfaceModemMessaging *iface)</div><div> static void</div><div> iface_modem_time_init (MMIfaceModemTime *iface)</div><div> {</div><div>+    iface->check_support = modem_time_check_support;</div><div>+    iface->check_support_finish = modem_time_check_support_finish;</div><div>+    iface->load_network_time = modem_time_load_network_time;</div><div>+    iface->load_network_time_finish = modem_time_load_network_time_finish;</div><div>+    iface->load_network_timezone = modem_time_load_network_timezone;</div><div>+    iface->load_network_timezone_finish = modem_time_load_network_timezone_finish;</div><div> }</div><div> </div><div> static void</div><div>diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c</div><div>index d40082d..9f0feea 100644</div><div>--- a/src/mm-modem-helpers.c</div><div>+++ b/src/mm-modem-helpers.c</div><div>@@ -2621,3 +2621,78 @@ mm_parse_gsn (const char *gsn,</div><div> </div><div>     return success;</div><div> }</div><div>+</div><div>+/*****************************************************************************/</div><div>+/* +CCLK response parser */</div><div>+</div><div>+gboolean</div><div>+mm_parse_cclk_response (const char *response,</div><div>+                        gchar **iso8601p,</div><div>+                        MMNetworkTimezone **tzp,</div><div>+                        GError **error)</div><div>+{</div><div>+    GRegex *r;</div><div>+    GMatchInfo *match_info = NULL;</div><div>+    GError *match_error = NULL;</div><div>+    guint year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;</div><div>+    gint tz = 0;</div><div>+    gboolean ret = FALSE;</div><div>+</div><div>+    g_assert (iso8601p || tzp); /* at least one */</div><div>+</div><div>+    /* Sample reply: +CCLK: "15/03/05,14:14:26-32" */</div><div>+    r = g_regex_new ("[+]CCLK: \"(\\d+)/(\\d+)/(\\d+),(\\d+):(\\d+):(\\d+)([-+]\\d+)\"", 0, 0, NULL);</div><div>+    g_assert (r != NULL);</div><div>+</div><div>+    if (!g_regex_match_full (r, response, -1, 0, 0, &match_info, &match_error)) {</div><div>+        if (match_error) {</div><div>+            g_propagate_error (error, match_error);</div><div>+            g_prefix_error (error, "Could not parse +CCLK results: ");</div><div>+        } else {</div><div>+            g_set_error_literal (error,</div><div>+                                 MM_CORE_ERROR,</div><div>+                                 MM_CORE_ERROR_FAILED,</div><div>+                                 "Couldn't match +CCLK reply");</div><div>+        }</div><div>+    } else {</div><div>+        /* Remember that g_match_info_get_match_count() includes match #0 */</div><div>+        g_assert (g_match_info_get_match_count (match_info) >= 8);</div><div>+</div><div>+        if (mm_get_uint_from_match_info (match_info, 1, &year) &&</div><div>+            mm_get_uint_from_match_info (match_info, 2, &month) &&</div><div>+            mm_get_uint_from_match_info (match_info, 3, &day) &&</div><div>+            mm_get_uint_from_match_info (match_info, 4, &hour) &&</div><div>+            mm_get_uint_from_match_info (match_info, 5, &minute) &&</div><div>+            mm_get_uint_from_match_info (match_info, 6, &second) &&</div><div>+            mm_get_int_from_match_info  (match_info, 7, &tz)) {</div><div>+            /* adjust year */</div><div>+            year += 2000;</div><div>+            /*</div><div>+             * tz = timezone offset in 15 minute intervals</div><div>+             */</div><div>+            if (iso8601p) {</div><div>+                /* Return ISO-8601 format date/time string */</div><div>+                *iso8601p = mm_new_iso8601_time (year, month, day, hour,</div><div>+                                                 minute, second,</div><div>+                                                 TRUE, (tz * 15));</div><div>+            }</div><div>+            if (tzp) {</div><div>+                *tzp = mm_network_timezone_new ();</div><div>+                mm_network_timezone_set_offset (*tzp, tz * 15);</div><div>+            }</div><div>+</div><div>+            ret = TRUE;</div><div>+        } else {</div><div>+            g_set_error_literal (error,</div><div>+                                 MM_CORE_ERROR,</div><div>+                                 MM_CORE_ERROR_FAILED,</div><div>+                                 "Failed to parse +CCLK reply");</div><div>+        }</div><div>+    }</div><div>+</div><div>+    if (match_info)</div><div>+        g_match_info_free (match_info);</div><div>+    g_regex_unref (r);</div><div>+</div><div>+    return ret;</div><div>+}</div><div>diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h</div><div>index 0ec59af..2e5d8e4 100644</div><div>--- a/src/mm-modem-helpers.h</div><div>+++ b/src/mm-modem-helpers.h</div><div>@@ -20,6 +20,9 @@</div><div> </div><div> #include <ModemManager.h></div><div> </div><div>+#define _LIBMM_INSIDE_MM</div><div>+#include <libmm-glib.h></div><div>+</div><div> #include "glib-object.h"</div><div> #include "mm-charsets.h"</div><div> </div><div>@@ -246,4 +249,10 @@ gboolean mm_parse_gsn (const char *gsn,</div><div>                        gchar **out_meid,</div><div>                        gchar **out_esn);</div><div> </div><div>+/* +CCLK response parser */</div><div>+gboolean mm_parse_cclk_response (const gchar *response,</div><div>+                                 gchar **iso8601p,</div><div>+                                 MMNetworkTimezone **tzp,</div><div>+                                 GError **error);</div><div>+</div><div> #endif  /* MM_MODEM_HELPERS_H */</div><div>diff --git a/src/tests/test-modem-helpers.c b/src/tests/test-modem-helpers.c</div><div>index b493e6b..9fd2501 100644</div><div>--- a/src/tests/test-modem-helpers.c</div><div>+++ b/src/tests/test-modem-helpers.c</div><div>@@ -2341,6 +2341,77 @@ test_supported_capability_filter (void *f, gpointer d)</div><div> }</div><div> </div><div> /*****************************************************************************/</div><div>+/* Test +CCLK responses */</div><div>+</div><div>+typedef struct {</div><div>+    const gchar *str;</div><div>+    gboolean ret;</div><div>+    gboolean test_iso8601;</div><div>+    gboolean test_tz;</div><div>+    gchar *iso8601;</div><div>+    gint32 offset;</div><div>+} CclkTest;</div><div>+</div><div>+static const CclkTest cclk_tests[] = {</div><div>+    { "+CCLK: \"14/08/05,04:00:21+40\"", TRUE, TRUE, FALSE,</div><div>+        "2014-08-05T04:00:21+10:00", 600 },</div><div>+    { "+CCLK: \"14/08/05,04:00:21+40\"", TRUE, FALSE, TRUE,</div><div>+        "2014-08-05T04:00:21+10:00", 600 },</div><div>+    { "+CCLK: \"14/08/05,04:00:21+40\"", TRUE, TRUE, TRUE,</div><div>+        "2014-08-05T04:00:21+10:00", 600 },</div><div>+</div><div>+    { "+CCLK: \"15/02/28,20:30:40-32\"", TRUE, TRUE, FALSE,</div><div>+        "2015-02-28T20:30:40-08:00", -480 },</div><div>+    { "+CCLK: \"15/02/28,20:30:40-32\"", TRUE, FALSE, TRUE,</div><div>+        "2015-02-28T20:30:40-08:00", -480 },</div><div>+    { "+CCLK: \"15/02/28,20:30:40-32\"", TRUE, TRUE, TRUE,</div><div>+        "2015-02-28T20:30:40-08:00", -480 },</div><div>+</div><div>+    { "+CCLK: \"XX/XX/XX,XX:XX:XX+XX\"", FALSE, TRUE, FALSE,</div><div>+        NULL, MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN },</div><div>+</div><div>+    { NULL, FALSE, FALSE, FALSE, NULL, MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN }</div><div>+};</div><div>+</div><div>+static void</div><div>+test_cclk_response (void)</div><div>+{</div><div>+    guint i;</div><div>+</div><div>+    for (i = 0; cclk_tests[i].str; i++) {</div><div>+        GError *error = NULL;</div><div>+        gchar *iso8601 = NULL;</div><div>+        MMNetworkTimezone *tz = NULL;</div><div>+        gboolean ret;</div><div>+</div><div>+        ret = mm_parse_cclk_response (cclk_tests[i].str,</div><div>+                                      cclk_tests[i].test_iso8601 ? &iso8601 : NULL,</div><div>+                                      cclk_tests[i].test_tz ? &tz : NULL,</div><div>+                                      &error);</div><div>+</div><div>+        g_assert (ret == cclk_tests[i].ret);</div><div>+        g_assert (ret == (error ? FALSE : TRUE));</div><div>+</div><div>+        g_clear_error (&error);</div><div>+</div><div>+        if (cclk_tests[i].test_iso8601)</div><div>+            g_assert_cmpstr (cclk_tests[i].iso8601, ==, iso8601);</div><div>+</div><div>+        if (cclk_tests[i].test_tz) {</div><div>+            g_assert (mm_network_timezone_get_offset (tz) == cclk_tests[i].offset);</div><div>+            g_assert (mm_network_timezone_get_dst_offset (tz) == MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN);</div><div>+            g_assert (mm_network_timezone_get_leap_seconds (tz) == MM_NETWORK_TIMEZONE_LEAP_SECONDS_UNKNOWN);</div><div>+        }</div><div>+</div><div>+        if (iso8601)</div><div>+            g_free (iso8601);</div><div>+</div><div>+        if (tz)</div><div>+            g_object_unref (tz);</div><div>+    }</div><div>+}</div><div>+</div><div>+/*****************************************************************************/</div><div> </div><div> void</div><div> _mm_log (const char *loc,</div><div>@@ -2501,6 +2572,8 @@ int main (int argc, char **argv)</div><div> </div><div>     g_test_suite_add (suite, TESTCASE (test_supported_capability_filter, NULL));</div><div> </div><div>+    g_test_suite_add (suite, TESTCASE (test_cclk_response, NULL));</div><div>+</div><div>     result = g_test_run ();</div><div> </div><div>     reg_test_data_free (reg_data);</div><div>-- </div><div>2.2.0.rc0.207.ga3a616c</div><div><br></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Mar 12, 2015 at 11:42 AM, Aleksander Morgado <span dir="ltr"><<a href="mailto:aleksander@aleksander.es" target="_blank">aleksander@aleksander.es</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">On Thu, Mar 12, 2015 at 6:39 PM, Jason Simmons <<a href="mailto:jsimmons@chromium.org">jsimmons@chromium.org</a>> wrote:<br>
> The Telit modem supports AT+CTZU=1 but doesn't seem to require it.  When I<br>
> start up the system, the modem has CTZU=0 by default, but queries to<br>
> AT+CCLK? yield the correct time and time zone.<br>
><br>
> I'll try moving the MMIfaceModemTime implementation from this patch up to<br>
> src/mm-broadband-modem.c<br>
><br>
> Also, I don't have an environment for testing with anything besides the<br>
> Telit modem, so I might need some help verifying that a generic<br>
> implementation does not cause problems for other modems.<br>
<br>
</span>We'll take care of that, np. :)<br>
<span class="HOEnZb"><font color="#888888"><br>
--<br>
Aleksander<br>
<a href="https://aleksander.es" target="_blank">https://aleksander.es</a><br>
</font></span></blockquote></div><br></div>