From: Forest Bond <[email protected]>

The modem is polled every 5 seconds (unless inhibited by PPP)
using AT!STATUS.
---
 drivers/sierramodem/cdma-netreg.c |  300 +++++++++++++++++++++++++++++++++++++
 1 files changed, 300 insertions(+), 0 deletions(-)

diff --git a/drivers/sierramodem/cdma-netreg.c 
b/drivers/sierramodem/cdma-netreg.c
index 109b245..4cca769 100644
--- a/drivers/sierramodem/cdma-netreg.c
+++ b/drivers/sierramodem/cdma-netreg.c
@@ -3,6 +3,9 @@
  *  oFono - Open Source Telephony
  *
  *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2008-2009  Novell, Inc.
+ *  Copyright (C) 2009-2012  Red Hat, Inc.
+ *  Copyright (C) 2012  Lanedo GmbH.
  *  Copyright (C) 2012  Outpost Embedded, LLC. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -27,6 +30,8 @@
 #define _GNU_SOURCE
 #include <glib.h>
 #include <errno.h>
+#include <ctype.h>
+#include <string.h>
 
 #include <ofono/log.h>
 #include <ofono/modem.h>
@@ -35,11 +40,300 @@
 #include "gatchat.h"
 
 #include "sierramodem.h"
+#include "eri.h"
 
 struct sierra_netreg_data {
        GAtChat *chat;
+       guint status_id;
+       guint status_source;
 };
 
+/* NOTE: STATUS parsing functions originated in ModemManager. */
+
+typedef enum {
+       SYS_MODE_UNKNOWN,
+       SYS_MODE_NO_SERVICE,
+       SYS_MODE_CDMA_1X,
+       SYS_MODE_EVDO_REV0,
+       SYS_MODE_EVDO_REVA
+} SysMode;
+
+#define MODEM_REG_TAG "Modem has registered"
+#define GENERIC_ROAM_TAG "Roaming:"
+#define ROAM_1X_TAG "1xRoam:"
+#define ROAM_EVDO_TAG "HDRRoam:"
+#define SYS_MODE_TAG "Sys Mode:"
+#define SYS_MODE_NO_SERVICE_TAG "NO SRV"
+#define SYS_MODE_EVDO_TAG "HDR"
+#define SYS_MODE_1X_TAG "1x"
+#define SYS_MODE_CDMA_TAG "CDMA"
+#define EVDO_REV_TAG "HDR Revision:"
+#define SID_TAG "SID:"
+
+static ofono_bool_t get_roam_value(const gchar *reply, const gchar *tag,
+                       ofono_bool_t is_eri, ofono_bool_t *out_roaming)
+{
+       gchar *p;
+       ofono_bool_t success;
+       guint32 ind = 0;
+
+       p = strstr(reply, tag);
+       if (!p)
+               return FALSE;
+
+       p += strlen(tag);
+       while (*p && isspace(*p))
+               p++;
+
+       /* Use generic ERI parsing if it's an ERI */
+       if (is_eri) {
+               success = sierramodem_parse_eri(p, out_roaming, &ind, NULL);
+               if (success) {
+                       /* Sierra redefines ERI 0, 1, and 2 */
+                       if (ind == 0)
+                               *out_roaming = FALSE;  /* home */
+                       else if (ind == 1 || ind == 2)
+                               *out_roaming = TRUE;   /* roaming */
+               }
+               return success;
+       }
+
+       /* If it's not an ERI, roaming is just true/false */
+       if (*p == '1') {
+               *out_roaming = TRUE;
+               return TRUE;
+       } else if (*p == '0') {
+               *out_roaming = FALSE;
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static ofono_bool_t sys_mode_has_service(SysMode mode)
+{
+       return (mode == SYS_MODE_CDMA_1X || mode == SYS_MODE_EVDO_REV0 ||
+                                               mode == SYS_MODE_EVDO_REVA);
+}
+
+static ofono_bool_t sys_mode_is_evdo(SysMode mode)
+{
+       return (mode == SYS_MODE_EVDO_REV0 || mode == SYS_MODE_EVDO_REVA);
+}
+
+static ofono_bool_t sys_mode_is_cdma(SysMode mode)
+{
+       return (mode == SYS_MODE_CDMA_1X);
+}
+
+static ofono_bool_t parse_status(GAtResult *result, gint *status)
+{
+       GAtResultIter iter;
+       const char *line;
+       ofono_bool_t registered = FALSE;
+       ofono_bool_t have_sid = FALSE;
+       SysMode evdo_mode = SYS_MODE_UNKNOWN;
+       SysMode sys_mode = SYS_MODE_UNKNOWN;
+       ofono_bool_t evdo_roam = FALSE, cdma1x_roam = FALSE;
+       int i;
+
+       /*
+        * Sierra CDMA parts have two general formats depending on whether they
+        * support EVDO or not.  EVDO parts report both 1x and EVDO roaming
+        * status while of course 1x parts only report 1x status.  Some modems
+        * also do not report the Roaming information (MP 555 GPS).
+        *
+        * AT!STATUS responses:
+        *
+        * Unregistered MC5725:
+        * -----------------------
+        * Current band: PCS CDMA
+        * Current channel: 350
+        * SID: 0  NID: 0  1xRoam: 0 HDRRoam: 0
+        * Temp: 33  State: 100  Sys Mode: NO SRV
+        * Pilot NOT acquired
+        * Modem has NOT registered
+        *
+        * Registered MC5725:
+        * -----------------------
+        * Current band: Cellular Sleep
+        * Current channel: 775
+        * SID: 30  NID: 2  1xRoam: 0 HDRRoam: 0
+        * Temp: 29  State: 200  Sys Mode: HDR
+        * Pilot acquired
+        * Modem has registered
+        * HDR Revision: A
+        *
+        * Unregistered AC580:
+        * -----------------------
+        * Current band: PCS CDMA
+        * Current channel: 350
+        * SID: 0 NID: 0  Roaming: 0
+        * Temp: 39  State: 100  Scan Mode: 0
+        * Pilot NOT acquired
+        * Modem has NOT registered
+        *
+        * Registered AC580:
+        * -----------------------
+        * Current band: Cellular Sleep
+        * Current channel: 548
+        * SID: 26  NID: 1  Roaming: 1
+        * Temp: 39  State: 200  Scan Mode: 0
+        * Pilot Acquired
+        * Modem has registered
+        */
+
+       /*
+        * We have to handle the two formats slightly differently; for newer
+        * formats with "Sys Mode", we consider the modem registered if the Sys
+        * Mode is not "NO SRV".  The explicit registration status is just
+        * icing on the cake.  For older formats (no "Sys Mode") we treat the
+        * modem as registered if the SID is non-zero.
+        */
+
+       g_at_result_iter_init(&iter, result);
+
+       for (i = 0; i < g_at_result_num_response_lines(result); i++) {
+               ofono_bool_t bool_val = FALSE;
+               char *p;
+
+               g_at_result_iter_next(&iter, NULL);
+
+               line = g_at_result_iter_raw_line(&iter);
+
+               if (!strncmp(line, MODEM_REG_TAG, strlen(MODEM_REG_TAG))) {
+                       registered = TRUE;
+                       continue;
+               }
+
+               /* Roaming */
+               get_roam_value(line, ROAM_1X_TAG, TRUE, &cdma1x_roam);
+               get_roam_value(line, ROAM_EVDO_TAG, TRUE, &evdo_roam);
+               if (get_roam_value(line, GENERIC_ROAM_TAG, FALSE, &bool_val))
+                       cdma1x_roam = evdo_roam = bool_val;
+
+               /* Current system mode */
+               p = strstr(line, SYS_MODE_TAG);
+               if (p) {
+                       p += strlen(SYS_MODE_TAG);
+                       while (*p && isspace(*p))
+                               p++;
+                       if (!strncmp(p, SYS_MODE_NO_SERVICE_TAG,
+                                       strlen(SYS_MODE_NO_SERVICE_TAG)))
+                               sys_mode = SYS_MODE_NO_SERVICE;
+                       else if (!strncmp(p, SYS_MODE_EVDO_TAG,
+                                               strlen(SYS_MODE_EVDO_TAG)))
+                               sys_mode = SYS_MODE_EVDO_REV0;
+                       else if (!strncmp(p, SYS_MODE_1X_TAG,
+                                               strlen(SYS_MODE_1X_TAG)) ||
+                                !strncmp(p, SYS_MODE_CDMA_TAG,
+                                               strlen(SYS_MODE_CDMA_TAG)))
+                               sys_mode = SYS_MODE_CDMA_1X;
+               }
+
+               /* Current EVDO revision if system mode is EVDO */
+               p = strstr(line, EVDO_REV_TAG);
+               if (p) {
+                       p += strlen(EVDO_REV_TAG);
+                       while (*p && isspace(*p))
+                               p++;
+                       if (*p == 'A')
+                               evdo_mode = SYS_MODE_EVDO_REVA;
+                       else if (*p == '0')
+                               evdo_mode = SYS_MODE_EVDO_REV0;
+               }
+
+               /* SID */
+               p = strstr(line, SID_TAG);
+               if (p) {
+                       p += strlen(SID_TAG);
+                       while (*p && isspace(*p))
+                               p++;
+                       if (isdigit(*p) && (*p != '0'))
+                               have_sid = TRUE;
+               }
+       }
+
+       /* Update current system mode */
+       if (sys_mode_is_evdo(sys_mode)) {
+               /* Prefer the explicit EVDO mode from EVDO_REV_TAG */
+               if (evdo_mode != SYS_MODE_UNKNOWN)
+                       sys_mode = evdo_mode;
+       }
+
+       /*
+        * If the modem didn't report explicit registration with "Modem has
+        * registered" then get registration status by looking at either system
+        * mode or (for older devices that don't report that) just the SID.
+        */
+       if (!registered) {
+               if (sys_mode != SYS_MODE_UNKNOWN)
+                       registered = sys_mode_has_service(sys_mode);
+               else
+                       registered = have_sid;
+       }
+
+       if (registered) {
+               ofono_bool_t roaming = FALSE;
+
+               if (sys_mode_is_evdo(sys_mode))
+                       roaming = evdo_roam;
+               else if (sys_mode_is_cdma(sys_mode))
+                       roaming = cdma1x_roam;
+
+               if (roaming)
+                       *status = CDMA_NETWORK_REGISTRATION_STATUS_ROAMING;
+               else
+                       *status = CDMA_NETWORK_REGISTRATION_STATUS_REGISTERED;
+       } else {
+               *status = CDMA_NETWORK_REGISTRATION_STATUS_NOT_REGISTERED;
+       }
+
+       return TRUE;
+}
+
+static void status_cb(ofono_bool_t ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_cdma_netreg *netreg = user_data;
+       int status;
+
+       if (!ok)
+               return;
+
+       if (parse_status(result, &status) == FALSE) {
+               ofono_error("Invalid STATUS values");
+               return;
+       }
+
+       ofono_cdma_netreg_status_notify(netreg, status);
+}
+
+static void read_status(struct ofono_cdma_netreg *netreg, GAtChat *chat)
+{
+       struct sierra_netreg_data *data = ofono_cdma_netreg_get_data(netreg);
+
+       /*
+        * Cancel our last request, which shouldn't have taken this long to
+        * complete anyway.  If it did, PPP is probably running and AT chat
+        * is suspended.  In any case, we only care about the latest info.
+        */
+       if (data->status_id > 0)
+               g_at_chat_cancel(chat, data->status_id);
+
+       data->status_id = g_at_chat_send(chat, "AT!STATUS", NULL, status_cb,
+                                                               netreg, NULL);
+}
+
+static ofono_bool_t read_status_cb(gpointer user_data)
+{
+       struct ofono_cdma_netreg *netreg = user_data;
+       struct sierra_netreg_data *data = ofono_cdma_netreg_get_data(netreg);
+
+       read_status(netreg, data->chat);
+
+       return TRUE;
+}
+
 static int sierra_cdma_netreg_probe(struct ofono_cdma_netreg *netreg,
                                unsigned int vendor, void *chat)
 {
@@ -52,6 +346,9 @@ static int sierra_cdma_netreg_probe(struct ofono_cdma_netreg 
*netreg,
        ofono_cdma_netreg_set_data(netreg, data);
 
        data->chat = g_at_chat_clone(chat);
+       data->status_source = g_timeout_add_seconds(5, read_status_cb, netreg);
+
+       read_status(netreg, data->chat);
 
        ofono_cdma_netreg_register(netreg);
 
@@ -66,6 +363,9 @@ static void sierra_cdma_netreg_remove(struct 
ofono_cdma_netreg *netreg)
 
        g_at_chat_unref(data->chat);
        data->chat = NULL;
+
+       g_source_remove(data->status_source);
+       data->status_source = 0;
 }
 
 static struct ofono_cdma_netreg_driver driver = {
-- 
1.7.0.4
_______________________________________________
ofono mailing list
[email protected]
http://lists.ofono.org/listinfo/ofono

Reply via email to