diff options
-rw-r--r-- | introspection/mm-gsm-modem.xml | 56 | ||||
-rw-r--r-- | src/mm-generic-gsm.c | 226 | ||||
-rw-r--r-- | src/mm-gsm-modem.c | 60 | ||||
-rw-r--r-- | src/mm-gsm-modem.h | 24 |
4 files changed, 341 insertions, 25 deletions
diff --git a/introspection/mm-gsm-modem.xml b/introspection/mm-gsm-modem.xml index 1cc0cd40..021e8ad8 100644 --- a/introspection/mm-gsm-modem.xml +++ b/introspection/mm-gsm-modem.xml @@ -116,6 +116,29 @@ </arg> </method> + <method name="GetRegistrationInfo"> + <tp:docstring> + Get the registration status and the current operator (if registered). + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_get_reg_info"/> + <arg name="status" type="u" direction="out" tp:type="MM_GSM_MODEM_REG_STATUS"> + <tp:docstring> + The current network mode. + </tp:docstring> + </arg> + <arg name="operator_code" type="s" direction="out"> + <tp:docstring> + The current operator code. + </tp:docstring> + </arg> + <arg name="operator_name" type="s" direction="out"> + <tp:docstring> + The current operator name. + </tp:docstring> + </arg> + </method> + <signal name="SignalQuality"> <tp:docstring> The signal quality changed. @@ -239,5 +262,38 @@ </tp:enumvalue> </tp:enum> + <tp:enum name="MM_GSM_MODEM_REG_STATUS" type="u"> + <tp:enumvalue suffix="IDLE" value="0"> + <tp:docstring> + Not registered, not searching for new operator to register. + </tp:docstring> + </tp:enumvalue> + <tp:enumvalue suffix="HOME" value="1"> + <tp:docstring> + Registered on home network. + </tp:docstring> + </tp:enumvalue> + <tp:enumvalue suffix="SEARCHING" value="2"> + <tp:docstring> + Not registered, searching for new operator to register with. + </tp:docstring> + </tp:enumvalue> + <tp:enumvalue suffix="DENIED" value="3"> + <tp:docstring> + Registration denied. + </tp:docstring> + </tp:enumvalue> + <tp:enumvalue suffix="UNKNOWN" value="4"> + <tp:docstring> + Unknown registration status. + </tp:docstring> + </tp:enumvalue> + <tp:enumvalue suffix="ROAMING" value="5"> + <tp:docstring> + Registered on a roaming network. + </tp:docstring> + </tp:enumvalue> + </tp:enum> + </interface> </node> diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c index 1ca4920c..518b8198 100644 --- a/src/mm-generic-gsm.c +++ b/src/mm-generic-gsm.c @@ -248,43 +248,102 @@ register_manual (MMGsmModem *modem, const char *network_id, MMCallbackInfo *info } } +static void +get_reg_status_done (MMSerial *serial, + int reply_index, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + switch (reply_index) { + case 0: + info->uint_result = (guint32) MM_GSM_MODEM_REG_STATUS_IDLE; + break; + case 1: + info->uint_result = (guint32) MM_GSM_MODEM_REG_STATUS_HOME; + break; + case 2: + info->uint_result = (guint32) MM_GSM_MODEM_REG_STATUS_SEARCHING; + break; + case 3: + info->uint_result = (guint32) MM_GSM_MODEM_REG_STATUS_DENIED; + break; + case 4: + info->uint_result = (guint32) MM_GSM_MODEM_REG_STATUS_ROAMING; + break; + case -1: + info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", + "Reading registration status timed out"); + break; + default: + info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", + "Reading registration status failed"); + break; + } + + mm_callback_info_schedule (info); +} + +static void +get_registration_status (MMGsmModem *modem, MMModemUIntFn callback, gpointer user_data) +{ + MMCallbackInfo *info; + char *responses[] = { "+CREG: 0,0", "+CREG: 0,1", "+CREG: 0,2", "+CREG: 0,3", "+CREG: 0,5", NULL }; + char *terminators[] = { "OK", "ERROR", "ERR", NULL }; + guint id = 0; + + info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); + + if (mm_serial_send_command_string (MM_SERIAL (modem), "AT+CREG?")) + id = mm_serial_wait_for_reply (MM_SERIAL (modem), 60, responses, terminators, + get_reg_status_done, info); + + if (!id) { + info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Reading registration status failed."); + mm_callback_info_schedule (info); + } +} + static gboolean automatic_registration_again (gpointer data) { MMCallbackInfo *info = (MMCallbackInfo *) data; - register_auto (MM_GSM_MODEM (mm_callback_info_get_data (info, "modem")), info); + register_auto (MM_GSM_MODEM (info->modem), info); - mm_callback_info_set_data (info, "modem", NULL, NULL); - return FALSE; } static void -register_auto_done (MMSerial *serial, - int reply_index, +register_auto_done (MMModem *modem, + guint result, + GError *error, gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - switch (reply_index) { - case 0: + if (error) { + info->error = g_error_copy (error); + goto out; + } + + switch (result) { + case MM_GSM_MODEM_REG_STATUS_IDLE: info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Automatic registration failed: not registered and not searching."); break; - case 1: + case MM_GSM_MODEM_REG_STATUS_HOME: g_message ("Registered on Home network"); break; - case 2: - mm_callback_info_set_data (info, "modem", g_object_ref (serial), g_object_unref); - MM_GENERIC_GSM_GET_PRIVATE (serial)->pending_id = g_timeout_add (1000, automatic_registration_again, info); + case MM_GSM_MODEM_REG_STATUS_SEARCHING: + MM_GENERIC_GSM_GET_PRIVATE (modem)->pending_id = g_timeout_add (1000, automatic_registration_again, info); return; break; - case 3: + case MM_GSM_MODEM_REG_STATUS_DENIED: info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Automatic registration failed: registration denied"); break; - case 4: + case MM_GSM_MODEM_REG_STATUS_ROAMING: g_message ("Registered on Roaming network"); break; case -1: @@ -295,24 +354,14 @@ register_auto_done (MMSerial *serial, break; } + out: mm_callback_info_schedule (info); } static void register_auto (MMGsmModem *modem, MMCallbackInfo *info) { - char *responses[] = { "+CREG: 0,0", "+CREG: 0,1", "+CREG: 0,2", "+CREG: 0,3", "+CREG: 0,5", NULL }; - char *terminators[] = { "OK", "ERROR", "ERR", NULL }; - guint id = 0; - - if (mm_serial_send_command_string (MM_SERIAL (modem), "AT+CREG?")) - id = mm_serial_wait_for_reply (MM_SERIAL (modem), 60, responses, terminators, - register_auto_done, info); - - if (!id) { - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Automatic registration failed."); - mm_callback_info_schedule (info); - } + get_registration_status (modem, register_auto_done, info); } static void @@ -331,6 +380,132 @@ do_register (MMGsmModem *modem, register_auto (modem, info); } + +static char * +parse_operator (const char *reply) +{ + char *operator = NULL; + + if (reply && !strncmp (reply, "+COPS: ", 7)) { + /* Got valid reply */ + GRegex *r; + GMatchInfo *match_info; + + reply += 7; + r = g_regex_new ("(\\d),(\\d),\"(.+)\"", G_REGEX_UNGREEDY, 0, NULL); + if (!r) + return NULL; + + g_regex_match (r, reply, 0, &match_info); + if (g_match_info_matches (match_info)) + operator = g_match_info_fetch (match_info, 3); + + g_match_info_free (match_info); + g_regex_unref (r); + } + + return operator; +} + +static void +reg_info_callback_wrapper (MMModem *modem, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMGsmModemRegInfoFn reg_info_fn; + + reg_info_fn = (MMGsmModemRegInfoFn) mm_callback_info_get_data (info, "reg-info-callback"); + reg_info_fn (MM_GSM_MODEM (modem), + GPOINTER_TO_UINT (mm_callback_info_get_data (info, "reg-info-status")), + (char *) mm_callback_info_get_data (info, "reg-info-oper-code"), + (char *) mm_callback_info_get_data (info, "reg-info-oper-name"), + error, + mm_callback_info_get_data (info, "reg-info-data")); +} + +static void +get_reg_name_done (MMSerial *serial, const char *reply, gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + char *oper; + + oper = parse_operator (reply); + if (oper) + mm_callback_info_set_data (info, "reg-info-oper-name", oper, g_free); + else + info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Could not parse operator"); + + mm_callback_info_schedule (info); +} + +static void +get_reg_code_done (MMSerial *serial, const char *reply, gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + char *oper; + + oper = parse_operator (reply); + if (oper) { + char *terminators = "\r\n"; + guint id = 0; + + mm_callback_info_set_data (info, "reg-info-oper-code", oper, g_free); + + if (mm_serial_send_command_string (serial, "AT+COPS=3,0;+COPS?")) + id = mm_serial_get_reply (MM_SERIAL (serial), 5, terminators, get_reg_name_done, info); + + if (!id) + info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Reading operator name failed."); + } else + info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Could not parse operator"); + + if (info->error) + mm_callback_info_schedule (info); +} + +static void +get_reg_info_status_done (MMModem *modem, + guint32 result, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + char *terminators = "\r\n"; + guint id = 0; + + if (!error) { + mm_callback_info_set_data (info, "reg-info-status", GUINT_TO_POINTER (result), NULL); + if (mm_serial_send_command_string (MM_SERIAL (modem), "AT+COPS=3,2;+COPS?")) + id = mm_serial_get_reply (MM_SERIAL (modem), 5, terminators, get_reg_code_done, info); + } + + if (!id) { + if (error) + info->error = g_error_copy (error); + else + info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", + "Reading operator code failed."); + + mm_callback_info_schedule (info); + } +} + +static void +get_registration_info (MMGsmModem *self, + MMGsmModemRegInfoFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + + info = mm_callback_info_new (MM_MODEM (self), reg_info_callback_wrapper, NULL); + info->user_data = info; + mm_callback_info_set_data (info, "reg-info-callback", callback, NULL); + mm_callback_info_set_data (info, "reg-info-data", user_data, NULL); + + get_registration_status (self, get_reg_info_status_done, info); +} + static void connect_done (MMSerial *serial, int reply_index, @@ -623,6 +798,7 @@ gsm_modem_init (MMGsmModem *gsm_modem_class) { gsm_modem_class->set_pin = set_pin; gsm_modem_class->do_register = do_register; + gsm_modem_class->get_registration_info = get_registration_info; gsm_modem_class->set_apn = set_apn; gsm_modem_class->scan = scan; gsm_modem_class->get_signal_quality = get_signal_quality; diff --git a/src/mm-gsm-modem.c b/src/mm-gsm-modem.c index 2972f7d5..f175354a 100644 --- a/src/mm-gsm-modem.c +++ b/src/mm-gsm-modem.c @@ -8,6 +8,7 @@ static void impl_gsm_modem_set_pin (MMGsmModem *modem, const char *pin, DBusGMethodInvocation *context); static void impl_gsm_modem_register (MMGsmModem *modem, const char *network_id, DBusGMethodInvocation *context); +static void impl_gsm_modem_get_reg_info (MMGsmModem *modem, DBusGMethodInvocation *context); static void impl_gsm_modem_scan (MMGsmModem *modem, DBusGMethodInvocation *context); static void impl_gsm_modem_set_apn (MMGsmModem *modem, const char *apn, DBusGMethodInvocation *context); static void impl_gsm_modem_get_signal_quality (MMGsmModem *modem, DBusGMethodInvocation *context); @@ -131,6 +132,65 @@ impl_gsm_modem_register (MMGsmModem *modem, mm_gsm_modem_register (modem, id, async_call_done, context); } +static void +reg_not_supported (MMModem *modem, GError *error, gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMGsmModemRegInfoFn callback = (MMGsmModemRegInfoFn) mm_callback_info_get_data (info, "reg-info-callback"); + + callback (MM_GSM_MODEM (modem), 0, NULL, NULL, error, mm_callback_info_get_data (info, "reg-info-data")); +} + +void +mm_gsm_modem_get_reg_info (MMGsmModem *self, + MMGsmModemRegInfoFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_GSM_MODEM (self)); + g_return_if_fail (callback != NULL); + + if (MM_GSM_MODEM_GET_INTERFACE (self)->get_registration_info) + MM_GSM_MODEM_GET_INTERFACE (self)->get_registration_info (self, callback, user_data); + else { + MMCallbackInfo *info; + + info = mm_callback_info_new (MM_MODEM (self), reg_not_supported, user_data); + info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, + "%s", "Operation not supported"); + + info->user_data = info; + mm_callback_info_set_data (info, "reg-info-callback", callback, NULL); + mm_callback_info_set_data (info, "reg-info-data", user_data, NULL); + + mm_callback_info_schedule (info); + } +} + +static void +get_reg_info_done (MMGsmModem *modem, + NMGsmModemRegStatus status, + const char *oper_code, + const char *oper_name, + GError *error, + gpointer user_data) +{ + DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data; + + if (error) + dbus_g_method_return_error (context, error); + else { + dbus_g_method_return (context, status, + oper_code ? oper_code : "", + oper_name ? oper_name : ""); + } +} + +static void +impl_gsm_modem_get_reg_info (MMGsmModem *modem, DBusGMethodInvocation *context) +{ + mm_gsm_modem_get_reg_info (modem, get_reg_info_done, context); +} + void mm_gsm_modem_scan (MMGsmModem *self, MMGsmModemScanFn callback, diff --git a/src/mm-gsm-modem.h b/src/mm-gsm-modem.h index 45dde07f..1d1ab569 100644 --- a/src/mm-gsm-modem.h +++ b/src/mm-gsm-modem.h @@ -39,6 +39,15 @@ typedef enum { MM_GSM_MODEM_BAND_LAST = MM_GSM_MODEM_BAND_U17IX } MMGsmModemBand; +typedef enum { + MM_GSM_MODEM_REG_STATUS_IDLE = 0, + MM_GSM_MODEM_REG_STATUS_HOME = 1, + MM_GSM_MODEM_REG_STATUS_SEARCHING = 2, + MM_GSM_MODEM_REG_STATUS_DENIED = 3, + MM_GSM_MODEM_REG_STATUS_UNKNOWN = 4, + MM_GSM_MODEM_REG_STATUS_ROAMING = 5 +} NMGsmModemRegStatus; + typedef struct _MMGsmModem MMGsmModem; typedef void (*MMGsmModemScanFn) (MMGsmModem *modem, @@ -46,6 +55,13 @@ typedef void (*MMGsmModemScanFn) (MMGsmModem *modem, GError *error, gpointer user_data); +typedef void (*MMGsmModemRegInfoFn) (MMGsmModem *modem, + NMGsmModemRegStatus status, + const char *oper_code, + const char *oper_name, + GError *error, + gpointer user_data); + struct _MMGsmModem { GTypeInterface g_iface; @@ -61,6 +77,10 @@ struct _MMGsmModem { MMModemFn callback, gpointer user_data); + void (*get_registration_info) (MMGsmModem *self, + MMGsmModemRegInfoFn callback, + gpointer user_data); + void (*scan) (MMGsmModem *self, MMGsmModemScanFn callback, gpointer user_data); @@ -112,6 +132,10 @@ void mm_gsm_modem_register (MMGsmModem *self, MMModemFn callback, gpointer user_data); +void mm_gsm_modem_get_reg_info (MMGsmModem *self, + MMGsmModemRegInfoFn callback, + gpointer user_data); + void mm_gsm_modem_scan (MMGsmModem *self, MMGsmModemScanFn callback, gpointer user_data); |